Task switching mechanism and interrupt scheduling optimization of μC/OS-II

Publisher:ZenMaster123Latest update time:2012-04-17 Source: 维库开发网 Keywords:μCOS-II Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

introduction

In the field of embedded operating systems, μC/OS developed by Jean J. Labrosse once caused a strong response in the field of embedded systems due to its open source code and powerful and stable functions. He has also long been a member of the advisory board of the Embedded Systems Conference (USA).

Whether for beginners or experienced engineers, μC/OS open source approach allows them to know not only what it is, but also why it is. Through in-depth understanding of the internal structure of the system, it is more convenient to develop and debug; and under this condition, it is completely possible to reasonably cut, expand, configure and transplant according to the design requirements. Usually, it often takes a lot of money to buy RTOS, which makes ordinary learners discouraged; while μC/OS is completely free for school research, and only a small amount of copyright fees are required when it is applied to profit-making projects. It is particularly suitable for general users to learn, research and develop. Since the first edition came out in 1992, thousands of developers have successfully applied it to various systems, and its safety and stability have been certified. It has now passed the US FAA certification.

1 Major components of μC/OS-II

μC/OS-II can be roughly divided into five parts: core, task processing, time processing, task synchronization and communication, and CPU transplantation.

The core part (OSCore.c) is the processing core of the operating system, including operating system initialization, operating system operation, interrupt in and out preamble, clock beat, task scheduling, event processing, etc. The parts that can maintain the basic work of the system are all here.

Task processing part (OSTask.c) The contents in the task processing part are closely related to the operation of tasks, including task creation, deletion, suspension, and resumption. Because μC/OS-II is scheduled based on tasks as the basic unit, this part is also very important.

Clock section (OSTime.c) The smallest clock unit in μC/OS-II is timetick (clock beat). Task delay and other operations are completed here.

The task synchronization and communication part is the event processing part, including semaphores, mailboxes, mailbox queues, event flags, etc.; it is mainly used for mutual communication between tasks and access to critical resources.

The interface part with the CPU refers to the porting part of μC/OS-II for the CPU used. Since μC/OS-II is a universal operating system, the implementation of key issues still needs to be ported according to the specific content and requirements of the specific CPU. This part of the content involves system pointers such as SP, so it is usually written in assembly language. It mainly includes the underlying implementation of interrupt-level task switching, the underlying implementation of task-level task switching, the generation and processing of clock beats, and the related processing parts of interrupts.

2 Interrupt processing for MSP430

2.1 Function call and interrupt call operations

The most commonly used C compiler for MSP430 should be IAR Embedd-ed WorkBench. Through analysis and research, it is found that this compiler has the following rules.

(1) Function call

If it is a function-level call, the compiler will first push the current function PC onto the stack when calling the function, then call the function and the PC value will change.

If the called function has parameters, the compiler follows the following rules.

If the two leftmost parameters are not structs or unions, they will be assigned to registers, otherwise they will be pushed onto the stack. The remaining parameters of the function will be pushed onto the stack. Depending on the type of the two leftmost parameters, they will be assigned to R12 (for 32-bit types, they will be assigned to R12:R13) and R14 (for 32-bit types, they will be assigned to R14:R15).

(2) Interrupt call

If the interrupt service subroutine is called during an interrupt, the compiler will push the PC of the current execution statement onto the stack and push the SR onto the stack. Then, depending on the complexity of the interrupt service subroutine, select registers R12 to R15 to push onto the stack. Then, the interrupt service subroutine is executed. After the interrupt processing is completed, the Rx register, SR register, and PC register are popped off the stack. The system is restored to the state before the interruption, so that the program can continue to run after the interruption.

2.2 Task switching steps and principles at task level and interrupt level

(1) Task-level task switching principle

μC/OS-II is a multi-tasking operating system. In the absence of user-defined interrupts, the switching steps between tasks are as follows: Switching between tasks generally calls the OSSched() function. The function structure is as follows:

