1 /*
2 ************************************************************************************************
3 * uC/OS-II实时控制内核
4 * 主要的包含文件
5 * 内核管理文件
6 *
7 * 文 件: OS_CORE.C 内核结构管理文件
8 * 作 者: Jean J. Labrosse
9 * 中文注解: 钟常慰 zhongcw @ 126.com 整理: lin-credible 译注版本:1.0 请尊重原版内容
10 ************************************************************************************************
11 */
12
13 #ifndef OS_MASTER_FILE //如果没有定义OS_MASTER_FILE主文件,则
14 #define OS_GLOBALS //定义全程变量 OS_GLOBALS
15 #include "includes.h" //包含"includes.h"文件,部分C语言头文件的汇总打包文件
16 #endif //定义结束
17
18 /*
19 ************************************************************************************************
20 * MAPPING TABLE TO MAP BIT POSITION TO BIT MASK
21 *
22 * 注意: 变址索引表是对应OSRbyTbl[i]的位值(0~7),给定值符合OSMapTbl[]的数据(二进制)
23 Index into table is desired bit position, 0..7
24 * Indexed value corresponds to bit mask
25 ************************************************************************************************
26 */
27 //OSMapTbl[]:就绪任务表;对应OSRdy Grp和OSRbyTbl[i]的位值(0~7)
28
29 INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
30
31 /*
32 ************************************************************************************************
33 * 最高优先级任务查找表(PRIORITY RESOLUTION TABLE)
34 *
35 * 注意: 变址索引表是位模式,找出就绪态最高优先级别任务,给定值应符合高优先级位位值(0~7)
36 Index into table is bit pattern to resolve highest priority
37 * Indexed value corresponds to highest priority bit position (i.e. 0..7)
38 ************************************************************************************************
39 */
40 //OSUnMapTbl[]:最高优先级任务查找表;对应OSRdy Grp和OSRbyTbl[i]的位值(0~7)
41
42 INT8U const OSUnMapTbl[] = {
43 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
44 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
45 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
46 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
47 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
48 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
49 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
50 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
51 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
52 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
53 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
54 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
55 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
56 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
57 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
58 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
59 };
60 /*$PAGE*/
61 /*
62 ************************************************************************************************
63 * 初始值(INITIALIZATION)
64 *
65 * 描述: 初始化uC/OS-II。对这个函数的调用必须在调用OSStart()函数之前。
66 * OSStart()函数真正开始运行多任务时
67 * 意见: 无
68 * 返回: 无
69 ************************************************************************************************
70 */
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)
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], // 分配任务堆栈栈顶指针
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 }
251 /*$PAGE*/
252 /*
253 ***********************************************************************************************
254 * 中断函数正在执行 ENTER ISR
255 *
256 * 描述: 通知uC/OS-II,一个中断处理函数正在进行,这有助于uC/OS-II掌握中断嵌套情况。
257 * OSIntEnter ()和OSIntExit (函数联合使用),调用者,只能在中断程序中。
258 *
259 * 参数: 无
260 *
261 * 返回: 无
262 *
263 * 注意: 1) 在任务级不能调用该函数
264 * 2) 如果系统使用的处理器能够执行自动的独立执行读取-修改-写入的操作,那么就可以直接递增
265 * 中断嵌套层数(OSIntNesting),这样可以避免调用函数所带来的额外开销。在中断服务子程序中
266 * 给OSIntNesting加1是不会有问题的,因为给OSIntNesting加1时,中断是关闭的
267 * 3) 中断嵌套深度可达255
268 ************************************************************************************************
269 */
270 void OSIntEnter (void) //中断函数正在执行()
271 {
272 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
273 OS_CPU_SR cpu_sr;
274 #endif
275
276
277 OS_ENTER_CRITICAL(); //关闭中断
278 if (OSIntNesting < 255) { //如果中断嵌套小于255
279 OSIntNesting++; //中断嵌套计数变量加1
280 }
281 OS_EXIT_CRITICAL(); //打开中断
282 }
283 /*$PAGE*/
284 /*
285 ***********************************************************************************************
286 * 中断函数已经完成 EXIT ISR
287 *
288 * 描述: 通知uC/OS-II,一个中断服务已经执行完成,这有助于uC/OS-II掌握中断嵌套的情况。通常
289 * OSIntExit()和OSIntEnter()联合使用。当最后一层嵌套的中断执行完毕时,如果有更高优先级任
290 * 务准备就绪,则uC/OS-II会调用任务调度函数。在这种情况下,中断返回到更高优先级的任务,而
291 * 不是被中断了的任务。调用者,只能在中断程序中。
292 *
293 * 参数: 无
294 *
295 * 返回: 无
296 *
297 * 注意: 1) 在任务级不能调用该函数,并且即使没有调用OSIntEnter()函数,而是使用直接递增
298 * OSIntNesting的方法,也必须调用OSIntExit()。
299 * 2) 给调度器上锁用于禁止任务调度 (查看 OSSchedLock()函数)
300 **********************************************************************************************
301 */
302
303 void OSIntExit (void) //脱离中断函数
304 {
305 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
306 OS_CPU_SR cpu_sr;
307 #endif
308
309
310 OS_ENTER_CRITICAL(); //关闭中断
311 if (OSIntNesting > 0) { //如果中断嵌套大于0
312 OSIntNesting--; //中断嵌套计数变量减1
313 }
314 //1)中断嵌套层数计数器和锁定嵌套计数器(OSLockNesting)二者都必须是零
315 //2)OSRdyTbl[]所需的检索值Y是保存在全程变量OSIntExitY中
316 //3)检查具有最高优先级别的就绪任务的优先级是否是正在运行的任务的优先级
317 //4)将任务控制块优先级表保存到指向最高级优先级就绪任务控制块的指针
318 //5)上下文切换的次数(统计任务计数器)
319 //6)做中断任务切换
320 if ((OSIntNesting == 0) && (OSLockNesting == 0)) { //1)
321 OSIntExitY = OSUnMapTbl[OSRdyGrp]; //2)
322 OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
323 if (OSPrioHighRdy != OSPrioCur) { //3)
324 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//4
325 OSCtxSwCtr++; //5)
326 OSIntCtxSw(); //6)
327 }
328 }
329 OS_EXIT_CRITICAL(); //打开中断
330 }
331 /*$PAGE*/
332 /*
333 ************************************************************************************************
334 * 给调度器上锁 PREVENT SCHEDULING
335 *
336 * 描述: 本函数用于禁止任务调度,直到任务完成后调用给调度器开锁函数OSSchedUnlock()为止。调用
337 * OSSchedlock()的任务保持对CPU的控制权,尽管有个优先级更高的任务进入了就绪态。然而,此时
338 * 中断是可以被识别的,中断服务也能得到(假设中断是开着的)。OSSchedlock()和OSSchedUnlock()
339 * 必须成对使用.变量OSLockNesting跟踪OSSchedLock()函数被调用的次数,以允许嵌套的函数包含临
340 * 界段代码,这段代码其它任务不得干预.uC/OS-II允许嵌套深度达255层.当OSLockNesting等于零时,
341 * 调度重新得到允许.函数OSSchedLock()和OSSchedUnlock()的使用要非常谨慎,因为它们影响uC/OS-II
342 * 对任务的正常管理。
343 * 说明:当OSLockNesting减到零的时候,OSSchedUnlock()调用OSSched。OSSchedUnlock()是被某任务调用的,
344 * 在调度器上锁的期间,可能有什么事件发生了并使一个更高优先级的任务进入就绪态。
345 *
346 * 参数: 无
347 *
348 * 返回: 无
349 *
350 * 警告:不得调用把当前任务挂起的程序
351 *
352 * 注意: 1) 调用OSSchedLock()以后,用户的应用程序不得使用任何能将现行任务挂起的系统调用。也就说,
353 * 用户程序不得调用OSMboxPend()、OSQPend()、OSSemPend()、OSTaskSuspend(OS_PR1O_SELF)、
354 * OSTimeDly()或OSTimeDlyHMSM(),直到OSLockNesting回零为止。因为调度器上了锁,用户就锁住
355 * 了系统,任何其它任务都不能运行。
356 * 2) 当低优先级的任务要发消息给多任务的邮箱、消息队列、信号量时,用户不希望高优先级的任
357 * 务在邮箱、队列和信号量没有得到消息之前就取得了CPU的控制权,此时,用户可以使用禁止
358 * 调度器函数。
359 ***********************************************************************************************
360 */
361
362 #if OS_SCHED_LOCK_EN > 0 //允许生产OSSchedLock()函数
363 void OSSchedLock (void) //给调度器上锁函数
364 {
365 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
366 OS_CPU_SR cpu_sr;
367 #endif
368
369
370 if (OSRunning == TRUE) { //如果有多个任务在期待
371 OS_ENTER_CRITICAL(); //关闭中断
372 if (OSLockNesting < 255) { //上锁嵌套是否大于255
373 OSLockNesting++; //给上锁嵌套加1
374 }
375 OS_EXIT_CRITICAL(); //打开中断
376 }
377 }
378 #endif
379
380 /*$PAGE*/
381 /*
383 * 给调度器解锁 ENABLE SCHEDULING
384 *
385 * 描述: 本函数用于解禁任务调度
386 *
387 * 参数: 无
388 *
389 * 返回: 无
390 *
391 * 注意: 1) OSSchedlock()和OSSchedUnlock()必须成对使用,在使用OSSchedUnlock()函数之前必须使
392 * 用OSSchedLock()函数
393 *********************************************************************************************
394 */
395
396 #if OS_SCHED_LOCK_EN > 0 //允许生产OSSchedUnlock()函数
397 void OSSchedUnlock (void) //给调度器解锁函数
398 {
399 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
400 OS_CPU_SR cpu_sr;
401 #endif
402
403
404 if (OSRunning == TRUE) { //如果有多个任务在期待
405 OS_ENTER_CRITICAL(); //关闭中断
406 if (OSLockNesting > 0) { //上锁嵌套是否大于0
407 OSLockNesting--; //给上锁嵌套减1
408 //如果函数不是在中断服务子程序中调用的,且调度允许的,
409 if ((OSLockNesting == 0) && (OSIntNesting == 0)) {
410
411 OS_EXIT_CRITICAL(); //打开中断
412 OS_Sched(); //进入任务调度
413 } else {
414 OS_EXIT_CRITICAL(); //打开中断
415 }
416 } else {
417 OS_EXIT_CRITICAL(); //打开中断
418 }
419 }
420 }
421 #endif
422
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 }
462 /*$PAGE*/
463 /*
464 **********************************************************************************************
465 * 统计任务初始化 STATISTICS INITIALIZATION
466 *
467 * 描述: 统计初始化函数OSStatInit()决定在没有其它应用任务运行时,空闲计数器(OSIdleCtr)的计数
468 * 有多快。这个任务每秒执行一次,以确定所有应用程序中的任务消耗了多少CPU时间。当用户的
469 * 应用程序代码加入以后,运行空闲任务的CPU时间就少了,OSIdleCtr就不会像原来什么任务都不
470 * 运行时有那么多计数。要知道,OSIdleCtr的最大计数值是OSStatInit()在初始化时保存在计数
471 * 器最大值OSIdleCtrMax中的。CPU利用率:
472 *
473 * 空闲计数值OSIdleCtr
474 * CPU 使用率Usage (%) = 100 * (1 - --------------------------------)
475 * 设定最大空闲计数值OSIdleCtrMax
476 *
477 * 参数: 无
478 *
479 * 返回: 无
480 **********************************************************************************************
481 */
482
483 #if OS_TASK_STAT_EN > 0 //允许生产OSStatInit()函数
484 void OSStatInit (void) //统计任务初始化
485 {
486 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
487 OS_CPU_SR cpu_sr;
488 #endif
489
490
491 OSTimeDly(2); //调用延迟函数OSTimeDly()将自身延时2个时钟节拍以停止自身的运行
492 //这是为了使OSStatInit()与时钟节拍同步
493 OS_ENTER_CRITICAL(); //关闭中断
494 OSIdleCtr = 0L; //执行OSStartInit()时,空闲计数器OSIdleCtr被清零
495 OS_EXIT_CRITICAL(); //打开中断
496 OSTimeDly(OS_TICKS_PER_SEC); //将自身延时整整一秒
497 //(因为没有其它进入就绪态的任务,OSTaskIdle()又获得了CPU的控制权)
498 OS_ENTER_CRITICAL(); //关闭中断
499 OSIdleCtrMax = OSIdleCtr; //空闲计数器将1秒钟内计数的值存入空闲计数器最大值OSIdleCtrMax中
500 OSStatRdy = TRUE; //将统计任务就绪标志OSStatRdy设为"真",以此来允许两个时钟节拍
501 //以后OSTaskStat()开始计算CPU的利用率
502 OS_EXIT_CRITICAL(); //打开中断
503 }
504 #endif
505 /*$PAGE*/
506 /*
507 **********************************************************************************************
508 * 时钟节拍函数 PROCESS SYSTEM TICK
509 *
510 * 描述: uC/OS需要用户提供周期性信号源,用于实现时间延时和确认超时。节拍率应在每秒10次到100次
511 * 之间,或者说10到100Hz。时钟节拍率越高,系统的额外负荷就越重.时钟节拍的实际频率取决于
512 * 用户应用程序的精度。时钟节拍源可以是专门的硬件定时器,也可以是来自50/60Hz交流电源的
513 * 信号
514 *
515 * 参数: 无
516 *
517 * 返回: 无
518 **********************************************************************************************
519 */
520
521 void OSTimeTick (void) //时钟节拍函数
522 {
523 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
524 OS_CPU_SR cpu_sr;
525 #endif
526 OS_TCB *ptcb; //定义任务控制块优先级表变量
527
528 OSTimeTickHook(); //调用户·自定义函数(钩子函数)
529 #if OS_TIME_GET_SET_EN > 0 //允许生成OSTimeGet() 函数代码
530 OS_ENTER_CRITICAL(); //关闭中断
531 OSTime++; //累加从开机以来的时间,用的是一个无符号32位变量
532 OS_EXIT_CRITICAL(); //打开中断
533 #endif
534 ptcb = OSTCBList; //保存任务控制块列表首地址
535
536 //从OSTCBList开始,沿着OS_TCB链表做,一直做到空闲任务
537 while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {
538 OS_ENTER_CRITICAL(); //关闭中断
539 if (ptcb->OSTCBDly != 0) { //如果任务等待时的最多节拍数不为0
540 if (--ptcb->OSTCBDly == 0) { //如果任务等待时的最多节拍数为0
541
542 //而确切被任务挂起的函数OSTaskSuspend()挂起的任务则不会进入就绪态
543 //执行时间直接与应用程序中建立了多少个任务成正比
544 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == 0x00) {
545 //当某任务的任务控制块中的时间延时项OSTCBDly减到了零,这个任务就进入了就绪态
546 OSRdyGrp |= ptcb->OSTCBBitY;
547 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
548 } else {
549 ptcb->OSTCBDly = 1; //否则
550 } //允许任务等待时的最多节拍数为1
551 }
552 }
553 ptcb = ptcb->OSTCBNext; //指向任务块双向链接表的后链接
554 OS_EXIT_CRITICAL(); //打开中断
555 }
556 }
557 /*$PAGE*/
558 /*
559 ***********************************************************************************************
560 * 获得版本号GET VERSION
561 *
562 * 描述: 这个函数是返回一个uC/OS-II的版本值. 这个返回值乘100是uC/OS-II的版本号. 也就是版本2.00
563 * would be returned as 200.
564 *
565 * 参数: 无
566 *
567 * 返回: uC/OS-II的版本号除以100.
568 ***********************************************************************************************
569 */
570
571 INT16U OSVersion (void) //获得版本号
572 {
573 return (OS_VERSION); //返回版本值
574 }
575
576 /*$PAGE*/
577 /*
578 ***********************************************************************************************
579 * 虚拟函数 DUMMY FUNCTION
580 *
581 * 描述: 这个函数不做任务工作. 它是随便访问OSTaskDel()函数.
582 *
583 * 参数: 无
584 *
585 * 返回: 无
586 ***********************************************************************************************
587 */
588
589 #if OS_TASK_DEL_EN > 0 //允许生成 OS_Dummy()函数
590 void OS_Dummy (void) //建立一个虚拟函数
591 { //不做任何工作
592 }
593 #endif
594
595 /*$PAGE*/
596 /*
597 **********************************************************************************************
598 * 使一个任务进入就绪态 MAKE TASK READY TO RUN BASED ON EVENT OCCURING
599 *
600 * 描述: 当发生了某个事件,该事件等待任务列表中的最高优先级任务(HPT)要置于就绪态时,该事件对应
601 * 的OSSemPost(),OSMboxPost(),OSQPost(),和OSQPostFront()函数调用OSEventTaskRdy()实现
602 * 该操作。换句话说,该函数从等待任务队列中删除HPT任务,并把该任务置于就绪态
603 *
604 * 参数: pevent is a pointer to the event control block corresponding to the event.
605 *
606 * msg is a pointer to a message. This pointer is used by message oriented services
607 * such as MAILBOXEs and QUEUEs. The pointer is not used when called by other
608 * service functions.
609 *
610 * msk is a mask that is used to clear the status byte of the TCB. For example,
611 * OSSemPost() will pass OS_STAT_SEM, OSMboxPost() will pass OS_STAT_MBOX etc.
612 *
613 * 返回: 无
614 *
615 * 注意: 这个函数是uC/OS-II内部函数,你不可以在应用程序调用它,调用此函数也应当关闭中断
616 **********************************************************************************************
617 */
618 #if OS_EVENT_EN > 0 //各类消息事件是否允许
619 //使一个任务进入就绪态
620 INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
621 {
622 OS_TCB *ptcb;
623 INT8U x;
624 INT8U y;
625 INT8U bitx;
626 INT8U bity;
627 INT8U prio;
628
629 //1)首先计算HPT任务在.OSEventTbl[]中的字节索引,其结果是一个从0到OS_LOWEST_PRIO/8+1之间的数
630 //2)并利用该索引得到该优先级任务在.OSEventGrp中的位屏蔽码
631 //3)判断HPT任务在.OSEventTbl[]中相应位的位置
632 //4)其结果是一个从0到OS_LOWEST_PRIO/8+1之间的数,以及相应的位屏蔽码
633 //5)根据以上结果,OSEventTaskRdy()函数计算出HPT任务的优先级
634 //6)然后就可以从等待任务列表中删除该任务了
635 y = OSUnMapTbl[pevent->OSEventGrp]; //1)
636 bity = OSMapTbl[y]; //2)
637 x = OSUnMapTbl[pevent->OSEventTbl[y]]; //3)
638 bitx = OSMapTbl[x]; //4)
639 prio = (INT8U)((y << 3) + x); //5)
640 if ((pevent->OSEventTbl[y] &= ~bitx) == 0x00) { //6)
641 pevent->OSEventGrp &= ~bity;
642 }
643 //7)任务的TCB中包含有需要改变的信息。知道了HPT任务的优先级,就可得到指向该任务的TCB的指针
644 //8)因为最高优先级任务运行条件已经得到满足,必须停止OSTimeTick()函数对.OSTCBDly域的递减操作,
645 // 所以OSEventTaskRdy()直接将该域清澈0
646 //9)因为该任务不再等待该事件的发生,所以本函数将其任务控制块中指向事件控制块的指针指向NULL
647 //10)如果OSEventTaskRdy()是由OSMboxPost()或者OSQPost()调用的,该函数还要将相应的消息传递给
648 // HPT,放在它的任务控制块中
649 ptcb = OSTCBPrioTbl[prio]; //7)
650 ptcb->OSTCBDly = 0; //8)
651 ptcb->OSTCBEventPtr = (OS_EVENT *)0; //9)
652 #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
653 ptcb->OSTCBMsg = msg; //10)
654 #else
655 msg = msg;
656 #endif
657 //11)当OSEventTaskRdy()被调用时,位屏蔽码msk作为参数传递给它。该参数是用于对任务控制块中的
658 // 位清零的位屏蔽码,和所发生事件的类型相对应
659 //12)根据.OSTCBStat判断该任务是否已处于就绪状态
660 //13)如果是, 则将HPT插入到uC/OS-II的就绪任务列表中。注意,HPT任务得到该事件后不一定进入就绪
661 // 状态,也许该任务已经由于其它原因挂起了
662 ptcb->OSTCBStat &= ~msk; //11)
663 if (ptcb->OSTCBStat == OS_STAT_RDY) { //12)
664 OSRdyGrp |= bity; //13)
665 OSRdyTbl[y] |= bitx; //返回就绪态任务的优先级
666 }
667 return (prio);
668 }
669 #endif
670 /*$PAGE*/
671 /*
672 **********************************************************************************************
673 * 使一个任务进入等待某事件发生状态 MAKE TASK WAIT FOR EVENT TO OCCUR
674 *
675 * 描述: 当某个任务须等待一个事件的发生时,信号量、互斥型信号量、邮箱以及消息队列会通过相应的
676 * PEND函数调用本函数,使当前任务从就绪任务表中脱离就绪态,并放到相应的事件控制块ECB的等
677 * 待任务表中
678 *
679 * 参数: pevent 分配给事件控制块的指针,为等待某事件发生的任务
680 *
681 * 返回: 无
682 *
683 * 注意: 这个函数是uC/OS-II内部函数,你不可在应用程序中调用它,调用OS_EventTO()也应当关闭中断
684 **********************************************************************************************
685 */
686 #if OS_EVENT_EN > 0 //各类消息事件是否允许
687 void OS_EventTaskWait (OS_EVENT *pevent) //使一个任务进入等待某事件发生状态(ECB指针)
688 { //将指向事件控制块ECB的指针放到任务的任务控制块TCB中,建立任务与事件控制块ECB之间的链接
689 OSTCBCur->OSTCBEventPtr = pevent;
690 //将任务从就绪任务表中删除
691 if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
692 OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
693 }
694 //把该任务放到事件控制块ECB的等待事件的任务列表中
695 pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
696 pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
697 }
698 #endif
699 /*$PAGE*/
700 /*
701 **********************************************************************************************
702 * 由于超时而将任务置为就绪态 MAKE TASK READY TO RUN BASED ON EVENT TIMEOUT
703 *
704 * 描述: 如果在预先指定的等待时限内任务等待的事件没有发生,那么本函数会因为等待超时而将任务的
705 * 状态置为就绪态。在这种情况下,信号量、互斥型信号量、邮箱以及消息队列会通过PEND函数调
706 * 用本函数,以完成这项工作
707 *
708 * 参数: pevent 分配给事件控制块的指针,为超时就绪态的任务
709 *
710 * 返回: 无
711 *
712 * 注意: 这个函数是uC/OS-II内部函数,你不可以在应用程序中调用它,调用OS_EventTO()也应当关闭中断
713 **********************************************************************************************
714 */
715 #if OS_EVENT_EN > 0 //消息事件是否 > 0
716 void OS_EventTO (OS_EVENT *pevent) //由于超时而将任务置为就绪态(ECB指针)
717 { //本函数必须从事件控制块ECB中等待任务列表中将该任务删除
718 if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
719 pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
720 }
721 OSTCBCur->OSTCBStat = OS_STAT_RDY; //该任务被置为就绪态
722 OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; //从任务控制块TCB中将事件控制块ECB的指针删除
723 }
724 #endif
725 /*$PAGE*/
726 /*
727 ***********************************************************************************************
728 * 事件控制块列表初始化 INITIALIZE EVENT CONTROL BLOCK'S WAIT LIST
729 *
730 * 描述: 当建立一个信号量、邮箱或者消息队列时,相应的建立函数OSSemInit(),OSMboxCreate(),或者
731 * OSQCreate()通过调用OSEventWaitListInit()对事件控制块中的等待任务列表进行初始化。该函数
732 * 初始化一个空的等待任务列表,其中没有任何任务。该函数的调用参数只有一个,就是指向需要初
733 * 始化的事件控制块的指针pevent。
734 *
735 * 参数: pevent 传递一个指针给事件控制块,该指针变量就是创建信号量、互斥型信号量、邮箱或消息队
736 * 列时分配的事件控制块的指针
737 *
738 * 返回: 无
739 *
740 * 注意: 这个函数是uC/OS-II内部函数,你不可以调用它。
741 **********************************************************************************************
742 */
743 #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
744 //当以上各种事件允许时
745 void OS_EventWaitListInit (OS_EVENT *pevent) //事件控制块列表初始化(事件控制块指针)
746 {
747 INT8U *ptbl; //地址指针变量
748
749
750 pevent->OSEventGrp = 0x00; //等待任务所在的组为空
751 ptbl = &pevent->OSEventTbl[0]; //保存任务等待表首地址
752 //OSEventTbl[](共8个字节OSMapTb1[prio&0x07])
753 #if OS_EVENT_TBL_SIZE > 0 //等待任务列表字节>0
754 *ptbl++ = 0x00; //OSEventTbl[0]清空
755 #endif
756
757 #if OS_EVENT_TBL_SIZE > 1 //等待任务列表字节>1
758 *ptbl++ = 0x00; //OSEventTbl[1]清空
759 #endif
760
761 #if OS_EVENT_TBL_SIZE > 2 //等待任务列表字节>2
762 *ptbl++ = 0x00; //OSEventTbl[3]清空
763 #endif
764
765 #if OS_EVENT_TBL_SIZE > 3 //等待任务列表字节>3
766 *ptbl++ = 0x00; //OSEventTbl[3]清空
767 #endif
768
769 #if OS_EVENT_TBL_SIZE > 4 //等待任务列表字节>4
770 *ptbl++ = 0x00; //OSEventTbl[4]清空
771 #endif
772
773 #if OS_EVENT_TBL_SIZE > 5 //等待任务列表字节>5
774 *ptbl++ = 0x00; //OSEventTbl[5]清空
775 #endif
776
777 #if OS_EVENT_TBL_SIZE > 6 //等待任务列表字节>6
778 *ptbl++ = 0x00; //OSEventTbl[6]清空
779 #endif
780
781 #if OS_EVENT_TBL_SIZE > 7 //等待任务列表字节>7
782 *ptbl = 0x00; //OSEventTbl[7]清空
783 #endif
784 }
785 #endif
786 /*$PAGE*/
787 /*
788 ***********************************************************************************************
789 * 任务调度
790 *
791 * 描述: uC/OS-II总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪
792 * 个任务运行了的工作是由调度器(Scheduler)完成的。任务级的调度是由函数OSSched()完成的。
793 * 中断级的调度是由另一个函数OSIntExt()完成的eduling).
794 *
795 * 参数: none
796 *
797 * 返回: none
798 *
799 * 注意: 1) 这是一个uC/OS-II内部函数,你不能在应用程序中使用它
800 * 2) 给调度器上锁用于禁止任务调度 (查看 OSSchedLock()函数)
801 *
802 * 说明: 1)任务切换很简单,由以下两步完成,将被挂起任务的微处理器寄存器推入堆栈,然后将较高优先
803 * 级的任务的寄存器值从栈中恢复到寄存器中。在uC/OS-II中,就绪任务的栈结构总是看起来跟刚
804 * 刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,uC/OS-II运行就绪态的任
805 * 务所要做的一切,只是恢复所有的CPU寄存器并运行中断返回指令。为了做任务切换,运行
806 * OS_TASK_SW(),人为模仿了一次中断。多数微处理器有软中断指令或者陷阱指令TRAP来实现上述操
807 * 作。中断服务子程序或陷阱处理(Trap hardler),也称作事故处理(exception handler),必须提
808 * 供中断向量给汇编语言函数OSCtxSw()。OSCtxSw()除了需要OS_TCBHighRdy指向即将被挂起的任务,
809 * 还需要让当前任务控制块OSTCBCur指向即将被挂起的任务,参见第8章,移植uC/OS-II,有关于
810 * OSCtxSw()的更详尽的解释。
811 * 2) OSSched()的所有代码都属临界段代码。在寻找进入就绪态的优先级最高的任务过程中,为防止中
812 * 断服务子程序把一个或几个任务的就绪位置位,中断是被关掉的。为缩短切换时间,OSSched()全
813 * 部代码都可以用汇编语言写。为增加可读性,可移植性和将汇编语言代码最少化,OSSched()是用
814 * C写的。
815 ***********************************************************************************************
816 */
817
818 void OS_Sched (void) //任务调度函数
819 {
820 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
821 OS_CPU_SR cpu_sr;
822 #endif
823 INT8U y; //定义一个8位整数y
824
825
826 OS_ENTER_CRITICAL(); //关闭中断
827 //如果中断嵌套次数>0,且上锁(调度器)嵌套次数>0,函退出,不做任何调度
828 if ((OSIntNesting == 0) && (OSLockNesting == 0)) {
829 //如果函数不是在中断服务子程序中调用的,且调度允许的,则任务调度函数将找出进入就绪态的
830 //最高优先级任务,进入就绪态的任务在就绪表中OSRdyTbl[ ]中相应位置位.
831 y = OSUnMapTbl[OSRdyGrp];
832 OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
833 //找到最高优先级任务后,函数检查这个优先级最高的任务是否是当前正在运行的任务,以避免不
834 //必要的任务调度,多花时间
835 if (OSPrioHighRdy != OSPrioCur) {
836 //为实现任务切换,OSTCBHighRdy必须指向优先级最高的那个任务控制块OS_TCB,这是通过将
837 //以OSPrioHighRdy为下标的OSTCBPrioTbl[]数组中的那个元素赋给OSTCBHighRdy来实现的
838 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
839 OSCtxSwCtr++; //统计计数器OSCtxSwCtr加1,以跟踪任务切换次数
840 OS_TASK_SW(); //最后宏调用OS_TASK_SW()来完成实际上的任务切换
841 }
842 }
843 OS_EXIT_CRITICAL(); //打开中断
844 }
845 /*$PAGE*/
846 /*
847 ***********************************************************************************************
848 * 空闲任务 IDLE TASK
849 *
850 * 描述: 这个函数是uC/OS-II内部函数,uC/OS-II总要建立一个空闲任务,这个任务在没有其它任务进入
851 * 就绪态时投入运行。这个空闲任务永远设为最低优先级,即OS_LOWEST_PRIO.空闲任务不可能被应
852 * 用软件删除。
853 *
854 * 参数: 无
855 *
856 * 返回: 无
857 *
858 * 注意: 1) OSTaskIdleHook()可以允许用户在函数中写入自己的代码,可以借助OSTaskIdleHook(),让
859 * CPU执行STOP指令,从而进入低功耗模式,当应用系统由电池供电时,这种方式特别有用。
860 * 2) 这个函数永远处于就绪态,所以不要在OSTaskIdleHook()中调用可以使任务挂起的PEND函数、
861 * OSTineDly???()函数和OSTaskSuspend()函数
862 **********************************************************************************************
863 */
864
865 void OS_TaskIdle (void *pdata) //空闲任务函数(指向一个数据结构)
866 {
867 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
868 OS_CPU_SR cpu_sr;
869 #endif
870
871
872 pdata = pdata; //参数等于本身,防止一些编译器不能编译
873 for (;;) { //无限循环
874 OS_ENTER_CRITICAL(); //关闭中断
875 OSIdleCtr++; //32位空闲计数器加1,提供给统计任务消耗CPU事件
876 OS_EXIT_CRITICAL(); //打开中断
877 OSTaskIdleHook(); //空闲任务钩子程序
878 }
879 }
880 /*$PAGE*/
881 /*
882 **********************************************************************************************
883 * 统计任务 STATISTICS TASK
884 *
885 * 描述: uC/OS-II有一个提供运行时间统计的任务。这个任务叫做OSTaskStat(),如果用户将系统定义常
886 * 数OS_TASK_STAT_EN(见文件OS_CFG.H)设为1,这个任务就会建立。一旦得到了允许,OSTaskStat()
887 * 每秒钟运行一次(见文件OS_CORE.C),计算当前的CPU利用率。换句话说,OSTaskStat()告诉用户
888 * 应用程序使用了多少CPU时间,用百分比表示,这个值放在一个有符号8位整数OSCPUsage中,精读
889 * 度是1个百分点。
890 * 如果用户应用程序打算使用统计任务,用户必须在初始化时建立一个唯一的任务,在这个任务中
891 * 调用OSStatInit()(见文件OS_CORE.C)。换句话说,在调用系统启动函数OSStart()之前,用户初
892 * 始代码必须先建立一个任务,在这个任务中调用系统统计初始化函数OSStatInit(),然后再建立
893 * 应用程序中的其它任务
894 *
895 * OSIdleCtr
896 * OSCPUUsage = 100 * (1 - ------------) (units are in %)
897 * OSIdleCtrMax
898 *
899 * 参数: pdata 指向一个数据结构,该结构用来在建立统计任务时向任务传递参数
900 *
901 * 返回: 无
902 *
903 * 注意: 1) uC/OS-II已经将空闲任务的优先级设为最低,即OS_LOWEST_PR10,统计任务的优先级设为次
904 * 低,OS_LOWEST_PR10-1.
905 * 2) 因为用户的应用程序必须先建立一个起始任务TaskStart()。在使用统计任务前,用户必须首
906 * 先调用的是uC/OS-II中的系统初始化函数OSInit(),
907 * 3) 在创建统计任务之前,为了保持系统达到稳定状态,需要延迟5秒钟,你必须至少延时2秒钟
908 * 以设定最大空闲计数值
909 * 3) We delay for 5 seconds in the beginning to allow the system to reach steady state
910 * and have all other tasks created before we do statistics. You MUST have at least
911 * a delay of 2 seconds to allow for the system to establish the maximum value for
912 * the idle counter.
913 ********************************************************************************************** 常慰
914 */
915
916 #if OS_TASK_STAT_EN > 0 //允许生产OS_TaskStat()函数
917 void OS_TaskStat (void *pdata) //统计任务(指向一个数据结构)
918 {
919 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
920 OS_CPU_SR cpu_sr;
921 #endif
922 INT32U run; //定义一个运算变量
923 INT8S usage; //定义一个使用变量
924
925
926 pdata = pdata; //数据结构指针等于本身,防止编译器不能编译
927 while (OSStatRdy == FALSE) { //判断统计任务就绪标志OSStatRdy设为"假"
928 OSTimeDly(2 * OS_TICKS_PER_SEC); //延时2秒钟,等待统计任务做好准备
929 }
930 for (;;) { //无限循环
931 OS_ENTER_CRITICAL(); //关闭中断
932 OSIdleCtrRun = OSIdleCtr; //获得当前的空闲计数值
933 run = OSIdleCtr; //并保存到运算变量中,以便运算
934 OSIdleCtr = 0L; //空闲计数器OSIdleCtr被清零
935 OS_EXIT_CRITICAL(); //打开中断
936 if (OSIdleCtrMax > 0L) { //如果最大空闲计数值 > 0
937 usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); 求CPU利用率
938 if (usage >= 0) { //不能是负的百分值
939 OSCPUUsage = usage; //如果是正的值,保存它
940 } else {
941 OSCPUUsage = 0; //如果是负值,设为0
942 }
943 } else { //如果最大空闲计数值不 > 0
944 OSCPUUsage = 0; //如果是负值,设为0
945 }
946 OSTaskStatHook(); //调用钩子函数,可添加自己的代码
947 OSTimeDly(OS_TICKS_PER_SEC); //调用延迟函数OSTimeDly()将自身延时1个时钟节拍以停止自身的运
行
948 }
949 }
950 #endif
951 /*$PAGE*/
952 /*
953 **********************************************************************************************
954 * 任务控制块初始化 INITIALIZE TCB
955 *
956 * 描述: 这个函数是uC/OS-II内部函数,在建立任务时调用的初始化任务控制块OS_TCB函数,含7个参数,
957 * (查看 OSTaskCreate() 和 OSTaskCreateExt()).
958 *
959 * 参数: prio 任务的优先级
960 *
961 * ptos OSTaskInit()建立栈结构以后,ptos是指向栈顶的指针,且保存在OS_TCB的OSTCBStkPrt中
962 *
963 * pbos 指向栈底的指针,保存在OSTCBStkBottom变元中
964 *
965 * id 任务标志符(0..65535),保存在.OSTCBId中
966 *
967 * stk_size 堆栈的容量,保存在OS_TCB的OSTABStkSize中
968 *
969 * pext OS_TCB中的扩展指针,.OSTCBExtPtr的值
970 *
971 * opt OS_TCB的选择项,保存在.OSTCBOpt中
972 *
973 * 返回: OS_NO_ERR 调用成功
974 * OS_NO_MORE_TCB 没有更多的任务控制块被分配,将无法创建新的任务
975 *
976 * 注意: 这个函数是uC/OS-II内部函数,你不可以调用它。
977 **********************************************************************************************
978 */
979 //初始化任务控制块TCB(优先级指针、栈顶指针、栈底指针、任务标志符、堆栈容量、扩展指针、选择项)
980 INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size,
981 void *pext, INT16U opt)
982 {
983 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
984 OS_CPU_SR cpu_sr;
985 #endif
986 OS_TCB *ptcb; //定义一个PCB变量
987
988
989 OS_ENTER_CRITICAL(); //关闭中断
990 ptcb = OSTCBFreeList; //分配一个空任务控制块给ptcb
991 if (ptcb != (OS_TCB *)0) { //如果缓冲池有空余TCB,这个TCB被初始化
992 OSTCBFreeList = ptcb->OSTCBNext; //指向TCB的双向链接的后链接
993 OS_EXIT_CRITICAL(); //打开中断
994 ptcb->OSTCBStkPtr = ptos; //指向当前TCB的栈顶指针(输入的数据)
995 ptcb->OSTCBPrio = (INT8U)prio; //保存当前TCB的优先级别(输入的数据)
996 ptcb->OSTCBStat = OS_STAT_RDY; //设定当前TCB的状态字(内容为(准备完毕))
997 ptcb->OSTCBDly = 0; //允许任务等待的最大字节节拍为0
998
999 #if OS_TASK_CREATE_EXT_EN > 0 //允许生成OSTaskCreateExt()函数
1000 ptcb->OSTCBExtPtr = pext; //指向用户定义的任务控制块(扩展指针)
1001 ptcb->OSTCBStkSize = stk_size; //设定堆栈的容量
1002 ptcb->OSTCBStkBottom = pbos; //指向指向栈底的指针
1003 ptcb->OSTCBOpt = opt; //保存OS_TCB的选择项
1004 ptcb->OSTCBId = id; //保存任务标志符
1005 #else //否则使用旧的参数
1006 pext = pext; //扩展指针
1007 stk_size = stk_size; //堆栈的容量
1008 pbos = pbos; //栈底的指针
1009 opt = opt; //选择项
1010 id = id; //任务标志符
1011 #endif
1012
1013 #if OS_TASK_DEL_EN > 0 //允许生成 OSTaskDel() 函数代码函数
1014 ptcb->OSTCBDelReq = OS_NO_ERR; //如果可以删除任务本身,可以从每个
1015 //OS_TCB中节省出一个布尔量
1016 #endif
1017 //对一些参数提前运算,为了节省CPU的操作事件
1018 ptcb->OSTCBY = prio >> 3;
1019 ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
1020 ptcb->OSTCBX = prio & 0x07;
1021 ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
1022
1023 #if OS_EVENT_EN > 0 //如果不打算在应用程序中使用各类事件
1024 ptcb->OSTCBEventPtr = (OS_EVENT *)0; //OS_TCB中OSTCBEventPt就不会出现
1025 #endif //针对的事件为信号量,互斥型信号量、消息邮箱、消息队列
1026 //当满足 版本大于2.51 且 事件标志允许 且 有最大事件标志 及 允许删除任务
1027 #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) && (OS_TASK_DEL_EN > 0)
1028 ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0;//则向事件标志节点的指针被初始化为空指针
1029 #endif
1030
1031 #if (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
1032 ptcb->OSTCBMsg = (void *)0; //满足以上条件,指向传递给任务的消息指针为0空指针
1033 #endif
1034
1035 #if OS_VERSION >= 204 //如果版本大于2.04
1036 OSTCBInitHook(ptcb); //允许使用OSTCBInitHook(ptcb)函数,可对其加代码
1037 #endif //主要增加OS_TCB扩展,浮点运算、MMU寄存器、与任务相关内容,调用此程序时中断开着的
1038
1039 OSTaskCreateHook(ptcb); //调用户建立任务钩子程序
1040 //该函数能够扩展[OSTaskCreate()或OSTaskCreateExt()函数]
1041 //当OS_CPU_HOOKS_EN为1时,OSTaskCreateHook()可以在OS_CPU.C中定义
1042 //若OS_CPU_HOOKS_EN为0时,则可以在任何其它地方定义
1043 //调用此程序时中断开着的。
1044 OS_ENTER_CRITICAL();
1045 OSTCBPrioTbl[prio] = ptcb;
1046 ptcb->OSTCBNext = OSTCBList; //链接到任务控制块链接串
1047 ptcb->OSTCBPrev = (OS_TCB *)0;
1048 if (OSTCBList != (OS_TCB *)0) {
1049 OSTCBList->OSTCBPrev = ptcb;
1050 }
1051 OSTCBList = ptcb; //让该任务进入就绪态
1052 OSRdyGrp |= ptcb->OSTCBBitY;
1053 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
1054 OS_EXIT_CRITICAL(); //打开中断
1055 return (OS_NO_ERR); //调用成功,最后让此函数返回到调用函数[OSTaskCreate()或
1056 } //OSTaskCreateExt()函数],返回值表示分配到任务控块,并初始化了
1057 OS_EXIT_CRITICAL(); //打开中断
1058 return (OS_NO_MORE_TCB); //没有更多的任务控制块被分配,将无法创建新的任务
1059 }
1060