After groping experiments in the work, it is concluded that there are three kinds of architectures for the general application of the single-chip microcomputer: 
1. Simple front-back sequential execution program, which is the method used by most people, without the specific architecture of the thinking program, directly through execution Just write the application in sequence. 

2. Time slice polling method, which is a method between sequential execution and the operating system. 

3. Operating system, this method should be the highest level of application writing. 

Let’s talk about the advantages and disadvantages of these three methods and the scope of adaptation. 

First, the sequential execution method 

This method, this application is relatively simple, real-time, parallel requirements are not very high, it is a good method, the program design is simple, the idea is clear. But when the application is more complicated, if there is not a complete flow chart, I am afraid that it is difficult for others to understand the running state of the program, and as the function of the program increases, the brain of the engineer who writes the application becomes confused. This is not conducive to upgrade maintenance, and is not conducive to code optimization. I wrote a few more complicated applications, I just used this method at the beginning, but although I can achieve the function, my thinking has been in a chaotic state. The program has not been able to satisfy itself. 

This method is used by most people, and the education we receive is basically using this method. For those of us MCU engineers who have never learned the data structure and program architecture, it is undoubtedly difficult to improve the design of the application, and it is difficult for the applications written by different engineers to mutually benefit and learn. 

I suggest that if you like to use this method of netizens, if you write a more complex application, you must first clear your mind, design a complete flow chart and then write the program, otherwise the consequences are very serious. Of course, the program itself is very simple, this method is still a very necessary choice. 

The following is a program model that is executed sequentially, which is convenient for comparison with the following two methods: 

code
/************************************************* ************************************* 
* FunctionName : main() 
* Description : Main function 
* EntryParameter : None 
* ReturnValue : None 
******************************************** ******************************************/ 
int main(void) 

    uint8 keyValue; 

    InitSys(); // Initialize 

    while (1) 
    { 
        TaskDisplayClock(); 
        keyValue = TaskKeySan(); 
        switch (keyValue) 
       { 
            case x: TaskDispStatus(); break; 
            … 
            default: break; 
        } 
    } 
}

Second, the time slice polling method 

Time film polling method, mentioned in many books, and many times with the operating system, which means that many times the operating system uses this method. However, the time slice polling method we are going to say here is not to hang under the operating system, but to use this method in the front and back programs. This is also the method to be explained and introduced in detail. 

For the time slice polling method, although many books have been introduced, most of them are not systematic, just mentioning concepts. Below I will introduce this mode in detail, and refer to the method of a time slice polling architecture program established by someone else’s code. I think it will be useful for beginners. 

Here we first introduce the timer’s multiplexing function. 

Use one timer, which can be any timer. There is no special explanation here. If there are 3 tasks below, then we should do the following work: 

1. Initialize the timer, here assume that the timer interrupt is 1ms (of course You can change it to 10ms. This is the same as the operating system. The interrupt is too frequent and the efficiency is low, the interrupt is too long, and the real-time performance is poor. 

2. Define a value: 
Code 
#define TASK_NUM (3) // The number of tasks defined here is 3, indicating that there are three tasks that will use this timer to time. 

Uint16 TaskCount[TASK_NUM] ; // Here three variables are defined for the three tasks to store the timing value 
uint8 TaskMark[TASK_NUM]; // The same corresponds to three flag bits, 0 means the time has not arrived, and 1 means the time is up. 

3. Add in the timer interrupt service function: 
code
/************************************************* ************************************* 
* FunctionName : TimerInterrupt() 
* Description : Timed interrupt service function 
* EntryParameter : None 
* ReturnValue : None 
****************************************** ********************************************/ 
void TimerInterrupt(void) 

    uint8 i; 

    for (i=0; i<TASKS_NUM; i++) 
    { 
        if (TaskCount[i]) 
        { 
              TaskCount[i]–; 
              if (TaskCount[i] == 0) 
              { 
                    TaskMark[i] = 0x01; 
              } 
        } 
   } 
}
Code explanation: The timer interrupt service function judges one by one in the interrupt. If the timing value is 0, it means that the timer is not used or the timer has completed timing, and no processing is required. Otherwise, the timer is decremented by one. When it is known to be zero, the corresponding flag bit value is 1, indicating that the timing value of this task has arrived. 

4. In our application, add the following code where you need the application timing. Let’s take task 1 as an example: 

