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
OS_SEM.C
最新推荐文章于 2021-08-17 11:46:35 发布