1 /*
2 *************************************************************************************************
3 * uC/OS-II实时控制内核
4 * 主要的包含文件
5 * --消息队列管理项--
6 *
7 * 文 件: OS_Q.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 #include "includes.h" //包含"includes.h"文件,部分C语言头文件的汇总打包文件
15 #endif //定义结束
16
17 #if (OS_Q_EN > 0) && (OS_MAX_QS > 0) //条件编译:OS_Q_EN 允许 (1)产生消息队列相关代码
18 //条件编译:应用中最多对列控制块的数目 > 0
19 /*
20 *************************************************************************************************
21 * 检查消息队列中是否已经有需要的消息(ACCEPT MESSAGE FROM QUEUE)
22 *
23 * 描述: 检查消息队列中是否已经有需要的消息.不同于OSQPend()函数,如果没有需要的消息,OSQAccept()
24 * 函数并不挂起任务。如果消息已经到达,该消息被传递到用户任务。通常中断调用该函数,因为中
25 * 断不允许挂起等待消息。
26 *
27 * 参数: pevent 是指向需要查看的消息队列的指针。当建立消息队列时,该指针返回到用户程序。
28 * (参考OSMboxCreate()函数)。
29 *
30 * 返回: 如果消息已经到达,返回指向该消息的指针;如果消息队列没有消息,返回空指针。
31 *
32 * 注意:必须先建立消息队列,然后使用。
33 *************************************************************************************************
34 */
35
36 #if OS_Q_ACCEPT_EN > 0 //条件编译:允许生成 OSQAccept()代码
37 void *OSQAccept (OS_EVENT *pevent) //检查消息队列中是否已经有需要的消息(消息队列的指针)
38 {
39 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
40 OS_CPU_SR cpu_sr;
41 #endif
42 void *msg; //定义消息队列指针(输出缓冲区)
43 OS_Q *pq; //定义消息队列事件
44
45
46 #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); //返回0
52 }
53 #endif
54 OS_ENTER_CRITICAL(); //关闭中断
55 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针
56 if (pq->OSQEntries != 0) { //当消息队列消息数
57 msg = *pq->OSQOut++; //输出消息内容到缓冲区
58 pq->OSQEntries--; //消息数减1
59 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 #endif
69 /*$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 //中断函数被设定为模式3
90 OS_CPU_SR cpu_sr;
91 #endif
92 OS_EVENT *pevent; //定义一个指向事件控制快的指针
93 OS_Q *pq; //定义一个队列控制模块指针(事件)
94
95
96 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只用于信号量,不用置0
121 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; //事件指针=0
128 }
129 }
130 return (pevent); //消息队列建立成功,返回一个消息队列得指针,并成为该消息句柄
131 }
132 /*$PAGE*/
133 /*
134 *************************************************************************************************
135 * 删除消息队列(DELETE A MESSAGE QUEUE)
136 *
137 * 描述: 删除消息队列。使用这个函数有风险,因为多任务中的其他任务可能还想用这个消息队列.使用这
138 * 个函数要特别小心.一般的说,应先删除可能会用到这个消息队列的所以任务,再调用本函数。
139
140 *
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 */
163
164 #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 //中断函数被设定为模式3
168 OS_CPU_SR cpu_sr;
169 #endif
170 BOOLEAN tasks_waiting; //定义布尔量,任务等待条件
171 OS_Q *pq; //定义一个队列控制模块指针(事件)
172
173
174 if (OSIntNesting > 0) { //中断嵌套数>0时,表示还有中断任务在运行
175 *err = OS_ERR_DEL_ISR; //错误等于(试图在中断程序中删除一个消息队列)
176 return ((OS_EVENT *)0); //返回0
177 }
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 #endif
188 OS_ENTER_CRITICAL(); //关闭中断
189 if (pevent->OSEventGrp != 0x00) { //事件等待标志(索引值是否有任务在等待)
190 tasks_waiting = TRUE; //有则任务等待标志=1
191 } else { //否则
192 tasks_waiting = FALSE; //没有则任务等待标志=0
193 }
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); //返回0
206 } else { //否则
207 OS_EXIT_CRITICAL(); //打开中断
208 *err = OS_ERR_TASK_WAITING; //有一个或一个以上的任务在等待消息队列中的消息
209 return (pevent); //返回消息队列指针
210 }
211
212 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); //返回0
228
229 default: // 3)以上两者都不是
230 OS_EXIT_CRITICAL(); //打开中断
231 *err = OS_ERR_INVALID_OPT; //'opt'参数没有在以上2个参数值之一
232 return (pevent); //返回指针
233 }
234 }
235 #endif
236
237 /*$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 */
254
255 #if OS_Q_FLUSH_EN > 0 //允许生成 OSQFlush()代码
256 INT8U OSQFlush (OS_EVENT *pevent) //清空消息队列(指向得到消息队列的指针)
257 {
258 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
259 OS_CPU_SR cpu_sr;
260 #endif
261 OS_Q *pq; //定义一个队列事件
262
263
264 #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 #endif
272 OS_ENTER_CRITICAL(); //关闭中断
273 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针
274 pq->OSQIn = pq->OSQStart; //插入指针=起始指针
275 pq->OSQOut = pq->OSQStart; //输出指针=起始指针
276 pq->OSQEntries = 0; //消息队列数目=0
277 OS_EXIT_CRITICAL(); //打开中断
278 return (OS_NO_ERR); //返回(消息队列被成功清空)
279 }
280 #endif
281
282 /*$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()函数返回空指针并且将 *err
313 * 设置为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 //中断函数被设定为模式3
323 OS_CPU_SR cpu_sr;
324 #endif
325 void *msg; //定义消息队列的指针、取出的暂存指针
326 OS_Q *pq; //定义一个队列事件
327
328
329 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 #endif
343 OS_ENTER_CRITICAL(); //关闭中断
344 pq = (OS_Q *)pevent->OSEventPtr; //队列指针=当前事件指针
345 if (pq->OSQEntries != 0) { //当前消息队列中消息数 > 0,有消息
346 msg = *pq->OSQOut++; //OSQOut将对应的地址的消息复制到msg
347 pq->OSQEntries--; //当前队列消息数减1
348 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; //指向事件控制块的指针=0
366 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); //返回0
374 }
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 */
403
404 #if OS_Q_POST_EN > 0 //允许生成 OSQPost()代码
405 INT8U OSQPost (OS_EVENT *pevent, void *msg)
406 { //向消息队列发送一则消息FIFO(消息队列指针、发送的消息)
407 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
408 OS_CPU_SR cpu_sr;
409 #endif
410 OS_Q *pq; //定义一个队列事件
411
412
413 #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 #endif
424 OS_ENTER_CRITICAL(); //关闭中断
425 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0
426 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; //插入当前的消息(内容),地址为指针加1
437 pq->OSQEntries++; //消息队列数加1
438 if (pq->OSQIn == pq->OSQEnd) { //当插入的消息指针=最后(结束)的指针
439 pq->OSQIn = pq->OSQStart; //插入指针跳到起始处指针
440 }
441 OS_EXIT_CRITICAL(); //打开中断
442 return (OS_NO_ERR); //消息成功的放到消息队列中
443 }
444 #endif
445 /*$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 */
473
474 #if OS_Q_POST_FRONT_EN > 0 //允许生成 OSQPost()代码
475 INT8U OSQPostFront (OS_EVENT *pevent, void *msg)
476 { //向消息队列发送一则消息LIFO(消息队列指针、发送的消息)
477 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
478 OS_CPU_SR cpu_sr;
479 #endif
480 OS_Q *pq; //定义一个队列事件
481
482 #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 #endif
493 OS_ENTER_CRITICAL(); //关闭中断
494 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0
495 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--; //插入指针减1
509 *pq->OSQOut = msg; //插入当前消息(内容)
510 pq->OSQEntries++; //消息数加1
511 OS_EXIT_CRITICAL(); //打开中断
512 return (OS_NO_ERR); //消息成功的放到消息队列中
513 }
514 #endif
515 /*$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 to
551 * ->等同于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 */
568
569 #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 //中断函数被设定为模式3
573 OS_CPU_SR cpu_sr;
574 #endif
575 OS_Q *pq; //定义一个队列事件
576
577
578 #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 #endif
589 OS_ENTER_CRITICAL(); //关闭中断
590 if (pevent->OSEventGrp != 0x00) { //是否有任务在等待该消息队列,索引值≠0
591 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--; //插入指针减1
612 *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++; //消息数加1
620 OS_EXIT_CRITICAL(); //消息成功的放到消息队列中
621 return (OS_NO_ERR); //消息成功的放到消息队列中
622 }
623 #endif
624 /*$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 */
649
650 #if OS_Q_QUERY_EN > 0 //允许生成 OSQQuery()代码
651 INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *pdata)
652 { //查询一个消息队列的当前状态(信号量指针、状态数据结构指针)
653 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
654 OS_CPU_SR cpu_sr;
655 #endif
656 OS_Q *pq; //定义一个队列事件指针
657 INT8U *psrc; //定义8位pevent->OSEventTbl[0]的地址指针
658 INT8U *pdest; //定义8位pdata->OSEventTbl[0]的地址指针
659
660
661 #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 #endif
669 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 #endif
677
678 #if OS_EVENT_TBL_SIZE > 1 //事件就绪对应表中的对应值>1时
679 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值
680 #endif
681
682 #if OS_EVENT_TBL_SIZE > 2 //事件就绪对应表中的对应值>2时
683 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值
684 #endif
685
686 #if OS_EVENT_TBL_SIZE > 3 //事件就绪对应表中的对应值>3时
687 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值常慰
688 #endif
689
690 #if OS_EVENT_TBL_SIZE > 4 //事件就绪对应表中的对应值>4时
691 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值
692 #endif
693
694 #if OS_EVENT_TBL_SIZE > 5 //事件就绪对应表中的对应值>5时
695 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值
696 #endif
697
698 #if OS_EVENT_TBL_SIZE > 6 //事件就绪对应表中的对应值>6时
699 *pdest++ = *psrc++; //地址指针继续下移一个类型地址,获取信号量的值
700 #endif
701
702 #if OS_EVENT_TBL_SIZE > 7 //事件就绪对应表中的对应值>7时
703 *pdest = *psrc; //获取最后地址的信号量的值
704 #endif
705 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 ()函数
717
718 /*$PAGE*/
719 /*
720 *************************************************************************************************
721 * 初始化Q
722 *
723 * 描述: 初始化Q。
724 *
725 * 参数: 无
726 *
727 * 返回: 无
728 *
729 * 注意:
730 *************************************************************************************************
731 */
732
733 void OS_QInit (void)
734 {
735 #if OS_MAX_QS == 1
736 OSQFreeList = &OSQTbl[0]; /* Only ONE queue! */
737 OSQFreeList->OSQPtr = (OS_Q *)0;
738 #endif
739
740 #if OS_MAX_QS >= 2
741 INT16U i;
742 OS_Q *pq1;
743 OS_Q *pq2;
744
745
746 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 #endif
756 }
757 #endif /* OS_Q_EN */
758