專注電子技術(shù)學(xué)習(xí)與研究
當前位置:單片機教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

ucos OSEventPendMulti 函數(shù)解析

作者:佚名   來源:本站原創(chuàng)   點擊數(shù):  更新時間:2014年04月27日   【字體:

 這個函數(shù)是請求信號量,郵箱,消息隊列的,但是對互斥信號量和標志不起作用。

#if ((OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u))
INT16U  OSEventPendMulti (OS_EVENT  **pevents_pend,//掛起的事件組(需要請求資源)
                          OS_EVENT  **pevents_rdy, //就緒的事件組
                          void      **pmsgs_rdy,   //保存請求中返回的信息
                          INT32U      timeout,     //請求資源的時間,如果超時任務(wù)進入休眠態(tài)
                          INT8U      *perr)        
{
    OS_EVENT  **pevents;
    OS_EVENT   *pevent;
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
    OS_Q       *pq;             //如果使用隊列且最大隊列控制塊數(shù)大于0則申請一個隊列控制塊指針
#endif
    BOOLEAN     events_rdy;     //是否有準備好的事件標志
    INT16U      events_rdy_nbr; //準備好的事件數(shù)
    INT8U       events_stat;    //事件狀態(tài)
#if (OS_CRITICAL_METHOD == 3u)  
    OS_CPU_SR   cpu_sr = 0u;
#endif
 
#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif
//驗證各個參數(shù)的有效性
#if (OS_ARG_CHK_EN > 0u)
    if (pevents_pend == (OS_EVENT **)0) {      
       *perr =  OS_ERR_PEVENT_NULL;
        return (0u);
    }
    if (*pevents_pend  == (OS_EVENT *)0) {    
       *perr =  OS_ERR_PEVENT_NULL;
        return (0u);
    }
    if (pevents_rdy  == (OS_EVENT **)0) {      
       *perr =  OS_ERR_PEVENT_NULL;
        return (0u);
    }
    if (pmsgs_rdy == (void **)0) {            
       *perr =  OS_ERR_PEVENT_NULL;
        return (0u);
    }
#endif
//初始化指向就緒事件數(shù)組的指針為NULL
   *pevents_rdy = (OS_EVENT *)0;              
//pevents指向掛起等待的事件組,已經(jīng)處于就緒態(tài)的事件組是不需要再判斷的,pevent指向該事件組中的事件.
    pevents     =  pevents_pend;
    pevent      = *pevents;
//對事件組中的所有事件進行類型檢查
    while  (pevent != (OS_EVENT *)0) {
        switch (pevent->OSEventType) {        
#if (OS_SEM_EN  > 0u)
            case OS_EVENT_TYPE_SEM:
                 break;
#endif
#if (OS_MBOX_EN > 0u)
            case OS_EVENT_TYPE_MBOX:
                 break;
#endif
#if ((OS_Q_EN   > 0u) && (OS_MAX_QS > 0u))
            case OS_EVENT_TYPE_Q:
                 break;
#endif
 
            case OS_EVENT_TYPE_MUTEX:
            case OS_EVENT_TYPE_FLAG:
            default:
                *perr = OS_ERR_EVENT_TYPE;
                 return (0u);
        }
        pevents++;        //即數(shù)組地址+1
        pevent = *pevents;//指向下一個元素
    }
//不能在中斷程序中調(diào)用
    if (OSIntNesting  > 0u) {                  
       *perr =  OS_ERR_PEND_ISR;                
        return (0u);
    }
//任務(wù)被鎖住也是不能掛起的
    if (OSLockNesting > 0u) {                  
       *perr =  OS_ERR_PEND_LOCKED;            
        return (0u);
    }
 
    OS_ENTER_CRITICAL();                 //進入中斷沿
    events_rdy     =  OS_FALSE;          //事件就緒標志初始化為0
    events_rdy_nbr =  0u;                //就緒事件數(shù)初始化為0
    events_stat    =  OS_STAT_RDY;       //事件狀態(tài)初始化為就緒態(tài)
    pevents        =  pevents_pend;      //pevents指向掛起等待的事件組 pevent指向事件組中的事件
    pevent         = *pevents;
    while (pevent != (OS_EVENT *)0) {    //當最后一個元素值為(OS_EVENT *)0時等待事件組都判斷完了
        switch (pevent->OSEventType) {   //對事件組中請求特定資源的事件做特定處理
#if (OS_SEM_EN > 0u)                       
            case OS_EVENT_TYPE_SEM:      //信號量事件,信號量大于0說明資源是可用的
                 if (pevent->OSEventCnt > 0u) {
                     pevent->OSEventCnt--;      
    //請求得到滿足,可用的信號量數(shù)就減少了所以要--
                    *pevents_rdy++ =  pevent;  
    //這個事件進入就緒事件表
                      events_rdy   =  OS_TRUE;
    //標志一下有事件就緒了
                    *pmsgs_rdy++   = (void *)0;
    //信號量沒有返回消息
                      events_rdy_nbr++;
    //就緒的事件數(shù)加1
                 } else {
                      events_stat |=  OS_STAT_SEM;
    //請求失敗則標記為事件組等待信號量
                 }
                 break;
#endif
 
#if (OS_MBOX_EN > 0u)
            case OS_EVENT_TYPE_MBOX://是郵箱事件如果郵箱有東西則請求得到滿足
                 if (pevent->OSEventPtr != (void *)0) {                                      *pmsgs_rdy++=(void *)pevent->OSEventPtr;
     //把郵箱的消息地址保存在message_ready數(shù)組中
                     pevent->OSEventPtr  = (void *)0;
     //請求了一個郵箱郵箱就由非空狀態(tài)變空
                     *pevents_rdy++=pevent;
                     //這個事件進入就緒態(tài)
                      events_rdy=OS_TRUE;        
                     //標志一下有事件就緒了
                      events_rdy_nbr++;
                     //準備好的事件數(shù)加1
                 } else {
                      events_stat |= OS_STAT_MBOX;
                     //請求郵箱失敗標記事件組等待郵箱
                 }
                 break;
#endif
 
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
            case OS_EVENT_TYPE_Q:                //如果是隊列事件
                 pq = (OS_Q *)pevent->OSEventPtr;//pq指向隊列事件指針
                 if (pq->OSQEntries > 0u) {      //如果隊列非空                          
                 *pmsgs_rdy++ = (void *)*pq->OSQOut++;
                         //把隊列的消息保存在pmsgs_rdy的數(shù)組中
                     if (pq->OSQOut == pq->OSQEnd) {
                         pq->OSQOut = pq->OSQStart;
                     }   //隊列的鏈表要把首尾連接起來
                     pq->OSQEntries--; 
                     //請求了一個隊列隊列中消息就減少了(隊列是鏈表,其中一塊少了即入口少了)
                    *pevents_rdy++ = pevent;      
                     //請求得到允許事件進入就緒態(tài)                    
                     events_rdy   = OS_TRUE;          //標志一下有事件就緒了  
                      events_rdy_nbr++;               //就緒事件數(shù)加1
                 } else {
                      events_stat |= OS_STAT_Q;    }
                     //請求隊列未得到允許則標志事件組狀態(tài)為等待隊列                 
                 break;
#endif
 
            case OS_EVENT_TYPE_MUTEX:
            case OS_EVENT_TYPE_FLAG:
            //對互斥信號量事件和標志事件則不做處理
            default:
            //如果都不是這些事件類型則退出中斷沿并返回事件類型錯誤
                 OS_EXIT_CRITICAL();
                *pevents_rdy = (OS_EVENT *)0;//這里用這個(OS_EVENT *)0返回空的消息
            //因為是先保存再加所以出現(xiàn)錯誤不會有后續(xù)判斷這里就不用++
                *perr=  OS_ERR_EVENT_TYPE;
                 return (events_rdy_nbr);    //返回就緒的事件數(shù)
        }
        pevents++;
        pevent = *pevents;                              
        //對事件組中的每一個事件做判斷
    }
 
    if ( events_rdy == OS_TRUE) {    
       *pevents_rdy = (OS_EVENT *)0;  
       //所有的事件都判斷完了補上一個 (OS_EVENT *)0的結(jié)束標志
        OS_EXIT_CRITICAL();
       *perr  =  OS_ERR_NONE;
        return (events_rdy_nbr);    //返回事件的就緒數(shù)(調(diào)用這個函數(shù)的任務(wù)繼續(xù)運行)
    }
       //如果沒有事件就緒即所有請求都失敗則掛起調(diào)用該函數(shù)的函數(shù)  
    OSTCBCur->OSTCBStat     |= events_stat  | OS_STAT_MULTI;
       //標記當前事件控制塊中的事件狀態(tài)為等待資源而且是等待多個資源
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
       //配置當前事件控制塊中的事件狀態(tài)標志為等待完成,因為這個事件即將被切換掉,進入掛起態(tài)
    OSTCBCur->OSTCBDly       = timeout;        
       //設(shè)置該任務(wù)掛起等待的時間為timeout時間,超時任務(wù)就被切換掉。
    OS_EventTaskWaitMulti(pevents_pend);      
    //暫停任務(wù)直到出現(xiàn)新的事件呼叫或者掛起時間到
    OS_EXIT_CRITICAL();
    //退出中斷沿
    OS_Sched();                                
    //如果當前任務(wù)不處于就緒態(tài)將進行任務(wù)調(diào)度將優(yōu)先級最高的任務(wù)調(diào)入運行態(tài)
    OS_ENTER_CRITICAL();
    //進入中斷沿
    //對當前最高優(yōu)先級的事件進行狀態(tài)判斷(下一個任務(wù)或還是之前的任務(wù))
    switch (OSTCBCur->OSTCBStatPend) {        
        case OS_STAT_PEND_OK:                  //等待完成和等待終止都進行相同處理
        case OS_STAT_PEND_ABORT:
             pevent = OSTCBCur->OSTCBEventPtr;
             if (pevent != (OS_EVENT *)0) {    
                //有任務(wù)就緒了
                *pevents_rdy++ =  pevent;      
                //把這個任務(wù)的地址存儲到pevents_rdy數(shù)組中
                *pevents_rdy   = (OS_EVENT *)0;
                //最后一個標志為結(jié)束符
                  events_rdy_nbr++;
                //就緒事件數(shù)加1
             } else {                            
                 OSTCBCur->OSTCBStatPend = OS_STAT_PEND_TO;
                //如果沒有任務(wù)就緒標志為超時處理,這里不明白########
                 OS_EventTaskRemoveMulti(OSTCBCur, pevents_pend);
                //解除暫停將任務(wù)從等待列表中刪除
             }
             break;
 
        case OS_STAT_PEND_TO:                  
        default:                              
             OS_EventTaskRemoveMulti(OSTCBCur, pevents_pend);//掛起時間到將任務(wù)從等待列表中刪除
             break;
    }
    //這個switch是用來消息傳遞的  
    switch (OSTCBCur->OSTCBStatPend) {
        case OS_STAT_PEND_OK:                   //掛起完成也標志該事件又處在就緒態(tài)了
             switch (pevent->OSEventType) {    
#if (OS_SEM_EN > 0u)
                 case OS_EVENT_TYPE_SEM:
                     *pmsgs_rdy++ = (void *)0;  
                    //信號量沒有返回消息
                      break;
#endif
 
#if ((OS_MBOX_EN > 0u) ||((OS_Q_EN    > 0u) && (OS_MAX_QS > 0u)))
                 case OS_EVENT_TYPE_MBOX:
                 case OS_EVENT_TYPE_Q:
                     *pmsgs_rdy++ = (void *)OSTCBCur->OSTCBMsg;
                      break;                   //把郵箱和隊列中的消息保存在pmsgs_rdy數(shù)組中
#endif
 
                 case OS_EVENT_TYPE_MUTEX:
                 case OS_EVENT_TYPE_FLAG:
                 default:
                      OS_EXIT_CRITICAL();
                     *pevents_rdy = (OS_EVENT *)0;
                     *perr        =  OS_ERR_EVENT_TYPE;
                      return (events_rdy_nbr);
             }
            *perr = OS_ERR_NONE;
             break;
 
        case OS_STAT_PEND_ABORT:              //等待終止返回空消息
            *pmsgs_rdy++ = (void *)0;        
            *perr        =  OS_ERR_PEND_ABORT;
             break;
 
        case OS_STAT_PEND_TO:                 //等待超時也返回空消息                  
        default:
            *pmsgs_rdy++ = (void *)0;          
            *perr        =  OS_ERR_TIMEOUT;    
             break;
    }
 
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;
//事件標記為就緒態(tài)
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;
//清除掛起標志
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#if ((OS_MBOX_EN > 0u) ||((OS_Q_EN    > 0u) && (OS_MAX_QS > 0u)))
    OSTCBCur->OSTCBMsg      = (void      *)0;  
#endif
    OS_EXIT_CRITICAL();
 
    return (events_rdy_nbr);
}
#endif
關(guān)閉窗口

相關(guān)文章