uC/OS-II系统学习笔记(5)—— uC/OS-II概述下篇
2017-08-20 16:09
274 查看
1 就绪表
1.1 OS_LOWEST_PRIO & OS_MAX_TASKS
每个任务有不同的优先级,从0到63,在OS_CFG.H里对最低的优先级有专门的宏定义OS_LOWEST_PRIO。可以通过修改这个宏定义,来修改系统任务的最低优先级,但是注意,最低优先级永远都是被空闲应用idle task占用的。另外还有一个宏定义OS_MAX_TASKS表示系统最多有几个任务。之所以在就绪表说这两个,是因为这两个对就绪表或多或少有影响。1.2 OSRedyGrp & OSRdyTbl[OS_RDY_TBL_SIZE]
每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRedyGrp和OSRdyTbl[]。在 OSRdyGrp 中,任务按优先级分组,8个任务为一组。OSRdyGrp 中的每一位表示 8 组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时, 就绪表 OSRdyTbl[]中的相应元素的相应位也置位。就绪表 OSRdyTbl[]数组的大小取决于OS_LOWEST_PR1O(见文件 OS_CFG.H)。当用户的应用程序中任务数目比较少时,减少 OS_LOWEST_PR1O 的值可以降低μC/OS-Ⅱ对RAM(数据空间)的需求量。引用中写的是OSRedyGrp,这个缩写怎么看怎么奇怪啊
4000
,去源码看了下,应该是OSRdyGrp,和OSRdyTbl[]一样都在文件ucos_ii.h里声明的,而且就单独一对放在一起,可见联系紧密了。
OS_EXT OS_PRIO OSRdyGrp; /* Ready list group */ OS_EXT OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */
教程没有提,但源码里有OSRdyTbl数组的容量OS_RDY_TBL_SIZE。
这个变量也是在ucos_ii.h里定义的。和它一起被定义还有些其他表的大小。如下即定义OS_RDY_TBL_SIZE的源码。
#if OS_LOWEST_PRIO <= 63u #define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u) /* Size of event table */ #define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u) /* Size of ready table */ #else #define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 16u + 1u)/* Size of event table */ #define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 16u + 1u)/* Size of ready table */ #endif
最低任务优先级肯定是小于64的,至于另外大于64的目前我也不清楚是干什么的。两个表容量,一个就绪表容量,一个事件表容量,后者以后会介绍到的。注意到表容量,最低优先级/8+1。可见最低优先级越小,表的大小就越小,这就可以节省RAM了。至于除以8再加1,举个例子,64个优先级,最低优先级为63,63 / 8 + 1 = 8。任务是每8个为一组,所以要除以8。
说了这么多,那引文中的就能解释了。任务优先级最大有64个,那么OSRdyTbl是一个容量最大为8的数组,数组中每一个变量都是8位的,每一位都可以代表一个任务,那么总共就可以最多代表64个任务了。任务进入就绪态时,对应的位置1。那么OSRdyGrp,也是个8位的变量,可以说相当于一个目录,当 OSRdyTbl的8个任务组中,有哪一组有任务进入就绪态了,那么对应的OSRdyGrp对应的位会置1。换句话说,OSRdyGrp的每一位,是OSRdyTbl中的每一个8位变量中,8个位的逻辑或值。
1.3 使任务进入就绪态
1.3.1 教程中给任务就绪标志位置1
OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
上述两行代码,实际上就是把OSRdyGrp和OSRdyTbl对应的位置1。OSMapTbl的值如下:
Index | Bit Mask(Binary) |
---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
先看第一行代码,那么右移三位相当于除以8,即prio >> 3 = 6。即OSMapTbl [prio >>3] = 01000000,那么第一行代码即是把OSRdyGrp的bit6置1。
第二行代码,prio & 0x07 即 prio % 8 = 2,即OSMapTbl[prio & 0x07] = 00000100。第二行代码即OSRdyTbl[6]的bit2置1。
总的来说,就是OSRdyTbl数组中的第7组任务中的第2个任务的标志位1,以及对应的OSRdyTbl第七组的标志位置1。
看下面这幅图,X就是一组任务里的任务,Y就是总共的8组任务。
1.3.2 源码中给任务就绪标志位置1
我去翻了一下源码,发现源码里没有找到OSMapTbl这个数组。源码中是利用任务控制块OS_TCBs的OSTCBBitX和OSTCBBitY进行置位的。OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
其中ptcb是OSTCBPrioTbl[OS_LOWEST_PRIO + 1u],是个任务表吧。。之前在教程也没看到,只在源码看到过。OS_LOWEST_PRIO之前说过了是最低优先级,加个1的原因是因为有优先级0,那总共任务优先级有OS_LOWEST_PRIO+1个。OSTCBPrioTbl的类型就是指向OS_TCBs的指针,之前说过的这个结构体中含有OSTCBBitY和OSTCBBitX。这样看这两个变量就充当了OSMap的角色了。
同上一节的图一样,Y表示8个任务组,X表示每个任务组的8个任务。
1.3.3 找出进入就绪态优先级最高的任务
y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x;
OSUnMapTbl是一个一维数组,共256位,如下所示
INT8U const OSUnMapTbl[256] = { 0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F */ 5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F */ 6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F */ 5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F */ 7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F */ 5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF */ 6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF */ 5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF */ 4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u /* 0xF0 to 0xFF */ };
通过OSRdyTbl和OSUnMapTbl找最高优先级的优先级编号。先自己排一下怎么找整理下思路,首先要从OSRdy中找出被置1的最低位,比如值为10101010,那么被置1的最低位是bit1,也就是说优先级最高的任务的优先级编号是8~15,属于任务组1。那么再从OSUnMapTbl[1]里找出被置1的最低位,比如值为10101010,那么被置1的最低位是bit1,也就是任务组1的任务优先级1,优先级位1*8 + 1 = 9。源码也是通过这样的思想来找到最高优先级的。
OSUnMapTbl看起来很乱,其实是有一定规律。这个表中是以16*16排列的。比如因为如果最低位为1,那么肯定是一个奇数,所以OSUnMapTbl所有的奇数位上的值都为0。如果bit4为最低位被置1的,也就是说OSUnMapTbl中,值为4的所在位置肯定是16的倍数,也就是表中的第一列,可以看到4是每隔1行出现一次。出现的位置编号里只能够被16整除而不能够被32,48之类的整除。思路有点乱,大致这个意思了。
2 任务调度
心态炸了,好不容易写完的不小心关闭没保存。void OSSched (void) { INT8U y; OS_ENTER_CRITICAL(); if ((OSLockNesting | OSIntNesting) == 0) { (1) y = OSUnMapTbl[OSRdyGrp]; (2) OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); (2) if (OSPrioHighRdy != OSPrioCur) { (3) OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (4) OSCtxSwCtr++; (5) OS_TASK_SW(); (6) } } OS_EXIT_CRITICAL(); }
以上源码是教程中给的,和我手里的源码不太一样。OSLockNesting,如果用户至少调用了一次给任务调度上锁函数OSSchedLock(),那么OSLockNesting > 0。OSIntNesting,如果在中断服务子程序中调用了OSSched(),那么OSIntNesting > 0。由此可见,只有两者都不满足的情况下才会进行任务调度。
标(2)的两句是为了选出就绪表任务里的最高优先级,选出之后,(3)判断所选出的最高优先级是不是就是当前的任务,如果不是才会进行任务调度,是的话就不需要调度了。当不是当前任务时,(4)要先把指针OSTCBHighRdy,指向就绪的最高优先级的任务的任务控制块TCBs。然后调用OS_TASK_SW()完成任务的切换。至于OSCrxSwCtr自增1,是为了计数任务切换次数,目前不知道 有什么用。
void OS_Sched (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif OS_ENTER_CRITICAL(); if (OSIntNesting == 0u) { /* Schedule only if all ISRs done and ... */ if (OSLockNesting == 0u) { /* ... scheduler is not locked */ OS_SchedNew(); OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ #if OS_TASK_PROFILE_EN > 0u OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */ #endif OSCtxSwCtr++; /* Increment context switch counter */ OS_TASK_SW(); /* Perform a context switch */ } } } OS_EXIT_CRITICAL(); }
以上是我手里源码的任务调度。头两句用#if-#endif包裹,是为了确定进入临界段代码的方法。以前说过一种,就是关闭中断,这只是其中一种办法,而现在这里用的办法是把CPU的状态字保存在一个局部变量里,讲道理我也不太懂什么意思。然后就是进入临界代码段,就是任务调度这部分是不允许被打断。首先两个判断嵌套,道理和上一份源码一样判断中断嵌套层数和是否关闭了任务调度,然后调用了一个OS_SchedNew()函数,是用来选出最高优先级的,顺便也把指针指向了最高优先级任务的任务控制块结构体。再之后判断这个最高优先级的任务是不是当前任务,这里和教程的源码刚好顺序相反,一个先指针赋值再判断,一个先判断再指针赋值。之后就是愉快的调用OS_TASK_SW()完成任务切换。当然这之前还有两行代码,也是OSCtxCtr++,同教程一样。比教程多一行的是被#if-#endif包裹的,判断条件是OS_TASK_PROFILE_EN > 0,其中这个值为1,其宏定义的注释是Include variables in OS_TCB for profiling,没咋看懂,反正里面的操作也是用来计数任务切换次数的。
3 调度器上锁和解锁
给调度器上锁使用的是OSSchedLock(),给调度器解锁用的是OSSchedUnLock()。当调度器上锁后,用户的应用程序不能够以任何形式将当前现行任务挂起,也就是说,不能够使用OsMoxPend(),OSQPend(),OSSemPend(),OSTaskSuspend,OSTimeDly()或OSTimeDlyHMSM(),直到调度器解锁。
上锁和解锁函数是要成对使用的。并且,好像上锁函数和解锁函数是可以嵌套的,在上一章节说调度器的时候提到过一个变量OSLockNesting,就是用来计数嵌套层数的,当这个变量为0时,才可以进行任务调度。
教程给的上锁开锁函数以及源码的也不一样,还是先说教程再说源码。
void OSSchedLock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); OSLockNesting++; OS_EXIT_CRITICAL(); } }
以上为教程提供的OSSchedLock()。先判断OSRunning为真时,才会进行调度器上锁。这个标志位时标志内核是否再运行的,以免内核没有运行时出现错误。但是需要考虑下什么时候内核不会运行?在初始化之前吗?这句话只是为了保证初始化之前错误调用调度器上锁函数,还是有其他原因,暂且不知道。但是内核在运行时,就会进行调度器上锁,上锁函数还是很简单的,只是把OSLockNesting自增1,这句代码需要在临界段执行,因为虽然是自增1,但可能分为几次运算实现。只要OSLockNesting不为0,在任务调度函数OSSched()是就会判断为禁止调度并且不执行调度的实际内容。
void OSSchedUnlock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting | OSIntNesting) == 0) { (1) OS_EXIT_CRITICAL(); OSSched(); (2) } else { OS_EXIT_CRITICAL(); } } else { OS_EXIT_CRITICAL(); } } }
以上为教程提供的OSSchedUnlock()函数。同样要判断内核是否在运行。同样是临界代码段。先判断OSLockNesting是否大于0,如果大于0,就会自减1。减完1之后还会顺带进行一次任务调度。
#if OS_SCHED_LOCK_EN > 0u void OSSchedLock (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (OSRunning == OS_TRUE) { /* Make sure multitasking is running */ OS_ENTER_CRITICAL(); if (OSIntNesting == 0u) { /* Can't call from an ISR */ if (OSLockNesting < 255u) { /* Prevent OSLockNesting from wrapping back to 0 */ OSLockNesting++; /* Increment lock nesting level */ } } OS_EXIT_CRITICAL(); } } #endif
以上为源码的OSSchedLock()。其实和教程提供的一样。只是多了一个宏判断OS_SCHED_LOCK_EN是否大于0以及一个进入临界段代码的方式。OS_SCHED_LOCK_EN是在OS_CFG.H里完成宏定义的。如果定义为0,那么就说明整个系统不采用调度器上锁。
#if OS_SCHED_LOCK_EN > 0u void OSSchedUnlock (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (OSRunning == OS_TRUE) { /* Make sure multitasking is running */ OS_ENTER_CRITICAL(); if (OSLockNesting > 0u) { /* Do not decrement if already 0 */ OSLockNesting--; /* Decrement lock nesting level */ if (OSLockNesting == 0u) { /* See if scheduler is enabled and ... */ if (OSIntNesting == 0u) { /* ... not in an ISR */ OS_EXIT_CRITICAL(); OS_Sched(); /* See if a HPT is ready */ } else { OS_EXIT_CRITICAL(); } } else { OS_EXIT_CRITICAL(); } } else { OS_EXIT_CRITICAL(); } } } #endif
源码里的OSSchedUnlock(),不多解释了。
4 空闲任务
系统总会至少有一个任务在跑的,这个任务就是空闲任务,是系统规定的,它的优先级是最低的,只完成给一个变量加1的操作。void OSTaskIdle (void *pdata) { pdata = pdata; for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); } }
void OS_TaskIdle (void *p_arg) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif p_arg = p_arg; /* Prevent compiler warning for not using 'p_arg' */ for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); OSTaskIdleHook(); /* Call user definable HOOK */ }
以上分別是教程和源码里的空闲任务,不多说了。源码里有两处,一是把p_arg自己赋值给自己,说是确保编译器提示没有用到p_arg,够严谨的。另外一个是OSTaskIdleHook(),因为在用户使用系统的时候有可能想在空闲任务上做一些自定义的事情,又不能随便乱改,所以专门调用这个函数,在这里进行自己的一些操作。
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251 void OSTaskIdleHook (void) { #if OS_APP_HOOKS_EN > 0 App_TaskIdleHook(); #endif } #endif
这个就是用户可以自定义的东西,可以看到里面又是调用了一个App_TaskIdleHook(),这个函数内核只做了声明,可以完全自己定义。另外要调用这个函数还做了判断,确保OS_APP_HOOKS_EN是非0的,就是使能这个可以用户自定义空闲任务操作的功能,以后这种东西就不多说了。
5 统计任务
和空闲任务一样,之前说过的,内核会保留优先级最低的两个任务,一个是最低优先级的空闲任务,另一个就是倒数第二的统计任务。void OSStatInit (void) { OSTimeDly(2); OS_ENTER_CRITICAL(); OSIdleCtr = 0L; OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC); OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; OSStatRdy = TRUE; OS_EXIT_CRITICAL(); }
以上为教程所给的统计任务初始化函数。在后面做源码分析。
void OSTaskStat (void *pdata) { INT32U run; INT8S usage; pdata = pdata; while (OSStatRdy == FALSE) { (1) OSTimeDly(2 * OS_TICKS_PER_SEC); } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; run = OSIdleCtr; OSIdleCtr = 0L; OS_EXIT_CRITICAL(); if (OSIdleCtrMax > 0L) { usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); (2) if (usage > 100) { OSCPUUsage = 100; } else if (usage < 0) { OSCPUUsage = 0; } else { OSCPUUsage = usage; } } else { OSCPUUsage = 0; } OSTaskStatHook(); (3) OSTimeDly(OS_TICKS_PER_SEC); } }
以上为教程所给的统计任务函数。在后面做源码分析。
#if OS_TASK_STAT_EN > 0u void OSStatInit (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif OSTimeDly(2u); /* Synchronize with clock tick */ OS_ENTER_CRITICAL(); OSIdleCtr = 0uL; /* Clear idle counter */ OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC / 10u); /* Determine MAX. idle counter value for 1/10 second */ OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; /* Store maximum idle counter count in 1/10 second */ OSStatRdy = OS_TRUE; OS_EXIT_CRITICAL(); }
以上为源码中的统计任务初始化。没有对教程的代码进行解析是觉得教程提供的讲解感觉有点乱,也可能是自己脑子不够用总之就是没怎么看懂。源码中,注释讲到这个统计任务初始化函数如何进行CPU使用率的统计的。首先要在没有其他任何用户任务运行时,先只跑空闲任务来确定,一秒钟内空闲任务里那个加1,能加多少次。这里注意是1秒钟,不是一个时钟节拍。CPU使用率计算方法是CPU Usage = 100*(1 - OSIdleCtr/OSIdleCtrMax)。这个OSIdleCtrMax不是确认的值,正是在没有用户任务情况下,空闲任务进行加1操作取得的值。而OSIdleCtr就是整个系统以及用户任务运行时,实际的空闲任务进行加1操作能取得的值。
#if OS_TASK_STAT_EN > 0u void OS_TaskStat (void *p_arg) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif p_arg = p_arg; /* Prevent compiler warning for not using 'p_arg' */ while (OSStatRdy == OS_FALSE) { OSTimeDly(2u * OS_TICKS_PER_SEC / 10u); /* Wait until statistic task is ready */ } OSIdleCtrMax /= 100uL; if (OSIdleCtrMax == 0uL) { OSCPUUsage = 0u; #if OS_TASK_SUSPEND_EN > 0u (void)OSTaskSuspend(OS_PRIO_SELF); #else for (;;) { OSTimeDly(OS_TICKS_PER_SEC); } #endif } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; /* Obtain the of the idle counter for the past second */ OSIdleCtr = 0uL; /* Reset the idle counter for the next second */ OS_EXIT_CRITICAL(); OSCPUUsage = (INT8U)(100uL - OSIdleCtrRun / OSIdleCtrMax); OSTaskStatHook(); /* Invoke user definable hook */ #if (OS_TASK_STAT_STK_CHK_EN > 0u) && (OS_TASK_CREATE_EXT_EN > 0u) OS_TaskStatStkChk(); /* Check the stacks for each task */ #endif OSTimeDly(OS_TICKS_PER_SEC / 10u); /* Accumulate OSIdleCtr for the next 1/10 second */ } } #endif
以上代码为源码的统计任务函数。不做介绍了,我自己也有点蒙。
6 中断处理
中断处理就是在响应中断服务函数的时候,内核对当前状态的一些操作。包括进入中断以及退出中断。void OSIntEnter (void) { OS_ENTER_CRITICAL(); OSIntNesting++; OS_EXIT_CRITICAL(); }
以上是教程提供的进入中断服务函数时进行的操作。就是单纯给OSIntNesting加1。这个不解释了。
void OSIntEnter (void) { if (OSRunning == OS_TRUE) { if (OSIntNesting < 255u) { OSIntNesting++; /* Increment ISR nesting level */ } } }
以上是源码提供的进入中断服务函数时进行的操作。比教程给的可靠一些。首先判断目前内核是否在运行,然后判断OSIntNesting是不是到了最大值,因为这个变量是8位的,最大只能加到255。我不明白的就是为什么这里加1操作没有在临界段进行。
void OSIntExit (void) { OS_ENTER_CRITICAL(); (1) if ((--OSIntNesting | OSLockNesting) == 0) { (2) OSIntExitY = OSUnMapTbl[OSRdyGrp]; (3) OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OSIntCtxSw(); (4) } } OS_EXIT_CRITICAL(); }
以上是教程提供的脱离中断的操作。首先整个操作在临界段执行。判断OSIntNesting自减1后是否为0,再顺带判断是否有给调度器上锁。如果满足两个变量都为0,则进行任务调度。否则,就直接退出。
void OSIntExit (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (OSRunning == OS_TRUE) { OS_ENTER_CRITICAL(); if (OSIntNesting > 0u) { /* Prevent OSIntNesting from wrapping */ OSIntNesting--; } if (OSIntNesting == 0u) { /* Reschedule only if all ISRs complete ... */ if (OSLockNesting == 0u) { /* ... and not locked. */ OS_SchedNew(); OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ #if OS_TASK_PROFILE_EN > 0u OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */ #endif OSCtxSwCtr++; /* Keep track of the number of ctx switches */ OSIntCtxSw(); /* Perform interrupt level ctx switch */ } } } OS_EXIT_CRITICAL(); } }
以上是源码提供的脱离中断的操作。机制一样,不多说了。
7 系统的初始化
void OSInit (void) { OSInitHookBegin(); /* Call port specific initialization code */ OS_InitMisc(); /* Initialize miscellaneous variables */ OS_InitRdyList(); /* Initialize the Ready List */ OS_InitTCBList(); /* Initialize the free list of OS_TCBs */ OS_InitEventList(); /* Initialize the free list of OS_EVENTs */ #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) OS_FlagInit(); /* Initialize the event flag structures */ #endif #if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u) OS_MemInit(); /* Initialize the memory manager */ #endif #if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u) OS_QInit(); /* Initialize the message queue structures */ #endif OS_InitTaskIdle(); /* Create the Idle Task */ #if OS_TASK_STAT_EN > 0u OS_InitTaskStat(); /* Create the Statistic Task */ #endif #if OS_TMR_EN > 0u OSTmr_Init(); /* Initialize the Timer Manager */ #endif OSInitHookEnd(); /* Call port specific init. code */ #if OS_DEBUG_EN > 0u OSDebugInit(); #endif }
源码提供的系统初始化函数。
8 系统的启动
void OSStart (void) { if (OSRunning == OS_FALSE) { OS_SchedNew(); /* Find highest priority's task priority number */ OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ } }
源码提供的系统多任务启动函数。需要注意的是,用户自定义的任务的创建,至少得有一个要在系统初始化和启动之间进行。只有完成了任务的创建,才能进行多任务并调度。
相关文章推荐
- uC/OS-II系统学习笔记(2)——实时操作系统概念中篇
- uC/OS-II系统学习笔记(4)—— uC/OS-II概述上篇
- uC/OS-II系统学习笔记(6)—— 任务管理
- uC/OS-II系统学习笔记(7)—— 时间管理
- uC/OS-II系统学习笔记(1)——实时操作系统概念上篇
- uC/OS-II系统学习笔记(3)——实时操作系统概念下篇
- 移植uc/os满足的条件
- 初学者EFM32上移植uC/OSII
- 二维指针*(void **)的研究(uC/OS-II案例)
- uC/OS-II 函数之邮箱管理相关函数
- 【专题】uC/OS-II内核架构解析(1)---嵌入式RTOS
- uC/OS-III 时钟节拍,时间管理,时间片调度
- uC/OS 的中断——uC/OS学习笔记(三)
- 分析M16C62在uC/OS-II嵌入式系统中的应用
- uC/OS II在Microchip MCU上的移植
- uC/OS-II源码分析(总体思路一)
- UC/OS基础知识之任务
- UC/OS-II基础知识之系统的初始化
- uC/OS II 函数说明 之–OSTaskCreate()与OSTaskCreateExt()
- uC/OS-III移植基本条件