OSTaskCreate 函数
任务的堆栈,任务的函数实体,任务的 TCB 最终需要联系起来才能由系统进行统一调
度。那么这个联系的工作就由任务创建函数 OSTaskCreate 来实现。
/*p_tcb 是任务控制块指针。OS_TCB是一个结构体,是一个任务的身份证,(1)*/
void OSTaskCreate ( OS_TCB *p_tcb,
OS_TASK_PTR p_task, /*p_task 是任务函数名,类型为 typedef
void (*OS_TASK_PTR) (void *p_arg); */ void *p_arg, /*p_arg 是任务形参,用于传递任务参数。
void 类型以便于参数类型的转换*/
CPU_STK *p_stk_base, /*p_stk_base 用于指向任务堆栈的起始地
址。也就是自己创建数组的第0个元素*/
CPU_STK_SIZE stk_size, /*stk_size 表示任务堆栈的大小。CPU_STK_SIZE 是一个 无符号32位的整型变量,在cpu.h中定义 typedef unsigned int CPU_INT32U; ypedef CPU_INT32U CPU_ADDR; typedef CPU_ADDR CPU_STK_SIZE;*/
OS_ERR *p_err) (2) /*p_err 用于存错误码, uC/OS-III 中为函数的返回值预先定义了很多错误码,通过这些错误码我们可以知道函数是因为什么出错。*/
{ //函数主体
CPU_STK *p_sp; //定义一个CPU_STK类型的指针,*p_sp
p_sp = OSTaskStkInit (p_task, /*OSTaskStkInit()是任务堆栈初始化函
数。 p_sp 指向任务栈顶*/
p_arg, //p_arg 是任务形参,就是上文函数需要传入的参数
p_stk_base, //p_stk_base 用于指向任务堆栈的起始址。
stk_size); (3) //stk_size 表示任务堆栈的大小。
p_tcb->StkPtr = p_sp; /*将剩余栈的栈顶指针 p_sp 保存到任务控制块 TCB 的第一个成员StkP中。*/
p_tcb->StkSize = stk_size; /*将任务堆栈的大小保存到任务控制块 TCB 的成员 StkSize 中。*/
*p_err = OS_ERR_NONE; /*函数执行到这里表示没有错误,即 OS_ERR_NONE。*/
}
(1)BCT 结构体:为任务的身份证,如下所示,刚开始我们只在tcb结构体中添加两个成员,之后会慢慢增加。
struct os_tcb
{
CPU_STK *StkPtr; //指向任务栈 栈顶指针。
CPU_STK_SIZE StkSize; //任务堆栈的大小。
};
/***********************************************************************************/
(2)ucos中部分错误代码的定义
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;
/***********************************************************************************/
(3)OSTaskStkInit()函数的分析 OSTaskStkInit() 是任务堆栈初始化函数。当任务第一次运行的时
候,加载到 CPU 寄存器的参数就放在任务堆栈里面,在任务创建的时候,预先初始化好堆栈。
/* 任务堆栈初始化 */
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task, /* p_task 是任务名,指示着任务的入口地址,在任务切换的时候,
需要加载到 R15,即 PC 寄存器,这样 CPU 就可以找到要运行的任务。*/
void *p_arg, /*p_arg 是任务的形参,用于传递参数,在任务切换的时候,需要
加载到寄存器 R0。 R0 寄存器通常用来传递参数。*/
CPU_STK *p_stk_base, /*p_stk_base 表示任务堆栈的起始地址下面可用于算出栈顶地址*/
CPU_STK_SIZE stk_size) /*stk_size 表示任务堆栈的大小,数据类型为 CPU_STK_SIZE,在
Cortex-M3 内核的处理器中等于 4 个字节,即一个字。*/
{ //函数主体
CPU_STK *p_stk;
p_stk = &p_stk_base[stk_size]; /*获取任务堆栈的栈顶地址, ARMCM3 处理器的栈是由高地址向
低地址生长的。所以初始化栈之前,要获取到栈顶地址,然后栈地址逐一递减即可。*/
/*任务第一次运行的时候,加载到 CPU 寄存器的环境参数我们要
预先初始化好。 初始化的顺序固定, 首先是异常发生时自动保存的 8 个寄存器,即 xPSR、
R15、 R14、 R12、 R3、 R2、 R1 和 R0。其中 xPSR 寄存器的位 24 必须是 1, R15 PC 指针必
须存的是任务的入口地址, R0 必须是任务形参,剩下的 R14、 R12、 R3、 R2 和 R1 为了调
试方便,填入与寄存器号相对应的 16 进制数。*/
/* 异常发生时自动保存的寄存器
*--p_stk = (CPU_STK)0x01000000u; /* xPSR的bit24必须置1 */
*--p_stk = (CPU_STK)p_task; /* 任务的入口地址 */
*--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 : 任务形参 */
/*剩下的是 8 个需要手动加载到 CPU 寄存器的参数,为了调试方
便填入与寄存器号相对应的 16 进制数。*/
/* 异常发生时需手动保存的寄存器 */
*--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); /*返回栈指针 p_stk,这个时候 p_stk 指向剩余栈的栈顶。*/
}
任务创建好之后,我们需要把任务添加到一个叫就绪列表的数组里面, 表示任务已经
就绪,系统随时可以调度。
/* 将任务加入到就绪列表 */
OSRdyList [0].HeadPtr = &Task1TCB; /*把任务 TCB 指针放到 OSRDYList 数组里面。
OSRDYList 是一个类型为 OS_RDY_LIST 的全局变量 OS_EXT OS_RDY_LIST OSRdyList [OS_CFG_PRIO_MAX];OS_CFG_PRIO_MAX 是一个定义,表示这个系统支持多少个优先级(刚开始暂时不支持多个优先级,往后章节会支持)例如 #define OS_CFG_PRIO_MAX 32u , OS_RDY_LIST 是就绪列表的数据类型,在 os.h 中声明*/ (4)
(4) OS_RDY_LIST 里面目前暂时只有两个 TCB 类型的指针,一个是头指针,一个是尾指针
typedef struct os_rdy_list OS_RDY_LIST;
struct os_rdy_list {
OS_TCB *HeadPtr; //TCB 类型的 头指针
OS_TCB *TailPtr; //TCB 类型的 尾指针 指向不同的TCB控制块,实现链表
};
在 uC/OS-III 中,将任务添加到就绪列表其实是在 OSTaskCreate()函数中完成的。每当任务创建好就把任务添加到就绪列表,表示任务已经就绪。只是目前这里的就绪列表的实现还是比较简单,不支持优先级,不支持双向链表,只是简单的将任务控制块放到就绪列表的数组里面。
总结:1. 任务创建函数 void OSTaskCreate (), (内有OSTaskStkInit () 初始化指向栈顶指针
2.把创建好的任务加入就序列表,OSRdyList [0].HeadPtr = &Task1TCB 就是将 结构体类型的数组 第0个元素内,os_rdy_list 结构体的成员 *HeadPtr 指向创建好的任务。 至此任务创建完成。