First, download the official ported version from the Micrium website (the compiler uses ARM/Keil, version V2.86, V2.85 has problems).
Download address: http://micrium.com/page/downloads/ports/st/stm32
After decompression, you will get the following folders and files:
Micrium\
AppNotes
Licensing
Software
ReadMe.pdf
AppNotes contains the ucosii porting description file. Of these two files, we only need to care about Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf. Because this file describes the code that needs to be modified in the CM3 kernel porting process for ucosii.
Licensing contains the ucosii license.
There are several folders under Software. In this article's porting, we only need to care about uCOS-II.
CPU: stm32 standard peripheral library
EvalBoards: micrium official evaluation board related code
uc-CPU: ucosii porting code based on micrium official evaluation board
uC-LCD: micrium official evaluation board LCD driver code
uc-LIB: micrium official library code
uCOS-II: ucosii source code
uC-Probe: and uC-Probe related code
ReadMe.pdf will not be mentioned.
OK, now that we have introduced the official stuff, it is time for us to create our own project and start the porting. I have already introduced how to create a project and use the stm32 standard peripheral library in my previous article "Detailed Explanation of Using the Stm32 Standard Peripheral Library". Here, please download the template code ( http://download.csdn.net/source/3448543 ). The porting in this article is based on this project.
Create folders template\src\ucosii, template\src\ucosii\src, template\src\ucosii\port;
copy the files under Micrium\Software\uCOS-II\Source to template\src\ucosii\src;
copy the files under Micrium\Software\uCOS-II\Ports\ARM-Cortex-M3\Generic\RealView to
template\src\ucosii\port;
The code under ucosii\src is the part of ucosii that does not need to be modified, and the code under ucosii\port needs to be modified during porting. To prevent the porting failure caused by the accidental modification of the source code, the code files under ucosii\src can be set to read-only.
Here, based on the basic knowledge of porting introduced in AN-1018.pdf and Porting Detailed Explanation 1 and 2, the code under ucosii\port is explained.
os_cpu.h
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
There is no explanation.
typedef unsigned int OS_STK;
typedef unsigned int OS_CPU_SR;
Because CM3 is 32-bit wide, OS_STK (the data type of the stack) is redefined as unsigned int.
Because the status register (xPSR) of CM3 is 32-bit wide, OS_CPU_SR is redefined as unsigned int. OS_CPU_SR is used to save the CPU status register in OS_CRITICAL_METHOD method 3. In CM3, porting OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() is the most appropriate method 3.
#define OS_CRITICAL_METHOD 3
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
Specifically define the macros OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL(), where OS_CPU_SR_Save() and OS_CPU_SR_Restore() are written in assembly code. The code is in os_cpu_a.asm and will be explained later.
#define OS_STK_GROWTH 1
In CM3, the stack grows from high address to low address, so OS_STK_GROWTH is defined as 1.
#define OS_TASK_SW() OSCtxSw()
defines the task switching macro. OSCtxSw() is written in assembly code. The code is in os_cpu_a.asm and will be explained later.
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void);
void OS_CPU_SysTickHandler(void);
void OS_CPU_SysTickInit(void);
INT32U OS_CPU_SysTickClkFreq(void);
Declare several functions. Note that the last three functions need to be commented out. Why?
OS_CPU_SysTickHandler() is defined in os_cpu_c.c. It is the interrupt handling function of SysTick interrupt. However, stm32f10x_it.c already has the definition of SysTick_Handler() for this interrupt function. It is not needed here. Isn't it strange why the official porting version does this? I will explain it later.
OS_CPU_SysTickInit() is defined in os_cpu_c.c. It is used to initialize the SysTick timer. It depends on OS_CPU_SysTickClkFreq(). We will implement this function ourselves, so comment it out.
OS_CPU_SysTickClkFreq() is defined in BSP.C (Micrium\Software\EvalBoards). BSP.C is not used in this porting. We will implement it ourselves later, so it can be commented out.
os_cpu_c.c
ucosii porting requires us to write 10 fairly simple C functions.
OSInitHookBegin()
OSInitHookEnd()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTaskStkInit()
OSTaskSwHook()
OSTCBInitHook()
OSTimeTickHook()
Except for OSTaskStkInit(), these functions are all hook functions. If these hook functions are not enabled, they will not be used. They are also relatively simple and should be understood by looking at them, so I will not introduce them. Let's
talk about OSTaskStkInit(). Before talking about it, we still have to talk about task switching, because initializing the task stack is for task switching. When the code is running normally, it is executed line by line. How can it run to another task (i.e. function) for execution? First of all, you can recall the interrupt process. When an interrupt occurs, the original function execution location (program counter PC, processor status register and all general registers, i.e. the current code scene) is saved in the stack, and then the interrupt vector is fetched and it runs to the interrupt function for execution. After the execution is completed, if you want to return to the original function execution location, what should you do? Just restore the original function execution information saved in the stack (reassign the code scene saved in the stack to each register of the CPU), and everything will go back, as if nothing happened. Everyone should be familiar with this process. What does task switching have to do with this? Imagine that if there are three functions foo1(), foo2(), and foo3(), they are saved to the stack as if they were just interrupted, and when the interrupt returns, they do something (the role of the scheduler) to return to whichever one they want. Isn't this a function (task) switch? You should understand the role of OSTaskStkInit() by now. It is called by the task creation function, so at the beginning, it is necessary to make the illusion that the task has just been interrupted in the stack. (The principle of task switching is introduced in Section 3.06 of Mr. Shao's book).
So what is the situation in the stack after the interrupt? It is introduced in 9.1.1 of <
Others:
xPSR = 0x01000000L, xPSR T bit (bit 24) is set to 1, otherwise a fault will occur when the task is executed for the first time.
PC must point to the task entry,
R14 = 0xFFFFFFFEL, the lowest 4 bits are E, which is an illegal value. The main purpose is to prevent the use of R14, that is, the task cannot return.
R0 is used to pass the parameters of the task function, so it is equal to p_arg.
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000L; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
/* R14 ( LR) (init value will cause fault if ever used)*/
*(--stk) = (INT32U)0xFFFFFFFEL;
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk ) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
return (stk);
}
Comment out OS_CPU_SysTickHandler() and OS_CPU_SysTickInit().
#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010))
#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014))
#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0 xE000E018))
#define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C))
#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000
#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002
#define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x0 0000001
Comment out the above macro definitions as well, because they are all used for OS_CPU_SysTickHandler(), OS_CPU_SysTickInit().
The file os_cpu_a.asm
contains the code that must be written in assembly.
EXTERN OSRunning; External references
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSTCBCur
EXTERN OSTCBHighRdy
EXTERN OSIntNesting
EXTERN OSIntExit
EXTERN OSTaskSwHook
declares that these variables are defined in other files and are only referenced in this file (some of them do not seem to be referenced, but it does not matter).
EXPORT OS_CPU_SR_Save ; Functions declared in this file
EXPORT OS_CPU_SR_Restore
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OS_CPU_PendSVHandler
Declare that these functions are defined in this file.
NVIC_INT_CTRL EQU 0xE000ED04 ;Address of interrupt control and status register ICSR
NVIC_SYSPRI14 EQU 0xE000ED22 ;Address of PendSV priority register
NVIC_PENDSV_PRI EQU 0xFF ;The priority of PendSV interrupt is 255 (lowest)
NVIC_PENDSVSET EQU 0x10000000 ;Bit 28 is 1
to define several constants, similar to the #define preprocessing instruction in C language.
OS_CPU_SR_Save
MRS R0, PRIMASK ; Read PRIMASK to R0, R0 is the return value
CPSID I ; PRIMASK=1, disable interrupts (NMI and hard fault can be responded to)
BX LR ; Return
OS_CPU_SR_Restore
MSR PRIMASK, R0 ; Read R0 into PRIMASK, R0 is parameter
BX LR ; Return
OSStartHighRdy() is called by OSStart() to start the highest priority task. Of course, the task must have been created before OSStart().
OSStartHighRdy
;Set the priority of PendSV interrupt #1
LDR R0, =NVIC_SYSPRI14 ;R0 = NVIC_SYSPRI14
LDR R1, =NVIC_PENDSV_PRI ;R1 = NVIC_PENDSV_PRI
STRB R1, [R0] ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
;Set PSP to 0 #2
MOVS R0, #0 ;R0 = 0
MSR PSP, R0 ;PSP = R0
;Set OSRunning to TRUE
LDR R0, =OSRunning ;R0 = OSRunning
MOVS R1, #1 ;R1 = 1
STRB R1, [R0] ;OSRunning = 1
;Trigger PendSV interrupt #3
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
CPSIE I ; Enable interrupt
OSStartHang; infinite loop, should not come here
B OSStartHang
#1. The priority of the PendSV interrupt should be the lowest priority. The reason is explained in Section 7.6 of <
#2. PSP is set to 0 to tell the specific task switch program (OS_CPU_PendSVHandler()) that this is the first task switch. After the switch, PSP will not be 0, as you will see later.
#3. Writing 1 to the 28th bit of the interrupt control and status register ICSR (0xE000ED04) can generate a PendSV interrupt. This is explained in <
When a task gives up the right to use the CPU, it will call the OS_TASK_SW() macro, which is OSCtxSw(). OSCtxSw() should do task switching. However, in CM3, all task switching is done in the PendSV interrupt handler, so OSCtxSw() only needs to trigger the PendSV interrupt. OS_TASK_SW() is called by OS_Sched().
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) {
if (OSLockNesting == 0) {
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) {
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++;
OS_TASK_SW(); /* Trigger PendSV interrupt*/
}
}
}
/* Once the interrupt is turned on, the PendSV interrupt function will be executed (of course, after the higher priority interrupt is processed) */
OS_EXIT_CRITICAL();
}
OSCtxSw
;Trigger PendSV interrupt
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
BX LR ;Return
When an interrupt handler exits, OSIntExit() is called to determine if there is a higher priority task to be executed. If there is, OSIntExit() calls OSIntCtxSw() to switch tasks.
OSIntCtxSw
; trigger PendSV interrupt
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
Seeing this, some students may wonder why OSCtxSw() and OSIntCtxSw() are exactly the same. In fact, the meanings of these two functions are different. OSCtxSw() switches between tasks, such as switching from task A to task B because it is waiting for a resource or doing a delay, while OSIntCtxSw() switches from the interrupt state to another task when the interrupt exits. When switching from an interrupt to a task, the work of pushing the CPU registers into the stack has been completed, so there is no need to do it a second time (refer to Section 3.10 of Mr. Shao's book). It's just that due to the special mechanism of CM3, in these two functions, only the PendSV interrupt needs to be triggered, and the specific switching is handled by the PendSV interrupt.
As mentioned before, the real task switching is done in the PendSV interrupt processing function. Since half of the registers of CM3 will be automatically saved to the task stack when an interrupt occurs, it is only necessary to save R4-R11 and adjust the stack pointer in the PendSV interrupt processing function.
The pseudo code of the PendSV interrupt handling function is as follows:
OS_CPU_PendSVHandler()
{
if (PSP != NULL) {
Save R4-R11 onto task stack;
OSTCBCur->OSTCBStkPtr = SP;
}
OSTaskSwHook();
OSPrioCur = OSPrioHighRdy;
OSTCBCur = OSTCBHighRdy;
PSP = OSTCBHighRdy->OSTCBStkPtr;
Restore R4-R11 from new task stack;
Return from exception;
}
OS_CPU_PendSVHandler ;xPSR, PC, LR, R12, R0-R3 have automatically saved
CPSID I ;Interrupts need to be disabled during task switching
MRS R0, PSP ;R0 = PSP
;If PSP == 0, jump to OS_CPU_PendSVHandler_nosave to execute #1
CBZ R0, OS_CPU_PendSVHandler_nosave
;Save R4-R11 to the task stack
SUBS R0, R0, #0x20 ;R0 -= 0x20
STM R0, {R4-R11} ;Save R4-R11 to the task stack
;OSTCBCur->OSTCBStkPtr = SP;
LDR R1, =OSTCBCur ;R1 = &OSTCBCur
LDR R1, [R1] ;R1 = *R1 (R1 = OSTCBCur)
STR R0, [R1] ;*R1 = R0 (*OSTCBCur = SP) #2
OS_CPU_PendSVHandler_nosave
;Call OSTaskSwHook()
PUSH {R14} ;Save R14, because the function will be called later
LDR R0, =OSTaskSwHook ;R0 = &OSTaskSwHook
BLX R0 ;Call OSTaskSwHook()
POP {R14} ;Restore R14
;OSPrioCur = OSPrioHighRdy;
LDR R0, =OSPrioCur ;R0 = &OSPrioCur
LDR R1, =OSPrioHighRdy ;R1 = &OSPrioHighRdy
LDRB R2, [R1] ;R2 = *R1 (R2 = OSPrioHighRdy)
STRB R2, [R0] ;*R0 = R2 (OSPrioCur = OSPrioHighRdy)
;OSTCBCur = OSTCBHighRdy;
LDR R0, =OSTCBCur ;R0 = &OSTCBCur
LDR R1, =OSTCBHighRdy ;R1 = &OSTCBHighRdy
LDR R2, [R1] ;R2 = *R1 (R2 = OSTCBHighRdy)
STR R2, [R0] ;*R0 = R2 (OSTCBCur = OSTCBHighRdy)
LDR R0, [R2] ;R0 = *R2 (R0 = OSTCBHighRdy), now R0 is the SP of the new task
;SP = OSTCBHighRdy->OSTCBStkPtr #3
LDM R0, {R4-R11} ;Restore R4-R11 from task stack SP
ADDS R0, R0, #0x20 ;R0 += 0x20
MSR PSP, R0 ;PSP = R0, load PSP with the SP of the new task
ORR LR, LR, #0x04 ;Make sure LR bit 2 is 1, use process stack #4 after returning
CPSIE I ;Open interrupt
BX LR ;Interrupt return
END
#1 If PSP == 0, it means that the task switch is done for the first time after OSStartHighRdy() is started, and R4-R11 has been saved in the stack when the task is just created, so there is no need to save it again.
#2 OSTCBStkPtr is the first variable of the task control block structure, so *OSTCBCur = SP (not very scientific) means OSTCBCur->OSTCBStkPtr = SP;
#3 Similar to #2.
#4 Because MSP is used in the interrupt handling function, PSP must be used after returning to the task, so LR bit 2 must be 1.
os_dbg.c
is used for system debugging and can be ignored.
The code that needs to be modified is introduced here. If you still don’t understand something, take a look at AN-1018.pdf, Mr. Shao’s book and <
Previous article:Detailed explanation of ucosii transplantation on stm32 2
Next article:Detailed explanation of ucosii transplantation on stm32 5
Recommended ReadingLatest update time:2024-11-16 14:44
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- 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!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- How to use arrays in C language for microcontrollers
- Implementation of booting Linux from TF card on i.MX6UL
- Can you help me transfer the Ambarella A2S HD camera PCB + schematic diagram to PDF?
- In-depth analysis of T-BOX system solution: wireless connection unit
- Water level measurement voice alarm device
- Application of wireless pressure transmitter in the field of Internet of Things!
- I want to know the specific communication process between the external 24C02 EEPROM and the microcontroller. Which forum friend can tell me? Thank you.
- Failed to run Verilog with Cypress PSoC
- The problem of overcurrent capability of two MOS tubes when turned on
- TI Power