
Limited Time Resource Download:Reply “Tutorial” to get the microcontroller eBook, reply “Simulation” to get Proteus simulation materials, Baidu Disk group share link update time: 2016-05-2,if expired, please leave a message at the end of the article, do not leave a message in the background,you can also search for more resources you want in the background menu “Resource Search”!
In early 2002, I started writing a working program for a prepaid IC card electric meter, which used Philips’s 8-bit 51 extended microcontroller 87LPC764. It required many functions, including display off, load calculation and control, indicator flashing, and querying various parameters of the meter. In short, it needed to use many time units. At that time, I completed the program using ASM51, and the final program size was just over 2KB. Later, for various reasons, this program was not actually used, but was modified for use in an aging device for timing and load calculation. About a year later, I rewrote these codes.
System Improvement: It can be said that the code implemented with ASM51 had no organization at all; whatever function was needed was added, resulting in a very loose program structure. This was actually the reason that led me to ultimately decide to rewrite these codes. Everyone knows that the 87LPC764 has 4KB of Flash ROM, and my program size is only just over 2KB. Therefore, the first idea was to switch to C language as the main development language, which should not lead to insufficient code space. Secondly, considering that there are many modules (or tasks, referred to as tasks hereafter) that require timing functions, it is necessary to manage these tasks in an orderly manner. I considered using a time-slice polling method, giving each task requiring time management an interval. When the interval arrives, its code is executed, achieving the purpose of reasonably using the system timer resources. In the case of the 51 system, generally at least one timer can be used for time-slice polling. Based on the above ideas, the following data type was constructed.
typedef unsigned char uInt8
typedef struct {
void (*proc)( void ); /* Processing function */
uInt8 ms_count; /* Time slice size */
} _op_;
After defining the data structure, the next step is to implement the code, which includes three parts: initializing data, refreshing the time slice, and executing when the time is up.
Initializing data.#define proc_cnt 0x08 // Define the number of processes or tasks
// Task stack initialization
code _op_ Op[proc_cnt] = { { ic_check 10 } { disp_loop 100 }
{ calc_power 150 } { set_led 2 } … };
// Set initial values for time slicesdata uInt8 time_val[proc_cnt]={10,100,150,2,…}; Refresh time slice.
void time_int1( void ) interrupt 3
{
uInt8 cnt;
Time_Counter: = Time_Unit;
for ( cnt = 0; cnt < proc_cnt; cnt++ )
{
time_val[cnt]–;
}
}
Task execution.
void main( void )
{
uInt8 cnt;
init(); /* Program initialization */
interrupt_on(); /* Enable interrupts */
do
{
for ( cnt = 0; cnt < proc_cnt; cnt++ )
{
if ( !time_val[cnt] )
{
time_val[cnt] = Op[cnt].ms_count;
Op[cnt].proc();
}
}
}
while ( 1 );
}
In the above structure definition, proc cannot have parameters. Communication between tasks can define a parameter memory block to exchange data information through a mechanism, such as defining a global variable. For low-capacity microcontroller systems, there are not many tasks that need to do this, and the total task volume will not be too much, so this coordination is not too difficult to handle.
Perhaps everyone has this understanding, that in a real-time system, almost all specific tasks have timing attributes; even processes or tasks that do not require timing do not necessarily need to be queried and refreshed all the time. For example, IC card medium detection is sufficient once per second. Thus, these tasks can also be included in this structure.
In the above program code, considering the RAM limitations of the microcontroller system, tasks cannot be established in RAM like some real-time OS. I established the task stack in code space, so tasks cannot be dynamically added during program execution, thus requiring the task stack to be determined at compile time. Meanwhile, a set of counting flag values time_val is defined to record the time amount during program execution, and it is refreshed in a timer interrupt. By changing the Time_Counter:=Time_Unit; statement in the time slice refresh interrupt process, the granularity of the system time slice refresh can be altered; generally, this value is determined by the system’s minimum time metric value.
At the same time, from the execution flow of the tasks, it can be seen that this system construction does not change the nature of its front-end/back-end system, but rather effectively manages the back-end logical operation sequence. At the same time, if some changes are made to the task execution flow and ensure that tasks with smaller time slices are prioritized, as in the following program.
do
{
for ( cnt = 0; cnt < proc_cnt; cnt++ )
{
if ( !time_val[cnt] )
{
time_val[cnt] = Op[cnt].ms_count;
Op[cnt].proc();
break; /* After execution, re-prioritize scheduling */
}
}
}
while ( 1 );
Then the system becomes a task scheduling system prioritized by execution frequency. Of course, this method must be set very carefully, and attention should be paid to the allocation of time slices. If the time slice is too small, tasks with lower execution frequencies may find it difficult to execute; if there are two tasks with the same time slice, it is even more dangerous, as it may lead to the second task with the same time slice not being executed. Therefore, the allocation of time slices must be reasonable and ensure uniqueness.
Performance Analysis and Task Splitting: The above two task management methods, the first one is scheduled according to the order of the task stack and the size of the time slice, tentatively called flow operation scheduling; while the latter is called frequency priority scheduling. Both methods have their advantages and disadvantages. Flow operation scheduling gives each task equal priority; tasks are called in order as soon as the time slice arrives, and there are no requirements for the order and uniqueness of time slice sizes; the downside is that it may cause tasks with small time slices, i.e., those requiring faster execution, to wait too long. Frequency priority scheduling divides tasks according to their time slice sizes, i.e., execution frequencies, where tasks with smaller time slices have higher execution frequencies and always have higher priority, but the allocation of time slices must be coordinated, otherwise, it may lead to tasks with lower execution frequencies waiting for a long time.
It is particularly important to note that both methods can potentially cause some tasks to wait for a long time. The time set for the time slice cannot be used as an accurate time reference. Depending on the requirements or needs of the system, it may even be necessary to perform some protective work during task execution, such as interrupt masking, etc. Therefore, when planning tasks, attention should be paid. If a task is cumbersome or may take a long time to wait, it should be considered to split the task, breaking a larger task into smaller tasks, dividing a long-lasting task into multiple shorter tasks to collaboratively complete its function. For example, in the case of long wait times, an additional timing task can be added, which sends a message flag when the timing task arrives. If the main process does not detect the message flag, it returns immediately; otherwise, it continues execution. Below is an example code, assuming that this task will wait a long time, now split into two tasks proc1 and proc2 to collaboratively complete the original work, where proc1 executes once every 100 time units, and proc2 executes once every 200 time units.
/* Define two tasks and add them to the task stack. */
code _op_Op[proc_cnt] = { …{ proc1 100 } { proc2 200 } };
data inttime1_Seg; /* Define a global flag */
/* Task implementation */
void proc1( void )
{
if ( time1_Seg )
exit;
else
time1_Seg = const_Time1; /* If the time is up, restore the initial value and */
/* continue executing the following code. */
… /* Actual task execution code */
}
void proc2( void )
{
if ( time1_Seg )
time1_Seg–;
}
As can be seen from the above example, after task splitting, it hardly takes up excessive CPU time, significantly reducing the waiting time of tasks and allowing the CPU enough time for task management and scheduling. It also greatly enhances the structure and readability of the program. Conclusion
Limited Time Resource Download: Reply “Tutorial” to get the microcontroller eBook, reply “Simulation” to get Proteus simulation materials。
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
Click the lower left corner “Read Original” to enterForum for Communication!!!