void OSSched(void){

Disable interrupts

if (not interrupt nesting and the system can be scheduled) {

Identify the highest priority tasks

if (top level task is not current task) {

Call OSCtxSw();

}

}

Enable interrupt

}

We call this function the leading function of task scheduling. It first determines the conditions for task switching. If the conditions allow task scheduling, OSCtxSw() is called. This function is the function that actually implements task scheduling. Since the stack needs to be operated during the process, OSCtxSw() is generally written in assembly language. It pushes the SR register of the CPU of the running task into the stack, and then pushes R4~R15 onto the stack. Then the current SP is saved in TCB->OSTCBStkPtr, and then the value of the highest priority TCB->OSTCBStkPtr is assigned to SP. At this time, SP has already pointed to the task stack of the highest priority task. Then the stack is popped and R15~R4 is popped. Then RETI is used to return, so that SR and PC are popped. Simply put, μC/OS-II switches to the highest priority task, just restoring all the registers of the highest priority task and running the interrupt return instruction (RETI). In fact, what is done is just artificially imitating an interrupt. [page]

(2) Task switching principle at interrupt level

The interrupt service subroutine of μC/OS-II is slightly different from the general foreground and background operations, and often requires the following operations:

Save all CPU registers

Call OSIntEnter() or OSIntNesting++

Open interrupt

Execute user code

Disable interrupts

Call OSIntExit();

Restore all CPU registers

RETI

OSIntEnter() is to add 1 to the global variable OSIntNesting. OSIntNesting is a variable for the number of nested interrupts. μC/OS-II uses it to ensure that no task scheduling is performed when interrupts are nested. After executing the user's code, μC/OS-II calls OSIntExit(), a function very similar to OSSched(). In this function, the system first decrements OSIntNesting by 1, and then determines whether the interrupt is nested. If not, and the current task is not the highest priority task, then find the highest priority task and execute the interrupt task switching function OSIntCtxSw(). Because the stack has been pushed before; in this function, R15~R4 must be popped. Moreover, some registers may have been pushed into the stack when the function was called before. Therefore, the stack pointer must be adjusted so that it can be popped from the correct position.

3 Problems and solutions when using μC/OS-II

Since μC/OS-II will occupy some resources on the microcontroller when it is applied, such as system clock, RAM, Flash or ROM, thus reducing the utilization of resources by the user program. For MSP430, RAM occupation is a particularly prominent problem. For 8- and 16-bit microcontrollers, the on-chip RAM capacity is very small, and the same is true for MSP430 (the largest on-chip RAM is only 2KB, such as MSP430F149). If extended memory is used, it will greatly increase the design difficulty.

Through the analysis of μC/OS-II, we can know that the RAM occupied by μC/OS-II is mainly used for the TCB of each task, the stack of each task, etc. Through further analysis, it is found that the reason why the task stack is large is that the interrupt stack and the task stack are not separated in the hardware design of MSP430. As a result, when applying μC/OS-II, when considering the task stack size of each task, it is necessary not only to calculate the number of local variables and function nesting levels in the task, but also to consider the maximum number of interrupt nesting levels. Because, for the original interrupt processing design of μC/OS-II, the register size and local variable memory size required for stacking in the interrupt nesting process during the interrupt processing need to be calculated in the task stack of each task, and this part of memory needs to be reserved for each task, so a lot of RAM is wasted. From this, it can be seen that the direct way to solve this problem is to separate the interrupt stack from the stack of each task. In this way, when calculating each task stack, it is not necessary to calculate the memory occupied in the interrupt processing (including the interrupt nesting process) into the task stack of each task. It is only necessary to calculate the memory size required by each task itself, thereby improving the utilization of RAM and alleviating the problem of memory shortage.

