Summary of the understanding of the microcontroller program framework and some common parts in development

From the university to participate in the electronic design competition to the present, on the road of learning microcontrollers, there have been several years of exploration, sharing some of their experiences and experience.

When learning a single-chip computer, it is often entangled in the application of its various module functions, such as serial port (232, 485) control of various functional ICs, motor control PWM, interrupt application, timer application, human-machine interface application, CAN bus, etc. This is a necessary stage in the learning process and a basic skill. Fortunately, the control around the MCU was very solid when attending the pre-match training of the Electronic Design Competition. After this stage, later contact with different MCUs will find that they are all similar, each has its own advantages. It is easy to learn any new MCU to include some complicated processors. Moreover, the programming control of the MCU will raise a high level of overview - that is, control of various peripherals (if the operation of complex algorithms will use DSP), and the communication between the peripheral and the MCU is generally several timings: IIC , SPI, intel8080, M6800. It seems that programming around the MCU is a very simple thing.

However, this is only a bit of a flaw in embedded development. After contacting a variety of MCUs, contacting complex design requirements, running through the operating system, etc., we will unwittingly return to the bare-metal development of the microcontroller. Considering the architectural issues of the entire programming; a good program architecture is a watershed for experienced engineers and a beginner.

The following is a summary of my understanding of the microcontroller program framework and some common parts of development:

Any time-critical demand is our enemy. When necessary, we only need to increase the hardware cost to eliminate it; for example, if you want 8 digital tubes to display, we must use MCU to dynamically without relevant hardware support. Scanning is done to make it work well; dynamic scanning will more or less prevent the MCU from handling other things. In the case where the MCU is heavily burdened, I will choose to use a similar max8279 peripheral ic to solve this problem;

Fortunately, there are many things that are not time-critical:

For example, the scanning of the keyboard, the rate at which people hit the keyboard is limited, we do not need to scan the keyboard in real time, or even scan every few tens of ms; however, this tens of ms interval, our MCU can also do a lot of thing;

Although the single-chip computer runs on bare metal, often the actual needs determine the posture we must run out of the operating system - multi-tasking program;

For example, a common situation has 4 tasks:

1 keyboard scanning; 2 led digital tube display; 3 serial port data needs to be accepted and processed; 4 serial port needs to send data;

How to construct this MCU program will be our focus; in the era of reading, I will put the keyboard scanning query in the main loop, while the serial port receives the data interrupt, and the corresponding frame format is formed in the interrupt service function. The corresponding flag bit is used to process the data in the loop of the main function, and the serial port sending data and the display of the led are also placed in the main loop; thus the whole program uses the communication mode of the flag variable, and cooperates with each other in the main loop and the background. Execution in the interrupt; however, it must be pointed out that the time slice of each task may be too long, which will result in poor real-time performance of the program. If you add more than one task in this way, making a loop too long, it is possible that the keyboard scan will be very insensitive. So to build a good general programming model, we must find a way to eliminate the time-consuming part of each task and decompose each task again; let's talk about the specific measures for each task:

1 keyboard scan

Keyboard scanning is a common function of single-chip microcomputers. The following points indicate the common obstacles in the keyboard scanning program that seriously hinder the real-time performance of the system. It is well known that the waveform after a key press is like this (assuming low effective):

After a key is pressed, the signal on the data line appears to be jittered for a period of time, then low, and then when the button is released, the signal jitters for a period of time and then goes high. Of course, in the process of the data line being low or high, some narrow interference signals may appear.