Code 
TaskCount[0] = 20; // Delay 20ms 
TaskMark[0] = 0x00; // Timer 
to start this task So far we only need to determine whether TaskMark[0] is 0x01 in the task. The other tasks are added the same, and the reuse problem of a timer is achieved. Use the friends you need to try, the effect is good. . . . . . . . . . . 

Through the above multiplexing of a timer, we can see that while waiting for a timing to arrive, we can loop through the flag bits and also perform other functions. 

Loop judgment flag: 
So we can think about it, if the loop judges the flag, is it the same as the sequence execution procedure described above? A large loop, but this delay is more precise than the normal for loop, which can achieve precise delay. 

Execute other functions: 
So if we perform other functions when a function is delayed, make full use of CPU time, is it similar to the operating system? But the task management and switching of the operating system is very complicated.Below we will use this method to architect new applications. 

The architecture of the time slice polling method: 

1. Design a structure: 
code 
// task structure 
typedef struct _TASK_COMPONENTS 
{
    Uint8 Run; // Program run flag: 0 – no run, 1 run 
    uint8 Timer; // timer 
    uint8 ItvTime; // task run interval 
    void (*TaskHook)(void); // task function to run 
} TASK_COMPONENTS // Task Definition 
The design of this structure is very important. One uses four parameters. The comments are very detailed and are not described here. 

2. The task run flag is displayed. This function is equivalent to the interrupt service function. This function needs to be called in the timer’s interrupt service function. It is isolated and ported and understood. 
Code 
/************************************************ ************************************** 
* FunctionName : TaskRemarks() 
* Description : Task flag processing 
* EntryParameter : None 
* ReturnValue : None 
****************************************** ********************************************/ 
void TaskRemarks(void) 

    uint8 i;
    For (i=0; i<TASKS_MAX; i++) // Process time by task 
    { 
         if (TaskComps[i].Timer) // Time is not 0 
        { 
            TaskComps[i].Timer–; // Subtract a beat 
            If (TaskComps[i].Timer == 0) // Time is reduced 
            { 
                 TaskComps[i].Timer = TaskComps[i].ItvTime; // Restore timer value, next time 
                 TaskComps[i].Run = 1 // The task can be run 
            } 
        } 
   } 

Everyone carefully compares the next function, is it the same as the above timed reuse function? 

3. Task Processing: 
Code 
/******************************************* ******************************************* 
* FunctionName : TaskProcess() 
* Description : Task Processing
* EntryParameter : None 
* ReturnValue : None 
****************************************** ********************************************/ 
void TaskProcess(void) 

    uint8 i; 
    for (i=0; i<TASKS_MAX; i++) // Process time by task 
    { 
         if (TaskComps[i].Run) // Time is not 0 
        { 
             TaskComps[i].TaskHook(); // Run the task 
             TaskComps[i].Run = 0; // Flag clear 0 
        } 
    }    

This function is to determine when to perform the task, to achieve the task management operation, the application only needs to call this in the main() function. The function is fine, and there is no need to call and process the task function separately. 

At this point, the architecture of a time-slice polling application is built. Is it very simple to see? This architecture only requires two functions, one structure, and an enumeration variable will be built for the application side. 

Let’s talk about how to apply it. Suppose we have three tasks: clock display, key scan, and work status display.

1. Define a structure variable defined above: 

Code 
/*********************************** ************************************************** * 
* Variable definition                            
********************************************** ****************************************/ 
static TASK_COMPONENTS TaskComps[] = 

    {0 , 60, 60, TaskDisplayClock}, // Display clock 
    {0, 20, 20, TaskKeySan}, // Key scan 
    {0, 30, 30, TaskDispStatus}, // Show work status 
     // Add your task here. . . . 
}; 
When defining variables, we have initialized the values. The initialization of these values ​​is very important, and it has something to do with the specific execution time priority. This needs to be mastered by ourselves. 

1 probably means that we have three tasks, not 1s to execute the following clock display, because our clock minimum unit is 1s, so it is enough to display once after the second change.

2Because the button will be jittery when pressed, and we know that the jitter of the general button is about 20ms, then we generally extend 20ms in the function of sequential execution, and here we scan every 20ms, it is very good, that is The purpose of debounce is achieved, and the key input is not missed. 

3 In order to be able to display other prompts and work interface after the button, we design it once every 30ms. If you feel that the reaction is slow, you can make these values ​​smaller. The latter name is the corresponding function name, you must write this function name in the application and the same three tasks. 

