系列文章
前言
其实在一年多以前我就已经萌生了去写一下UCOSii的源码解析的想法,但是无奈当时水平有限,就算写了恐怕也只是表面上API的应用而已,这一次我向写一些对于UCOS更深层次的理解。其实在我看来所有的RTOS都具有十分类似的框架结构,只是其中填入的算法不同而已。其中最为核心的也就是一个优先级算法,以及调度算法。
(一)UCOSII各变量说明
(二)UCOSII启动流程分析
OSInit();
OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
OSStart();
我们首先来看一下UCOS的启动流程。上面调用了三个函数,中间的函数就是创建起始任务,我们不细看,我们重点关注第一个函数以及最后一个函数。
我们首先定位到OSInit的函数内容
(1)OSInit()函数分析
************************************************************************************************
* 初始值(INITIALIZATION)
*
* 描述: 初始化uC/OS-II。对这个函数的调用必须在调用OSStart()函数之前。
* OSStart()函数真正开始运行多任务时
* 意见: 无
* 返回: 无
* ************************************************************************************************
71
72 void OSInit (void) //初始化UCOS-II函数
73 {
74 INT16U i; //定义一个16位变量i
75 INT8U *prdytbl; //定义一个就绪态最高级任务列表指针
76 OS_TCB *ptcb1; //定义任务控制块优先级表指针1
77 OS_TCB *ptcb2; //定义任务控制块优先级表指针2
78 #if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 1) //如果有消息事件,并且最大消息事件值>1
79 OS_EVENT *pevent1; //定义事件指针1
80 OS_EVENT *pevent2; //定义事件指针2
81 #endif
82
83
84 #if OS_VERSION >= 204 //如果版本大于2.04版
85 OSInitHookBegin(); //调初始化钩子函数,可加入用户代码
86 #endif
87
88 #if OS_TIME_GET_SET_EN > 0 //允许生成 OSTimeGet() 和 OSTimeSet() 函数代码
89 OSTime = 0L; //清除32的系统时钟
90 #endif
91 OSIntNesting = 0; //清除中断嵌套计数器
92 OSLockNesting = 0; //清除上锁嵌套计数器
93 OSTaskCtr = 0; //清除任务计数器
94 OSRunning = FALSE; //任务处于不运行状态
95 OSIdleCtr = 0L; //清除32位空闲任务的计数器
96 //允许生成OSTaskCreate()函数和OSTaskCreateExt()函数
97 #if (OS_TASK_STAT_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
98 OSIdleCtrRun = 0L; //空闲任务计数器每秒的计数值清0
99 OSIdleCtrMax = 0L; //每秒空闲任务计数的最大值清0
100 OSStatRdy = FALSE; //统计任务是否就绪的标志为空
101 #endif
102 OSCtxSwCtr = 0; //上下文切换的次数(统计任务计数器)清0
103 OSRdyGrp = 0x00; //清除OSRdyTbl[i]组对应的任务就绪列表
104 prdytbl = &OSRdyTbl[0];
105 for (i = 0; i < OS_RDY_TBL_SIZE; i++) {
106 *prdytbl++ = 0x00; //所有的就绪列表指针内容全部清0
107 }
108
109 OSPrioCur = 0; //正在运行的任务的优先级
110 OSPrioHighRdy = 0; //具有最高优先级别的就绪任务的优先级
111 OSTCBHighRdy = (OS_TCB *)0; //指向最高级优先级就绪任务控制块的指针清0
112 OSTCBCur = (OS_TCB *)0; //指向正在运行任务控制块的指针清0
113 OSTCBList = (OS_TCB *)0; //任务控制块链接表的指针清0
114 //清除所有的优先级控制块优先级列表e
115 for (i = 0; i < (OS_LOWEST_PRIO + 1); i++) {
116 OSTCBPrioTbl[i] = (OS_TCB *)0;
117 }
118 ptcb1 = &OSTCBTbl[0]; //查找任务控制块列表(0)的对应地址
119 ptcb2 = &OSTCBTbl[1]; //查找任务控制块列表(1)的对应地址
120 //释放所有的任务控制块列表
121 for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) {
122 ptcb1->OSTCBNext = ptcb2;
123 ptcb1++;
124 ptcb2++;
125 }
126 ptcb1->OSTCBNext = (OS_TCB *)0; //将最后的任务块双向链接表的后链接为0
127 OSTCBFreeList = &OSTCBTbl[0]; //空任务控制块地址为当前任务控制块列表的首地址
128
129 #if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0) //如果有消息事件,并且最大消息事件数>0
130 #if OS_MAX_EVENTS == 1 //如果最大消息事件数>1
131 //只能拥有单独的一个消息事件
132 OSEventFreeList = &OSEventTbl[0]; //空余事件管理列表=任务等待表首地址
133 OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED; //事件的类型=空闲
134 OSEventFreeList->OSEventPtr = (OS_EVENT *)0; //消息或消息队列的指针为空
135 #else
136 pevent1 = &OSEventTbl[0]; //查找任务等待表(0)对应首地址
137 pevent2 = &OSEventTbl[1]; //查找任务等待表(1)对应地址
138 //释放所有的任务等待表,并将事件的类型=空闲
139 for (i = 0; i < (OS_MAX_EVENTS - 1); i++) {
140 pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
141 pevent1->OSEventPtr = pevent2;
142 pevent1++;
143 pevent2++;
144 }
145 pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; //首地址的事件的类型=空闲
146 pevent1->OSEventPtr = (OS_EVENT *)0; //首地址的消息或消息队列的指针为空
147 OSEventFreeList = &OSEventTbl[0]; //空余事件管理列表=任务等待表首地址
148 #endif
149 #endif
150 //条件编译:UCOS版本>= 251 且 OS_FLAG_EN 允许产生事件标志程序代码 且 最大事件标志>0
151 #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
3 H:\SOURCE中文源代码\OS_CORE.C
152 OS_FlagInit(); //初始化事件标志结构
153 #endif
154 //条件编译:OS_Q_EN 允许 (1)产生消息队列相关代码 并且 应用中最多对列控制块的数目 > 0
155 #if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
156 OS_QInit(); //初始化事件队列结构
157 #endif
158 //条件编译:若两个条件满足时,产生以下代码
159 //OS_MEM_EN允许 (1) 或者禁止 (0) 产生内存相关代码
160 //OS_MAX_MEM_PART 最多内存块的数目
161 #if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
162 OS_MemInit(); //初始化内存块结构
163 #endif
164
165 /* ---------------------- 产生一个空闲的任务(CREATION OF 'IDLE' TASK) ----------------------- */
166 #if OS_TASK_CREATE_EXT_EN > 0 // 允许生成OSTaskCreateExt()函数
167 #if OS_STK_GROWTH == 1 // 堆栈生长方向向下
168 // 建立扩展任务[...
169 (void)OSTaskCreateExt(OS_TaskIdle, // 空闲任务
170 (void *)0, // 没有(传递参数指针)
171 &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], // 分配任务堆栈栈顶指针
172 OS_IDLE_PRIO, // 分配任务优先级
173 OS_TASK_IDLE_ID, // (未来的)优先级标识(与优先级相同)
174 &OSTaskIdleStk[0], // 分配任务堆栈栈底指针
175 OS_TASK_IDLE_STK_SIZE, // 指定堆栈的容量(检验用)
176 (void *)0, // 没有(指向用户附加的数据域的指针)
177
178 OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
179 #else // 建立扩展任务[... //堆栈生长方向向上
180 (void)OSTaskCreateExt(OS_TaskIdle, // 空闲任务
181 (void *)0, // 没有(传递参数指针)
182 &OSTaskIdleStk[0], // 分配任务堆栈栈底指针
183 OS_IDLE_PRIO, // 分配任务优先级
184 OS_TASK_IDLE_ID, // (未来的)优先级标识(与优先级相同)
185 // 分配任务堆栈栈顶指针
186 &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],
187 OS_TASK_IDLE_STK_SIZE, // 指定堆栈的容量(检验用)
188 (void *)0, // 没有(指向用户附加的数据域的指针)
189 // 没有(指向用户附加的数据域的指针)
190 OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
191 #endif
192 #else //否则只能生成OSTaskCreate()函数
193 #if OS_STK_GROWTH == 1 // 堆栈生长方向向下
194 (void)OSTaskCreate(OS_TaskIdle, // 建立任务[空闲任务、
195 (void *)0, // 没有(传递参数指针)
196 // 分配任务堆栈栈顶指针
197 &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],
198 OS_IDLE_PRIO); // 分配任务优先级
199 #else // 否则堆栈生长方向向上
200 (void)OSTaskCreate(OS_TaskIdle, // 建立任务[空闲任务、
201 (void *)0, // 没有(传递参数指针)
202 &OSTaskIdleStk[0], // 分配任务堆栈栈底指针
203 OS_IDLE_PRIO); // 分配任务优先级
204 #endif
205 #endif
206
207 /* -------------------产生一个统计任务(CREATION OF 'STATISTIC' TASK) ---------------------- */
208 #if OS_TASK_STAT_EN > 0
209 #if OS_TASK_CREATE_EXT_EN > 0 // 允许生成OSTaskCreateExt()函数
210 #if OS_STK_GROWTH == 1 // 堆栈生长方向向下
211 // 建立扩展任务[...
212 (void)OSTaskCreateExt(OS_TaskStat, // 产生一个统计任务
213 (void *)0, // 没有(传递参数指针)
214 &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1], // 分配任务堆栈栈顶指针
215 OS_STAT_PRIO, // 分配任务优先级
216 OS_TASK_STAT_ID, // (未来的)优先级标识(与优先级相同)
217 &OSTaskStatStk[0], // 分配任务堆栈栈底指针
218 OS_TASK_STAT_STK_SIZE, // 指定堆栈的容量(检验用)
219 (void *)0, // 没有(指向用户附加的数据域的指针)
220 OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
221 #else // 建立扩展任务[... //堆栈生长方向向上
222 (void)OSTaskCreateExt(OS_TaskStat, // 产生一个统计任务
223 (void *)0, // 没有(传递参数指针)
224 &OSTaskStatStk[0], // 分配任务堆栈栈底指针
225 OS_STAT_PRIO, // 分配任务优先级
226 OS_TASK_STAT_ID, // (未来的)优先级标识(与优先级相同)
227 &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1], // 分配任务堆栈栈顶指针
4 H:\SOURCE中文源代码\OS_CORE.C
228 OS_TASK_STAT_STK_SIZE, // 指定堆栈的容量(检验用
229 (void *)0, // 没有(指向用户附加的数据域的指针)
230 OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
231 #endif
232 #else //否则只能生成OSTaskCreate()函数
233 #if OS_STK_GROWTH == 1 // 堆栈生长方向向下
234 (void)OSTaskCreate(OS_TaskStat, // 产生一个统计任务
235 (void *)0, // 没有(传递参数指针)
236 &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1], // 分配任务堆栈栈顶指针
237 OS_STAT_PRIO); // 分配任务优先级
238 #else // 否则堆栈生长方向向上
239 (void)OSTaskCreate(OS_TaskStat, // 产生一个统计任务
240 (void *)0, // 没有(传递参数指针)
241 &OSTaskStatStk[0], // 分配任务堆栈栈底指针
242 OS_STAT_PRIO); // 分配任务优先级
243 #endif
244 #endif
245 #endif
246
247 #if OS_VERSION >= 204 // 判断版本是否是大于或等于2.41版
248 OSInitHookEnd(); // 调用OSInitHookEnd()钩子程序
249 #endif
250 }
OSInitHookBegin(); /* Call port specific initialization code */
OSInitHookEnd(); /* Call port specific init. code */
分析源码可知OSInit()的主要作用就是初始化各种变量。该赋初值的赋初值,被预处理的模块分别初始化一下。
需要注意的是两个钩子函数:所谓钩子函数在我看来就是一个库为了让用户可以在其中做一点事情而提供的接口。
*********************************************************************************************************
* OS INITIALIZATION HOOK
* (BEGINNING)
*初始化开始是的钩子函数
* Description: This function is called by OSInit() at the beginning of OSInit().
*
* Arguments : none
*
* Note(s) : 1) Interrupts should be disabled during this call.
*********************************************************************************************************
*/
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookBegin (void)
{
#if OS_TMR_EN > 0
OSTmrCtr = 0;
#endif
}
#endif
/*
*********************************************************************************************************
* OS INITIALIZATION HOOK
* (END)
*INIT结束时的钩子函数
* Description: This function is called by OSInit() at the end of OSInit().
*
* Arguments : none
*
* Note(s) : 1) Interrupts should be disabled during this call.
*********************************************************************************************************
*/
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookEnd (void)
{
}
#endif
一般是在os_cpu.c文件完成这个函数。
(2)OS_Start()函数分析
423 /*$PAGE*/?
424 /*
425 **********************************************************************************************
426 * 启动多个任务 START MULTITASKING
427 *
428 * 描述: 当调用OSStart()时,OSStart()从任务就绪表中找出那个用户建立的优先级最高任务的任务控制
429 * 块。然后,OSStart()调用高优先级就绪任务启动函数OSStartHighRdy(),(见汇编语言文件
430 * OS_CPU_A.ASM),这个文件与选择的微处理器有关。实质上,函数OSStartHighRdy()是将任务栈中
431 * 保存的值弹回到CPU寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码。
432 * 高优先级就绪任务启动函数OSStartHighRdy()。
433 *
434 * 参数: 无
435 *
436 * 返回: 无
437 *
438 * 注意: OSStartHighRdy() 必须:
439 * a) OSRunning为真,指出多任务已经开始
440 * b) 启动uC/OS-II之前,至少必须建立一个应用任务
441 * c) OSStartHighRdy()将永远不返回到OSStart()
442 **********************************************************************************************
443 */
444
445 void OSStart (void) //启动多个任务
446 {
447 INT8U y;
448 INT8U x;
449
450
451 if (OSRunning == FALSE) { //OSRunning已设为“真”,指出多任务已经开始
452 y = OSUnMapTbl[OSRdyGrp]; //查找最高优先级别任务号码
453 x = OSUnMapTbl[OSRdyTbl[y]];
454 OSPrioHighRdy = (INT8U)((y << 3) + x); //找出就绪态最高级任务控制块
455 OSPrioCur = OSPrioHighRdy;
456 //OSPrioCur和OSPrioHighRdy存放的是用户应用任务的优先级
457 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
458 OSTCBCur = OSTCBHighRdy;
459 OSStartHighRdy(); //调用高优先级就绪任务启动函数
460 }
461 }
OSStart()函数就是为了进行任务调度的,因为我们很快就会跳出main.c的函数,不会再跳进来,所以我们需要进行内部的指针跳出。
这个函数几乎就是任务调度的开始,从这里开始UCOS才算真正被启动。
当检测到这是一个刚刚第一次被调用的时候,首先就会计算当前最高优先级的任务是啥。这个过程有点麻烦下一篇文章单独讲。
在获得最高优先级的TCB块以后启动OSStartHighRdy()函数,这个函数需要根据平台单独实现,这里以cotex-m3平台的为例。
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; 设置PendSV的优先级
LDR R5, =NVIC_PENDSV_PRI
STR R5, [R4]
MOV R4, #0 ; set the PSP to 0 for initial context switch call
MSR PSP, R4 ; 将PSP指针拨到空闲任务的开始位置
; PENDSV回去执行PendSV_Handler_Nosave
LDR R4, =OSRunning ; OSRunning = TRUE
MOV R5, #1 ; 告诉系统开始运行
STRB R5, [R4]
;
LDR R4, =NVIC_INT_CTRL ;trigger the PendSV exception (causes context switch)
LDR R5, =NVIC_PENDSVSET ;触发PENDSV中断
STR R5, [R4]
CPSIE I ;enable interrupts at processor level
OSStartHang
B OSStartHang ;should never get here
(三)底层移植文件分析
os_cpu_a.asm
;/*********************** (C) COPYRIGHT 2010 Libraworks *************************
;* File Name : os_cpu_a.asm
;* Author : Librae
;* Version : V1.0
;* Date : 06/10/2010
;* Description : 关于STM32的底层代码提供
;*******************************************************************************/
IMPORT OSRunning ; 引入外界的变量
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSIntNesting
IMPORT OSIntExit
IMPORT OSTaskSwHook
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OS_CPU_SR_Save ; 向外部提供的函数
EXPORT OS_CPU_SR_Restore
EXPORT PendSV_Handler
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制寄存器的地址
NVIC_SYSPRI2 EQU 0xE000ED20 ; 系统优先级寄存器
NVIC_PENDSV_PRI EQU 0xFFFF0000 ; PendSV的优先级(最低)
;
NVIC_PENDSVSET EQU 0x10000000 ; 触发PendSV中断的数值
PRESERVE8
AREA |.text|, CODE, READONLY
THUMB
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL(); /* cpu_sr = OS_CPU_SaveSR(); */
; :
; :
; OS_EXIT_CRITICAL(); /* OS_CPU_RestoreSR(cpu_sr); */
; :
; :
; }
;********************************************************************************************************
OS_CPU_SR_Save
MRS R0, PRIMASK ;加载PRIMASK寄存器地址
CPSID I ;PRIMASK=1,关闭所有中断(除了nmi and hardfault)
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;
BX LR ;
;********************************************************************************************************
; START MULTITASKING 开始任务
; void OSStartHighRdy(void)
;
; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause
; the first task to start.函数触发PendSV异常(从本质上讲,是导致上下文切换)导致第一个任务开始
;
; 2) OSStartHighRdy() MUST: 关键流程
; a) Setup PendSV exception priority to lowest;
; 设置PendSV异常优先级到最低
; b) Set initial PSP to 0, to tell context switcher this is first run;
; 设置初始进程堆栈到0位置,目的是告诉上下文/任务切换器这个函数第一个执行
; c) Set OSRunning to TRUE;
; 设置OSRunning标志为TRUE
; d) Trigger PendSV exception;
; 切换PendSV异常
; e) Enable interrupts (tasks will run with interrupts enabled).
; 使能中断(任务将通过启用中断运行)
; 其通过OSSTart()完成调用
;********************************************************************************************************
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; set the PendSV exception priority
LDR R5, =NVIC_PENDSV_PRI
STR R5, [R4]
MOV R4, #0 ; set the PSP to 0 for initial context switch call
MSR PSP, R4 ; 将PSP指针拨到空闲任务的开始位置
; PENDSV回去执行PendSV_Handler_Nosave
LDR R4, =OSRunning ; OSRunning = TRUE
MOV R5, #1 ; 告诉系统开始运行
STRB R5, [R4]
;
LDR R4, =NVIC_INT_CTRL ;trigger the PendSV exception (causes context switch)
LDR R5, =NVIC_PENDSVSET ;触发PENDSV中断
STR R5, [R4]
CPSIE I ;enable interrupts at processor level
OSStartHang
B OSStartHang ;should never get here
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level) 从任务级执行一个上下文切换
; void OSCtxSw(void)
;
; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function
; triggers the PendSV exception which is where the real work is done.
;OSCtxSw 和 OSIntCtxSw 这两个是用来做任务切换的,这两个看起来都是一样的,其实它
;们都只是触发一个 PendSV 中断,具体的切换过程在 PendSV 中断服务函数里面进行。这两个
;函数看起来是一样的,但是他们的意义是不同的,OSCtxSw 是任务级切换,比如从任务 A 切换
;********************************************************************************************************
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;中断控制寄存器所在的地址 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
;中断切换到任务
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
NOP ;提示MCU我已经完成了入栈不需要再进行一次
PendSV_Handler
CPSID I ; 关闭中断 Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, PendSV_Handler_Nosave ; PSP是否等于0,判断是不是第一次调用
; 如果是的话下面一段就不需要执行了Skip register save the first time
SUBS R0, R0, #0x20 ; 0x20代表需要向堆栈压入32个字节Save remaining regs r4-11 on process stack
STM R0, {R4-R11}
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
PendSV_Handler_Nosave
PUSH {R14} ; 调用狗子函数Save LR exc_return value
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; *R0=R2 既OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy ; 是任务控制块结构体指针可以指向最该优先级的指针
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ; R0=OSTCBHighRdy R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; 恢复堆栈Restore r4-11 from new process stack
ADDS R0, R0, #0x20
MSR PSP, R0 ; R0+=0X20 R0就是星任务的栈顶指针Load PSP with new process SP
ORR LR, LR, #0x04 ; 将PSP的值变为R0 Ensure exception return uses process stack
CPSIE I ; 将LR 位2置1 ,表示返回后回到进程
BX LR ; Exception return will restore remaining context
end
OSStartHighRdy
OSCtxSw
OSIntCtxSw
OS_CPU_SR_Save
OS_CPU_SR_Restore
这个文件主要向外部提供了这五个函数,
OSStartHighRdy 主要会被OSStart调用
OSCtxSw
OSIntCtxSw
这两个是任务切换的核心函数会被os_core.c中的OSIntExit (void)和void OS_Sched (void)函数调用,在往上追溯可以看到最后在SystemTick_Handle函数中调用了 OSIntExit(); 函数至此任务调度的基本框架也就出来了。
SystemTick_Handle -》OSIntExit()-》OSCtxSw-》PendSV_Handler触发任务调度
os_cpu.c
#define OS_CPU_GLOBALS
#include "includes.h"
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* 加载任务的栈顶指针Load stack pointer */
//这些数值除了前几个,其他都是瞎几把乱写的
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000L; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
*(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
return (stk);
}
这个文件真正要看的只有这个函数其他的函数全是钩子函数,没必要看。其实也没什么就是把任务的堆栈从栈顶向下进行初始化你可能回问,这些数字是干什么的,个人感觉就是作者看心情写的。只要有个数据在上面就行。