uCOS-II基础知识点归纳
TCB内容
ucosii.h中,对TCB定义如下 (截取重要的):
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; // 堆栈栈顶指针
...
struct os_tcb *OSTCBNext; // 指向下一个TCB的指针
struct os_tcb *OSTCBPrev; // 指向上一个TCB的指针
...
INT16U OSTCBDly; // 任务延迟的时间
INT8U OSTCBStat; // 任务的状态:就绪、阻塞、运行、等待、睡眠、ISR
INT8U OSTCBPrio; // 任务的优先级
// 优先级位图法用到的结构
INT8U OSTCBX;
INT8U OSTCBY;
INT8U OSTCBBitX;
INT8U OSTCBBitY;
...
}OS_TCB;
优先级位图法代码分析—原理不讲
OS_TCBInit 函数(优先级位图法部分):
{
...
//从OSTCBFreeList上取下一个空闲的,已经初始化过的TCB
ptcb = OSTCBFreeList;
...
//初始化OSTCBX/OSTCBY/OSTCBBitX/OSTCBBitY,ptcb为指向TCB的指针
ptcb->OSTCBY = (INT8U)(prio>>3); //prio>>3为取3~5位,及优先级的十位
OSTCBBitY = OSMapTbl[ptcb->OSTCBY]; //将其转化为one-hot编码(类似掩码)
ptcb->OSTCBX = (INT8U)(prio & 0X07);
OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
...
//将TCB连接到OSTCBList上
OSTCBList = ptcb; //OSTCBList将就绪状态的Task的TCB全串联在一起,用于OSTimeDly()函数对每个TASK的OSTCBDly进行修改
//当OSTCBDly减为0时,则可能会触发调度(取决于优先级)
//以下作用类似于把就绪的Task放入就绪队列中,但uCOS-II本质上无就绪队列
OSRdyGrp |= ptcb->OSTCBBitY; //将十位的掩码与OSRdyGrp相与,更新OSRdyGrp
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
...
}
附表:OSMapTbl
0 | 00000000 |
---|---|
1 | 00000001 |
2 | 00000010 |
3 | 00000100 |
4 | 00001000 |
5 | 00010000 |
6 | 00100000 |
7 | 01000000 |
8 | 10000000 |
OS_SchedNew 函数(优先级位图法部分):
OS_SchedNew 函数在 OS_Sched 函数中被调用,用来找到当前最高优先级:
{
...
//仅讨论 OS_LOWEST_PRIO <= 63的情况(优先级数字约大优先级越低)
INT8U y;
y = OSUnMapTbl[OSRdyGrp]; //y存放的值为OSRdyGrp数组中,获取十位第一个出现1的位数(0~7位),也是OSRdyTbl的第y行
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); //同理,OSUnMapTbl[OSRdyTbl[y]]获取个位第一个 //出现1的位数
// (y << 3) + OSUnMapTbl[OSRdyTbl[y]]即为优先级Prio,然后将其放入OSPrioHighRdy中去
// 即更新 优先级最高 的 就绪任务 的 优先级
...
}
因此,出现有优先级位图法的函数即为OS_TCBInit -> OS_SchedNew,一个是初始化,一个是调度
几个重要参数的意义
ucos_ii.h的部分宏定义代码如下
OS_EXT INT8U OSPrioCur; /* Priority of current task */
OS_EXT INT8U OSPrioHighRdy; /* Priority of highest priority task */
OS_EXT OS_TCB *OSTCBCur; /* Pointer to currently running TCB */
OS_EXT OS_TCB *OSTCBHighRdy; /* Pointer to highest priority TCB R-to-R */
参数归纳表
OSTCBCur | 指向正在运行的任务的TCB——指针 |
---|---|
OSPrioHighRdy | 优先级最高的 “就绪任务” 的优先级 |
OSTCBPrioTbl | 一个数组,数组下标大小对应任务优先级的大小,存放指向任务TCB的指针——数组 |
OSPrioCur | 当前任务的优先级 |
OSTCBHighRdy | 指向最高优先级的TCB——指针 |
上下文切换步骤
OSCtxSw代码如下:
;----------------------------------------------------------------------------------
; 保护现场,将原Task的R0~012,LR,PC,CPSR压回OSTCBStkPtr指向的栈区
;----------------------------------------------------------------------------------
STMFD SP!, {LR} ;保存返回地址,也就是PC
STMFD SP!, {R0-R12, LR} ;R0-R12 LR,从右往左压
MRS R0, CPSR ;通过R0来压入CPSR,为了防止在压栈过程中改变CPSR的值
STMFD SP!, {R0}
;----------------------------------------------------------------------------------
; OSTCBCur->OSTCBStkPtr = SP, 以便返回的时候找到现场的数据;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBCur
LDR R0, [R0]
STR SP, [R0]
BL OSTaskSwHook
;---------------------------------------------------------------------------------
; OSTCBCur = OSTCBHighRdy, 让OSTCBCur指向就绪队列中最高优先级的任务;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R1, =OSTCBCur
LDR R0, [R0]
STR R0, [R1]
;---------------------------------------------------------------------------------
; OSPrioCur = OSPrioHighRdy, 更新当前的最高优先级;
;---------------------------------------------------------------------------------
LDR R0, =OSPrioHighRdy
LDR R1, =OSPrioCur
LDRB R0, [R0]
STRB R0, [R1]
;----------------------------------------------------------------------------------
; SP = OSTCBHighRdy->OSTCBStkPtr, 读取存放有现场的堆栈地址;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;----------------------------------------------------------------------------------
; Restore New task context
;----------------------------------------------------------------------------------
LDMFD SP!, {R0} ;把CPSR出栈到R0中
MSR SPSR_cxsf, R0 ;将CPSR的值写入到SPSR
LDMFD SP!, {R0-R12, LR, PC}^ ;出栈,^代表用SPSR拷贝到CPSR中,等效于压栈CPSR
几个重要函数的执行流程
OSTaskCreate:
-
在OSTCBPrioTbl中占位
-
堆栈初始化 (调用OSTaskStkInit) ——> 要先堆栈初始化以后才能把堆栈的地址放入OSTCBStkPtr中
-
TCB初始化 (调用OS_TCBInit)
-
若符合条件,则调度 (调用OS_Sched);否则将OSTCBPrioTbl对应位清0
OSTaskStkInit:
-
*(stk) = (OS_STK)task 使第一个单元指向函数
-
*(–stk) = (INT32U)0 初始化LR,R12~R1
-
*(–stk) = (INT32U)p_arg 将任务参数放入R0
-
*(–stk) = (INT32U)0x013L 设置CPSR为SVC模式,打开IRQ与FIQ
OS_TCBInit:
- 从FreeList中摘取一空闲TCB
- 为TCB中的几个参数赋值
- 为优先级位图法的几个参数赋值
- 将TCB链接到OSTCBPrioTbl中
- 把TCB放到OSTCBList链表的开头
OS_Sched:
- 判断是否有中断嵌套,调度是否打开
- 调用OS_SchedNew来获取最高优先级的任务
- 执行任务上下文切换
OSCtxSw:
- 压栈保存当前任务的现场
- 将TCB的堆栈地址(压栈完后为SP)放入OSTCBStkPtr中
- 让OSTCBCur指向当前最高优先级任务的TCB
- 更新当前的最高优先级
- 读出最高优先级任务的TCB地址存入SP
- 出栈
拷贝代码段到RAM
把ResetHandler代码段(Norflash) 存放到Text_start段(RAM):
低地址到高地址为:ResetHandler —— Text_start —— BSS_start
ADR r1, ResetHandler /* ADR为小范围的装入,装入的是当前状态下实时的位置 */
LDR r2, =text_start /* LDR为大范围的装入,装入的是链接文件中规定的绝对地址 */
LDR r3, =bss_start
copyloop:
LDR r0, [r1], #4
STR r0, [r2], #4
CMP r2, r3
BNECC copyloop