uC/OS-III 内核实现与应用开发实战学习(一)

参考教程

[野火]《uCOS-III内核实现与应用开发实战指南》
开发板---->野火MINI_STM32F103RCT6
uC/OS-III 内核实现与应用开发实战学习(一)_第1张图片
1.轮询系统
2.前后台系统
3.多任务系统

事件的响应和处理方式
轮询系统 在主程序响应和处理
前后台系统 中断响应,在主程序处理
多任务系统 中断响应,在任务中处理

堆栈的认识

我们所说的堆栈其实是两个东西,分别是堆和栈,当然如果有人给你说起堆栈这个名词,那他想要表达的一定是栈这个概念。除了放置全局变量是在堆区,其他像局部变量,函数参数全部分配在栈区,栈是由高地址向低地址生长的。这一点与堆不一样,堆由低地址向高地址生长。
分配方式:堆都是动态分配的,没有静态分配的堆。

cpu.h程序

/*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

结构体能不能作为参数传入函数?

os_task.c程序

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

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;

-------------------------------写于2022.1.10 晚上12.25,明天再继续看

1.10 下午7.10 Hello!兄弟们,继续啃!
在OSTaskCreate()函数中我们调用了OSTaskStkInit()函数,我们来看看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);

}	

刚开始我发现寄存器号与16进制数”居然一样“,疑惑下,不会这么巧?看到后面发现处于调试方便,故意这样做的
给出程序的解释:
(1)p_task 是任务名,指示着任务的入口地址,在任务切换的时候,
需要加载到 R15,即 PC 寄存器,这样 CPU 就可以找到要运行的任务。
(2)p_arg 是任务的形参,用于传递参数,在任务切换的时候,需要
加载到寄存器 R0。R0 寄存器通常用来传递参数。
(3)p_stk_base 表示任务堆栈的起始地址。
(4)stk_size 表示任务堆栈的大小,数据类型为 CPU_STK_SIZE,在
Cortex-M3 内核的处理器中等于 4 个字节,即一个字。

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

(5)获取栈顶地址,前面我们说过栈有个特殊的地方,就是由高地址向低地址生长,
参考博客:手撕startup_stm32f10x_hd.s启动文件源码
(6)任务第一次运行,加载到CPU寄存器的环境参数我们要预先初始化好。初始化的顺序固定(这个是为什么?我认为xPSR,PC,LR固定顺序就OK啊?其他的没必要啊)
异常发生时自动保存的8个寄存器,即xPSR,PC,LR,R12,R3,R2,R1,R0(这个为什么就是自动保存的?其他为什么又要手动的?)
xPSR寄存器的位24又为什么必须为1?
(7)手动加载的R11~R4CPU寄存器参数
(8)return返回栈顶指针,赋值给p_sp,然后p_sp保存在TCB结构体的第一个成员StkPtr。
stk_size任务堆栈的大小保存在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;

--------------------------------------分割线------------------------------------------------------
1.走在这一步,我又去查阅资料,参考这篇博客ARM Cortex -M 体系结构————————ARM微控制器与嵌入式系统
发现这来自于Joseph Yiu ,宋岩 译著《Cortex M3权威指南》,哈哈,差点忘了这本经典的工具书!如本书所述:
uC/OS-III 内核实现与应用开发实战学习(一)_第2张图片
(1)R0-R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位,复位后的初始值是不可预料的
(2)R8-R12 也被称为高组寄存器。这是因为只有很少的 16 位 Thumb 指令能访问它们,32 位的thumb-2 指令则不受限制。它们也是 32 位字长,且复位后的初始值是不可预料的。
uC/OS-III 内核实现与应用开发实战学习(一)_第3张图片
对xPSR寄存器的解答:
这个xPSR中的x代表“任意不定状态”,可分为三个子状态寄存器APSR,IPSR,EPSR。
uC/OS-III 内核实现与应用开发实战学习(一)_第4张图片
xPSR寄存器的位24又为什么必须为1?
—>Thumb状态,总是1,清楚此位会引起错误异常。
下面一张图我没有在《Cortex M3权威指南》,为什么会没有呢?而是在一篇博客找到答案:《嵌入式 - 嵌入式大杂烩》深入理解ARM寄存器
uC/OS-III 内核实现与应用开发实战学习(一)_第5张图片

--------------------------------------分割线------------------------------------------------------

你可能感兴趣的:(单片机系列,单片机,stm32,c语言,uC/OS-III,内核学习)