1. 对于函数名里带有 '_' 下线符的,要么是static静态函数,要么是uc/os2要求不允许使用者调用的函数,因此对于uc/os2中,使用者能调用的函数均是OSXXXXX(XXX)这种纯字母名字的函数!
2. 注意OS_Sched()函数在调用时并不会使调用任务进入等待,而是寻找当前已ready的最高优先级任务,因此如果调用任务的优先级是最高的,那么实际上并不会引发任务切换。尤其是对于各种OSXXXPOST函数调用时,虽然有任务在pend中等待着这个post,但是如果调用Post的任务的优先级比pend的任务的优先级更高,那么此时也不会引发任务切换,pend的任务并不会立即得到执行而是要等当前任务进入dly后的sched之后再看是否是当前最高优先级的任务。
3. 在OSMemCreate函数里有一段:
plink = (void **)addr; /* Create linked list of free memory blocks */
pblk = (INT8U *)addr;
loops = nblks - 1u;
for (i = 0u; i < loops; i++) {
pblk += blksize; /* Point to the FOLLOWING block */
*plink = (void *)pblk; /* Save pointer to NEXT block in CURRENT block */
plink = (void **)pblk; /* Position to NEXT block */
}
其作用是在内存区里,把各个内存开的头4字节(指针大小)复制下个内存块的地址。
第一行的(void **)addr是把void * 型的addr指针强制转换为void **也就是指针的指针,也就是把addr这个为首地址的内存区的第一个内存块的前4个字节内存转换为一个指针,在for循环里的 *plink=(void *)pblk这一行,就是给这个指针赋值为指向pblk的指针,而pblk也就是下一个内存块的首地址。plink = (void **)pblk 这一样继续把下一个内存块的首地址的前4字节内存转换为一个指针。
再看OSMemGet()函数里有这么一段:
if (pmem->OSMemNFree > 0u) { /* See if there are any free memory blocks */
pblk = pmem->OSMemFreeList; /* Yes, point to next free memory block */
pmem->OSMemFreeList = *(void **)pblk; /* Adjust pointer to new free list */
pmem->OSMemNFree--; /* One less memory block in this partition */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; /* No error */
return (pblk); /* Return memory block to caller */
}
其中第三行 pmem->OSMemFreeList=*(void **)pblk 这一行,pblk是当前空余内存块的首地址,先将pblk强制转换为指针的指针,则*pblk 从32位长度的值变成4字节的指针,也是指向下一个内存块的指针,再赋值给pmem的OSMemFreeList,挺不错的技巧!
4. 在TMR段里,有一句 typedef void (*OS_TMR_CALLBACK)(void *ptmr, void *parg);
这是用typedef定义了一个函数指针类型,OS_TMR_CALLBACK就是这个类型,以后可以直接 OS_TMR_CALLBACK func; 来定义一个函数指针,其参数为两个void *,返回无
5. 对于定时器的使用,要注意一点是
void OSTimeTickHook (void)
{
#if OS_APP_HOOKS_EN > 0
App_TimeTickHook();
#endif
#if OS_TMR_EN > 0
OSTmrCtr++;
if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) {
OSTmrCtr = 0;
OSTmrSignal();
}
#endif
}
其定时器的基数是在心跳里进行累加到指定数值后再发出信号然后在OSTmr_Task里对OSTmrTime进行累加的,因此如果在OSTmrStart时,如果OSTmrCtr是大于0的,那么实际上第一个定时的时间会少OSTmrCtr个心跳的时间,这个要注意一下。
另外如果使能了OSStat,在OSStatInit里有OSTimeDly存在,因此会发现初始化后系统开跑时会有OSTime不为0。
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();
}
6. 注意二值信号量与互斥量的区别,互斥量在post时,只有调用post的任务拥有当前的互斥量才会有效,否则无效,因此对于互斥量,任务调用了pend就必须在后面调用post,否则下次pend是永远得不到互斥量的。而对于二值信号量,post信号量的可以是其他的任务,因此可以在其他地方post来给pend的任务信号量,比如定时器中。
pip = (INT8U)(pevent->OSEventCnt >> 8u); /* Get priority inheritance priority of mutex */
prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get owner's original priority */
if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) { /* See if posting task owns the MUTEX */
OS_EXIT_CRITICAL();
return (OS_ERR_NOT_MUTEX_OWNER);
}
7. 对于OS_ENTER_CRITICAL和OS_EXIT_CRITICAL的使用要小心,如果在两者之间有需要进行任务调度的函数存在则会出错,比如OSStatInit()对统计任务进行数据初始化的函数就需要任务调度,不要放在enter与exit之间!
OSStatInit();
OSTimeSet(0);
mtmr=OSTmrCreate(0,20,OS_TMR_OPT_PERIODIC,mycallback,(void *)0,(INT8U *)"Test Timer",&err);
OSTmrStart(mtmr,&err);
Str_mbox=OSMboxCreate((void *)0);
LED_Sem=OSSemCreate(0);
OS_ENTER_CRITICAL();
// OSStatInit(); 错误用法!
OSTaskCreate(my_task,(void*)0,(OS_STK*)&MY_TASK_STK[TASK_STK_SIZE-1],MY_TASK_PRIO);
OSTaskCreate(you_task,(void*)0,(OS_STK*)&YOU_TASK_STK[TASK_STK_SIZE-1],YOU_TASK_PRIO);
OS_EXIT_CRITICAL();