Starting from the memory space management method of Keil C51, this paper focuses on the reentry problem of real-time operating system in task scheduling, and analyzes some basic ways and methods to solve reentry: analyzing the preemptiveness of task scheduling in real-time operating system, it is proposed that non-preemptive task scheduling is a scheduling method that is more suitable for Keil C51. For this reason, this real-time operating system is constructed, and the heap management method, task establishment and task switching of this system are introduced in a targeted manner.
At present, most product development is based on some small-capacity single-chip microcomputers. The 51 series single-chip microcomputer is one of the most widely used single-chip microcomputer series in China. It has a very broad application environment and prospects. Years of resource accumulation make the 51 series single-chip microcomputer still the first choice for many developers. In response to this situation, many expansion chips based on the 51 core have emerged in recent years, with more and more complete functions and faster speeds, which also shows the vitality of the 51 series single-chip microcomputer in China from one aspect.
For many years, we have been looking for a suitable real-time operating system as the basis for our development. According to the development requirements, we need to integrate some commonly used embedded components to save development time and reduce the development workload as much as possible; in addition, we need this real-time operating system to be easily embedded in small-capacity chips. After all, large systems are few, while small applications are many and widespread. Obviously, μC/OS-II is not suitable for the above requirements, and the RTX Tiny provided by Keil C does not come with source code and is not transparent, not to mention its FULL version.
1 KeiI C51 and the reentrancy problem
When it comes to real-time operating systems, we cannot ignore the reentry problem. For a large memory processor like a PC, this does not seem to be a very troublesome problem. To borrow the words of μC/OS-II RTOS, it is required to use local variables in the reentry function. However, the stack space of the 5l series microcontroller is very small, limited to 256 bytes, and it is impossible to allocate a local heap space for each function. It is for this reason that Keil C51 uses the so-called overlay technology:
① Local variables are stored in the global RAM space (without considering the case of expanding external memory);
②When compiling and linking, the local variables have been located;
③If there is no direct or indirect calling relationship between functions, their local variable spaces can be overwritten.
It is for the above reasons that in the Keil C51 environment, pure functions cannot be re-used without processing (such as adding a simulation stack). So how to make the function re-used in the Keil C51 environment? The following analyzes the basic structure and mode of tasks under the real-time operating system:
vold TaskA(void*ptr){
UINT8 vaL_a;
//Some other variable definitions
do{
//Actual user task processing code
}while(1);
}
void TaskB(void*ptr){
UINT8 vaLb;
//Some other variable definitions
do{
Funcl();
//Other actual user task processing code
)while(1);
void Funcl(){
UlNT8 v al_fa;
//Definition of other variables
//Function processing code
}
In the above code, TaskA and TaskB do not have a direct or indirect calling relationship, so their local variables val_a and val_b can be overwritten, that is, they may be located in the same RAM space. In this way, when TaskA runs for a period of time and changes val_a, TaskB may change val_b when it obtains CPU control and runs. Because they point to the same RAM space, when TaskA regains CPU control, the value of val-a has changed, causing the program to run incorrectly, and vice versa. On the other hand, Funcl() has a direct calling relationship with TaskB, so its local variables val_fa and val_b will not be overwritten, but it cannot guarantee that its local variable val_fa will not form an overwriting relationship with the local variables of TaskA or other tasks.
Defining local variables such as val_a, val_b, and val_fa as static variables (adding the static indicator) can solve this problem. But the problem is that defining a large number of static type variables will lead to a large amount of RAM space being occupied, which may directly lead to insufficient RAM space. Especially in some small-capacity microcontrollers, which generally only have 128 or 256 bytes, a large number of static variable definitions are obviously not suitable under such small RAM resource conditions. Therefore, there is another solution, as shown in the following code:
void TaskC(void){
UINT8 x,v;
whlk(1){
OS_ENTER_CRITICAL();
x=GetX(); (1)
y=GetY(); (2)
// Other codes of the task
OS_EXIT_CRITICAL(); (3)
0SSleep(100); (4)
}
}
In the above code TaskC uses the critical protection method to protect the code from being preempted by interrupts, which effectively solves the problem that the RAM space is too small and it is not suitable to define a large number of static variables. However, if each task adopts this structure, the interrupt will be turned off at the beginning of the task, which will make the real-time performance unable to be guaranteed. It turns out that this delay is quite considerable. To use an example to illustrate, if you want to use a dynamically refreshed LED display in the system, it is difficult to ensure the stability and continuity of the display, even if a separate timer is used in the system to do this work (after entering the critical section, EA=0). Secondly, this structure actually converts the preemptive task scheduling into non-preemptive task scheduling. In fact, if there is no interruption between (3) and (4) and a task scheduling does not happen, it can be understood that the task actively gives up the control of the CPU. If an interrupt happens to occur between (3) and (4) and a task scheduling occurs, it is just an unnecessary task scheduling. And it is not expected that there will be two or more task schedulings after (3). I believe that readers also have this wish.
In addition, we can find a feature of the task: when the task restarts from (1), it does not matter what the values of local variables x and y are, that is, even if x and y change after (3), it is no longer important and will not affect the correctness of the program. In fact, this feature is also a common feature of most tasks, at least most local variables of most tasks - if the task will not give up the CPU control (be preempted) during the entire execution process, then most of its local variables do not need special protection, that is, their scope is only the current execution of the task, and for the above code, it is the code area within the critical protection zone. [page]
2 Should the real-time operating system take precedence?
From the above analysis, if you want to keep a function re-entrant, you have to use static variables, and the system's RAM resources will be a severe test; if you use critical sections to protect the operating environment, the real-time performance of the system cannot be guaranteed, and there is a risk of converting preemptive task scheduling to non-preemptive task scheduling. Obviously, using static variables is simple, but there are more inapplicability, and it is also an obstacle to future function adjustments, so it is generally not adopted. Then, we can only work on environmental protection, but can we really only sacrifice the real-time performance of the system by entering the critical section to ensure that the task is not preempted? Let's take a look at the basic idea of the critical protection method:
① In a task, if local variables are not preemptively switched within their scope, then after the task is deprived of CPU control, these variables will not affect the correct execution of the task regardless of their values;
②Using critical area protection can meet the above requirements;
③ The resulting reduction in real-time performance and preemptive switching is acceptable. It can be seen that not being preempted is the key to task protection of local variables. In this case, why not abandon preemptive task scheduling? This is a good starting point. For Keil C51, non-preemptive task scheduling may be a better method to better coordinate the established resources of the 51 series microcontroller. Write a system like this:
①Use non-preemptive task scheduling;
② It can be used in small-capacity chips. The development goal is that even a small chip like 8051 can use this real-time operating system;
③Support priority scheduling to ensure its real-time performance as much as possible.
3 Implementation of Real-time Operating System
Based on the above analysis and purpose, this operating system was recently completed. The management method of RTx is used on the stack, that is, the current task uses all the heap space
3.1 Stack initialization and task creation
The stack initialization actually initializes the 0STaskStackBotton array, and designates the current task as an idle task, and the next running task as the highest priority task, that is, the task with a priority of zero. During initialization, the value of SP is stored in OSTaskStackBotton[O], the value of SP+2 is stored in OSTaskStacKBotton[1], and so on. The task is created by calling the 0STa-skCreate function. In fact, it just fills the address of the task (assuming it is task number n) into the position pointed to by OSTaskStackBotton[n], and moves SP backward by 2 bytes.
Why use this rule instead of other ways? This is because after the task is created and before the task is scheduled, the stack of each task is actually its own address, so its stack depth is 2, which is directly filled in for the simplicity of the program.
void main(void){
OSInit(); /*Initialize OSTaskStackBcBotton queue*/
TMOD=(TMOD&0XFO)│ 0XOl;
TL0=0xBF;
TH0=0xFC;
TRO=1;
ETO=1;
TFO=O:
OSTaskCreate(TaskA,NULL ,0);
OSTaskCreate(TaskB.NULL,1);
OSTaskCreate(TaskC,NULL,2);
OSStart();
In the above code, after all tasks are created, OSStart() is called to start task scheduling. OSStart() is a macro definition, as shown below:
#deflne OSStart() d0{\
OSTaskCreate(TaskIdle,NULL,OS_MAX_TASKS);\
EA=l:\
return;\
}while(O)
First, it creates an idle task and turns on interrupts, then returns. Where does it return to? We know that the idle task is the lowest priority task. When OSTaskCreate is called to create it, its address will be filled in the SP position, and SP will be moved back 2 bytes (see Figure 2 and description). Therefore, the idle task Taslddle must be at the top of the stack at this time. This means that the return here will definitely return to the idle task. At this point, the system enters normal operation.
3.2 Task Switching
There are two situations for task switching. When the current task priority is lower than the next task to obtain CPU control, the contents between the top of the stack of the next task to obtain CPU control and the top of the stack of the current task are moved to the high end of the RAM space to free up all the RAM space for the next task's stack space. At the same time, the corresponding OSTaskStackBotton is updated to point to the bottom of the stack of the new correct task. If the current task priority is higher than the next task priority, the opposite move is performed.
All tasks must actively call OSSleep to give up control of the CPU. After a task calls OSSleep, it will select the highest priority ready task to run.
Conclusion
After the system is completed, the kernel code size is about 400 bytes, occupying 1 timer interrupt and a small amount of memory space. The system is set to have 8 tasks, and the actual number of tasks available to users is 7, which can meet general needs and also meet the development requirements for application in small-capacity chips. Since preemptive task scheduling is not adopted, except for some local variables of individual tasks related to the whole process, other local variables no longer have an overwriting relationship. Since the task actively gives up the CPU control, it is also easy to process individual variables that need to be protected separately. In the system, there is no need to repeatedly switch interrupts throughout the process, and the real-time performance is also very good. Except for peripherals with strict timing requirements (such as DSl8820).
Previous article:Design and simulation of temperature measurement and display system based on DS18B20
Next article:Problems that should be paid attention to when developing 51 single-chip microcomputer operating system
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- New breakthrough! Ultra-fast memory accelerates Intel Xeon 6-core processors
- New breakthrough! Ultra-fast memory accelerates Intel Xeon 6-core processors
- Consolidating vRAN sites onto a single server helps operators reduce total cost of ownership
- Consolidating vRAN sites onto a single server helps operators reduce total cost of ownership
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Drawing
- EEWORLD University Hall - Open Source Power Linux Kernel Source Code Study and Practical Exercises
- I forgot the principle of the key detection I made. Can you guys help me take a look?
- MSP430F149 implements TB capture to measure the frequency of input signals
- Wireless module cleaning and maintenance tips
- A complete guide to visiting exhibitions | 11.6-8 Shenzhen Annual Chip×Packaging and Testing×Embedded Exhibition is finally here!
- What are the advantages and disadvantages of msp430 compared with stm32?
- netgen -ofmt verilog -sim ramtest.ngc ramtest_synthesis.v under ISE
- Xunwei i.MX6ULL development board ported Gdb+Gdbserver debugging
- MSP430G2553 analog-to-digital converter ADC10