1. 钱柜娱乐官网
  2. 论坛
  3. 外包
  4. 下载
  5. Datasheet
  6. 21ic公开课
  7. 更多

您当前的位置:钱柜娱乐官网 > 基础知识 > 单片机  > 单片机常见问题

嵌入式μC/OS-II系统基础之邵贝贝节选

来源:互联网 关键字:   
 μC/OS-II 通过 uCOS_II.H 中定义的 OS_EVENT 数据结构来维护一个事件控制块的所有信息 [程序清单 L6.1],也就是本章开篇讲到的事件控制块 ECB。该结构中除了包含了事件本身的定 义,如用于信号量的计数器,用于指向邮箱的指针,以及指向消息队列的指针数组等,还定义 了等待该事件的所有任务的列表。 typedef struct { void *OSEventPtr; /* 指向消息或者消息队列的指针 */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表 */ INT16U OSEventCnt; /* 计数器(当事件是信号量时) */ INT8U OSEventType; /* 时间类型 */ INT8U OSEventGrp; /* 等待任务所在的组 */ } OS_EVENT; .OSEventPtr 指针,只有在所定义的事件是邮箱或者消息队列时才使用。 当所定义的事件是 邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构,详见 6.06 节消息邮箱和 6.07 节消息队列。 .OSEventTbl[] 和 .OSEventGrp 很像前面讲到的 OSRdyTbl[]和 OSRdyGrp,只不过前两者 包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。(见 3.04 节 就 绪表) .OSEventCnt 当事件是一个信号量时,.OSEventCnt 是用于信号量的计数器,(见 6.05 节 信号量)。 e .OSEventType 定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱 (OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值 来调用相应的系统函数,以保证对其进行的操作的正确性。 下面的代码将一个任务放到事件的等待任务列表中。 程序清单 L6.2 ——将一个任务插入到事件的等待任务列表中 pevent->OSEventGrp |= OSMapTbl[prio >> 3]; pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; 程序清单 L6.3 从等待任务列表中删除一个任务 if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) { pevent->OSEventGrp &= ~OSMapTbl[prio >> 3]; } 程序清单 L6.4 在等待任务列表中查找最高优先级的任务 y = OSUnMapTbl[pevent->OSEventGrp]; x = OSUnMapTbl[pevent->OSEventTbl[y]]; prio = (y << 3) + x; void OSEventWaitListInit (OS_EVENT *pevent) { INT8U i; pevent->OSEventGrp = 0x00; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { pevent->OSEventTbl[i] = 0x00; } } 程序清单 L6.6 使一个任务进入就绪状态 void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk) { OS_TCB *ptcb; INT8U x; INT8U y; INT8U bitx; INT8U bity; INT8U prio; y = OSUnMapTbl[pevent->OSEventGrp]; (1) bity = OSMapTbl[y]; (2) x = OSUnMapTbl[pevent->OSEventTbl[y]]; (3) bitx = OSMapTbl[x]; (4) prio = (INT8U)((y << 3) + x); (5) if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { (6) pevent->OSEventGrp &= ~bity; } ptcb = OSTCBPrioTbl[prio]; (7) ptcb->OSTCBDly = 0; (8) ptcb->OSTCBEventPtr = (OS_EVENT *)0; (9) #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN ptcb->OSTCBMsg = msg; (10) #else msg = msg; #endif ptcb->OSTCBStat &= ~msk; (11) if (ptcb->OSTCBStat == OS_STAT_RDY) { (12) OSRdyGrp |= bity; (13) OSRdyTbl[y] |= bitx; } } 程序清单 L6.7 使一个任务进入等待状态 void OSEventTaskWait (OS_EVENT *pevent) { OSTCBCur->OSTCBEventPtr = pevent; (1) if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { (2) OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; (3) pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; } 程序清单 L6.8 因为等待超时将任务置为就绪状态 void OSEventTO (OS_EVENT *pevent) { if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { (1) pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBStat = OS_STAT_RDY; (2) OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (3) } 信号量 μC/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整 数 (0 到 65,535 之间) ; 另一个是由等待该信号量的任务组成的等待任务表。 用户要在 OS_CFG.H 中将 OS_SEM_EN 开关量常数置成 1,这样μC/OS-II 才能支持信号量。信号量 μC/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整 数 (0 到 65,535 之间) ; 另一个是由等待该信号量的任务组成的等待任务表。 用户要在 OS_CFG.H 中将 OS_SEM_EN 开关量常数置成 1,这样μC/OS-II 才能支持信号量。 程序清单 L6.9 建立一个信号量 OS_EVENT *OSSemCreate (INT16U cnt) { OS_EVENT *pevent; OS_ENTER_CRITICAL(); pevent = OSEventFreeList; (1) if (OSEventFreeList != (OS_EVENT *)0) { (2) OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { (3) pevent->OSEventType = OS_EVENT_TYPE_SEM; (4) pevent->OSEventCnt = cnt; (5) OSEventWaitListInit(pevent); (6) } return (pevent); (7) } 程序清单 L6.10 等待一个信号量 void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1) OS_EXIT_CRITICAL(); *err = OS_ERR_EVENT_TYPE; } if (pevent->OSEventCnt > 0) { (2) pevent->OSEventCnt--; (3) OS_EXIT_CRITICAL(); *err = OS_NO_ERR; 6-15 } else if (OSIntNesting > 0) { (4) OS_EXIT_CRITICAL(); *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_SEM; (5) OSTCBCur->OSTCBDly = timeout; (6) OSEventTaskWait(pevent); (7) OS_EXIT_CRITICAL(); OSSched(); (8) OS_ENTER_CRITICAL(); if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { (9) OSEventTO(pevent); (10) OS_EXIT_CRITICAL(); *err = OS_TIMEOUT; } else { OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (11) OS_EXIT_CRITICAL(); *err = OS_NO_ERR; } } } 程序清单 L6.11 发出一个信号量 INT8U OSSemPost (OS_EVENT *pevent) { OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1) OS_EXIT_CRITICAL(); return (OS_ERR_EVENT_TYPE); } if (pevent->OSEventGrp) { (2) OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); (3) OS_EXIT_CRITICAL(); OSSched(); (4) return (OS_NO_ERR); } else { if (pevent->OSEventCnt < 65535) { pevent->OSEventCnt++; (5) OS_EXIT_CRITICAL(); return (OS_NO_ERR); } else { OS_EXIT_CRITICAL(); return (OS_SEM_OVF); } } } 程序清单 L6.12 无等待地请求一个信号量 INT16U OSSemAccept (OS_EVENT *pevent) { INT16U cnt; OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1) OS_EXIT_CRITICAL(); return (0); } cnt = pevent->OSEventCnt; (2) if (cnt > 0) { (3) pevent->OSEventCnt--; (4) } OS_EXIT_CRITICAL(); return (cnt); (5) } 程序清单 L6.13 查询一个信号量的状态 INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata) { INT8U i; INT8U *psrc; INT8U *pdest; OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1) OS_EXIT_CRITICAL(); return (OS_ERR_EVENT_TYPE); } pdata->OSEventGrp = pevent->OSEventGrp; (2) psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSCnt = pevent->OSEventCnt; (3) OS_EXIT_CRITICAL(); return (OS_NO_ERR); } 程序清单 L6.14 建立一个邮箱 OS_EVENT *OSMboxCreate (void *msg) 6-20 { OS_EVENT *pevent; OS_ENTER_CRITICAL(); pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; (1) pevent->OSEventPtr = msg; (2) OSEventWaitListInit(pevent); } return (pevent); (3) } 程序清单 L6.15 等待一个邮箱中的消息 void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { void *msg; OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1) OS_EXIT_CRITICAL(); *err = OS_ERR_EVENT_TYPE; return ((void *)0); } msg = pevent->OSEventPtr; if (msg != (void *)0) { (2) pevent->OSEventPtr = (void *)0; (3) OS_EXIT_CRITICAL(); *err = OS_NO_ERR; } else if (OSIntNesting > 0) { (4) OS_EXIT_CRITICAL(); *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_MBOX; (5) OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OS_EXIT_CRITICAL(); OSSched(); OS_ENTER_CRITICAL(); if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { (6) OSTCBCur->OSTCBMsg = (void *)0; OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; OS_EXIT_CRITICAL(); *err = OS_NO_ERR; } else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) { (7) OSEventTO(pevent); (8) OS_EXIT_CRITICAL(); msg = (void *)0; (9) *err = OS_TIMEOUT; } else { 6-22 msg = pevent->OSEventPtr; (10) pevent->OSEventPtr = (void *)0; (11) OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (12) OS_EXIT_CRITICAL(); *err = OS_NO_ERR; } } return (msg); } 程序清单 L6.16 向邮箱中发送一条消息 INT8U OSMboxPost (OS_EVENT *pevent, void *msg) { OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1) OS_EXIT_CRITICAL(); return (OS_ERR_EVENT_TYPE); } if (pevent->OSEventGrp) { (2) OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3) OS_EXIT_CRITICAL(); OSSched(); (4) return (OS_NO_ERR); } else { if (pevent->OSEventPtr != (void *)0) { (5) OS_EXIT_CRITICAL(); return (OS_MBOX_FULL); } else { pevent->OSEventPtr = msg; (6) OS_EXIT_CRITICAL(); return (OS_NO_ERR); } } } 程序清单 L6.16 向邮箱中发送一条消息 INT8U OSMboxPost (OS_EVENT *pevent, void *msg) { OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1) OS_EXIT_CRITICAL(); return (OS_ERR_EVENT_TYPE); } if (pevent->OSEventGrp) { (2) OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3) OS_EXIT_CRITICAL(); OSSched(); (4) return (OS_NO_ERR); } else { if (pevent->OSEventPtr != (void *)0) { (5) OS_EXIT_CRITICAL(); return (OS_MBOX_FULL); } else { pevent->OSEventPtr = msg; (6) OS_EXIT_CRITICAL(); return (OS_NO_ERR); } } } 程序清单 L6.17 无等待地从邮箱中得到消息 void *OSMboxAccept (OS_EVENT *pevent) { void *msg; OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1) OS_EXIT_CRITICAL(); return ((void *)0); } msg = pevent->OSEventPtr; (2) if (msg != (void *)0) { (3) pevent->OSEventPtr = (void *)0; (4) } OS_EXIT_CRITICAL(); return (msg); (5) } 程序清单 L6.18 查询邮箱的状态 INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata) { INT8U i; 6-25 INT8U *psrc; INT8U *pdest; OS_ENTER_CRITICAL(); if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1) OS_EXIT_CRITICAL(); return (OS_ERR_EVENT_TYPE); } pdata->OSEventGrp = pevent->OSEventGrp; (2) psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSMsg = pevent->OSEventPtr; (3) OS_EXIT_CRITICAL(); return (OS_NO_ERR); } 程序清单 L6.19 使用邮箱作为二值信号量 OS_EVENT *MboxSem; void Task1 (void *pdata) { INT8U err; for (;;) { OSMboxPend(MboxSem, 0, &err); /* 获得对资源的访问权 */ 6-26 . . /* 任务获得信号量,对资源进行访问 */ . OSMboxPost(MboxSem, (void*)1); /* 释放对资源的访问权 */ } } 程序清单 L6.21 建立一个消息队列 OS_EVENT *OSQCreate (void **start, INT16U size) { OS_EVENT *pevent; OS_Q *pq; OS_ENTER_CRITICAL(); pevent = OSEventFreeList; (1) if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; (2) } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { OS_ENTER_CRITICAL(); pq = OSQFreeList; (3) if (OSQFreeList != (OS_Q *)0) { OSQFreeList = OSQFreeList->OSQPtr; } OS_EXIT_CRITICAL(); if (pq != (OS_Q *)0) { pq->OSQStart = start; (4) pq->OSQEnd = &start[size]; pq->OSQIn = start; pq->OSQOut = start; pq->OSQSize = size; pq->OSQEntries = 0; pevent->OSEventType = OS_EVENT_TYPE_Q; (5) pevent->OSEventPtr = pq; (6) OSEventWaitListInit(pevent); (7) } else { OS_ENTER_CRITICAL(); pevent->OSEventPtr = (void *)OSEventFreeList; (8) 6-33 OSEventFreeList = pevent; OS_EXIT_CRITICAL(); pevent = (OS_EVENT *)0; } } return (pevent); (9) } 9.03 OS_CPU.H 文件 OS_CPU.H 文件中包含与处理器相关的常量,宏和结构体的定义。程序清单L9.2是为80x86编 写的OS_CPU.H文件的内容。 9.04 OS_CPU_A.ASM μC/OS-II 的移植需要用户改写OS_CPU_A.ASM中的四个函数: OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() 9.04.01 OSStartHighRdy() 该函数由SStart()函数调用,功能是运行优先级最高的就绪任务,在调用OSStart()之前, 用户必须先调用OSInit(),并且已经至少创建了一个任务(请参考OSTaskCreate()和 OSTaskCreateExt()函数)。OSStartHighRdy()默认指针OSTCBHighRdy指向优先级最高就绪任务 的任务控制块(OS_TCB)(在这之前OSTCBHighRdy已由OSStart()设置好了)。图F9.3给出了由 函 数 OSTaskCreate() 或 OSTaskCreateExt() 创 建 的 任 务 的 堆 栈 结 构 。 很 明 显 , OSTCBHighRdy->OSTCBStkPtr指向的是任务堆栈的顶端。 程序清单L 9.3 OSStartHighRdy(). _OSStartHighRdy PROC FAR MOV AX, SEG _OSTCBHighRdy ; 载入 DS MOV DS, AX ; LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr (1) MOV SS, ES:[BX+2] ; MOV SP, ES:[BX+0] ; ; POP DS ; 恢复任务环境 (2) POP ES ; (3) POPA ; (4) ; IRET ; 运行任务 (5) _OSStartHighRdy ENDP 9.05 OS_CPU_C.C μC/OS-II 的移植需要用户改写OS_CPU_C.C中的六个函数: OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskStatHook() OSTimeTickHook() OSTaskResume ()的返回值为下述之一: ? OS_NO_ERR:函数调用成功。 ? OS_TASK_RESUME_PRIO:要唤醒的任务不存在。 ? OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态。 ? OS_PRIO_INVALID:参数指定的优先级大于或等于 OS_LOWEST_PRIO。 OS_MAX_EVENTS 定义系统中最大的事件控制块的数量。系统中的每一个消息邮箱,消息队 列,信号量都需要一个事件控制块。例如,系统中有 10 个消息邮箱,5 个消息队列,3 个信号 量,则 OS_MAX_EVENTS 最小应该为 18。只要程序中用到了消息邮箱,消息队列或是信号量, 则 OS_MAX_EVENTS 最小应该设置为 2。
钱柜娱乐官网