信號量有兩種類型:一種是隻有0和1兩種值的信號量,稱爲二值信號量;另一種是可以有多種值的信號量,稱爲計數式信號量。計數式信號量的值的大小取決於信號量的數據類型,如若是8位整型變量,則其值可以是0~255;若是16位整型變量,則其值可以是0~65 535。
µC/OS-Ⅱ的信號量由兩個部分組成:一個是信號量的計數值,範圍是0~65 535;另一個是由等待該信號量的任務組成的等待任務列表。
信號量可以使用在如下場合:
- 允許一個任務與其它任務或中斷同步;
- 取得共享資源的使用權;
- 標誌事件的發生。
對µC/OS-Ⅱ信號量初始值的賦值方法如下:
- 信號量的初始值爲0~65 535;
- 如果表示一個或者多個事件的發生,那麼初始值應設爲0;
- 如果是用於對共享資源的訪問,那麼該初始值應設爲1。例如,把它當作二值信號量使用;
- 如果是用來表示允許任務訪問n個相同的資源,那麼該初始值應該是n,並把該信號量作爲可計數的信號量使用。
µC/OS-Ⅱ提供了6種對信號量進行管理的函數,所屬文件是OS_SEM.C。
OSSemDel()
函數用於刪除一個信號量,調用者只能是任務在使用OSSemDel()
函數時應注意如下事項:
- 由於其它函數可能還會用到這個信號量,因此在刪除信號量之前,必須首先刪除等待該信號量的所有任務。
- 當掛起的任務進入就緒狀態時,中斷是關閉的,這就是說中斷延遲與等待信號量的任務的數量密切相關。
- opt:定義信號量刪除條件的選項。它有兩個選擇:①
OS_DEL_NO_PEND
:規定只能在已經沒有任何任務等待信號量時,才能刪除該信號量。②OS_DEL_ALWAYS
:規定不管有沒有任務在等待,都立即刪除這個信號量,刪除後所有等待該信號量的任務立即進入就緒狀態,並執行一次任務調度。 - 如果信號量刪除成功,則返回空指針;若信號量沒有能被刪除,則返回pevent,這時應該檢查出錯代碼,以查明原因。
OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
{
BOOLEAN tasks_waiting;
OS_EVENT *pevent_return;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (perr == (INT8U *)0) { /* Validate 'perr' */
return (pevent);
}
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
*perr = OS_ERR_PEVENT_NULL;
return (pevent);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return (pevent);
}
if (OSIntNesting > 0) { /* See if called from ISR ... */
*perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */
return (pevent);
}
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0) { /* See if any tasks waiting on semaphore */
tasks_waiting = OS_TRUE; /* Yes */
} else {
tasks_waiting = OS_FALSE; /* No */
}
switch (opt) {
case OS_DEL_NO_PEND: /* Delete semaphore only if no task waiting */
if (tasks_waiting == OS_FALSE) {
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?'; /* Unknown name */
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
} else {
OS_EXIT_CRITICAL();
*perr = OS_ERR_TASK_WAITING;
pevent_return = pevent;
}
break;
case OS_DEL_ALWAYS: /* Always delete the semaphore */
while (pevent->OSEventGrp != 0) { /* Ready ALL tasks waiting for semaphore */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
}
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?'; /* Unknown name */
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */
OS_Sched(); /* Find highest priority task ready to run */
}
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
break;
default:
OS_EXIT_CRITICAL();
*perr = OS_ERR_INVALID_OPT;
pevent_return = pevent;
break;
}
return (pevent_return);
}
- 當任務需要請求信號量時,就需要使用
OSSemPend()
函數。被OSSemPend()
函數掛起的任務,只能被一下三種條件下回復:1. 直到有其它的任務/中斷調用OSSemPost()
置位信號量; 2. 信號量超出等待的預期時間; 3. 被OSSemPendAbort
恢復。如果在預期的時鐘節拍內信號量被置位,那麼μC/OS-Ⅱ默認最高優先級的任務取得信號量並轉入就緒。
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (perr == (INT8U *)0) { /* Validate 'perr' */
return;
}
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
*perr = OS_ERR_PEVENT_NULL;
return;
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return;
}
if (OSIntNesting > 0) { /* See if called from ISR ... */
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
if (OSLockNesting > 0) { /* See if called with scheduler locked ... */
*perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */
return;
}
OS_ENTER_CRITICAL();
if (pevent->OSEventCnt > 0) { /* If sem. is positive, resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return;
}
/* Otherwise, must wait until event occurs */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */
case OS_STAT_PEND_OK:
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */
break;
case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */
#if (OS_EVENT_MULTI_EN > 0)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OS_EXIT_CRITICAL();
}
OSSemPost()
函數用於置位指定的信號量,或者說用於發送信號量。如果沒有任務在等待信號量,那麼OSSem Post()
函數使該信號量加1並返回;如果有任務在等待信號量,那麼最高優先級的任務將得到信號量並進入就緒狀態。任務調度函數將進行任務調度,決定當前運行的任務是否仍然爲最高優先級的任務。
INT8U OSSemPost (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
return (OS_ERR_PEVENT_NULL);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0) { /* See if any task waiting for semaphore */
/* Ready HPT waiting on event */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find HPT ready to run */
return (OS_ERR_NONE);
}
if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */
pevent->OSEventCnt++; /* Increment semaphore count to register event */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */
return (OS_ERR_SEM_OVF);
}