Unsigned char kbscan(void){unsigned char sccode,recode;P2=0xf8; if ((P2&0xf8)!=0xf8) {delay(100); //delay 20ms to debounce--------this is too time consuming , very bad if((P2&0xf8)!=0xf8) {sccode=0xfe; while((sccode&0x08)!=0) {P2=sccode; if ((P2&0xf8)!=0xf8) break;sccode=(sccode<<1 )|0x01;} recode=(P2&0xf8)|0x0f; return(sccode&recode); } }return (KEY_NONE);}

Keyboard scanning requires software debounce, which is not controversial. However, the function uses software delay to debounce (ms level delay), which is a big taboo to maintain the real-time performance of the system;

There is also a code to determine the release of the button:

While( kbscan() != KEY_NONE); //Infinite loop waiting

This is very bad. If you press and hold the keyboard, this will cause other tasks in the whole system to be executed. This will be a serious bug.

Someone will handle this like this:

While(kbsan() != KEY_NONE ){Delay(10);If(Num++ > 10)Break;}

That is, if the keyboard is pressed all the time, it will be treated as a valid key. Although this does not cause the other tasks of the whole system to be inoperable, it also greatly reduces the real-time performance of the system because he uses the delay function; we use two effective methods to solve this problem:

1 In the case of simple button function, we still use the above kbscan () function to scan, just delay the software used to debounce, debounce and judge the release of the button with a function to deal with it, it Instead of software delay, use the timing of the timer (using normal timing); the code is as follows

Void ClearKeyFlag(void){KeyDebounceFlg = 0;KeyReleaseFlg = 0;}void ScanKey(void){++KeyDebounceCnt;//Debounce timing (this timing can also be handled in the background timer timing function) KeyCode = kbscan() ;if (KeyCode != KEY_NONE){if (KeyDebounceFlg)// Enter the debounce state flag {if (KeyDebounceCnt > DEBOUNCE_TIME) / / is greater than the debounce specified time {if (KeyCode == KeyOldCode) / / button still If it exists, return the key value {KeyDebounceFlg = 0; KeyReleaseFlg = 1; // release flag return; //Here exit with keycode}ClearKeyFlag(); //KeyCode != KeyOldCode, just dither}}else{if (KeyReleaseFlg = = 0) {KeyOldCode = KeyCode; KeyDebounceFlg = 1; KeyDebounceCnt = 0;}else{if (KeyCode != KeyOldCode)ClearKeyFlag();}}}else{ClearKeyFlag();//Clear the flag without a button}KeyCode = KEY_NONE; }

In the case of complicated button conditions, such as long buttons, key combinations, and keys, and other complex functions, we tend to use the state machine to achieve keyboard scanning;

//

The avr MCU 4*3 scan state machine implements char read_keyboard_FUN2() { static char key_state = 0, key_value, key_line, key_time; char key_return = No_key,i; switch (key_state) { case 0: //the initial state, proceed 3 *4 keyboard scan key_line = 0b00001000; for (i=1; i<=4; i++) // scan keyboard { PORTD = ~key_line; // output line level PORTD = ~key_line; // must be sent 2 times ! ! ! (Note 1) key_value = Key_mask & PIND; // Read column level if (key_value == Key_mask) key_line <<= 1; // No button, continue to scan else { key_state++; // There is a button to stop scanning break; / Turn the debounce confirmation state} } break; case 1: // This state to determine if the button is caused by jitter if (key_value == (Key_mask & PIND)) // Read the column level again, {key_state++; // turn Into wait button release state key_time=0;} else key_state--; // Two column levels are different to return state 0, (debounce processing) break; case 2: // Wait for button release state PORTD = 0b00000111; // Line all output low PORTD = 0b00000111; // Repeat once if ( (Key_mask & PIND) == Key_mask) {key_state=0; // The column lines are all high. Return status 0 key_return= (key_line | key_value) ;/ / got the key value } else if (++key_time>=100) / / if not released for a long time {key_time = 0; key_state = 3; / / enter the key state key_return = (key_line | key_value); } break ; case 3:// For the key, the key value is obtained every 50ms, the windows xp system is doing this PORTD = 0b00000111; // Lines all output low PORTD = 0b00000111; // Repeat once if ( (Key_mask & PIND) == Key_mask) key_state=0; // Column lines are all high return status 0 else if(++ Key_time>=5) //The button for each combo every 50MS {key_time=0;key_return= (key_line | key_value);} break; }return key_return;

The above four states are used. The general keyboard scan only uses the first three states, and the latter state is designed to increase the "connect key" function. Connect key—that is, if you press a key, it responds quickly to the key value multiple times until it is released. The keyboard scan function can be executed once every 10ms in the main loop; we set the time limit to 10ms, of course, the requirements are not strict.

2 digital tube display

In general, the eight-in-one digital tube we use uses dynamic scanning to complete the display; it is very gratifying that the human eye can't find it when it flashes above 50hz. Therefore, we have ample time to dynamically scan the digital tube. Here we set the time limit to 4ms (250HZ), use the timer to be 2ms, scan the display in the timer interrupt program, only one of them is displayed at a time; of course, the time limit can also be lengthened, the more recommended method is Put the display function into the main loop, and set the corresponding flag bit in the timer interrupt;

// Timer 0 compare match interrupt service, 4ms timing interrupt [TIM0_COMP] void timer0_comp_isr(void) { display(); // Call LED scan display........................}void display(void) // 8-bit LED Digital tube dynamic scanning function { PORTC = 0xff; // It is necessary to turn off the segment selection here, otherwise the digital tube will produce a smear PORTA = led_7[dis_buff[posit]]; PORTC = position[posit]; if (+ +posit >=8 ) posit = 0; }

Summary of the understanding of the microcontroller program framework and some common parts in development

3 serial port receiving data frame

When the serial port is received with interrupt mode, this is understandable. But if you try to complete the reception of one frame of data in the interrupt service routine, it will be a big problem. Always remember that the shorter the interrupt service function, the better, otherwise it will affect the real-time performance of this program. A data frame usually consists of several bytes. We need to judge whether a frame is completed and whether the verification is correct. In this process, we can't use software delay, and we can't wait in an infinite loop;

So in the serial port receive interrupt function, we just put the data in a buffer queue. As for the composition of the frame, and the work of checking the frame, we solve it in the main loop, and we only process one data in each loop. The processing interval of each byte of data is more flexible because we have already cached it in the queue.

/*=========================================================================================== Time event description: put every 10ms in the big loop output: none input: none================================= ==========*/void UARTimeEvent(void){if (TxTimer != 0)//The time to wait for the transmission to decrement--TxTimer;if (++RxTimer > RX_FRAME_RESET) //RxCnt = 0 // If the timeout is accepted (ie, an incomplete frame or a frame is received), the received incomplete frame is overwritten}/*====================== ===================== Function: Serial Receive Interrupt Description: Receive a data, store it in cache output: none input: none========= ==================================================================================================================================== Status = UCSRA;data = UDR;if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0){RxBuf[RxBufWrIdx] = data;if (++RxBufWrIdx == RX_BUFFER_SIZE) //Receive data in buffer RxBufWrIdx = 0; if (++RxBufCnt == RX_BUFFER_SIZE){RxBufCnt = 0;//RxBufferOvf=1;}}}/*======================= ==================== Function: Serial port Data frame description: When non-zero output, receive one frame of data and execute output in large loop: ==0: no data frame!=0: data frame command word input: none========= ============================================================================================================================= INT8U INT8U ret; ret = RX_NULL; if (RxBufCnt != 0){RxTimer = 0; // Clear the count time, UARTimeEvent() does the processing of abandoning the entire frame for the receive timeout //Display();cnt = RxCnt ;dat = RxBuf[RxBufRdIdx]; // Get Charif (++RxBufRdIdx == RX_BUFFER_SIZE) RxBufRdIdx = 0; Cli();--RxBufCnt;Sei();FrameBuf[cnt++] = dat;if (cnt >= FRAME_LEN) // compose a frame {sum = 0; for (cnt = 0;cnt < (FRAME_LEN - 1);cnt++)sum+= FrameBuf[cnt];if (sum == dat)ret = FrameBuf[0];cnt = 0 ;}RxCnt = cnt;}return ret;}

The above code ChkRxFrame() can be placed in the serial port receiving data processing function RxProcess() and then executed in the main loop. The above uses a timing variable RxTimer, which is a subtle solution to the abandon frame processing of the receive frame timeout. It does not use any wait, and the main loop only receives one byte of data at a time, which is very short. We began to structure the framework of the entire system:

We choose a system that is not commonly used by TIMER to generate the system benchmark beat required by the system. Here we use 4ms; in meg8 our code is as follows:

// Timer 0 overflow interrupt service routineinterrupt [TIM0_OVF] void timer0_ovf_isr(void){// Reinitialize Timer 0 valueTCNT0=0x83;// Place your code hereif ((++Time1ms & 0x03) == 0)TimeIntFlg = 1;}

Then we design a TimeEvent() function to call some functions that need to be called cyclically at the specified frequency. For example, we will feed the dog and the digital tube dynamic scan display every 4ms. Every 1s, we will call the LED flashing program. We perform a keyboard scanning program every 20ms;

Void TimeEvent (void){if (TimeIntFlg){TimeIntFlg = 0; ClearWatchDog();display(); // In the 4ms event, call the LED scan display, and feed the dog if (++Time4ms > 5){Time4ms = 0 ;TimeEvent20ms();//In the 20ms event, we process the keyboard scan read_keyboard_FUN2() if (++Time100ms > 10){Time100ms = 0;TimeEvent1Hz();// In the 1s event, we make the work indicator flash} }UARTimeEvent();//Serial data reception event, processed in 4ms event}}

Obviously the whole idea has been very clear, and the loop events that the CPU needs to handle can be easily added to the function according to its requirements for time. However, we have requirements for this event: the execution speed is fast, short, and there is no long delay waiting. The execution time of all events must be less than 4ms of the system's reference time slice (the system reference beat can be increased as needed).

So our keyboard scanner, digital tube display program, serial port receiving program are as I showed earlier. If you have to use a long delay (such as the delay used in the analog IIc timing) we designed such a delay function: void RunTime250Hz (INT8U delay) / / this delay function unit is 4ms (system Benchbeat beat)

{while (delay){if (TimeIntFlg){--delay;TimeEvent();}TxProcess();RxProcess(); }}

We need to delay the time = delay * system remembers the beat 4ms, this function ensures that while the delay, our other events (keyboard scan, led display, etc.) have not been delayed; well, our main function Main() will be short:

Void main (voie){Init_all();while (1){ TimeEvent(); //Processing for loop events RxProcess(); //Serial for processing received data TxProcess();// Serial port send data processing}}

Overall, our system has become a nearly omnipotent template. According to the cpu of your choice, you can select a timer and add your own event function. It is very flexible and convenient, and the general MCU can be competent. The template can be fixed.

The whole system takes the global mark as the main line, and the system is not scattered. The system is relatively small, but it sacrifices a Timer. It is very suitable in the single-chip microcomputer with lack of resources. I once saw a template of a user's "single-chip practical system". Taking 51 as an example, the overall idea is similar to this one, but he writes more compact and very appreciative; but personally feel that the code overhead is larger, and the same is used. However, because the system uses the global flag as the driving event, the comparison feels messy. The global is best to make comments, but it should pay attention to some invisible function recursion. Don't recurs too deeply. (Some microcontrollers do not support it.) ).

Energy 5000 Puffs

Energy 5000 Puffs,Rechargeable Vape 5000 Puffs,Energy Drink Disposable Vape,Energy 5000Puffs Disposable Vape

Nanning Nuoxin Technology Co., LTD , https://www.nx-vapes.com

Posted on