OS_SEM.C

1 /*
2 *************************************************************************************
3 *                                 uC/OS-II实时控制内核
4 *                                    主要的包含文件
5 *                                    信号量程序函数
6 *
7 * 文 件: OS_SEM.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_SEM_EN > 0        //条件编译:当OS_SEM_EN允许产生信号量程序代码
18 /*
19 *************************************************************************************
20 *                             无条件地等待请求一个信号量
21 *
22 * 描述: 该函数是查看资源是否使用或事件是否发生。中断调用该函数查询信号量。
23 *       不同于OSSemPend()函数,如果资源不可使用,OSSemAccept()函数并不挂起任务。
24
25 * 参数: pevent 指向需要保护地共享资源地信号量。当建立信号量时,用户得到该指针
26 *
27 * 返回: 当调用OSSemAccept()函数时;
28         共享资源信号量的值 > 0,则说明共享资源可以使用,这个值被返回调用者,信号量的值减1;
29         共享资源信号量的值 = 0,则说明资源不能使用,返回0。
30 *************************************************************************************
31 */
32
33 #if OS_SEM_ACCEPT_EN > 0                   //允许生成 OSSemAccept()函数
34 INT16U  OSSemAccept (OS_EVENT *pevent)     //无条件地等待请求一个信号量函数
35 {
36 #if OS_CRITICAL_METHOD == 3                //中断函数被设定为模式3
37     OS_CPU_SR  cpu_sr;
38 #endif
39     INT16U     cnt;                        //信号量的内容暂时存储变量
40
41
42 #if OS_ARG_CHK_EN > 0                      //所有参数必须在指定的参数内
43     if (pevent == (OS_EVENT *)0) {         //当信号量指针为NULL时,返回0,空指针
44         return (0);
45     }
46     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   //当事件类型≠信号量类型
47         return (0);                        //返回0
48     }
49 #endif
50     OS_ENTER_CRITICAL();                   //关闭中断
51     cnt = pevent->OSEventCnt;              //取信号值
52     if (cnt > 0) {                         //当信号值>0时,该值有效
53         pevent->OSEventCnt--;              //信号量减1
54     }
55     OS_EXIT_CRITICAL();                    //打开中断
56     return (cnt);                          //返回信号值
57 }
58 #endif
59
60 /*$PAGE*/
61 /*
62 *******************************************************************************************
                                            建立一个信号量
63 *
64 * 描述: 建立并初始化一个信号量。信号量的作用为:
65 *       1、允许一个任务与其它任务或中断同步;
66 *       2、取得共享资源的使用权;
67 *       3、标志事件的发生
68 *
69 * 参数: cnt   建立信号量的初始值,可以为0 ~ 65 535的任何值
70 *
71 * 注意:必须先建立信号量,然后才能使用
72 *
73 * 返回: != (void *)0  返回指向分配给所建立的消息邮箱的事件控制块指针;
74 *   == (void *)0  如果没有可用的事件控制块,返回空指针
75 ********************************************************************************************
76 */
77
78 OS_EVENT  *OSSemCreate (INT16U cnt)        //建立并初始化一个信号量(输入一个信号量值)
79 {
80 #if OS_CRITICAL_METHOD == 3                //中断函数被设定为模式3
81     OS_CPU_SR  cpu_sr;
82 #endif
83     OS_EVENT  *pevent;                     //建立信号量的初始值,可以在0至65535之间
84
85
86     if (OSIntNesting > 0) {                //中断嵌套数>0时,表示还有中断任务在运行
87         return ((OS_EVENT *)0);            //返回0;
88     }
89     OS_ENTER_CRITICAL();                   //关闭中断
90     pevent = OSEventFreeList;              //pevent=空余事件管理列表
91     if (OSEventFreeList != (OS_EVENT *)0) {    //如果有空余事件管理块
92         OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
93     }                                      //空余事件控制链表指向下一个空余事件控制块
94     OS_EXIT_CRITICAL();                    //打开中断
95     if (pevent != (OS_EVENT *)0) {         //如果有事件控制块ECB可用
96         pevent->OSEventType = OS_EVENT_TYPE_SEM;   //事件类型=信号类型
97         pevent->OSEventCnt  = cnt;         //将信号量值存入事件管理块中(信号量的计数器)
98         OS_EventWaitListInit(pevent);      //初始化一个事件控制块
99     }
100     return (pevent);                       //返回指针
101 }
102
103 /*$PAGE*/
104 /*
105 ***********************************************************************************************
106 *                                         删除一个信号量
107 * 描述: 用于删除一个信号量。
108 *       使用本函数有风险,因为多任务中的其它任务可能还想使用这个信号量,必须特别小心。
109 *       一般而言,在删除信号量之前,应该先删除所有可能会用到的这个信号量的任务。
110 *
111 * 参数: pevent  指向信号量指针。该指针的值在建立该信号量时得到。(参见OSSemCreate ()函数)
112 *
113 *       opt     该选项定义信号量的删除条件。可以选择只能在已经没有任何任务在等待该信号量时,才
114 *               能删除该信号量(OS_DEL_NO_PEND);或者,不管有没有任务在等待该信号量,立即删除该
115 *               信号量(OS_DEL_ALWAYS),在这种情况下,所有等待该信号量的任务都立即进入就绪态
116 *
117 *       err     指向包含错误码的变量的指针。返回的错误码可能为以下几种:
118 *               OS_NO_ERR               调用成功,信号量已被删除;
119 *               OS_ERR_DEL_ISR          试图在中断服务子程序中删除信号量;
120 *               OS_ERR_INVALID_OPT      没有将opt参数定义为2种合法的参数之一;
121 *               OS_ERR_TASK_WAITING     有一个或一个以上的任务在等待信号量;
122 *               OS_ERR_EVENT_TYPE       pevent不是指向信号量的指针;
123 *               OS_ERR_PEVENT_NULL      已经没有可用的OS_EVENT数据结构了。
124 *
125 * 返回: pevent  如果信号量已被删除,返回空指针;
126 *               若信号量没有删除,则返回pevent(信号量指针),可查看出错代码。
127 *
128 * 注意: 1) 使用此函数必须特别小心,因为多任务中的其它任务可能还想使用这个信号量;
129         2)当挂起的任务进入就绪态时,中断是关闭的,这意味着中断延迟时间与等待信号量的任务数有关。
130 ************************************************************************************************
131 */
132
133 #if OS_SEM_DEL_EN > 0                         //允许生成 OSSemDel()代码
134 OS_EVENT  *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *err)
135 {                                             //删除一个信号量(信号指针、删除条件、错误指针)
136 #if OS_CRITICAL_METHOD == 3                   //中断函数被设定为模式3
137     OS_CPU_SR  cpu_sr;
138 #endif
139     BOOLEAN tasks_waiting;                 //定义布尔量,任务等待条件
140
141
142     if (OSIntNesting > 0) {                   //中断嵌套数>0时,表示还有中断任务在运行
143         *err = OS_ERR_DEL_ISR;                //错误等于(试图在中断程序中删除一个信号量事件)
144         return (pevent);                      //返回信号量指针
145     }
146 #if OS_ARG_CHK_EN > 0                         //所有参数在指定的范围之内
147     if (pevent == (OS_EVENT *)0) {            //当信号量指针为NULL,即0(空)
148         *err = OS_ERR_PEVENT_NULL;            //错误等于(已经没有可用的OS_EVENT数据结构了)
149         return (pevent);                      //返回信号量指针
150     }
151     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {    //当事件类型不否是信号量类型
152         *err = OS_ERR_EVENT_TYPE;             //pevent指针不是指向信号量
153         return (pevent);                      //返回信号量指针
154     }
155 #endif
156     OS_ENTER_CRITICAL();                      //关闭中断
157     if (pevent->OSEventGrp != 0x00) {         //事件等待标志,索引值≠0,有任务在等待
158         tasks_waiting = TRUE;                 //有任务在等待=1(TRUE真)
159     } else {
160         tasks_waiting = FALSE;                //否则,没有任务在等待=0,(FALSE假)
161     }
162     switch (opt) {                            //条件选择
163         case OS_DEL_NO_PEND:                  // 1)没有任务在等待该信号量
164              if (tasks_waiting == FALSE) {    // 如果没有事件在等待
165                  pevent->OSEventType = OS_EVENT_TYPE_UNUSED;   //事件类型=空闲
166                  pevent->OSEventPtr  = OSEventFreeList;  //信号量对应的指针=空余块链接表
167                  OSEventFreeList     = pevent;           //空余块链接表=当前事件指针
168                  OS_EXIT_CRITICAL();           //打开中断
169                  *err = OS_NO_ERR;             //错误等于(成功删除)
170                  return ((OS_EVENT *)0);       //返回0
171              } else {                          //否则,有任务在等待
172                  OS_EXIT_CRITICAL();           //打开中断
173                  *err = OS_ERR_TASK_WAITING;   //错误等于(有一个或一个以上的任务在等待信号量)
174                  return (pevent);              //返回信号量指针
175              }
176
177         case OS_DEL_ALWAYS:                    // 2)多任务等待,尽管有任务在等待,还是要删除
178              while (pevent->OSEventGrp != 0x00) {          //等待标志≠0,还是要删除
179                  OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);
180              }                                             //使一个任务进入就绪态
181              pevent->OSEventType = OS_EVENT_TYPE_UNUSED;   //事件类型=空闲
182              pevent->OSEventPtr  = OSEventFreeList;        //信号量对应的指针=空余块链接表
183              OSEventFreeList     = pevent;                 //空余块链接表=当前事件指针
184              OS_EXIT_CRITICAL();                //打开中断
185              if (tasks_waiting == TRUE) {       //当任务等待=1,真
186                  OS_Sched();                    //任务调度,最高优先级进入运行状态
187              }
188              *err = OS_NO_ERR;                  //错误等于(成功删除)
189              return ((OS_EVENT *)0);            //返回0
190
191         default:                                // 3)当以上两种情况都不是
192              OS_EXIT_CRITICAL();                //打开中断
193              *err = OS_ERR_INVALID_OPT;         //错误等于(没有将opt参数定义为2种合法的参数之一)
194              return (pevent);                   //返回信号量指针
195     }
196 }
197 #endif
198
199 /*$PAGE*/
200 /*
201 ************************************************************************************************
202 *                                        等待一个信号量
203 * 描述: 等待一个信号量。
204 *       任务试图取得共享资源使用权、任务需要与其它任务或中断同步及任务需要等待特定事件的发生的场合。
205 *       若任务调用该函数,且信号量的值>0,那么OSSemPend()递减该值并返回该值;
206 *       若任务调用该函数,且信号量的值=0,那么OSSemPend()函数将任务加入该信号量的等待列表中。
207 *
208 * 参数: pevent    指向信号量指针。该指针的值在建立该信号量时得到。(参见OSSemCreate ()函数)
209 *
210 *       timeout   允许任务在经过指定数目的时钟节拍后还没有得到需要的信号量时;恢复运行状态。如果
211 *                 该值为0。则表示任务将持续地等待信号量。最长等待时间为65 535个时钟节拍。这个时
212 *                 间长度并不是严格的,可能存在一个时间节拍的误差,因为自由一个时钟节拍结束后,才
213 *                 会给定义的等待超时时钟节拍减1。
214 *       err       指向包含错误码的变量的指针。返回的错误码可能为以下几种;
215 *
216 *                 OS_NO_ERR           成功,信号量是可用的;
217 *                 OS_TIMEOUT          信号量没有在指定的周期数内置位;
218 *                 OS_ERR_EVENT_TYPE   pevent不是指向信号量的指针;
219 *                 OS_ERR_PEND_ISR     在中断中调用该函数。虽然规定了不允许在中断中调用该函数,但
220 *                                     ucos仍然包含了检测这种情况的功能;
221 *                 OS_ERR_PEVENT_NULL  pevent是空指针。
222 * 返回: 无
223 * 注意:必须先建立信号量,然后才能使用。
224 ************************************************************************************************
225 */
226 //等待一个信号量函数(信号量指针、允许等待的时钟节拍、代码错误指针)
227 void  OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
228 {
229 #if OS_CRITICAL_METHOD == 3                      //中断函数被设定为模式3
230     OS_CPU_SR  cpu_sr;
231 #endif
232
233
234     if (OSIntNesting > 0) {                      //中断嵌套数>0时,表示还有中断任务在运行
235         *err = OS_ERR_PEND_ISR;                  //错误等于(试图在中断程序中等待一个信号量事件)
236         return;                                  //返回
237     }
238 #if OS_ARG_CHK_EN > 0                            //所有参数在指定的范围之内
239     if (pevent == (OS_EVENT *)0) {               //当信号量指针为NULL,即0(空)
240         *err = OS_ERR_PEVENT_NULL;               //pevent是空指针
241         return;                                  //返回
242     }
243     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   //当事件类型不否是信号量类型
244         *err = OS_ERR_EVENT_TYPE;                //pevent指针不是指向信号量
245         return;                                  //返回
246     }
247 #endif
248     OS_ENTER_CRITICAL();                         //关闭中断
249     if (pevent->OSEventCnt > 0) {                //当信号量计数器>0时,
250         pevent->OSEventCnt--;                    //信号量计数器减1
251         OS_EXIT_CRITICAL();                      //打开中断
252         *err = OS_NO_ERR;                        //错误等于(成功,信号量是可用的)
253         return;                                  //返回
254     }
255
256     OSTCBCur->OSTCBStat |= OS_STAT_SEM; //将任务状态置1,进入睡眠状态,只能通过信号量唤醒
257     OSTCBCur->OSTCBDly   = timeout;              //最长等待时间=timeout,递减式
258     OS_EventTaskWait(pevent);                    //使任务进入等待时间唤醒状态
259     OS_EXIT_CRITICAL();                          //打开中断
260     OS_Sched();                                  //进入调度任务,使就绪态优先级最高任务运行
261     OS_ENTER_CRITICAL();                         //关闭中断
262     if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {     //检查任务状态是否还是在睡眠状态,即信号量没有唤醒
263         OS_EventTO(pevent);                      //如果没有等到信号量,由等待事件返回
264         OS_EXIT_CRITICAL();                      //打开中断
265         *err = OS_TIMEOUT;                       //错误等于(信号量没有在指定的周期数内置位)
266         return;                                  //返回
267     }
268     OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;     //将信号量ECB的指针从该任务控制块中删除
269     OS_EXIT_CRITICAL();                          //打开中断
270     *err = OS_NO_ERR;                            //错误等于(成功,信号量是可用的)
271 }
272 /*$PAGE*/
273 /*
274 *************************************************************************************************
275 *                                       发出一个信号量
276 *
277 * 描述: 置位指定的信号量。如果指定的信号量是0或大于0,该函数则递增该信号量并返回;
278         如果有任何任务在等待信号量,那么最高优先级任务将得到该信号量并进入就绪态;
279         如果被唤醒的任务就是最高优先级的就绪态任务,则任务调度函数将进入任务调度。
280 *
281 * 参数: pevent   指向信号量指针。该指针的值在建立该信号量时得到。(参见OSSemCreate ()函数)
282 *
283 * 返回: OS_NO_ERR           信号量成功的置位;
284 *       OS_SEM_OVF          信号量的值溢出;
285 *       OS_ERR_EVENT_TYPE   pevent不是指向信号量的指针;
286 *       OS_ERR_PEVENT_NULL  pevent是空指针。
287 *************************************************************************************************
288 */
289
290 INT8U  OSSemPost (OS_EVENT *pevent)              //发出一个信号量函数(信号量指针)
291 {
292 #if OS_CRITICAL_METHOD == 3                      //中断函数被设定为模式3
293     OS_CPU_SR  cpu_sr;
294 #endif
295
296
297 #if OS_ARG_CHK_EN > 0                            //所有参数在指定的范围之内
298     if (pevent == (OS_EVENT *)0) {               //当信号量指针为NULL,即0(空)
299         return (OS_ERR_PEVENT_NULL);             //pevent是空指针
300     }
301     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {    //当事件类型不否是信号量类型
302         return (OS_ERR_EVENT_TYPE);              //pevent指针不是指向信号量
303     }
304 #endif
305     OS_ENTER_CRITICAL();                         //关闭中断
306     if (pevent->OSEventGrp != 0x00) {            //有任务在等待信号量,等待事件的任务组=0
307         OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);   //使任务进入就绪态
308         OS_EXIT_CRITICAL();                      //打开中断
309         OS_Sched();                              //进入调度任务,使就绪态优先级最高任务运行
310         return (OS_NO_ERR);                      //返回(信号量成功的置位)
311     }
312     if (pevent->OSEventCnt < 65535) {            //当信号量值< 65535时,
313         pevent->OSEventCnt++;                    //信号量计数加1
314         OS_EXIT_CRITICAL();                      //打开中断
315         return (OS_NO_ERR);                      //返回(信号量成功的置位)
316     }
317     OS_EXIT_CRITICAL();                          //打开中断
318     return (OS_SEM_OVF);                         //返回(信号量的值溢出)
319 }
320 /*
321 **************************************************************************************************
322 *                                   查询一个信号量的当前状态
323 *
324 * 描述: 用于获取某个信号量的信息。在使用该函数之前,应用程序先要建立OS_SEM_DATA的数据结构,用来保存
325 *       从信号量的事件控制中取得的数据。使用该函数可以得知,是否有以及多少任务目前位于信号量的任务
326 *       等待对列中(查询OSEventTbl()域中的数目),并还可以获取信号量的值。
327 *
328 * 参数: pevent  指向信号量指针。该指针的值在建立该信号量时得到。(参见OSSemCreate ()函数)
329 *
330 *       pdata   一个指向数据结构OS_SEM_DATA的指针。
331 *
332 * 返回: OS_NO_ERR           用成功;
333 *       OS_ERR_EVENT_TYPE   pevent不是指向信号量的指针;
334 *       OS_ERR_PEVENT_NULL  pevent是空指针。
335 **************************************************************************************************
336 */
337
338 #if OS_SEM_QUERY_EN > 0                           //允许生成 OSSemQuery()代码
339 INT8U  OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)
340 {                                        //查询一个信号量的当前状态(信号量指针、状态数据结构指针)
341 #if OS_CRITICAL_METHOD == 3                       //中断函数被设定为模式3
342     OS_CPU_SR  cpu_sr;
343 #endif
344     INT8U     *psrc;                              //定义8位pevent->OSEventTbl[0]的地址指针
345     INT8U     *pdest;                             //定义8位pdata->OSEventTbl[0]的地址指针
346
347
348 #if OS_ARG_CHK_EN > 0                             //所有参数在指定的范围之内
349     if (pevent == (OS_EVENT *)0) {                //当信号量指针为NULL,即0(空)
350         return (OS_ERR_PEVENT_NULL);              //pevent是空指针
351     }
352     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   //当事件类型不是信号量类型
353         return (OS_ERR_EVENT_TYPE);               //pevent指针不是指向信号量
354     }
355 #endif
356     OS_ENTER_CRITICAL();                          //关闭中断
357     //将事件(信号量)结构中的等待任务列表复制到pdata数据结构中
358     pdata->OSEventGrp = pevent->OSEventGrp;       //等待事件的任务组中的内容传送到状态数据结构中
359     psrc              = &pevent->OSEventTbl[0];   //保存pevent->OSEventTbl[0]对应的地址
360     pdest             = &pdata->OSEventTbl[0];    //保存pdata->OSEventTbl[0]对应的地址
361 #if OS_EVENT_TBL_SIZE > 0                         //当事件就绪对应表中的对应值>0时
362     *pdest++          = *psrc++;                  //地址指针下移一个类型地址,获取信号量的值
363 #endif
364
365 #if OS_EVENT_TBL_SIZE > 1                         //事件就绪对应表中的对应值>1时
366     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
367 #endif
368
369 #if OS_EVENT_TBL_SIZE > 2                         //事件就绪对应表中的对应值>2时
370     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
371 #endif
372
373 #if OS_EVENT_TBL_SIZE > 3                         //事件就绪对应表中的对应值>3时
374     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
375 #endif
376
377 #if OS_EVENT_TBL_SIZE > 4                         //事件就绪对应表中的对应值>4时
378     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
379 #endif
380
381 #if OS_EVENT_TBL_SIZE > 5                         //事件就绪对应表中的对应值>5时
382     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
383 #endif
384
385 #if OS_EVENT_TBL_SIZE > 6                         //事件就绪对应表中的对应值>6时
386     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值
387 #endif
388
389 #if OS_EVENT_TBL_SIZE > 7                         //事件就绪对应表中的对应值>7时
390     *pdest             = *psrc;                    //获取最后地址的信号量的值
391 #endif
392     pdata->OSCnt      = pevent->OSEventCnt;       //数据计数=当前信号事件对应的计数值(?任务数)
393     OS_EXIT_CRITICAL();                           //打开中断
394     return (OS_NO_ERR);                           //返回成功运行
395 }
396 #endif                                            //OS_SEM_QUERY_EN函数结束
397 #endif                                            //OS_SEM_EN文件结束
398

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值