計數信號量有兩種典型的用法:
1.事件計數:
每次事件發生時,中斷ISR會釋放(Give)信號量,信號量的計數值加1 。事件處理任務每次處理一個事件會獲取(Take)一次信號量,計數值減1. 信號量的值就是已發生事件數目與已處理事件數目之間的差值。用於事件的計數的計數信號量,在被創建時其計數值被初始化為0。
2.資源管理:
信號量的計數值表示可用資源的數目,一個任務獲取資源的控制權,必須先獲取(Take)信號量,使信號的計數值減1。當資源總數為0,表示沒有資源可用。當任務利用資源完成工作后,歸還信號量,信號量的計數值加1.用於資源管理的信號量,在被創建時其計數值為可用的最大資源數。
典型的生產者與消費者問題如下圖示:
這里對生產者與消費者問題,進行驗證:
資源:生產線上有5個箱子(共享資源),一開始都是空的。
生產者:先判斷在5個箱子(共享資源)中有木有空箱子,若有空箱子則向空箱子放入數字(依次放入1,2,…10);若無空箱子則需要等待。
消費者:先判斷在5個箱子(共享資源)中有木有已放入數字的箱子,若有則取出箱子的數字,進行累加處理,若無則需要等待。
1 #define BOX_NUM 5 2 uint32_t box[BOX_NUM]; 3 uint32_t put = 0,get = 0; 4 5 6 void MX_FREERTOS_Init(void) { 7 /* USER CODE BEGIN Init */ 8 9 /* USER CODE END Init */ 10 11 12 /* Create the semaphores(s) */ 13 /* definition and creation of lockSem */ 14 osSemaphoreDef(lockSem); 15 //lockSem用於對共享資源的互斥訪問 16 lockSemHandle = osSemaphoreCreate(osSemaphore(lockSem), 1); 17 18 /* definition and creation of emptySem */ 19 osSemaphoreDef(emptySem); 20 //emptySem代表空箱子的資源數,初始計數值5 21 emptySemHandle = osSemaphoreCreate(osSemaphore(emptySem), 5); 22 23 /* definition and creation of fullsem */ 24 //fullSem代表裝有數字箱子的資源數,初始計數值0 25 fullsemHandle = xSemaphoreCreateCounting(5, 0); 26 27 /* USER CODE BEGIN RTOS_TIMERS */ 28 /* start timers, add new ones, ... */ 29 /* USER CODE END RTOS_TIMERS */ 30 31 /* Create the thread(s) */ 32 /* definition and creation of vTask1 */ 33 osThreadDef(vTask1, producer_task, osPriorityNormal, 0, 256); 34 vTask1Handle = osThreadCreate(osThread(vTask1), NULL); 35 36 /* definition and creation of vTaks2 */ 37 osThreadDef(vTaks2, consumer_task, osPriorityHigh, 0, 256); 38 vTaks2Handle = osThreadCreate(osThread(vTaks2), NULL); 39 40 /* USER CODE BEGIN RTOS_THREADS */ 41 /* add threads, ... */ 42 /* USER CODE END RTOS_THREADS */ 43 44 /* USER CODE BEGIN RTOS_QUEUES */ 45 /* add queues, ... */ 46 /* USER CODE END RTOS_QUEUES */ 47 } 48 49 /* producer_task function */ 50 void producer_task(void const * argument) 51 { 52 53 /* USER CODE BEGIN producer_task */ 54 static int Count = 0; 55 /* Infinite loop */ 56 while(Count < 10) 57 { 58 //嘗試獲取一個空箱子 59 osSemaphoreWait(emptySemHandle,osWaitForever); 60 61 //互斥訪問共享資源 62 osSemaphoreWait(lockSemHandle,osWaitForever); 63 64 //向空箱子放入數字 65 box[put % BOX_NUM] = Count + 1; 66 67 printf("the producer package box[%d] which content is : %d\n",put % BOX_NUM,box[put % BOX_NUM]); 68 69 //放入數字次數 70 put++; 71 72 //當對共享資源訪問完畢,解鎖。 73 osSemaphoreRelease(lockSemHandle); 74 75 //釋放一個“滿”信號量,代表已完成對一個box的加工 76 osSemaphoreRelease(fullsemHandle); 77 78 //循環執行次數 79 Count++; 80 81 //生產者休息一會 82 osDelay(20); 83 } 84 85 printf("producer finish!!!!!\n"); 86 87 osThreadSuspend(vTask1Handle); 88 /* USER CODE END producer_task */ 89 } 90 91 /* consumer_task function */ 92 void consumer_task(void const * argument) 93 { 94 /* USER CODE BEGIN consumer_task */ 95 static int sum = 0; 96 /* Infinite loop */ 97 while(1) 98 { 99 //嘗試獲取一個已放入數字箱子 100 osSemaphoreWait(fullsemHandle,osWaitForever); 101 102 //互斥訪問共享資源 103 osSemaphoreWait(lockSemHandle,osWaitForever); 104 105 //獲取箱子里數字進行累加計算 106 sum = sum + box[get % BOX_NUM]; 107 108 printf("the consumer : box[%d] get a num %d\n",get%BOX_NUM, box[get%BOX_NUM]); 109 110 //獲取數字的次數 111 get++; 112 113 //當對共享資源訪問完畢,解鎖。 114 osSemaphoreRelease(lockSemHandle); 115 116 //釋放一個空箱子資源 117 osSemaphoreRelease(emptySemHandle); 118 119 //獲取10次數字后退出 120 if(get == 10) 121 { 122 break; 123 } 124 125 osDelay(120); 126 } 127 128 printf("the consumer sum is: %d\n", sum); 129 printf("the consumer exit!\n"); 130 131 osThreadSuspend(vTaks2Handle); 132 /* USER CODE END consumer_task */ 133 }
測試驗證結果: