1 /*2 *************************************************************************************************3 * uC/OS-II实时控制内核4 * 主要的包含文件5 * --消息队列管理项--6 *7 * 文 件: OS_Q.C 消息队列管理代码8 * 作 者: Jean J. Labrosse9 * 中文注解: 钟常慰 zhongcw @ 126.com 整理:lin-credible 译注版本:1.0 请尊重原版内容10 *************************************************************************************************11 */1213 #ifndef OS_MASTER_FILE //是否已定义OS_MASTER_FILE主文件14 #include "includes.h" //包含"includes.h"文件,部分C语言头文件的汇总打包文件15 #endif //定义结束1617 #if (OS_Q_EN > 0) && (OS_MAX_QS > 0) //条件编译:OS_Q_EN 允许 (1)产生消息队列相关代码18 //条件编译:应用中最多对列控制块的数目 > 019 /*20 *************************************************************************************************21 * 检查消息队列中是否已经有需要的消息(ACCEPT MESSAGE FROM QUEUE)22 *23 * 描述: 检查消息队列中是否已经有需要的消息.不同于OSQPend()函数,如果没有需要的消息,OSQAccept()24 * 函数并不挂起任务。如果消息已经到达,该消息被传递到用户任务。通常中断调用该函数,因为中25 * 断不允许挂起等待消息。26 *27 * 参数: pevent 是指向需要查看的消息队列的指针。当建立消息队列时,该指针返回到用户程序。28 * (参考OSMboxCreate()函数)。29 *30 * 返回: 如果消息已经到达,返回指向该消息的指针;如果消息队列没有消息,返回空指针。31 *32 * 注意:必须先建立消息队列,然后使用。33 *************************************************************************************************34 */3536 #if OS_Q_ACCEPT_EN > 0 //条件编译:允许生成 OSQAccept()代码37 void *OSQAccept (OS_EVENT *pevent) //检查消息队列中是否已经有需要的消息(消息队列的指针)38 {39 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式340 OS_CPU_SR cpu_sr;41 #endif42 void *msg; //定义消息队列指针(输出缓冲区)43 OS_Q *pq; //定义消息队列事件444546 #if OS_ARG_CHK_EN > 0 //所有参数必须在指定的参数内47 if (pevent == (OS_EVENT *)0) { //当消息队列指针为NULL时,返回0,空指针48 return ((void *)0);49 }50 if (pevent->OSEventType != OS_EVENT_TYPE_Q) //当事件类型≠消息队列类型51 return ((void *)0); //返回052 }53 #endif54 OS_ENTER_CRITICAL(); //关闭中断55 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针56 if (pq->OSQEntries != 0) { //当消息队列消息数57 msg = *pq->OSQOut++; //输出消息内容到缓冲区58 pq->OSQEntries--; //消息数减159 if (pq->OSQOut == pq->OSQEnd) { //当输出指针=结束指针60 pq->OSQOut = pq->OSQStart; //输出指针跳转到起始指针61 }62 } else { //否则63 msg = (void *)0; //将定义消息队列指针(输出缓冲区)清空64 }65 OS_EXIT_CRITICAL(); //打开中断66 return (msg); //返回(消息=为空没有消息;消息=不为空,有消息)67 }68 #endif69 /*$PAGE*/70 /*71 *************************************************************************************************72 * 建立一个消息队列(CREATE A MESSAGE QUEUE)73 *74 * 描述: 建立一个消息队列。任务或中断可以通过消息队列向其他一个或多个任务发送消息.消息的含义是75 * 和具体的应用密切相关的.76 *77 * 参数: start 是消息内存区的基地址,消息内存区是一个指针数组。78 * size 是消息内存区的大小。79 *80 * 返回: OSQCreate()函数返回一个指向消息队列事件控制块的指针;81 * 如果没有空余的事件空闲块,OSQCreate()函数返回空指针。82 *83 * 注意 必须先建立消息队列,然后使用84 *************************************************************************************************85 */86 //建立一个消息队列(消息内存区的基地址(指针数组)、消息内存区的大小)87 OS_EVENT *OSQCreate (void **start, INT16U size)88 {89 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式390 OS_CPU_SR cpu_sr;91 #endif92 OS_EVENT *pevent; //定义一个指向事件控制快的指针93 OS_Q *pq; //定义一个队列控制模块指针(事件)949596 if (OSIntNesting > 0) { //中断嵌套数>0时,表示还有中断任务在运行97 return ((OS_EVENT *)0); //返回0;98 }99 OS_ENTER_CRITICAL(); //关闭中断100 pevent = OSEventFreeList; //pevent=空余事件管理列表101 if (OSEventFreeList != (OS_EVENT *)0) { //如果有空余事件管理块102 OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;103 } //空余事件控制链表指向下一个空余事件控制块104 OS_EXIT_CRITICAL(); //打开中断105 if (pevent != (OS_EVENT *)0) { //如果有事件控制块ECB可用106 OS_ENTER_CRITICAL(); //关闭中断107 pq = OSQFreeList; //队列指针指向空余队列控制链表的队列控制块108 if (OSQFreeList != (OS_Q *)0) { //当是空余列表时(即链接表中还有空余)109 OSQFreeList = OSQFreeList->OSQPtr; //控制链接表指向下一个空余控制块110 }111 OS_EXIT_CRITICAL(); //打开中断112 if (pq != (OS_Q *)0) { //消息队列初始化113 pq->OSQStart = start; //指向消息队列的起始指针114 pq->OSQEnd = &start[size]; //指向消息队列结束下一个单元地址115 pq->OSQIn = start; //指向消息队列下一个要插入的消息指针116 pq->OSQOut = start; //指向消息队列下一个取出消息指针117 pq->OSQSize = size; //消息队列可容纳的最多消息数118 pq->OSQEntries = 0; //消息队列中的消息数119 pevent->OSEventType = OS_EVENT_TYPE_Q; //事件类型为消息队列120 pevent->OSEventPtr = pq; //OSEventcnt只用于信号量,不用置0121 OS_EventWaitListInit(pevent); //初始化一个事件控制块122 } else { //否则,123 OS_ENTER_CRITICAL(); //关闭中断124 pevent->OSEventPtr = (void *)OSEventFreeList; //事件控制块ECB返回125 OSEventFreeList = pevent; //空余块链接表=当前事件指针126 OS_EXIT_CRITICAL(); //打开中断127 pevent = (OS_EVENT *)0; //事件指针=0128 }129 }130 return (pevent); //消息队列建立成功,返回一个消息队列得指针,并成为该消息句柄131 }132 /*$PAGE*/133 /*134 *************************************************************************************************135 * 删除消息队列(DELETE A MESSAGE QUEUE)136 *137 * 描述: 删除消息队列。使用这个函数有风险,因为多任务中的其他任务可能还想用这个消息队列.使用这138 * 个函数要特别小心.一般的说,应先删除可能会用到这个消息队列的所以任务,再调用本函数。139140 *141 * 参数: pevent 是指向消息队列的指针。该指针的值在建立该队列时可以得到。(参考OSQCreate()函数)142 *143 * opt 该选项定义消息队列删除条件:144 * opt==OS_DEL_NO_PEND 可以选择在没有任何任务在等待该消息队列时,才删除该消息队列;145 * opt==OS_DEL_ALWAYS 不管有没有任务在等待该消息队列的消息,立刻删除该消息队列.146 * 后一种情况下,所有等待该消息的任务都立刻进入就绪态147 *148 * err 指向错误代码的指针,出错代码为以下之一:149 * OS_NO_ERR 调用成功,消息队列已被删除;150 * OS_ERR_DEL_ISR 试图在中断服务子程序中删除消息队列;151 * OS_ERR_INVALID_OPT 'opt'参数没有在以上2个参数值之一;152 * OS_ERR_TASK_WAITING 有一个或一个以上的任务在等待消息队列中的消息;153 * OS_ERR_EVENT_TYPE 'pevent'不是指向消息队列的指针;154 * OS_ERR_PEVENT_NULL 已经没有OS_EVENT(事件)数据结构可以使用了.155 *156 * 返回: pevent 如果消息队列删除成功, 则返回空指针;157 * 如果消息队列没有被删除, 则返回pevent. 在后一种情况查看出错代码,找出原因.158 *159 * 注意: 1) 调用本函数需十分小心,因为多任务中的其他任务可能还想用这个消息队列;160 * 2) 当挂起任务进入就绪态时,中断是关闭的,这就意味着中断延迟时间取决于等待消息队列的任务数目161 *************************************************************************************************162 */163164 #if OS_Q_DEL_EN > 0 //允许生成 OSSemDel()代码165 OS_EVENT *OSQDel (OS_EVENT *pevent, INT8U opt, INT8U *err)166 { //删除一个消息队列(消息队列指针、删除条件、错误指针)167 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3168 OS_CPU_SR cpu_sr;169 #endif170 BOOLEAN tasks_waiting; //定义布尔量,任务等待条件171 OS_Q *pq; //定义一个队列控制模块指针(事件)172173174 if (OSIntNesting > 0) { //中断嵌套数>0时,表示还有中断任务在运行175 *err = OS_ERR_DEL_ISR; //错误等于(试图在中断程序中删除一个消息队列)176 return ((OS_EVENT *)0); //返回0177 }178 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内179 if (pevent == (OS_EVENT *)0) { //当信号量指针为NULL,即0(空)180 *err = OS_ERR_PEVENT_NULL; //已经没有可用的OS_EVENT数据结构可以使用了181 return (pevent); //返回指针182 }183 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //事件类型是否时消息队列184 *err = OS_ERR_EVENT_TYPE; //'pevent'不是指向消息队列的指针185 return (pevent); //返回指针186 }187 #endif188 OS_ENTER_CRITICAL(); //关闭中断189 if (pevent->OSEventGrp != 0x00) { //事件等待标志(索引值是否有任务在等待)190 tasks_waiting = TRUE; //有则任务等待标志=1191 } else { //否则192 tasks_waiting = FALSE; //没有则任务等待标志=0193 }194 switch (opt) { //条件195 case OS_DEL_NO_PEND: // 1)选择没有任务在等待该消息队列196 if (tasks_waiting == FALSE) { //没有任务在等待197 pq = pevent->OSEventPtr; //队列指针=当前事件指针198 pq->OSQPtr = OSQFreeList; //队列空余指针=当前空闲队列链接表199 OSQFreeList = pq; //空闲队列链接表=当前队列指针200 pevent->OSEventType = OS_EVENT_TYPE_UNUSED; //事件类型=空闲201 pevent->OSEventPtr = OSEventFreeList; //队列对应得事件指针=空余块链接表202 OSEventFreeList = pevent; //空余块链接表=当前事件指针203 OS_EXIT_CRITICAL(); //打开中断204 *err = OS_NO_ERR; //调用成功,消息队列已被删除205 return ((OS_EVENT *)0); //返回0206 } else { //否则207 OS_EXIT_CRITICAL(); //打开中断208 *err = OS_ERR_TASK_WAITING; //有一个或一个以上的任务在等待消息队列中的消息209 return (pevent); //返回消息队列指针210 }211212 case OS_DEL_ALWAYS: // 2)尽管有(多)任务在等待,还是要删除213 while (pevent->OSEventGrp != 0x00) { //事件等待标志≠0,还是要删除214 OS_EventTaskRdy(pevent, (void *)0, OS_STAT_Q);215 } //使一个任务进入到就绪状态216 pq = pevent->OSEventPtr; //队列指针=当前事件指针217 pq->OSQPtr = OSQFreeList; //队列空余指针=当前空闲队列链接表218 OSQFreeList = pq; //空闲队列链接表=当前队列指针219 pevent->OSEventType = OS_EVENT_TYPE_UNUSED; //事件类型=空闲220 pevent->OSEventPtr = OSEventFreeList; //队列对应得事件指针=空余块链接表221 OSEventFreeList = pevent; //空余块链接表=当前事件指针222 OS_EXIT_CRITICAL(); //打开中断223 if (tasks_waiting == TRUE) { //当有任务在等待(1)224 OS_Sched(); //任务调度函数,最高任务优先级进入就绪态225 }226 *err = OS_NO_ERR; //调用成功,消息队列已被删除227 return ((OS_EVENT *)0); //返回0228229 default: // 3)以上两者都不是230 OS_EXIT_CRITICAL(); //打开中断231 *err = OS_ERR_INVALID_OPT; //'opt'参数没有在以上2个参数值之一232 return (pevent); //返回指针233 }234 }235 #endif236237 /*$PAGE*/238 /*239 *************************************************************************************************240 * 清空消息队列并且忽略发送往队列的所有消息(FLUSH QUEUE)241 *242 * 描述: 清空消息队列并且忽略发送往队列的所有消息.243 * 不管队列中是否有消息,这个函数的执行时间都是相同的.244 *245 * 参数: 无246 *247 * 返回: OS_NO_ERR 消息队列被成功清空248 * OS_ERR_EVENT_TYPE 试图清除不是消息队列的对象249 * OS_ERR_PEVENT_NULL 'pevent'是空指针250 *251 * 注意: 必须先建立消息队列,然后使用252 *************************************************************************************************253 */254255 #if OS_Q_FLUSH_EN > 0 //允许生成 OSQFlush()代码256 INT8U OSQFlush (OS_EVENT *pevent) //清空消息队列(指向得到消息队列的指针)257 {258 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3259 OS_CPU_SR cpu_sr;260 #endif261 OS_Q *pq; //定义一个队列事件262263264 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内265 if (pevent == (OS_EVENT *)0) { //当信号量指针为NULL,即0(空)266 return (OS_ERR_PEVENT_NULL); //pevent是空指针267 }268 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //当事件类型不是消息队列269 return (OS_ERR_EVENT_TYPE); //试图清除不是消息队列的对象270 }271 #endif272 OS_ENTER_CRITICAL(); //关闭中断273 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针274 pq->OSQIn = pq->OSQStart; //插入指针=起始指针275 pq->OSQOut = pq->OSQStart; //输出指针=起始指针276 pq->OSQEntries = 0; //消息队列数目=0277 OS_EXIT_CRITICAL(); //打开中断278 return (OS_NO_ERR); //返回(消息队列被成功清空)279 }280 #endif281282 /*$PAGE*/283 /*284 *************************************************************************************************285 * 任务等待消息队列中的消息(PEND ON A QUEUE FOR A MESSAGE)286 *287 * 描述: 用于任务等待消息。消息通过中断或另外的任务发送给需要的任务。288 * 消息是一个以指针定义的变量,在不同的程序中消息的使用也可能不同。如果调用OSQPend()函数时289 * 队列中已经存在需要的消息,那么该消息被返回给OSQPend()函数的调用者,队列中清除该消息.如果290 * 调用OSQPend()函数时队列中没有需要的消息,OSQPend()函数挂起当前任务直到得到需要的消息或291 * 超出定义的超时时间.如果同时有多个任务等待同一个消息,uC/OS-ii默认最高优先级的任务取得消292 * 息并且任务恢复执行.一个由OSTaskSuspend()函数挂起的任务也可以接受消息,但这个任务将一直293 * 保持挂起状态直到通过调用OSTaskResume()函数恢复任务的运行。294 *295 * 参数: pevent 是指向即将接受消息的队列的指针。296 * 该指针的值在建立该队列时可以得到。(参考OSMboxCreate()函数)297 *298 * timeout 允许一个任务在经过了指定数目的时钟节拍后还没有得到需要的消息时恢复运行状态。299 * 如果该值为零表示任务将持续的等待消息.最大的等待时间为65535个时钟节拍.这个时300 * 间长度并不是非常严格的,可能存在一个时钟节拍的误差,因为只有在一个时钟节拍结301 * 束后才会减少定义的等待超时时钟节拍。302 *303 * err 是指向包含错误码的变量的指针。OSQPend()函数返回的错误码可能为下述几种:304 * OS_NO_ERR 消息被正确的接受;305 * OS_TIMEOUT 消息没有在指定的周期数内送到;306 * OS_ERR_EVENT_TYPE 'pevent'不是指向消息队列的指针;307 * OS_ERR_PEVENT_NULL 'pevent'是空指针;308 * OS_ERR_PEND_ISR 从中断调用该函数。虽然规定了不允许从中断调用该函数,但309 * uC/OS-ii仍然包含了检测这种情况的功能310 *311 * 返回: OSQPend()函数返回接受的消息并将 *err置为OS_NO_ERR。312 * 如果没有在指定数目的时钟节拍内接受到需要的消息, OSQPend()函数返回空指针并且将 *err313 * 设置为OS_TIMEOUT.314 *315 * 注意: 1、必须先建立消息邮箱,然后使用;316 * 2、不允许从中断调用该函数.317 *************************************************************************************************318 */319 //任务等待消息队列中的消息(消息队列指针、允许等待的时钟节拍、代码错误指针)320 void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)321 {322 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3323 OS_CPU_SR cpu_sr;324 #endif325 void *msg; //定义消息队列的指针、取出的暂存指针326 OS_Q *pq; //定义一个队列事件327328329 if (OSIntNesting > 0) { //中断嵌套数>0时,表示还有中断任务在运行330 *err = OS_ERR_PEND_ISR; //试图从中断调用该函数331 return ((void *)0); //返回空(0)332 }333 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内334 if (pevent == (OS_EVENT *)0) { //当信号量指针为NULL,即0(空)335 *err = OS_ERR_PEVENT_NULL; //pevent是空指针336 return ((void *)0); //返回337 }338 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //当事件类型不否是消息队列类型339 *err = OS_ERR_EVENT_TYPE; //'pevent'不是指向消息队列的指针340 return ((void *)0); //返回空(0)341 }342 #endif343 OS_ENTER_CRITICAL(); //关闭中断344 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针345 if (pq->OSQEntries != 0) { //当前消息队列中消息数 > 0,有消息346 msg = *pq->OSQOut++; //OSQOut将对应的地址的消息复制到msg347 pq->OSQEntries--; //当前队列消息数减1348 if (pq->OSQOut == pq->OSQEnd) { //当取出指针=最高消息队列单元时349 pq->OSQOut = pq->OSQStart; //取出指针跳转到起始单元350 }351 OS_EXIT_CRITICAL(); //打开中断352 *err = OS_NO_ERR; //消息被正确的接受353 return (msg); //返回消息暂存(数据)指针354 } // 无消息355 OSTCBCur->OSTCBStat |= OS_STAT_Q; //将事件进入睡眠状态,由消息队列唤醒356 OSTCBCur->OSTCBDly = timeout; //等待时间置入任务控制中357 OS_EventTaskWait(pevent); //使任务进入等待消息队列状态358 OS_EXIT_CRITICAL(); //打开中断359 OS_Sched(); //任务调度函数,调用一个就绪的高优先级任务运行360 OS_ENTER_CRITICAL(); //关闭中断361 msg = OSTCBCur->OSTCBMsg; //接收消息=指向当前任务的消息指针362 if (msg != (void *)0) { //检查消息是否为空363 OSTCBCur->OSTCBMsg = (void *)0; //传递给消息的指针为空364 OSTCBCur->OSTCBStat = OS_STAT_RDY; //表示任务处于就绪状态365 OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; //指向事件控制块的指针=0366 OS_EXIT_CRITICAL(); //打开中断367 *err = OS_NO_ERR; //成功等待消息队列368 return (msg); //返回消息暂存(数据)指针369 }370 OS_EventTO(pevent); //如果没有获得消息,由于等待起始时间371 OS_EXIT_CRITICAL(); //打开中断372 *err = OS_TIMEOUT; //消息没有在指定的时间送到373 return ((void *)0); //返回0374 }375 /*$PAGE*/376 /*377 *************************************************************************************************378 * 向消息队列发送一则消息(POST MESSAGE TO A QUEUE)379 *380 * 描述: 通过消息队列向任务发送消息.消息是一个指针长度的变量,在不同的程序中消息的使用也可能不同.381 * 如果队列中已经存满消息,返回错误码. OSQPost()函数立即返回调用者,消息也没有能够发到队列.382 * 如果有任何任务在等待队列中的消息,最高优先级的任务将得到这个消息. 如果等待消息的任务优先383 * 级比发送消息的任务优先级高, 那么高优先级的任务将得到消息而恢复执行,也就是说, 发生了一次钟常慰384 * 任务切换. 消息队列是先入先出(FIFO)机制的,先进入队列的消息先被传递给任务.385 *386 * 参数: pevent 是指向即将接受消息的消息队列的指针。该指针的值在建立该队列时可以得到。387 * (参考OSQCreate()函数)388 *389 * msg 是即将实际发送给任务的消息. 消息是一个指针长度的变量,在不同的程序中消息的使用也390 * 可能不同. 不允许传递一个空指针.391 *392 * 返回:393 * OS_NO_ERR 消息成功的放到消息队列中;394 * OS_Q_FULL 消息队列中已经存满;395 * OS_ERR_EVENT_TYPE 'pevent'不是指向消息队列的指针;396 * OS_ERR_PEVENT_NULL 'pevent'是空指针;397 * OS_ERR_POST_NULL_PTR 用户发出空指针。根据规则,这里不支持空指针.398 *399 * 注意: 1、必须先建立消息队列,然后使用;400 * 2、不允许从中断调用该函数。401 *************************************************************************************************402 */403404 #if OS_Q_POST_EN > 0 //允许生成 OSQPost()代码405 INT8U OSQPost (OS_EVENT *pevent, void *msg)406 { //向消息队列发送一则消息FIFO(消息队列指针、发送的消息)407 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3408 OS_CPU_SR cpu_sr;409 #endif410 OS_Q *pq; //定义一个队列事件411412413 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内414 if (pevent == (OS_EVENT *)0) { //当消息队列指针为NULL,即0(空)415 return (OS_ERR_PEVENT_NULL); //pevent是空指针416 }417 if (msg == (void *)0) { //检查消息队列是否为空,用户试发出空消息418 return (OS_ERR_POST_NULL_PTR); //用户发出空指针。根据规则,这里不支持空指针.419 }420 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //事件类型是否为消息队列421 return (OS_ERR_EVENT_TYPE); //'pevent'不是指向消息队列的指针422 }423 #endif424 OS_ENTER_CRITICAL(); //关闭中断425 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0426 OS_EventTaskRdy(pevent, msg, OS_STAT_Q); //使最高优先级任务进入就绪态427 OS_EXIT_CRITICAL(); //打开中断428 OS_Sched(); //任务调度函数,调用一个就绪的高优先级任务运行429 return (OS_NO_ERR); //消息成功的放到消息队列中430 }431 pq = (OS_Q *)pevent->OSEventPtr; //消息队列指针=当前事件指针432 if (pq->OSQEntries >= pq->OSQSize) { //消息队列当前消息数>=消息中可容纳的消息数433 OS_EXIT_CRITICAL(); //打开中断434 return (OS_Q_FULL); //返回消息队列已满435 }436 *pq->OSQIn++ = msg; //插入当前的消息(内容),地址为指针加1437 pq->OSQEntries++; //消息队列数加1438 if (pq->OSQIn == pq->OSQEnd) { //当插入的消息指针=最后(结束)的指针439 pq->OSQIn = pq->OSQStart; //插入指针跳到起始处指针440 }441 OS_EXIT_CRITICAL(); //打开中断442 return (OS_NO_ERR); //消息成功的放到消息队列中443 }444 #endif445 /*$PAGE*/446 /*447 *************************************************************************************************448 * 通过消息队列向任务发送消息(POST MESSAGE TO THE FRONT OF A QUEUE)449 *450 * 描述: 通过消息队列向任务发送消息. OSQPostFront()函数和OSQPost()函数非常相似, 不同之处在于451 * OSQPostFront()函数将发送的消息插到消息队列的最前端.也就是说, OSQPostFront()函数使得452 * 消息队列按照后入先出(LIFO)的方式工作, 而不是先入先出(FIFO)). 消息是一个指针长度的变453 * 量,在不同的程序中消息的使用也可能不同. 如果队列中已经存满消息,返回错误码. OSQPost()454 * 函数立即返回调用者, 消息也没能发到队列. 如果有任何任务在等待队列中的消息,最高优先级455 * 的任务将得到这个消息. 如果等待消息的任务优先级比发送消息的任务优先级高,那么高优先级456 * 的任务将得到消息而恢复执行, 也就是说, 发生了一次任务切换.457 *458 * 参数: pevent 是指向即将接受消息的消息队列的指针.459 * 该指针的值在建立该队列时可以得到.(参考OSQCreate()函数).460 * msg 是即将实际发送给任务的消息. 消息是一个指针长度的变量, 在不同的程序中消息的使461 * 用也可能不同. 不允许传递一个空指针.462 *463 * 返回: OS_NO_ERR 消息成功的放到消息队列中;464 * OS_Q_FULL 消息队列已满;465 * OS_ERR_EVENT_TYPE 'pevent'不是指向消息队列的指针;466 * OS_ERR_PEVENT_NULL 'pevent'是指空指针;467 * OS_ERR_POST_NULL_PTR 用户发出空指针。根据规则,这里不支持空指针.468 *469 * 注意: 1、必须先建立消息队列,然后使用。470 * 2、不允许传递一个空指针471 *************************************************************************************************472 */473474 #if OS_Q_POST_FRONT_EN > 0 //允许生成 OSQPost()代码475 INT8U OSQPostFront (OS_EVENT *pevent, void *msg)476 { //向消息队列发送一则消息LIFO(消息队列指针、发送的消息)477 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3478 OS_CPU_SR cpu_sr;479 #endif480 OS_Q *pq; //定义一个队列事件481482 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内483 if (pevent == (OS_EVENT *)0) { //当消息队列指针为NULL,即0(空)484 return (OS_ERR_PEVENT_NULL); //pevent是空指针485 }486 if (msg == (void *)0) { //检查消息队列是否为空,用户试发出空消息487 return (OS_ERR_POST_NULL_PTR); //用户发出空指针。根据规则,这里不支持空指针.488 }489 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //事件类型是否为消息队列490 return (OS_ERR_EVENT_TYPE); //'pevent'不是指向消息队列的指针491 }492 #endif493 OS_ENTER_CRITICAL(); //关闭中断494 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0495 OS_EventTaskRdy(pevent, msg, OS_STAT_Q); //使最高优先级任务进入就绪态496 OS_EXIT_CRITICAL(); //打开中断497 OS_Sched(); //任务调度函数,调用一个就绪的高优先级任务运行498 return (OS_NO_ERR); //消息成功的放到消息队列中499 }500 pq = (OS_Q *)pevent->OSEventPtr; //消息队列指针=当前事件指针501 if (pq->OSQEntries >= pq->OSQSize) { //消息队列当前消息数>=消息中可容纳的消息数502 OS_EXIT_CRITICAL(); //打开中断503 return (OS_Q_FULL); //返回消息队列已满504 }505 if (pq->OSQOut == pq->OSQStart) { //当插入指针=指针的起始地址(指针)506 pq->OSQOut = pq->OSQEnd; //插入指针跳转到最后地址(指针)507 }508 pq->OSQOut--; //插入指针减1509 *pq->OSQOut = msg; //插入当前消息(内容)510 pq->OSQEntries++; //消息数加1511 OS_EXIT_CRITICAL(); //打开中断512 return (OS_NO_ERR); //消息成功的放到消息队列中513 }514 #endif515 /*$PAGE*/516 /*517 *************************************************************************************************518 * 消息队列向任务发消息(POST MESSAGE TO A QUEUE)519 *520 * 描述: 通过消息队列向任务发消息。消息是一个以指针表示的某种数据类型的变量,在不同的程序中消521 * 息的使用也可能不同。如果消息队列中的消息已满,则返回出错代码,说明消息队列已满。522 * OSQPostOpt()函数立即返回调用者,消息也没有能够发到消息队列,如果有任何任务在等待消息523 * 队列中的消息,那么 OSQPostOpt()允许选择以下2种情况之一:524 * 1、让最高优先级的任务得到这则消息(opt置为OS_POST_OPT_NONE);525 * 2、或者让所有等待队列消息的任务都得到消息(opt置为OS_POST_OPT_BROADCAST)526 * ->无论在哪种情况下,如果得到消息的任务优先级比发送消息的任务优先级高,那么得到消息527 * 的最高优先级的任务恢复执行,发消息的任务将被挂起。也就是执行一次任务切换。528 *529 * OSQPostOpt()仿真OSQPost()和OSQPostFront()这2个函数,并允许程序发消息给多个任务。换句530 * 话说。OSQPostOpt()允许将消息广播给所有的等待队列消息的任务。OSQPostOpt()实际上可以取531 * 代OSQPost(),因为可以通过设定opt参数定义队列消息的接收方式,这样做可以减少ucos_ii占用532 * 的代码空间。533 *534 * 参数: pevent 是指向即将接收消息的消息队列的指针。该指针的值在建立该消息邮箱时可以得到。535 * (参考OSQCreate()函数)。536 *537 * msg 即将发送给任务的消息. 消息是一个指向某种变量类型的指针,在不同的应用程序中,538 * 消息的类型是用户定义的。不允许传递一个空指针。539 *540 * opt 决定消息发送方式的选项:541 * OS_POST_OPT_NONE 发送一个消息给一个等待消息的任务(等同于OSQPost())542 * OS_POST_OPT_BROADCAST 发送消息给所有等待队列消息的任务543 * OS_POST_OPT_FRONT 以后进先出方式发消息(仿真OSQPostFront())544 *545 * 以下是所有标志可能的组合:546 *547 * 1) OS_POST_OPT_NONE 等同于OSQPost()548 * 2) OS_POST_OPT_FRONT 等同于OSQPostFront()549 * 3) OS_POST_OPT_BROADCAST 等同于OSQPost(),但广播给所有等待队列消息的任务550 * 4) OS_POST_OPT_FRONT + OS_POST_OPT_BROADCAST is identical to551 * ->等同于OSQPostFront()不同的是,它将消息广播给所有等待队列消息的任务552 *553 * 返回: OS_NO_ERR 调用成功,消息已经发出;554 * OS_Q_FULL 消息队列已满,不能再接收新消息;555 * OS_ERR_EVENT_TYPE 'pevent'指向的数据类型错;556 * OS_ERR_PEVENT_NULL 'pevent'是空指针;557 * OS_ERR_POST_NULL_PTR 用户程序试图发出空指针.558 *559 * 警告: 1、必须先建立消息队列,然后使用;560 * 2、不允许传递一个空指针;561 * 3、如故想使用本函数,又希望压缩代码长度,则可以将OSQPost()函数的开关量关闭(置562 * OS_CFG.H文件中的OS_Q_POST_EN为0),并将OSQPostFront()的开关量关闭(置OS_CFG.H文件563 * 中的OS_Q_POST_FRONT_EN为0),因为OSQPostOpt()可以仿真这2个函数;564 * 4、OSQPostOpt()在广播方式下,即已将opt置为OS_POST_OPT_BROADCAST,函数的执行时间取565 * 决于等待队列消息的任务的数目566 *************************************************************************************************567 */568569 #if OS_Q_POST_OPT_EN > 0 //允许生成 OSQPostOpt()代码570 INT8U OSQPostOpt (OS_EVENT *pevent, void *msg, INT8U opt)571 { //向消息队列发送一则消息LIFO(消息队列指针、发送的消息、发送条件)572 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3573 OS_CPU_SR cpu_sr;574 #endif575 OS_Q *pq; //定义一个队列事件576577578 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内579 if (pevent == (OS_EVENT *)0) { //当消息队列指针为NULL,即0(空)580 return (OS_ERR_PEVENT_NULL); //pevent是空指针581 }582 if (msg == (void *)0) { //检查消息队列是否为空,用户试发出空消息583 return (OS_ERR_POST_NULL_PTR); //用户发出空指针。根据规则,这里不支持空指针584 }585 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //事件类型是否为消息队列586 return (OS_ERR_EVENT_TYPE); //'pevent'不是指向消息队列的指针587 }588 #endif589 OS_ENTER_CRITICAL(); //关闭中断590 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0591 if ((opt & OS_POST_OPT_BROADCAST) != 0x00) { // 1)发送消息给所有等待队列消息的任务592 while (pevent->OSEventGrp != 0x00) { //如果有任务在等待该消息队列593 OS_EventTaskRdy(pevent, msg, OS_STAT_Q); //发送消息给所有等待队列消息的任务594 }595 } else { // 2)否则596 OS_EventTaskRdy(pevent, msg, OS_STAT_Q); //如果没有广播请求,最优先级进入请求597 }598 OS_EXIT_CRITICAL(); //打开中断599 OS_Sched(); //任务调度函数,调用一个就绪的高优先级任务运行600 return (OS_NO_ERR); //消息成功的放到消息队列中601 }602 pq = (OS_Q *)pevent->OSEventPtr; //消息队列指针=当前事件指针603 if (pq->OSQEntries >= pq->OSQSize) { //消息队列当前消息数>=消息中可容纳的消息数604 OS_EXIT_CRITICAL(); //打开中断605 return (OS_Q_FULL); //返回消息队列已满606 }607 if ((opt & OS_POST_OPT_FRONT) != 0x00) { // 1)如果选择后进先出608 if (pq->OSQOut == pq->OSQStart) { //当插入指针=指针的起始地址(指针)609 pq->OSQOut = pq->OSQEnd; //插入指针跳转到最后地址(指针)610 }611 pq->OSQOut--; //插入指针减1612 *pq->OSQOut = msg; //插入当前消息(内容)613 } else { // 2)否则,选择先进先出614 *pq->OSQIn++ = msg; //插入当前消息(内容)615 if (pq->OSQIn == pq->OSQEnd) { //当插入指针=指针的起始地址(指针)616 pq->OSQIn = pq->OSQStart; //插入指针跳转到最后地址(指针)617 }618 }619 pq->OSQEntries++; //消息数加1620 OS_EXIT_CRITICAL(); //消息成功的放到消息队列中621 return (OS_NO_ERR); //消息成功的放到消息队列中622 }623 #endif624 /*$PAGE*/625 /*626 *************************************************************************************************627 * 取得消息队列的信息(QUERY A MESSAGE QUEUE)628 *629 * 描述: 取得消息队列的信息。用户程序必须建立一个OS_Q_DATA的数据结构,该结构用来保存从消息队630 * 列的事件控制块得到的数据.通过调用OSQQuery()函数可以知道任务是否在等待消息、有多少个631 * 任务在等待消息、队列中有多少消息以及消息队列可以容纳的消息数。OSQQuery()函数还可以得632 * 到即将被传递给任务的消息的信息。633 *634 * 参数: pevent 是指向即将接受消息的消息队列的指针。该指针的值在建立该消息邮箱时可以得到。635 * (参考OSQCreate()函数)。636 *637 * pdata 是指向OS_Q_DATA数据结构的指针,该数据结构包含下述成员:638 * Void *OSMsg; // 下一个可用的消息639 * INT16U OSNMsgs; // 队列中的消息数目640 * INT16U OSQSize; // 消息队列的大小641 * INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; // 消息队列的等待队列642 * INT8U OSEventGrp;643 *644 * 返回: OS_NO_ERR 调用成功;645 * OS_ERR_EVENT_TYPE pevent不是指向消息队列的指针;646 * OS_ERR_PEVENT_NULL pevent是空指针。647 *************************************************************************************************648 */649650 #if OS_Q_QUERY_EN > 0 //允许生成 OSQQuery()代码651 INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *pdata)652 { //查询一个消息队列的当前状态(信号量指针、状态数据结构指针)653 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3654 OS_CPU_SR cpu_sr;655 #endif656 OS_Q *pq; //定义一个队列事件指针657 INT8U *psrc; //定义8位pevent->OSEventTbl[0]的地址指针658 INT8U *pdest; //定义8位pdata->OSEventTbl[0]的地址指针659660661 #if OS_ARG_CHK_EN > 0 //所有参数在指定的范围之内662 if (pevent == (OS_EVENT *)0) { //当消息队列指针为NULL,即0(空)663 return (OS_ERR_PEVENT_NULL); //pevent是空指针664 }665 if (pevent->OSEventType != OS_EVENT_TYPE_Q) { //当事件类型不是消息队列类型666 return (OS_ERR_EVENT_TYPE); //pevent指针不是指向消息队列667 }668 #endif669 OS_ENTER_CRITICAL(); //关闭中断670 //将事件(消息队列)结构中的等待任务列表复制到pdata数据结构中671 pdata->OSEventGrp = pevent->OSEventGrp; //等待事件的任务组中的内容传送到状态数据结构中672 psrc = &pevent->OSEventTbl[0]; //保存pevent->OSEventTbl[0]对应的地址673 pdest = &pdata->OSEventTbl[0]; //保存pdata->OSEventTbl[0]对应的地址674 #if OS_EVENT_TBL_SIZE > 0 //当事件就绪对应表中的对应值>0时675 *pdest++ = *psrc++; //地址指针下移一个类型地址,获取信号量的值676 #endif677678 #if OS_EVENT_TBL_SIZE > 1 //事件就绪对应表中的对应值>1时679 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值680 #endif681682 #if OS_EVENT_TBL_SIZE > 2 //事件就绪对应表中的对应值>2时683 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值684 #endif685686 #if OS_EVENT_TBL_SIZE > 3 //事件就绪对应表中的对应值>3时687 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值常慰688 #endif689690 #if OS_EVENT_TBL_SIZE > 4 //事件就绪对应表中的对应值>4时691 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值692 #endif693694 #if OS_EVENT_TBL_SIZE > 5 //事件就绪对应表中的对应值>5时695 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值696 #endif697698 #if OS_EVENT_TBL_SIZE > 6 //事件就绪对应表中的对应值>6时699 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值700 #endif701702 #if OS_EVENT_TBL_SIZE > 7 //事件就绪对应表中的对应值>7时703 *pdest = *psrc; //获取最后地址的信号量的值704 #endif705 pq = (OS_Q *)pevent->OSEventPtr; //将队列事件指针保存到pq 中706 if (pq->OSQEntries > 0) { //如果消息队列指针中有消息707 pdata->OSMsg = pq->OSQOut; //将最早进入队列得消息复制到数据结构的OSMsg中708 } else {709 pdata->OSMsg = (void *)0; //如果队列中没有消息(包含一个空指针)710 }711 pdata->OSNMsgs = pq->OSQEntries; //消息队列中的消息数放置在数据结构的(OSNMsgs)中712 pdata->OSQSize = pq->OSQSize; //消息队列中的消息队列容量放置在数据结构得(OSQSize)中713 OS_EXIT_CRITICAL(); //打开中断714 return (OS_NO_ERR); //返回调用成功715 }716 #endif //结束OSQQuery ()函数717718 /*$PAGE*/719 /*720 *************************************************************************************************721 * 初始化Q722 *723 * 描述: 初始化Q。724 *725 * 参数: 无726 *727 * 返回: 无728 *729 * 注意:730 *************************************************************************************************731 */732733 void OS_QInit (void)734 {735 #if OS_MAX_QS == 1736 OSQFreeList = &OSQTbl[0]; /* Only ONE queue! */737 OSQFreeList->OSQPtr = (OS_Q *)0;738 #endif739740 #if OS_MAX_QS >= 2741 INT16U i;742 OS_Q *pq1;743 OS_Q *pq2;744745746 pq1 = &OSQTbl[0];747 pq2 = &OSQTbl[1];748 for (i = 0; i < (OS_MAX_QS - 1); i++) { /* Init. list of free QUEUE control blocks */749 pq1->OSQPtr = pq2;750 pq1++;751 pq2++;752 }753 pq1->OSQPtr = (OS_Q *)0;754 OSQFreeList = &OSQTbl[0];755 #endif756 }757 #endif /* OS_Q_EN */758