The task switching functions in ucos are all written in assembly language and belong to the "need to be ported" files.
The assembly file name is generally called: OS_CPU_A.ASM
If you want to understand the principle of task switching, the first difficulty you encounter is the large number of uncommon assembly pseudo-instructions in the assembly file OS_CPU_A.ASM. Understanding these instructions is the first step to understand the principles of the program.
This article will only analyze these assembly instructions.
This file provides 4 API functions for the ucos operating system, namely:
PUBLIC OSStartHighRdy; Function: Switch to the task with the highest horizontal and vertical priority among the ready tasks
PUBLIC OSCtxSw ; Function: general context switch, ContextSwitch, context switch is also called task switch
PUBLIC OSIntCtxSw ; Function: Context switch in interrupt
PUBLIC OSTickISR ; Function: system tick
PULIC is an assembly pseudocode indicating that the declared function can be called by other files
First, let's learn a knowledge point. How to write a function in assembly code so that this function can be called by other files? It is not as simple as adding the PUBLIC keyword. In addition, we must also abide by certain specifications. You can refer to this article, link: click to open the link
If the link is down, just search for the keyword: "Mutual calls between assembly functions and C functions"
In addition to being used by external files to reference their own functions, this file OS_CPU_A.ASM also needs to reference functions and variables of other files, for example:
EXTRN IDATA (OSRunning); declare the variable OSRunning that references the IDATA area
MOV R0,#LOW (OSRunning) ; Use external variables in assembly
EXTRN CODE (_?OSTaskSwHook) ;Declare reference to external function (code), OSTaskSwHook()
LCALL _?OSTaskSwHook ;Call C language function OSTaskSwHook() in assembly
Explanation: The function OSTaskSwHook is written in C language and is named OSTaskSwHook. However, if you want to reference it in assembly, you must add a prefix in front of it. From the article in the previous link, we know that if we reference a reentrant function in assembly, we must add a _? prefix in front of the function name so that it can be recognized by the assembly file. Why do we need to add a prefix? Because when the C51 C language function is converted to assembly, the keil compiler will automatically change the C language function name. Of course, the changes made by keil are regular. For example, when we declare a reentrant C function, keil will automatically add the prefix "_?" to the original function name after converting it to assembly.
What prefix will keil automatically add? What are the specifications for the added prefix? For these questions, you can refer to the keil help file. Click the menu bar -> help -> uVision help in sequence. Search for "Segment Naming Conventions" in the opened help file to view relevant information.
The standard format for writing a function in assembly for calling from C is as follows:
?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE; First declare a relocatable code segment (the usage and explanation of this statement can be referred to another article in this blog)
RSEG ?PR?OSStartHighRdy?OS_CPU_A; for relocation, the following codes will be linked to the segment specified by the RSEG instruction
OSStartHighRdy: ; Address number, used as function name
·········; The function body of the assembly function, until it encounters segment allocation instructions such as CSEG/DSEG/RSEG
Explanation of the above code: ?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE This sentence not only declares a segment name of a relocatable segment, but also the prefix of this segment name is ?PR?, which means that this segment is a function segment (Executable program code)
Let's look at an example of assembling a C language function:
In the main.c file, we define a function like this:
char add_two(char a1, char a2) REENTRANT
{
return a1+a2;
}
This function is as follows after Keil compilation:
85: char add_two(char a1, char a2) REENTRANT
86: {
C:0x269B 90FFFF MOV DPTR,#0xFFFF
C:0x269E 120436 LCALL C?ADDXBP(C:0436)
C:0x26A1 ED MOV A,R5
C:0x26A2 F0 MOVX @DPTR,A
C:0x26A3 90FFFF MOV DPTR,#0xFFFF
C:0x26A6 120436 LCALL C?ADDXBP(C:0436)
C:0x26A9 EF MOV A,R7
C:0x26AA F0 MOVX @DPTR,A
87: return a1+a2;
C:0x26AB 850883 MOV DPH(0x83),?C_XBP(0x08)
C:0x26AE 850982 MOV DPL(0x82),OutTxBuf(0x09)
C:0x26B1 A3 INC DPTR
C:0x26B2 E0 MOVX A,@DPTR
C:0x26B3 FF MOV R7,A
C:0x26B4 850883 MOV DPH(0x83),?C_XBP(0x08)
C:0x26B7 850982 MOV DPL(0x82),OutTxBuf(0x09)
C:0x26BA E0 MOVX A,@DPTR
C:0x26BB 2F ADD A,R7
C:0x26BC FF MOV R7,A
88: }
C:0x26BD 900002 MOV DPTR,#0x0002
C:0x26C0 020436 LJMP C?ADDXBP(C:0436)
From the assembly code, we can see that the function is placed at address 0x269B. Continue to observe the .m51 file (map file) generated by keil, search for add_two, and find the following related content:
(1) CODE 269BH 0028H UNIT ?PR?_?ADD_TWO?MAIN
(2) C:269BH PUBLIC _?add_two
(3) ------- PROC _?ADD_TWO
x:0000H SYMBOL a1
x:0001H SYMBOL a2
C:269BH LINE# 85
C:26ABH LINE# 87
C:26BDH LINE# 88
------- ENDPROC _?ADD_TWO
From the above information, it is found that keil does three things in the process of compiling the add_two() function:
(1) A segment is declared for the add_two() function, starting at 269BH and with a size of 0028H. This segment contains only the code segment of the add_two function, and does not contain other functions or any data segments. The segment name is: ?PR?_?ADD_TWO?MAIN (the segment name consists of three parts: one is the fixed prefix ?PR?, the second is the uppercase ADD_TWO of the function name, and when Keil generates assembly for the function, it automatically adds the prefix _? to the function name of the reentrant function, and the third is the module name. The default module name is the file name where the function is located).
(2) Keil declares the "assembler function generated for add_two()" as PUBLIC so that other files can call the function. However, when declaring the function name, it adds the _? prefix: _?add_two. If an assembly file intends to call the add_two() function, it must be like this:
EXTRN CODE (_?add_two) ;Declare reference to external function (code): add_two()
LCALL _?add_two ; Call the C language function add_two() in assembly
According to Keil's naming convention,
Functions prefixed with _? are reentrant functions.
Additional information: The naming conventions for other types of functions are as follows:
Function without parameters: ?PR?function name?file name
Function with parameters: ?PR?_function name?file name
Reentrant function: ?PR?_?function name?file name
(3) This is the symbol table of the add_two() function, which indicates the storage location of the parameters and local variables in the add_two() function. So here is a question, why are a1 and a2 placed at addresses 00h and 01H? Aren't these two addresses registers R0 and R1? This involves the compilation rules of Keil. When the type and number of parameters are different, the method of passing parameters is different. There are special articles introducing where to put the parameters. Generally speaking, when there are fewer parameters and local variables, all of them are passed and stored in register Rn; when there are more parameters and local variables of non-reentrant functions, when Rn is not enough, the rest are stored in fixed memory addresses. When Rn is not enough for the parameters and local variables of reentrant functions, the rest are placed in the simulation stack.
Previous article:51 MCU IO port expansion
Next article:Snake game based on 51 single chip microcomputer
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Detailed explanation of intelligent car body perception system
- How to solve the problem that the servo drive is not enabled
- Why does the servo drive not power on?
- What point should I connect to when the servo is turned on?
- How to turn on the internal enable of Panasonic servo drive?
- What is the rigidity setting of Panasonic servo drive?
- How to change the inertia ratio of Panasonic servo drive
- What is the inertia ratio of the servo motor?
- Is it better for the motor to have a large or small moment of inertia?
- What is the difference between low inertia and high inertia of servo motors?
- COVID-19 and other challenges to LED design and coping strategies
- purchase
- [Liquid Level Sensor Evaluation] A/D Data Acquisition and Numerical Display
- [Evaluation of EC-01F-Kit, the NB-IoT development board of Anxinke] + Try to connect the serial port assistant to Alibaba Cloud
- [Jihai APM32E103VET6S MINI Development Board Review] Part 4: Key Interrupt
- Technology Popularization: Do you know why base stations are painted in colors?
- Usage of ^ in Verilog
- A brief list of long and short distance wireless communication technologies
- A classic foreign book about semaphores
- If there is a hidden function on the right side, the reading area will be larger.