2. Task list: 
Code 
// Task list 
typedef enum _TASK_LIST 

    TAST_DISP_CLOCK, // Display clock 
    TAST_KEY_SAN, // Key scan 
    TASK_DISP_WS, // Work status display 
     // Add your task here. . . . 
     TASKS_MAX // Total number of scheduled tasks available for assignment 
} TASK_LIST; 
Take a good look, the purpose of defining this task list here is actually the value of the parameter TASKS_MAX. Other values ​​have no specific meaning, just for clear surface tasks. Relationship only. 

3. Write a task function: 
code
/************************************************* ************************************* 
* FunctionName : TaskDisplayClock() 
* Description : Display task 
* EntryParameter : None 
* ReturnValue : None 
******************************************** ******************************************/ 
void TaskDisplayClock(void) 


/************************************************* ************************************* 
* FunctionName : TaskKeySan() 
* Description : Scan task 
* EntryParameter : None 
* ReturnValue : None 
******************************************** ******************************************/ 
void TaskKeySan(void) 

}
/************************************************* ************************************* 
* FunctionName : TaskDispStatus() 
* Description : Work status display 
* EntryParameter : None 
* ReturnValue : None 
******************************************* *******************************************/ 
void TaskDispStatus(void) 


// Add other tasks here. . . . . . . . . 

Now you can write tasks according to your needs. 

4. Main function: 
code 
/******************************************* ******************************************* 
* FunctionName : main() 
* Description : Main function 
* EntryParameter : None 
* ReturnValue : None
************************************************** ************************************/ 
int main(void) 

    InitSys(); // Initialize 
    While (1) 
    { 
        TaskProcess(); // Task Processing 
    } 
} At 
this point our time slice polls the application’s architecture and it’s done, you just need to add your own task function where we are prompted. Is it very simple, is there a feeling of operating system inside? 

Do not try to see if the tasks do not interfere with each other? Running in parallel? Of course, it is important to note that when you transfer data between tasks, you need to use global variables. In addition, you need to pay attention to dividing tasks and execution time of tasks. When writing tasks, try to get the tasks to complete as soon as possible. . . . . . . . 

Third, the operating system

The operating system itself is a more complicated thing, the management of the task, the implementation of the ability does not require us to understand. But it is very difficult to transplant. It is very difficult to say that although some people have said, “If you have used the system, you will not be using the pre-background program.” But there aren’t many people who can actually use the operating system, not only because the system itself is complex, but also requires a license (ucos is no exception, if it is commercial). 

I don’t want to introduce the operating system itself too much here, because not one or two sentences can be explained. The following is a list of the programs that should be written under UCOS. You can compare the advantages and disadvantages of each of these three methods. 

Code
/************************************************* ************************************* 
* FunctionName : main() 
* Description : Main function 
* EntryParameter : None 
* ReturnValue : None 
******************************************** ******************************************/ 
int main(void) 

    OSInit (); // Initialize uCOS-II 
    OSTaskCreate((void (*) (void *)) TaskStart, // task pointer 
                (void *) 0, // parameter 
                (OS_STK *) &TaskStartStk[TASK_START_STK_SIZE – 1], // stack Pointer 
                (INT8U) TASK_START_PRIO); // Task priority 
    OSStart(); // Start multitasking environment 

    return (0);

Code 
/*********************************************** *************************************** 
* FunctionName : TaskStart()          
* Description : Task creation , only create tasks, do not complete other work 
* EntryParameter : None 
* ReturnValue : None 
********************************* ************************************************** ***/ 
void TaskStart(void* p_arg) 

    OS_CPU_SysTickInit(); // Initialize the SysTick. 
#if (OS_TASK_STAT_EN > 0) 
    OSStatInit(); // This can measure CPU usage 
#endif 
OSTaskCreate((void (* ) (void *)) TaskLed, // task 1 
                (void *) 0, // without arguments
                (OS_STK *) & TaskLedStk [TASK_LED_STK_SIZE – 1], // stack pointer 
                (INT8U) TASK_LED_PRIO); // priority 
// Here Creating your The Task of 

    the while (. 1) 
    { 
        OSTimeDlyHMSM (0, 0, 0, 100); 
    } 

Summary

is easy to see, the polling time slice method advantage is quite large, i.e., the advantages of the sequential method is performed, the operating system also has the advantage. The structure is clear, simple and very easy to understand.