uC/OS-III Kernel Implementation and Application Development Practical Learning (1)

Reference tutorial

[Wildfire] "uCOS-III Kernel Implementation and Application Development Practical Guide"
development board---->Wildfire MINI_STM32F103RCT6
insert image description here
1. Polling system
2. Front and back system
3. Multitasking system

Incident response and handling
polling system Respond and process in the main program
front and back system Interrupt response, handled in the main program
multitasking system Interrupt response, handled in the task

stack awareness

The stack we are talking about is actually two things, namely heap and stack. Of course, if someone mentions the term stack to you, what he wants to express must be the concept of stack . In addition to placing global variables in the heap area, other functions like local variables and function parameters are all allocated in the stack area, and the stack grows from high addresses to low addresses. This is different from the heap, which grows from lower addresses to higher addresses.
Allocation method: The heap is dynamically allocated, and there is no statically allocated heap.

cpu.h program

/*cpu.h头文件内容*/
#ifndef CPU_H
#define CPU_H

typedef unsigned short CPU_INT16U;
typedef unsigned int   CPU_INT32U;
typedef unsigned char  CPU_INT08U;

typedef CPU_INT32U CPU_ADDR;

/*堆栈数据类型重定义*/

typedef CPU_INT32U CPU_STK;
typedef CPU_ADDR   CPU_STK_SIZE;
typedef volatile CPU_INT32U CPU_REG32;

#endif

Can a struct be passed as a parameter to a function?

os_task.c program

/*实现任务创建函数*/

void OSTaskCreate(OS_TCB       *p_tcb,
                  OS_TASK_PTR   p_task,
				  void         *p_arg,
				  CPU_STK      *p_stk_base,
				  CPU_STK_SIZE  stk_size,
				  OS_ERR       *p_err)
{
    
    
   CPU_STK  *p_sp;
   p_sp = OSTaskStkInit( p_task, 
                         p_arg,
						 p_stk_base,
						 stk_size);
				
    p_tcb->StkPtr = p_sp;
	p_tcb->StkSize = stk_size;
	
	*p_err = OS_ERR_NONE;//执行到这一步,表示没有错误!
 
}
/*(1)p_tcb是任务控制块TCB指针
  (2)p_task是任务函数名,类型为OS_TASK_PTR,原型声明
  在os.h  typedef void  (*OS_TASK_PTR)(void *p_arg);
  (3)p_arg是任务形参,用于传递任务参数
  (4)p_stk_base 用于指向任务堆栈的起始地址
  (5)stk_size 表示任务堆栈的大小
  (6)p_err用于存放错误码,这个错误码是uC/OS-III已经
  预定义好的,错误码是枚举类型
*/

/*错误码枚举类型*/
 typedef enum os_err {
    
    
 OS_ERR_NONE = 0u,
 
 OS_ERR_A = 10000u,
 OS_ERR_ACCEPT_ISR = 10001u,
 
 OS_ERR_B = 11000u,
 
 OS_ERR_C = 12000u,
 OS_ERR_CREATE_ISR = 12001u,
 
 /* 篇幅限制,中间部分删除,具体的可查看本章配套的例程 */
 
 OS_ERR_X = 33000u,
 
 OS_ERR_Y = 34000u,
 OS_ERR_YIELD_ISR = 34001u,
 
 OS_ERR_Z = 35000u
} OS_ERR;

------------------------------- Written at 12.25 pm on 10.1.2022, continue to read tomorrow

1.10 7.10 pm Hello! Brothers, keep nibbling!
In the OSTaskCreate() function we call the OSTaskStkInit() function, let's take a look at the function prototype of OSTaskStkInit():

