基本特性
計數信號量,也可以看成是隊列,但是長度大於1。用戶只需關心是否爲空。
典型應用
(1)計數
事件發生的時候,在事件處理函數中給一個信號量(既就是信號量值計數值加1),任務處理函數獲取這個信號量(既就是信號量計數值值減1)。
信號量計數值初始爲0。
(2)資源管理
用於指示可用的資源。
當信號好計數值到0的時候,表示沒有資源可用。
一個任務想要使用資源,需要先獲取信號量——也就是信號量值減1;當一個任務使用完資源(釋放資源)的時候,需要給一個信號——也就是信號量值加1。
信號量初始值爲最大值。
典型場景:
比如有個30人的電腦機房,我們就可以創建信號量的初始化值是30,表示30個可用資源,是的,信號量說白了就是共享資源的數量。另外我們要求一個同學使用一臺電腦,這樣每有一個同學使用一臺電腦,那麼信號量的數值就減一,直到30臺電腦都被佔用,此時信號量的數值就是0。如果此時還有幾個同學沒有電腦可以使用,那麼這幾個同學就得等待,直到有同學離開。有一個同學離開,那麼信號量的數值就加1,有兩個就加2,依此類推。剛纔沒有電腦用的同學此時就有電腦可以用了,有幾個同學用,信號量就減幾,直到再次沒有電腦可以用,這麼一個過程就是使用信號量來管理共享資源的過程。
以上兩種應用的最大區別就是,信號量計數值的初始值。
部分API
typedef void * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;//信號量句柄,從這裏也可以看到源碼實現還是用的隊列
xSemaphoreGive( SemaphoreHandle_t xSemaphore ) //給一個信號量
xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)//獲取信號量
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken)
//給一個信號量,用於中斷函數裏面
可以看出以上的API與二值信號量API是一樣的。
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
//創建計數信號量,uxMaxCount:表示最大計數值,uxInitialCount :初始化計數值
uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore )//獲取當前的計數
上面兩個是計數信號量特有的API。
測試程序
總體設計:2個任務,1個計數信號量。
printcounttask:打印當前計數信號量的值;
serialtask:獲取計數信號量,並打印獲取之後的計數信號量的值;
串口中斷:接收到串口數據後,給一個信號量。
創建任務和計數信號量
#define PRINT_COUNT_TASK_PRIO 1 //任務優先級
#define PRINT_COUNT_TASK_STK_SIZE 80 //任務堆棧大小
TaskHandle_t PrintCountTaskHandler; //任務句柄
void PrintCountFunc(void *pvParameters); //任務函數
#define SERIAL_TASK_PRIO 4 //任務優先級
#define SERIAL_TASK_STK_SIZE 80 //任務堆棧大小
TaskHandle_t SerialTaskHandler; //任務句柄
void SerialTaskFunc(void *pvParameters); //任務函數
SemaphoreHandle_t TaskToTrqSemaphoreCounting;
void OtherTest(void )
{
BaseType_t ret;
BoardInitMcu();
BoardInitPeriph();
TaskToTrqSemaphoreCounting=xSemaphoreCreateCounting( 10, 0 );
ret=xTaskCreate((TaskFunction_t )PrintCountFunc,
(const char* )"printcount",
(uint16_t )PRINT_COUNT_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )PRINT_COUNT_TASK_PRIO,
(TaskHandle_t* )&PrintCountFunc);
ret=xTaskCreate((TaskFunction_t )SerialTaskFunc,
(const char* )"serialtask",
(uint16_t )SERIAL_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )SERIAL_TASK_PRIO,
(TaskHandle_t* )&SerialTaskHandler);
vTaskStartScheduler();
}
任務函數
void PrintCountFunc(void *pvParameters)
{
UBaseType_t count;
for(;;)
{
count=uxSemaphoreGetCount(TaskToTrqSemaphoreCounting);
printf("before =%d\r\n",(int)count);
vTaskDelay(500);
}
}
void SerialTaskFunc(void *pvParameters)
{
UBaseType_t count;
for(;;)
{
if(xSemaphoreTake(TaskToTrqSemaphoreCounting,10))//阻塞10ms
{
count=uxSemaphoreGetCount(TaskToTrqSemaphoreCounting);
printf("after =%d\r\n",(int)count);
}
vTaskDelay(1000);
}
}
串口中斷
void USART1_IRQHandler( void )
{
BaseType_t pxHigherPriorityTaskWoken=pdFALSE;
//省略部分代碼,只列出重要部分
if(RESET != __HAL_UART_GET_FLAG(&UartHandle,UART_FLAG_IDLE))//空閒中斷
{
__HAL_UART_CLEAR_IDLEFLAG(&UartHandle);
/*設置信號量*/
xSemaphoreGiveFromISR(TaskToTrqSemaphoreCounting,&pxHigherPriorityTaskWoken);
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
}
運行結果