[野火]《uCOS-III内核实现与应用开发实战指南》
开发板---->野火MINI_STM32F103RCT6
1.轮询系统
2.前后台系统
3.多任务系统
事件的响应和处理方式 | |
---|---|
轮询系统 | 在主程序响应和处理 |
前后台系统 | 中断响应,在主程序处理 |
多任务系统 | 中断响应,在任务中处理 |
我们所说的堆栈其实是两个东西,分别是堆和栈,当然如果有人给你说起堆栈这个名词,那他想要表达的一定是栈这个概念。除了放置全局变量是在堆区,其他像局部变量,函数参数全部分配在栈区,栈是由高地址向低地址生长的。这一点与堆不一样,堆由低地址向高地址生长。
分配方式:堆都是动态分配的,没有静态分配的堆。
/*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
结构体能不能作为参数传入函数?
/*实现任务创建函数*/
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权威指南》,哈哈,差点忘了这本经典的工具书!如本书所述:
(1)R0-R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位,复位后的初始值是不可预料的
(2)R8-R12 也被称为高组寄存器。这是因为只有很少的 16 位 Thumb 指令能访问它们,32 位的thumb-2 指令则不受限制。它们也是 32 位字长,且复位后的初始值是不可预料的。
对xPSR寄存器的解答:
这个xPSR中的x代表“任意不定状态”,可分为三个子状态寄存器APSR,IPSR,EPSR。
xPSR寄存器的位24又为什么必须为1?
—>Thumb状态,总是1,清楚此位会引起错误异常。
下面一张图我没有在《Cortex M3权威指南》,为什么会没有呢?而是在一篇博客找到答案:《嵌入式 - 嵌入式大杂烩》深入理解ARM寄存器
--------------------------------------分割线------------------------------------------------------