CPU_STK *OSTaskStkInit(OS_TASK_PTR   p_task,)
                       void         *p_arg,
					   CPU_STK      *p_stk_base,
					   CPU_STK_SIZE  stk_size)
{
    
    
    CPU_STK *p_stk;
	
	p_stk = &p_stk_base[stk_size];
	
	//异常发生时自动保存的寄存器
	
	*--p_stk = (CPU_STK)0x01000000u;  //xPSR的bit24必须置1
	*--p_stk = (CPU_STK)p_task;       //R15(PC)任务的入口地址
	*--p_stk = (CPU_STK)0x14141414u;  //R14(LR)              	
    *--p_stk = (CPU_STK)0x12121212u;  //R12
	*--p_stk = (CPU_STK)0x03030303u;  //R3
	*--p_stk = (CPU_STK)0x02020202u;  //R2
	*--p_stk = (CPU_STK)0x01010101u;  //R1
	*--p_stk = (CPU_STK)p_arg;        //R0:任务参数
	
	//异常发生时需手动保存的寄存器R4~R11
	*--p_stk = (CPU_STK)0x11111111u;   //R11
	*--p_stk = (CPU_STK)0x10101010u;   //R10
	*--p_stk = (CPU_STK)0x09090909u;   //R9
	*--p_stk = (CPU_STK)0x08080808u;   //R8
	*--p_stk = (CPU_STK)0x07070707u;   //R7
	*--p_stk = (CPU_STK)0x06060606u;   //R6
	*--p_stk = (CPU_STK)0x05050505u;   //R5
	*--p_stk = (CPU_STK)0x04040404u;   //R4
	
	return (p_stk);

}	

At the beginning, I found that the register number and the hexadecimal number "are actually the same". I wondered, wouldn't it be such a coincidence? After seeing that it is convenient for debugging, I deliberately do this to
give an explanation of the program:
(1) p_task is the task name, indicating the entry address of the task. When the task is switched, it
needs to be loaded into R15, that is, the PC register, so that the CPU to find the task to run.
(2) p_arg is the formal parameter of the task, which is used to transfer the parameter. When the task is switched, it needs to be
loaded into the register R0. The R0 register is usually used to pass parameters.
(3) p_stk_base represents the starting address of the task stack.
(4) stk_size represents the size of the task stack, the data type is CPU_STK_SIZE,
which is equal to 4 bytes, that is, one word in the processor of the Cortex-M3 core.

typedef unsigned int      CPU_INT32U;
typedef CPU_INT32U        CPU_ADDR;
typedef CPU_ADDR          CPU_STK_SIZE;

(5) Get the top address of the stack. We said earlier that the stack has a special place, that is, it grows from a high address to a low address.
Refer to the blog: Tear startup_stm32f10x_hd.s startup file source code
(6) The task is run for the first time and loaded into We need to initialize the environment parameters of the CPU registers in advance. The order of initialization is fixed (why is this? I think the fixed order of xPSR, PC, LR is OK? Others are unnecessary)
8 registers that are automatically saved when an exception occurs, namely xPSR, PC, LR, R12, R3, R2 , R1, R0 (why is this automatically saved? Why are the others manually?)
Why must bit 24 of the xPSR register be 1?
(7) Manually loaded R11~R4CPU register parameters
(8) return returns the stack top pointer, assigns it to p_sp, and then p_sp is stored in the first member StkPtr of the TCB structure.
The size of the stk_size task stack is stored in the second member of the TCB, StkSize

//在函数OSTaskStkInit中
return (p_stk);
//在函数OSTaskCreate中
p_sp = OSTaskStkInit( p_task, p_arg, p_stk_base,stk_size);
p_tcb->StkPtr = p_sp;
p_tcb->StkSize = stk_size;

--------------------------------------Dividing line---------- --------------------------------------------
1. Take this step , I went to check the information again, refer to this blog ARM Cortex -M Architecture ————— ARM Microcontroller and Embedded System
I found that this came from Joseph Yiu, translated by Song Yan "Cortex M3 Authoritative Guide" , haha, almost forgot this classic reference book! As described in this book:
insert image description here
(1) R0-R7 are also known as low bank registers. All commands can access them . Their word lengths are all 32 bits, and the initial value after reset is unpredictable
(2) R8-R12 are also called high group registers. This is because only a few 16-bit Thumb instructions can access them, and 32-bit thumb-2 instructions are not restricted . They are also 32-bit words long, and the initial value after reset is unpredictable.
insert image description here
Answer to the xPSR register:
The x in this xPSR stands for "arbitrary indeterminate state", which can be divided into three sub-state registers APSR, IPSR, and EPSR.
insert image description here
Why must bit 24 of the xPSR register be 1?
—> Thumb status, always 1, clearing this bit will cause an error exception.
The following picture is not in the "Cortex M3 Authoritative Guide", why is it not? Instead, I found the answer in a blog: "Embedded - Embedded Hodgepodge" in-depth understanding of ARM registers
insert image description here

--------------------------------------Dividing line---------- --------------------------------------------

Guess you like

Origin blog.csdn.net/Eterlove/article/details/122398896