uC/OS-II源碼解析(os_sem.c)

/*
** ver   : 2.52
** file  : os_sem.c
** brief : 信號量相關函數C文件
*/


#ifndef  OS_MASTER_FILE
#include "includes.h"                     /* 包含頭文件 */
#endif

#if OS_SEM_EN > 0
/*
***************************************************************************
*                                           無等待的請求信號量
*
* brief  : 該函數用於無等待的請求信號量,信號量無效時也不會使任務掛起
*
* pevent : 指向事件控制塊的指針
*
* Returns    : >  0       信號量有效
*              == 0       信號量無效
***************************************************************************
*/

#if OS_SEM_ACCEPT_EN > 0
INT16U  OSSemAccept (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3                          
    OS_CPU_SR  cpu_sr;
#endif    
    INT16U     cnt;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {       /* 無效的事件控制塊指針  */
        return (0);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {                       
        return (0);                       /* 無效的事件類型       */ 
    }
#endif
    OS_ENTER_CRITICAL();
    cnt = pevent->OSEventCnt;             /* 記錄當前信號量值     */
    if (cnt > 0) {                        /* 信號量有效           */
        pevent->OSEventCnt--;             /* 信號量值減 1         */
    }
    OS_EXIT_CRITICAL();
    return (cnt);                         /* 返回信號量值         */
}
#endif    

/*$PAGE*/
/*
*******************************************************************************
*                                           創建信號量
*
* brief   : 該函數用於創建信號量
*
* cnt     : 信號量初始值.如果信號量是用來表示一個或多個事件發生的
*           那麼該信號量的初始值通常賦爲0,如果信號量用於對共享資
*           源的訪問,那麼該信號量的初始化賦爲1(例如把它當做二值
*           信號量使用),如果信號量用來表示允許任務訪問n個相同的
*           資源,那麼該信號量的初始值應被賦爲n
*
* returns :    != (void *)0  事件控制塊指針,創建信號量成功
*              == (void *)0  創建失敗
********************************************************************************
*/

OS_EVENT  *OSSemCreate (INT16U cnt)
{
#if OS_CRITICAL_METHOD == 3                              
    OS_CPU_SR  cpu_sr;
#endif    
    OS_EVENT  *pevent;


    if (OSIntNesting > 0) {                 /* 中斷中不允許創建信號量  */
        return ((OS_EVENT *)0);       
    }
    OS_ENTER_CRITICAL();
    pevent = OSEventFreeList;               /* 獲取一個空事件控制塊    */
    if (OSEventFreeList != (OS_EVENT *)0) { /* 調整事件控制塊鏈表      */
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    if (pevent != (OS_EVENT *)0) {          /* 獲得一個事件控制塊       */
        pevent->OSEventType = OS_EVENT_TYPE_SEM;
        pevent->OSEventCnt  = cnt;          /* 設置信號量初始值         */
        pevent->OSEventPtr  = (void *)0;    /* 不在屬於空事件控制塊鏈表 */
        OS_EventWaitListInit(pevent);       /* 初始化等待任務表         */
    }
    return (pevent);
}

/*$PAGE*/
/*
********************************************************************************
*                                         刪除信號量
*
* brief  : 該函數用於刪除信號量
*
* pevent : 指向事件控制塊的指針
*
* opt    : 刪除信號量的選項:
*            opt == OS_DEL_NO_PEND   無任務等待時刪除
*            opt == OS_DEL_ALWAYS    有無任務等待都刪除,如有等待則進入就緒態
*
* err    : 指向錯誤代碼的指針,可能取值:
*            OS_NO_ERR               成功刪除
*            OS_ERR_DEL_ISR          不能再中斷中刪除信號量
*            OS_ERR_INVALID_OPT      無效的刪除選項
*            OS_ERR_TASK_WAITING     有任務正在等待信號量
*            OS_ERR_EVENT_TYPE       錯誤的事件類型
*            OS_ERR_PEVENT_NULL      事件控制塊指針爲空
*
* returns    : pevent        出錯
*              (OS_EVENT *)0 成功刪除
*
* Note(s)    : 1) 該函數必須謹慎的使用.任務可能會請求已經刪除了的信號量
*                  應該檢查OSSemPend()的返回值.
*              2) 調用OSSemAccept()的函數不知道信號量已經被刪除了
*              3) 該函數將中斷關閉.  中斷關閉的時間取決於正在等待信號量
*                  的任務數
*              4) 因爲所有等待的任務都將進入就緒態,所以必須小心的使用
*                  該函數
*******************************************************************************
*/

#if OS_SEM_DEL_EN > 0
OS_EVENT  *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3                               
    OS_CPU_SR  cpu_sr;
#endif    
    BOOLEAN    tasks_waiting;


    if (OSIntNesting > 0) {               /* 不允許在中斷中刪除信號量  */
        *err = OS_ERR_DEL_ISR;           
        return (pevent);
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {        /* 無效的時間控制塊指針      */
        *err = OS_ERR_PEVENT_NULL;
        return (pevent);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {       
        *err = OS_ERR_EVENT_TYPE;         /* 無效的事件類型            */
        return (pevent);
    }
#endif
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0x00) {     /* 查詢是否有任務正等待信號量 */
        tasks_waiting = TRUE;             /* 有                         */
    } else {
        tasks_waiting = FALSE;            /* 無                         */
    }
    switch (opt) {
        case OS_DEL_NO_PEND:              /* 無任務等待刪除 */
             if (tasks_waiting == FALSE) {
                 pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
                 pevent->OSEventPtr  = OSEventFreeList;    /* 事件類型              */
                 OSEventFreeList     = pevent;             /* 返還事件控制塊        */
                 OS_EXIT_CRITICAL();
                 *err = OS_NO_ERR;
                 return ((OS_EVENT *)0);                   /* 信號量已被刪除        */
             } else {                     
                 OS_EXIT_CRITICAL();
                 *err = OS_ERR_TASK_WAITING;               /* 有任務正等待信號量    */
                 return (pevent);
             }

        case OS_DEL_ALWAYS:                                /* 有無任務等待都刪除    */
             while (pevent->OSEventGrp != 0x00) {          /* 所有等待任務就緒      */
                 OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);
             }
             pevent->OSEventType = OS_EVENT_TYPE_UNUSED;   /* 事件類型              */
             pevent->OSEventPtr  = OSEventFreeList;        /* 返還事件控制塊        */
             OSEventFreeList     = pevent;                 
             OS_EXIT_CRITICAL();
             if (tasks_waiting == TRUE) {                 
                 OS_Sched();                               /* 任務調度              */
             }
             *err = OS_NO_ERR;
             return ((OS_EVENT *)0);                       /* 信號量已被刪除        */

        default:
             OS_EXIT_CRITICAL();
             *err = OS_ERR_INVALID_OPT;                    /* 無效的刪除選項        */
             return (pevent);
    }
}
#endif

/*$PAGE*/
/*
******************************************************************************
*                                           請求一個信號量
*
* brief   : 該函數用於請求信號量
*
* pevent  : 指向信號量的指針
*
* timeout : 等待超時時限.
*            爲0   : 無限的等待或者等待的事件發生
*            不爲0 : 超時進入就緒或者等待的事件發生
*
* err     : 指向出錯代碼的指針,可能取值:
*
*               OS_NO_ERR           成功
*               OS_TIMEOUT          等待超時
*               OS_ERR_EVENT_TYPE   錯誤的事件類型
*               OS_ERR_PEND_ISR     不能在中斷中請求信號量
*               OS_ERR_PEVENT_NULL  錯誤的事件控制塊指針
*
*******************************************************************************
*/

void  OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3                          
    OS_CPU_SR  cpu_sr;
#endif    


    if (OSIntNesting > 0) {             /* 中斷中不能請求信號量  */
        *err = OS_ERR_PEND_ISR;                                   
        return;
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {      /* 無效的事件控制塊指針  */
        *err = OS_ERR_PEVENT_NULL;
        return;
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   
        *err = OS_ERR_EVENT_TYPE;       /* 無效的事件類型        */
        return;
    }
#endif
    OS_ENTER_CRITICAL();
    if (pevent->OSEventCnt > 0) {       /* 信號量有效             */
        pevent->OSEventCnt--;           /* 信號量值減 1           */
        OS_EXIT_CRITICAL();
        *err = OS_NO_ERR;
        return;
    }
                                        /* 無有效的信號量         */
    OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* 任務狀態爲等待信號量   */
    OSTCBCur->OSTCBDly   = timeout;     /* 超時時限               */
    OS_EventTaskWait(pevent);           /* 任務進入等待狀態       */
    OS_EXIT_CRITICAL();
    OS_Sched();                         /* 任務調度               */
    OS_ENTER_CRITICAL();
    if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {/* 超時而進入就緒態   */
        OS_EventTO(pevent);
        OS_EXIT_CRITICAL();
        *err = OS_TIMEOUT;               /* 等待超時      */
        return;
    }
    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
    OS_EXIT_CRITICAL();
    *err = OS_NO_ERR;
}
/*$PAGE*/
/*
******************************************************************************
*                                         釋放信號量
*
* brief   : 該函數用於釋放信號量
*
* pevent  : 指向事件控制塊的指針
*
* Returns :    OS_NO_ERR           成功
*              OS_SEM_OVF          信號量值超過最大值
*              OS_ERR_EVENT_TYPE   無效的事件類型
*              OS_ERR_PEVENT_NULL  無效的事件控制塊指針
******************************************************************************
*/

INT8U  OSSemPost (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3                               
    OS_CPU_SR  cpu_sr;                               
#endif    


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {          /* 無效的事件控制塊指針 */
        return (OS_ERR_PEVENT_NULL);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {        
        return (OS_ERR_EVENT_TYPE);         /* 無效的事件類型       */
    }
#endif
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0x00) {       /* 是否有任務在等待信號量   */
        OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);/* 優先級最高的等待任務就緒 */
        OS_EXIT_CRITICAL();
        OS_Sched();                         /* 任務調度             */
        return (OS_NO_ERR);
    }
    if (pevent->OSEventCnt < 65535) {       /* 確保信號量值正確     */
        pevent->OSEventCnt++;               /* 增加信號量值         */
        OS_EXIT_CRITICAL();
        return (OS_NO_ERR);
    }
    OS_EXIT_CRITICAL();                     /* 信號量值錯誤         */
    return (OS_SEM_OVF);
}
/*$PAGE*/
/*
******************************************************************************
*                                          查詢信號量
*
* brief   : 該函數用於獲取信號量的信息
*
* pevent  : 指向事件控制塊的指針
*
* pdata   : 指向返回信號量信息數據結構的指針
*
* Returns :    OS_NO_ERR           成功
*              OS_ERR_EVENT_TYPE   無效的事件類型
*              OS_ERR_PEVENT_NULL  無效的事件控制塊指針
******************************************************************************
*/

#if OS_SEM_QUERY_EN > 0
INT8U  OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)
{
#if OS_CRITICAL_METHOD == 3                              
    OS_CPU_SR  cpu_sr;
#endif    
    INT8U     *psrc;
    INT8U     *pdest;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                 /* 無效的事件控制塊指針 */
        return (OS_ERR_PEVENT_NULL);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 無效的事件類型      */
        return (OS_ERR_EVENT_TYPE);
    }
#endif
    OS_ENTER_CRITICAL();
    pdata->OSEventGrp = pevent->OSEventGrp;         /* 複製等待任務表      */
    psrc              = &pevent->OSEventTbl[0];
    pdest             = &pdata->OSEventTbl[0];
#if OS_EVENT_TBL_SIZE > 0
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 1
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 2
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 3
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 4
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 5
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 6
    *pdest++          = *psrc++;
#endif

#if OS_EVENT_TBL_SIZE > 7
    *pdest            = *psrc;
#endif
    pdata->OSCnt      = pevent->OSEventCnt;          /* 複製信號量值         */
    OS_EXIT_CRITICAL();
    return (OS_NO_ERR);
}
#endif                                                                          
#endif                                                                               
發佈了47 篇原創文章 · 獲贊 85 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章