MCU interrupt Division

Source: Internet
Author: User

Because of internal resource restrictions, MCU software design has its own particularity. Generally, the program does not have complex algorithms and data structures, and the code volume is not large. Generally, the operating system (OS) is not used ), for an MCU with only a few k rom and more than one hundred bytes of RAM, a simple OS also consumes most of the resources.

 

For a system without OS, the popular design is the interruption of the main program (main loop) + (timed). Although this structure conforms to the natural idea, it has many disadvantages, the first is that the interruption can occur anywhere in the main program. Second, the coupling (correlation) between the master program and the interrupt is large. This practice binds the winner program with the interrupt and must be carefully handled to prevent unexpected events.

 

In another way, what if I put all the main programs into (Scheduled) interruptions? In doing so, we can see at least a few benefits immediately: the system can be in low-power sleep state and wake up from the interruption into the main program; if the program runs, the interruption can be pulled back; there is no distinction between master and slave (other interruptions are counted separately), and the program is easy to modularize.

 

(Digression: There is no saying that this method will not feed the dog, nor will it interrupt the debate about whether it should be as short as possible)

 

In order to put all the main program into (Scheduled) interruptions, the program must be divided into modules, that is, tasks. Each task completes a specific function, such as scanning the keyboard and checking the buttons. Set a reasonable time base (tick), such as 5, 10 or 20 MS, with scheduled interruptions at each time and run all the tasks again to reduce complexity, generally, dynamic scheduling is not performed (a fixed array is used at most to simplify the design, and dynamic scheduling is close to the OS). This is actually a variant of round robin of time slices without priority. Let's take a look at the composition of the main program:

Void main ()

{

.... // Initialize

While (true ){

Idle; // sleep

}

}

 

Here, the idle is a sleep command that enables the MCU to enter the low-power mode. Composition of interrupt programs

 

Void timer_interrupt ()

{

Settimer ();

Resetstack ();

Enable_timer_interrupt;

....

 

 

After the interruption occurs, reset the timer first. This is mainly for the 8051,805-1 auto-Reload divider, which only has 8-bit, and it is difficult to perform timing for a long time; reset the stack, that is, the stack pointer is assigned to the top or bottom of the stack (this is not necessary for MCU using cyclic stacks such as PIC and ti dsp) to indicate a break from the past, in addition, you are not prepared to return to the central breakpoint to ensure that the body of the program in the stack is not retained. Enable_timer_interrupt is mainly for 8051. 8051 as interrupt control is weak, there are only two levels of Interrupt priority, and if the interrupt program does not use reti to return, it cannot respond to the same level of interrupt, so for 8051, one reti must be called to open the interrupt:

 

_ Enable_timer_interrupt:

Acall _ RETI

_ Reti: RETI

The following is the execution of the task. There are several methods. The first method is to adopt a fixed sequence. Because the complexity of the MCU program is not high, this method can be used in most cases:

 

...

Enable_timer_interrupt;

Processkey ();

Runtask2 ();

...

Runtaskn ();

While (1) idle;

 

You can see that the interruption calls all the tasks again. The programmer controls whether the tasks need to be run. Another method is to use the function pointer array:

 

# Define countofarray (x) (sizeof (X)/sizeof (X [0])

Typedef void (* functionptr )();

 

Const functionptr [] tasks = {

Processkey,

Runtask2,

...

Runtaskn

};

 

Void timer_interrupt ()

{

Settimer ();

Resetstack ();

Enable_timer_interrupt;

For (I = 0; I <countofarray (tasks), I ++)

(* Tasks [I]) ();

While (1) idle;

}

 

 

Using const is to place the array content in code segment (ROM) instead of Data Segment (RAM), and using code as a substitute for const in 8051.

 

(Question: the question about whether the address operator is required when the function pointer is assigned a value is the same as the array name, depending on compiler. for those familiar with assembly, the function name and array name are both constant addresses and cannot be obtained without any need. For those who are not familiar with the Assembly, it is a matter of course to use & retrieve the address. Visual c ++ 2005 supports both of them)

 

This method is hashed in the Assembly. A small trick is to use the stack to obtain the jump table entry:

MoV A, state

Acall multijump

Ajmp state0

Ajmp state1

...

 

Multijump: Pop DPH

Pop DPL

RL

JMP @ A + dptr

 

 

Another way is to put the function pointer array (dynamic array, better linked list, but not applicable in MCU) in data segment, so that you can modify the function pointer to run different tasks, this is close to dynamic scheduling:

 

Functionptr [countoftasks] tasks;

 

Tasks [0] = processkey;

Tasks [0] = runtaskm;

Tasks [0] = NULL;

 

...

Functionptr pfunc;

For (I = 0; I <countoftasks; I ++ ){

Pfunc = tasks [I]);

If (pfunc! = NULL)

(* Pfunc )();

}

 

 