In this design, the interrupt stack area uses the original system stack area in MSP430. In the foreground and background design, the push and pop operations in the interrupt are completed in the system stack area. Based on the principle of task switching of μC/OS-II, we divide the functions of the task stack and the system stack as follows: when the task generates an interrupt and the task switches during operation, the PC, SR and register Rx are all saved in the task stack of each task; and the push and pop operations generated by interrupt nesting are all placed in the system stack. This division method is designed based on the idea of ​​separating interrupt tasks from ordinary tasks as much as possible.

From the analysis of the default operation of IAR EW above, there are two possible stack structures. One is to design the μC/OS-II task stack as shown in Figure 1. This method puts the compiler's default stack push operation in front, and then pushes the remaining registers into the stack. However, since the compiler pushes an indefinite number of registers into the stack when processing interrupt service routines of varying complexity, it will increase the complexity of the subsequent stack push and pop operations of the remaining registers. Here, we use the method shown in Figure 2 to generate the stack. In this stack, after PC and SR are pushed, the SP pointer is adjusted so that the R4~R15 registers cover the compiler's default stacked registers. In this way, the processing difficulty will be a little less.

For such a design, the CPU must be able to:

◆ There are corresponding CPU registers that can imitate some functions of SP, and corresponding instructions can be used to complete some operations similar to SP;

◆ It is best not to use the register used as SP by the compiler by default during the compilation process. In the IAR compiler, there is an option to avoid using R4 and R5 during the compilation process.

MSP430 can do both of these.

The following is an analysis of several situations that may occur when a running task with a priority level of 6 is interrupted.

1) During the interrupt processing, no higher priority interrupt is generated, that is, interrupt nesting will not occur.

Figure 3 shows the operations performed on the task stack of task priority 6 after an interrupt occurs. After an interrupt occurs, PC and SR are pushed onto the stack by the system②. For the IAR C compiler, some registers are pushed onto the stack by default③ according to the requirements of interrupt service routines of different complexities. Because the stack format we require is as shown in Figure 2, we need to adjust SP to the back of SR④, and then push R4~R15 onto the stack to form the stack format we require⑤.

After pushing the task stack, you can adjust the SP pointer to the system stack, as shown in Figure 4. After pushing, the SP points to the last pushed content①. We assign the value of SP to TCB->OSTCBStkPtr of the priority 6 task so that it can be popped for task scheduling②. Then, adjust SP to the system stack③. During interrupt processing, a push operation may occur, in which case the SP pointer will move accordingly. Since it is now in the interrupt stack, the format of the task stack will not be destroyed.

[page]

Since there is no interrupt nesting, no other interrupt occurs during interrupt processing, so the return step is exactly the opposite of the above-mentioned stacking operation. After the interrupt is processed, SP will automatically return to the SP position ③ in Figure 4. Then, the system will query the task with the highest priority, and then move the SP pointer to the task stack of the task with the highest priority, perform the stacking of R15~R4, and finally use the RETI interrupt return instruction to return to the new task. Because we have defined all task stacks in the same format, there will be no problems between them. It should be noted here that because the system will pop the registers that are pushed by default when the interrupt enters the stack during the interrupt processing of the C compiler, when designing the stacking program, these contents must be pushed first so that they can be popped correctly.

2) During the interrupt processing, other interrupts are generated, resulting in interrupt nesting.

As shown in Figure 5, when processing an interrupt, the SP has been moved to the system stack. Only when the interrupt exits can the SP be moved to the task stack of another task. Therefore, when an interrupt is nested, the interrupt processing is the same as the first time. The difference is that this time the registers saved in the stack are not the registers in the task running, but the registers in the interrupt processing, and they are saved in the system stack instead of the task stack. From this, we can see the effect of optimizing memory. All registers in the interrupt nesting are pushed into the system stack, which greatly reduces the requirement for the task stack memory size.

