OS_SEM.C

来源:互联网 发布:android 网络图片压缩 编辑:程序博客网 时间:2024/06/04 18:09
1 /*2 *************************************************************************************3 *                                 uC/OS-II实时控制内核4 *                                    主要的包含文件5 *                                    信号量程序函数6 *7 * 文 件: OS_SEM.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_SEM_EN > 0        //条件编译:当OS_SEM_EN允许产生信号量程序代码18 /*19 *************************************************************************************20 *                             无条件地等待请求一个信号量21 *22 * 描述: 该函数是查看资源是否使用或事件是否发生。中断调用该函数查询信号量。23 *       不同于OSSemPend()函数,如果资源不可使用,OSSemAccept()函数并不挂起任务。2425 * 参数: pevent 指向需要保护地共享资源地信号量。当建立信号量时,用户得到该指针26 *27 * 返回: 当调用OSSemAccept()函数时;28         共享资源信号量的值 > 0,则说明共享资源可以使用,这个值被返回调用者,信号量的值减1;29         共享资源信号量的值 = 0,则说明资源不能使用,返回0。30 *************************************************************************************31 */3233 #if OS_SEM_ACCEPT_EN > 0                   //允许生成 OSSemAccept()函数34 INT16U  OSSemAccept (OS_EVENT *pevent)     //无条件地等待请求一个信号量函数35 {36 #if OS_CRITICAL_METHOD == 3                //中断函数被设定为模式337     OS_CPU_SR  cpu_sr;38 #endif39     INT16U     cnt;                        //信号量的内容暂时存储变量404142 #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);                        //返回048     }49 #endif50     OS_ENTER_CRITICAL();                   //关闭中断51     cnt = pevent->OSEventCnt;              //取信号值52     if (cnt > 0) {                         //当信号值>0时,该值有效53         pevent->OSEventCnt--;              //信号量减154     }55     OS_EXIT_CRITICAL();                    //打开中断56     return (cnt);                          //返回信号值57 }58 #endif5960 /*$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 */7778 OS_EVENT  *OSSemCreate (INT16U cnt)        //建立并初始化一个信号量(输入一个信号量值)79 {80 #if OS_CRITICAL_METHOD == 3                //中断函数被设定为模式381     OS_CPU_SR  cpu_sr;82 #endif83     OS_EVENT  *pevent;                     //建立信号量的初始值,可以在0至65535之间848586     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 }102103 /*$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 */132133 #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                   //中断函数被设定为模式3137     OS_CPU_SR  cpu_sr;138 #endif139     BOOLEAN tasks_waiting;                 //定义布尔量,任务等待条件140141142     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 #endif156     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);       //返回0171              } else {                          //否则,有任务在等待172                  OS_EXIT_CRITICAL();           //打开中断173                  *err = OS_ERR_TASK_WAITING;   //错误等于(有一个或一个以上的任务在等待信号量)174                  return (pevent);              //返回信号量指针175              }176177         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);            //返回0190191         default:                                // 3)当以上两种情况都不是192              OS_EXIT_CRITICAL();                //打开中断193              *err = OS_ERR_INVALID_OPT;         //错误等于(没有将opt参数定义为2种合法的参数之一)194              return (pevent);                   //返回信号量指针195     }196 }197 #endif198199 /*$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                      //中断函数被设定为模式3230     OS_CPU_SR  cpu_sr;231 #endif232233234     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 #endif248     OS_ENTER_CRITICAL();                         //关闭中断249     if (pevent->OSEventCnt > 0) {                //当信号量计数器>0时,250         pevent->OSEventCnt--;                    //信号量计数器减1251         OS_EXIT_CRITICAL();                      //打开中断252         *err = OS_NO_ERR;                        //错误等于(成功,信号量是可用的)253         return;                                  //返回254     }255256     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 */289290 INT8U  OSSemPost (OS_EVENT *pevent)              //发出一个信号量函数(信号量指针)291 {292 #if OS_CRITICAL_METHOD == 3                      //中断函数被设定为模式3293     OS_CPU_SR  cpu_sr;294 #endif295296297 #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 #endif305     OS_ENTER_CRITICAL();                         //关闭中断306     if (pevent->OSEventGrp != 0x00) {            //有任务在等待信号量,等待事件的任务组=0307         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++;                    //信号量计数加1314         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 */337338 #if OS_SEM_QUERY_EN > 0                           //允许生成 OSSemQuery()代码339 INT8U  OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)340 {                                        //查询一个信号量的当前状态(信号量指针、状态数据结构指针)341 #if OS_CRITICAL_METHOD == 3                       //中断函数被设定为模式3342     OS_CPU_SR  cpu_sr;343 #endif344     INT8U     *psrc;                              //定义8位pevent->OSEventTbl[0]的地址指针345     INT8U     *pdest;                             //定义8位pdata->OSEventTbl[0]的地址指针346347348 #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 #endif356     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 #endif364365 #if OS_EVENT_TBL_SIZE > 1                         //事件就绪对应表中的对应值>1时366     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值367 #endif368369 #if OS_EVENT_TBL_SIZE > 2                         //事件就绪对应表中的对应值>2时370     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值371 #endif372373 #if OS_EVENT_TBL_SIZE > 3                         //事件就绪对应表中的对应值>3时374     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值375 #endif376377 #if OS_EVENT_TBL_SIZE > 4                         //事件就绪对应表中的对应值>4时378     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值379 #endif380381 #if OS_EVENT_TBL_SIZE > 5                         //事件就绪对应表中的对应值>5时382     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值383 #endif384385 #if OS_EVENT_TBL_SIZE > 6                         //事件就绪对应表中的对应值>6时386     *pdest++          = *psrc++;                  //地址指针继续下移一个类型地址,获取信号量的值387 #endif388389 #if OS_EVENT_TBL_SIZE > 7                         //事件就绪对应表中的对应值>7时390     *pdest             = *psrc;                    //获取最后地址的信号量的值391 #endif392     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