OS_Q.C

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
时间: 2024-08-22 14:42:37

OS_Q.C的相关文章

探索ucos-ii之路(一)--内核目录结构和内核组成

UCOS-ii2.5.2内核目录结构和组成   1.uCOS-II Source->source:(与处理器类型无关的代码) [cpp] view plain copy print? *********************************************************************************   *   OS_CORE.C                       系统初始化,开启多任务环境等的代码          ***********

uCOS_II.C

  Lin-credible!  Linux内核读不懂,先读读 uCOS的中文注释的源码!  1 /* 2 ************************************************************************************************* 3 * uC/OS-II实时控制内核 4 * 主要的包含文件 5 * 6 * 7 * 文 件: uCOS_II.C 包含主要uC/OS-II构造文件 8 * 作 者: Jean J. Labros