Because μC/OS-II will set the global variable OSIntNesting++ when entering an interrupt, and OSIntNesting-- when exiting an interrupt. Before exiting an interrupt and switching tasks, μC/OS-II will first determine whether OSIntNesting is 0. If it is 0, it will schedule tasks. When the second interrupt ends and the interrupt nesting is exited, OSIntNesting is not 0, and task scheduling will not be performed. Therefore, the system stack is still popped, and the system will continue the interrupt service program that was not completed before.

Then the order of exiting interrupts is the same as the order of non-interrupt nesting. After the interrupt is processed, SP will automatically return to the SP position ③ in Figure 4. Then, the system will query the task with the highest priority, and then move the SP pointer to the task stack of the task with the highest priority. Pop R15~R4, and finally use the RETI interrupt return instruction to return to the new task.

The interrupt situations are basically the two mentioned above. As for the situation mentioned in some literature that a higher priority task will be scheduled in an interrupt, I think it should not happen. Because from the above analysis, it can be seen that the default (μC/OS-II design idea) interrupt processing will increase and decrease the global variable OSIntNesting at the same time to give the conditions for whether task scheduling is needed. Then even if a higher priority task is ready in the interrupt service program, it will wait until the interrupt exits before scheduling, unless a higher priority task function is directly called in the interrupt. But this method should be contrary to the principles of μC/OS-II, and the previous front-end and back-end design ideas are used.

For such a design, the clock beat processing method must be the same as the general interrupt processing method. Generally speaking, MSP430 uses the WATCHDOG clock interrupt as the source of the clock beat. In essence, the clock beat itself is also an interrupt processing process, so the processing of the clock beat should be the same as other interrupt processing processes. In fact, there may also be interrupt nesting problems in the clock beat processing process.

The program flow of the interrupt stack and task stack separation design is shown in Figure 6.

4. Some suggestions

① When writing interrupt programs, try to use assembly language if possible, because this can avoid some operations performed by the compiler itself and reduce the number of pointer adjustments.

② When programming interrupt services in C, some functions must be implemented by calling assembly functions. When calling functions, sometimes the PC pushed on the stack will destroy the stack structure. At this time, the stack needs to be properly adjusted to ensure the correct stack format.

③ When OSIntExit() is called during interrupt processing, because the SP pointer is sometimes not adjusted in the original design of μC/OS-II, after OSIntExit() returns, it is necessary to determine whether the interrupt is nested. This is because sometimes it is necessary to switch tasks.

Keywords:μCOS-II Reference address:Task switching mechanism and interrupt scheduling optimization of μC/OS-II

Previous article:Development of embedded low power RF/IR conversion controller
Next article:Application of FLASH in MSP430F149 Embedded System

Recommended ReadingLatest update time:2024-11-17 00:51

Design and implementation of a universal driver framework under μC/OS-II
1. Overview Using embedded operating systems in embedded application systems can improve the development efficiency of application systems and enhance the stability and reliability of embedded application systems. Therefore, using embedded operating systems in embedded application systems will become the mainst
[Microcontroller]
Design and implementation of a universal driver framework under μC/OS-II
Design of an Online Sodium Ion Analyzer Based on ATmega128 and μC/OS-II
1 Introduction Water and steam are important working media for boilers, steam turbines, superheaters and other equipment in thermal systems. When a thermal power plant is operating normally, water or steam is flowing in the thermal equipment. The quality of water and steam has specified indicators. Once the water and
[Test Measurement]
Design of an Online Sodium Ion Analyzer Based on ATmega128 and μC/OS-II
Design of VG2 Ethernet and USB Interface Based on μC/OS-II
1 Introduction In recent years, with the rapid development of computer network technology, TCP/IP protocol has become the most widely used network interconnection protocol. USB (Universal Serial Bus) has become a universal interface for data storage and exchange with its advantages of flexibility, convenience, stabl
[Microcontroller]
Design of VG2 Ethernet and USB Interface Based on μC/OS-II
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号