An operating system is a system software used to manage system hardware, software and data resources, control program execution, and provide support for other application software. According to different types, it can be divided into real-time operating systems, desktop operating systems, server operating systems, etc. For some small applications that have high requirements for system real-time performance and limited hardware resources, it is necessary to avoid using complex and large operating systems (such as Linux) as much as possible. Using a small real-time operating system (such as uCOS) can better meet the needs of the application. Here, the author gives a brief introduction to the transplantation of uCOS-II.
1. Code preparation
uCOS-II V2.91 source code, this version of source code is the latest version of uCOS-II, please download this version of source code from Micrium official website or other websites, of course, other versions of uCOS-II are transplanted in the same way. Micrium official website also provides some CPU transplantation examples for reference, here is to download the source code and transplant step by step.
s3c2416 startup code project, the startup code is the code that must be run before the s3c2416/50/51 series arm9 chips run the user c code main function. The startup code supports sd and Nand startup, sets the system clock for the user, initializes the memory, automatically identifies the startup device and moves the code to RAM, MMU mapping, interrupt management, etc. Users only need to focus on developing other functional functions with c. The author has a very detailed introduction to the startup code and the implementation process of the startup code in the previous chapter. Here, we will use the transplantation of uCOS under GCC as an example. Download the startup code source code in "GCC startup code project application example". If you develop under MDK, download the startup code source code in "MDK startup code project application example".
User code, all functional codes developed in C, where the entry point of user code is the main() function, where any running code of uCOS is implemented.
2. Project construction
Create a new uCOS project directory in any path under the Linux operating system, and create a new uCOS-II directory in this directory to store uCOS related parts. Download the uCOS-II V2.91 source code and decompress it, copy the entire Source directory to the uCOS-II directory, and create a new Cfg directory in the directory to store uCOS configuration files, and a new Ports directory to store uCOS transplant interface files.
Copy the startup code directory start_code to the UCGUI directory. This part of the code does not need any modification. And keep the Makefile files. The project management Makefile under the GCC startup code is extracted from uboot, which can easily add source code and code directory.
Create a new apps directory under the UCGUI directory to store application-related source code.
The final UCGUI directory contents are as follows:
uCOS/start_code, save the relevant part of s3c2416 startup code
uCOS/app, save the application part of a project
uCOS/uCOS-II/Cfg, save the configuration part of uCOS
uCOS/uCOS-II/Ports, save the uCOS porting part
uCOS/uCOS-II/Source, saves the source code of uCOS, usually directly replaces the source code of a higher version
3. uCOS transplant
When uCOS-II is used on different CPUs, it is necessary to modify and write the three files os_cpu.h, os_cpu_a.s, and os_cpu_c.c in the uCOS-II/Ports directory.
3.1. Writing os_cpu.h
3.1.1. External Declarations
uCOS-II uses OS_CPU_GLOBALS and OS_CPU_EXT to declare external variables and symbols. This part is as follows:
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
3.1.2. Data Type Definition
In order to ensure the portability of uC/OS-II, a series of type definitions are declared in os_cpu.h. These types are independent of C data types such as int, short, long, etc. The data type definitions are as follows:
typedef unsigned char BOOLEAN; /* Boolean variable */
typedef unsigned char INT8U; /* Unsigned 8-bit integer variable*/
typedef signed char INT8S; /* Signed 8-bit integer variable*/
typedef unsigned short INT16U; /* Unsigned 16-bit integer variable*/
typedef signed short INT16S; /* Signed 16-bit integer variable*/
typedef unsigned int INT32U; /* Unsigned 32-bit integer variable*/
typedef signed int INT32S; /* Signed 32-bit integer variable*/
typedef float FP32; /* Single-precision floating point number (32-bit length) */
typedef double FP64; /* Double-precision floating point number (64-bit length) */
3.1.3. Stack Configuration
uCOS-II is suitable for 8-bit, 16-bit, and 32-bit CPUs. CPUs with different word lengths have different stack word lengths. uCOS-II uses OS_STK to represent the stack type. At the same time, the stack can grow from high address to low address, or from low address to high address. For arm architecture CPUs, the stack can grow downward or upward. However, for each compiler, it is agreed that the stack grows from high address to low address, and the stack word length is 32 bits. The stack configuration content is as follows:
typedef INT32U OS_STK; /* The stack is 32 bits wide*/
#define OS_STK_GROWTH 1 /* The stack grows from high to low*/
3.1.4. Critical Section Access
For preemptive operating systems, there is a small section of critical code that must be accessed exclusively. If a task (thread) is accessing the critical code, other tasks (threads) can no longer enter the code until the task (thread) with access rights exits the critical section. When accessing the kernel critical section, uCOS-II prohibits task preemption through the two macro switch interrupts OS_ENTER_CRITICAL()/OS_EXIT_CRITICAL() to ensure that the critical section is not destroyed. Usually, there are three ways to access the critical section. One is to directly switch the interrupt, the second is to save/restore the interrupt status from the stack and then switch the interrupt, and the third is to save/restore the interrupt status from the local variable and then switch the interrupt. uCOS-II uses the third switch interrupt method, which requires the implementation of the state save and restore switch interrupt CPU_SR_Save()/CPU_SR_Restore(). A variable of type OS_CPU_SR needs to be introduced to save the CPU interrupt status. The critical section interrupt access content is as follows:
#define OS_CRITICAL_METHOD 3 /*Local variables save/restore state and then turn on/off interrupts*/
typedef INT32U OS_CPU_SR; /*Used to save/restore interrupt status before switching interrupts*/
#define OS_ENTER_CRITICAL() {cpu_sr = CPU_SR_Save ();} /* Disable interrupt */
#define OS_EXIT_CRITICAL() {CPU_SR_Restore (cpu_sr);} /* Enable interrupt */
3.1.5. Function Declaration
uCOS-II needs to be assembled to implement switch interrupts, task switching and other architecture-related functions, which are implemented in the assembly file os_cpu_a.s. The header file declares the functions, which include the following functions:
#define OS_TASK_SW() OSCtxSw() /* Task-level task switching function*/
OS_CPU_SR CPU_SR_Save(void);
void CPU_SR_Restore(OS_CPU_SR cpu_sr);
void OSStartHighRdy(void);
void OSCtxSw(void);
void OSIntCtxSw(void);
3.2. Writing os_cpu_a.s
High-level languages cannot implement save/restore registers, so uCOS-II needs to write assembly to implement six simple functions: CPU_SR_Save(), CPU_SR_Restore(), OSStartHighRdy(), OSCtxSw(), OSIntCtxSw(), and IRQ_SaveContext().
3.2.1. CPU_SR_Save() function
Since the interrupt status is saved/restored from local variables and then the interrupt is turned on and off, R0 is used to return the interrupt status and turn off the interrupt. This function is a macro implementation of OS_ENTER_CRITICAL().
.globl CPU_SR_Save
CPU_SR_Save:
MRS R0, CPSR
ORR R1, R0, #0xC0 // Set IRQ and FIQ to disable interrupts
MSR CPSR_c, R1
BX LR // Disable interrupts and return interrupt status to R0
3.2.2. CPU_SR_Restore() function
After accessing the critical section, the interrupt status before disabling interrupts needs to be restored. This function is a macro implementation of OS_EXIT_CRITICAL().
.globl CPU_SR_Restore
CPU_SR_Restore:
MSR CPSR_c, R0
BX LR
3.2.3. OSStartHighRdy() function
When the user starts the uCOS kernel for management through OSStart(), OSStart() will first call OSStartHighRdy() to run the task with the highest priority among the created tasks. OSStartHighRdy() needs to complete the following tasks:
(1) Disable interrupts and switch to management mode. All tasks work in management mode.
(2) Call the task switching hook function, that is, first call the OSTaskSwHook() function
(3) Mark that the uCOS-II kernel has started running, OSRunning = 1
(4) Get the highest priority task TCB, get the task stack pointer, and switch the SP to the task stack
(5) Pop the task stack in SP, including the task status register CPSR, R0-R12, LR, and continue executing the task.
#define I_Bit 0x80 // IRQ interrupt disable bit
#define F_Bit 0x40 // FIQ interrupt disable bit
#define Mode_SVC 0x13 // Management mode
#define Mode_SYS 0x1f // System mode
.extern OSTaskSwHook
.extern OSRunning
.extern OSTCBHighRdy
.globl OSStartHighRdy
OSStartHighRdy:
MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Disable interrupts and switch to management mode
LDR R0, =OSTaskSwHook // Call task switching hook function
MOV LR, PC // Prepare function return address
BX R0 // Support Thumb and ARM mixed coding
LDR R0, =OSRunning //Set OSRunning to 1
MOV R1, #1
STRB R1, [R0]
LDR R0, =OSTCBHighRdy // Get the highest priority task TCB
LDR R0, [R0] // Get the task stack pointer
LDR SP, [R0] // Switch to the new task stack
LDMFD SP!, {R0} // Pop the CPSR of the new task
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^ // Pop the context of the new task
3.2.4. OSCtxSw() function
uCOS-II schedules tasks through the OS_Sched() function and performs actual task switching by calling OS_TASK_SW(). OSCtxSw() is the macro implementation of OS_TASK_SW(). The task switching function OSCtxSw() needs to complete the following tasks:
(1) Save the context of the current task (R0-R12, LR, PC address of task interruption, status register CPSR) to the current task stack
(2) According to the current task TCB (task control block), obtain the current task stack pointer and save the current task SP stack into the stack pointer
(3) Call the task switching hook function, that is, first call the OSTaskSwHook() function
(4) Update the priority of the highest priority task to be run to the current priority variable
(5) Update the TCB (task control block) address of the highest priority task to be run to the current TCB (task control block) address variable
(6) Obtain the highest priority task stack pointer, SP switches to the highest priority task stack, pops the context of the new task, and executes the new task.
#define Mode_THUMB 0x20 // THUMB mode
.extern OSTCBCur
.extern OSTCBHighRdy
.extern OSPrioCur
.extern OSPrioHighRdy
.globl OSCtxSw
.globl OSIntCtxSw
OSCtxSw:
STMFD SP!, {LR} // Push the current task PC onto the stack
STMFD SP!, {LR} // Push the current task LR onto the stack
STMFD SP!, {R0-R12} // Push the current task R0-R12
MRS R0, CPSR // Get the current task CPSR
TST LR, #1 // Test whether the task is working in Thumb mode
ORRNE R0, R0, #Mode_THUMB // If it is Thumb, the state changes to Thumb mode
STMFD SP!, {R0} // Push CPSR
LDR R0, =OSTCBCur // Get the current task TCB
LDR R1, [R0] // Get the current task stack pointer from TCB
STR SP, [R1] // SP stack saves the current task stack pointer
OSIntCtxSw:
LDR R0, =OSTaskSwHook // Call task switching hook function
MOV LR, PC // Prepare function return address
BX
LDR R0, =OSPrioCur // Get the current task priority save pointer
LDR R1, =OSPrioHighRdy // Get the highest priority task priority save pointer
LDRB R2, [R1] // Get the highest priority task priority
STRB R2, [R0] // Save into the current task priority pointer variable
LDR R0, =OSTCBCur // Get the current task TCB save pointer
LDR R1, =OSTCBHighRdy // Get the highest priority task TCB save pointer
LDR R2, [R1] // The highest priority TCB address is saved into the current task TCB pointer
STR R2, [R0]
LDR SP, [R2] // SP switches to the highest priority task stack
LDMFD SP!, {R0} // Pop the CPSR of the new task
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^ // Pop the context of the new task
3.2.5. OSIntCtxSw() function
OSIntCtxSw() is used to implement interrupt-level task switching. When all interrupts (nested interrupts) are executed, the kernel needs to switch to the task to continue execution. Therefore, interrupt-level task switching is consistent with ordinary task switching. The difference is that the task context has been saved when an exception occurs, and interrupt-level task switching does not need to save the task context. It only lacks steps 1 and 2 compared to OSCtxSw(), and the rest are the same. Therefore, OSIntCtxSw() can be merged and written on OSCtxSw(). See the OSIntCtxSw() function label on OSCtxSw().
(1) Call the task switching hook function, that is, first call the OSTaskSwHook() function
(2) Update the priority of the highest priority task to be run to the current priority variable
(3) Update the TCB (task control block) address of the highest priority task to be run to the current TCB (task control block) address variable
(4) Obtain the highest priority task stack pointer, SP switches to the highest priority task stack, pops the context of the new task, and executes the new task.
3.2.6. IRQ_SaveContext() function
When any exception occurs, the task will be interrupted. When entering the exception, the context of the current task should be saved to the current task stack before executing the exception handling. IRQ exception is no exception, because uCOS-II needs a timer interrupt Tick, so IRQ handling is also part of the porting. IRQ_SaveContext() needs to complete the following work:
(1) Temporarily use some registers and push the used registers onto the IRQ stack
(2) Switch to management mode, disable interrupts, and run the task in management mode. This step will switch the SP to the task stack interrupted by the interrupt
(3) Push the context of the interrupted task into the task stack.
(4) Track the interrupt nesting count to determine whether the task is interrupted or the interrupt is nested. Interrupt nesting does not require updating the task stack.
(5) Non-interrupt nesting: obtain the stack pointer according to the current task TCB (task control block) and save the interrupted task SP stack into the stack pointer
(6) Call OSIntEnter() function to nest interrupts and count up
(7) Switch to system mode and push LR. This step is to use the system mode stack to handle interrupt functions and reduce the use of the task stack.
(8) Call the IRQ_Handler() function to actually handle the IRQ interrupt service. The IRQ interrupt can be enabled again during the interrupt service, and interrupt nesting is supported.
(9) After the interrupt service is executed, the LR is popped out of the stack and the mode is switched to management mode. Interrupts are disabled and the SP is switched to the task stack of the interrupted task.
(10) Call OSIntExit() to decrement the interrupt nesting count. If the interrupt nesting count OSIntNesting is 0, it means that all interrupts have exited. OSIntCtxSw() will be called to switch the interrupt level task and continue to execute the task.
(11) If the interrupt nesting count OSIntNesting is not 0 and all interrupts have not been exited, the context of the previous interrupt is popped out of the stack and the nested upper interrupt is executed.
.extern OSIntEnter
.extern OSIntExit
.extern OSIntNesting
.extern IRQ_Handler
.globl IRQ_SaveContext
IRQ_SaveContext:
SUB LR, LR, #4 // IRQ exception return address LR-4
STMFD SP!, {R0-R2} // Temporary working registers are pushed into the IRQ stack
MRS R0, SPSR // Save the CPSR before the exception occurs
MOV R1, LR // save LR
MOV R2, SP // Save IRQ stack pointer, used to pop the working register
ADD SP, SP, #(3 * 4) // Adjust back to the position of the IRQ stack
MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Disable interrupts and switch to management mode
STMFD SP!, {R1} // Push the PC of the interrupted task
STMFD SP!, {LR} // Push the LR of the interrupted task
STMFD SP!, {R3-R12} // Push the stack to interrupt the task's R12-R3
LDMFD R2!, {R5-R7} // Restore R2-R0 from IRQ stack
STMFD SP!, {R5-R7} // Push the stack to interrupt the task's R2-R0
STMFD SP!, {R0} // Push the CPSR of the interrupted task
LDR R0, =OSIntNesting //Get interrupt nesting count
LDRB R1, [R0]
CMP R1, #0 //Judge whether the task is interrupted or nested
BNE IntteruptNesting // Interrupt nesting does not need to update the task stack pointer
LDR R0, =OSTCBCur // The task is interrupted, get the interrupt task TCB
LDR R1, [R0] // Get the interrupt task stack pointer
STR SP, [R1] // SP stack saves the interrupt task stack pointer
IntteruptNesting:
LDR R0, =OSIntEnter //Call OSIntEnter() to count interrupt nesting
MOV LR, PC
BX
MSR CPSR_c, #(I_Bit+F_Bit+Mode_SYS) // Switch to system mode and use the system mode stack to handle interrupts
STMFD SP!, {LR} // Push system mode LR
LDR R0, =IRQ_Handler // Call IRQ processing function
MOV LR, PC
BX
LDMFD SP!, {LR} // Pop system mode LR
MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Switch to management mode and use the task stack to pop
LDR R0, =OSIntExit // Call OSIntExit() to decrement the interrupt, which may not return
MOV LR, PC
BX
LDMFD SP!, {R0} // Interrupts are nested, pop the context of the previous interrupt
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^
3.3. Writing the os_cpu_c.c file
uCOS-II needs to write ten simple hook functions, which can be left blank if there is no special requirement. OSInitHookBegin(), OSInitHookEnd(), OSTaskCreateHook(), OSTaskDelHook(), OSTaskIdleHook(), OSTaskStatHook(), OSTaskSwHook(), OSTCBInitHook(), OSTimeTickHook(), OSTaskReturnHook(). Among them, the more important one is OSTaskStkInit() function, which is used to initialize the task stack and task status, and is necessary.
3.3.1. OSTaskStkInit() function
#define Mode_SVC 0x13
#define Mode_THUMB 0x20
#define Mode_ARM 0x00
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos,INT16U opt)
{
OS_STK *stk;
(void)opt; /* avoid compiler warnings */
stk = ptos; /* Get the stack pointer */
*stk = (OS_STK) task; /* pc */
*--stk = (OS_STK) task; /* lr */
*--stk = 0; /* r12 */
*--stk = 0; /* r11 */
*--stk = 0; /* r10 */
*--stk = 0; /* r9 */
*--stk = 0; /* r8 */
*--stk = 0; /* r7 */
*--stk = 0; /* r6 */
*--stk = 0; /* r5 */
*--stk = 0; /* r4 */
*--stk = 0; /* r3 */
*--stk = 0; /* r2 */
*--stk = 0; /* r1 */
*--stk = (unsigned int)pdata; /* Use R0 to pass parameters */
if (((OS_STK)task & 0x01u) ==0x01u) { /* Determine whether the task is running in Thumb mode or ARM mode*/
*--stk = (OS_STK)(Mode_SVC |Mode_THUMB); /* CPSR, task works in Thumb state management mode*/
} else {
*--stk = (OS_STK)(Mode_SVC |Mode_ARM); /* CPSR, task works in ARM state management mode*/
}
return (stk);
}
3.3.2. Hook Function
The hook function can extend the user's code into the kernel to implement some specific functions. If there is no special functional requirement, it can be left blank.
void OSInitHookBegin (void)
{
}
void OSInitHookEnd (void)
{
}
void OSTaskCreateHook (OS_TCB *ptcb)
{
ptcb = ptcb;
}
void OSTaskDelHook (OS_TCB *ptcb)
{
(void)ptcb;
}
void OSTaskSwHook (void)
{
}
void OSTaskStatHook (void)
{
}
void OSTCBInitHook (OS_TCB *ptcb)
{
(void)ptcb;
}
void OSTimeTickHook (void)
{
}
void OSTaskIdleHook (void)
{
}
void OSTaskReturnHook(OS_TCB *p_tcb)
{
(void)(p_tcb);
}
Previous article:S3C2416 bare metal development series 15_uCOS porting under GCC (2)
Next article:S3C2416 bare metal development series 14_Transplantation of UCGUI under GCC (2)
Recommended ReadingLatest update time:2024-11-16 18:00
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
- Free application for new products: Arteli Value Edition, M4 core AT32F421 board
- Is the era of 5G small base stations about to begin?
- Contest entries submitted to Environmental Monitoring Terminal
- STM32 driver MAX7219 failed, help
- Research and implementation of median filter algorithm for license plate recognition based on FPGA.pdf
- Electrolytic Capacitors
- 【2022 Digi-Key Innovation Design Competition】Font Application in U8g2
- I want to buy a power supply. Does anyone have any recommendations?
- SinlinxA33 SD card programming
- 【ST NUCLEO-H743ZI Review】——by 54chenjq