Through the above means, an interrupt-driven framework is formed. The following is to ensure that the total running time of all tasks in each tick cannot exceed the time of one tick. To achieve this, each task must be divided into time slices and each tick must run one piece. State machine is introduced here to achieve splitting. I have introduced the state machine in many books. I will not talk about it here.

 

(Digression: Practice promotes theory, and then applies theory to practice. I did not know for a long time that the method I have been using is state machine, until I learned UML/C ++. The book introduces tachniques for identifying dynamic behvior. Kung fu mastering C ++ and even C # Java in addition to poetry will be of great help to understand embedded program design)

 

The Program Implementation of the state machine is quite simple. The first method is to use swich-case:

 

Void runtaskn ()

{

Switch (state ){

Case 0: state0 (); break;

Case 1: state1 (); break;

...

Case M: STATEM (); break;

Default:

State = 0;

}

}

 

Another method is to use a more general and concise array of function pointers:

Const functionptr [] States = {state0, state1 ,..., STATEM };

 

Void runtaskn ()

{

(* States [State]) ();

}

 

The following is an example of state machine control:

 

Void state0 (){}

Void state1 () {state ++;} // next state;

Void state2 () {state + = 2;} // go to state 4;

Void state3 () {state --;} // go to previous State;

Void State4 () {delay = 100; State ++ ;}

Void state5 () {delay --; If (delay <= 0) State ++;} // delay 100 * tick

Void state6 () {state = 0;} // go to the first State

 

One trick is to set the first status state0 to an empty status, that is:

 

Void state0 (){}

 

In this way, state = 0 can stop the entire task. If you need to put it into operation, simply set state = 1.

 

The following is an example of a Keyboard Scan, where assume that tick = 20 MS, scankeyboard () function controls the output of the port line scan, and detects that the input is converted to the keycode, dejitters at intervals of 20 MS between each State.

 

Enum enumkey {

Enumkey_nokey = 0,

...

};

Struct structkey {

Int keyValue;

Bool keypressed;

};

 

Struct structkeyprocess key;

 

Void processkey () {(* States [State]) ();}

 

Void state0 (){}

Void state1 () {key. keypressed = false; State ++ ;}

Void state2 () {If (scankey ()! = Enumkey_nokey) State ++;} // next State if a key pressed

Void state3 ()

{// Debouncing state

Key. keyValue = scankey ();

If (key. keyValue = enumkey_nokey)

State --;

Else {

Key. keypressed = true;

State ++;

}

}

Void State4 () {If (scankey () = enumkey_nokey) State ++;} // next state if the key released

Void state5 () {scankey () = enumkey_nokey? State = 1: State --;}

 

 

The above keyboard processing process is obviously simpler and clearer than the program that uses the sign to shake, and there is no trouble of software delay to shake. Similarly, each task can be divided into several States, each of which actually takes a small amount of processing time. Some tasks can be divided into several subtasks, and each subtask is divided into several States.

 

(Subject: for constant types, we recommend that you use Enum to classify and organize them to avoid using a large number of # define to define constants)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.