信號量的特性
消息隊列用于傳輸多個數據,但是有時候我們只需要傳遞狀態,這個狀態值需要用一個數值表示。套用隊列筆記中的流水線例子,可以理解為流水線上工件的數量。
信號:起通知作用
量:還可以用來表示資源的數量
當"量"沒有限制時,它就是"計數型信號量"(Counting Semaphores)
當"量"只有0、1兩個取值時,它就是"二進制信號量"(Binary Semaphores)
支持的動作:"give"給出資源,計數值加1;"take"獲得資源,計數值減1。
隊列 | 信號量 | |
---|---|---|
數據存儲 | 可容納多個數據。創建時要分配隊列結構體和存儲數據的空間 | 只有計數值。創建時只需要分配信號量結構體 |
生產者 | 沒有空間存入時可以阻塞 | 不阻塞,計數值最大時返回失敗 |
消費者 | 沒有數據時可阻塞 | 計數值最小時可阻塞 |
信號量分為二進制信號量和計數型信號量。二進制信號量初始值為0,計數型信號量的最大值不是1。
信號量函數
創建
// 動態創建二進制信號量
SemaphoreHandle_t xSemaphoreCreateBinary( void );// 靜態創建二進制信號量
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );//動態創建計數型信號量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
//靜態創建計數型信號量
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer );
刪除
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
存取
存
/*在任務中使用*/
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
/*在中斷中使用*/
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);
xSemaphore:信號量句柄
*pxHigherPriorityTaskWoken:如果在信號量操作中喚醒了一個更高優先級的任務,會被設置為 pdTRUE,否則為pdFALSE
取
/*在任務中使用*/
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);
/*在中斷中使用*/
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);
xTicksToWait:阻塞的時間
信號量實驗
模擬車輛進站,假設車輛要有票才能同行,用一個信號量來模擬總共有多少票。當有票的時候,車拿走一張并開出,當其到達終點,就把票放回票倉,下一輛車再開出。
static SemaphoreHandle_t g_xSemTicks;
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
// 定義汽車結構體,包括位置和控制鍵
struct car {int x;int y;int control_key;
};
// 定義三輛汽車的初始位置和控制鍵
struct car g_cars[3] = {{0, 0, IR_KEY_1},{0, 17, IR_KEY_2},{0, 34, IR_KEY_3},
};
// 汽車圖片的二進制數據
static const byte carImg[] = {0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};
// 清除區域的圖像數據,用于隱藏汽車
static const byte clearImg[30] = {0};
// 顯示汽車
static void ShowCar(struct car *pcar)
{draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, false, 0);draw_flushArea(pcar->x, pcar->y, 15, 16);
}
// 隱藏汽車
static void HideCar(struct car *pcar)
{draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, false, 0);draw_flushArea(pcar->x, pcar->y, 15, 16);
}// 每輛汽車的任務
static void CarTask(void *params)
{struct car *pcar = params;// 任務開始時等待信號量,以同步啟動所有車輛xSemaphoreTake(g_xSemTicks, portMAX_DELAY);while (1){if (pcar->x < g_xres - CAR_LENGTH){HideCar(pcar); // 隱藏汽車以更新位置// 更新汽車位置pcar->x += 1;ShowCar(pcar); // 顯示新位置的汽車vTaskDelay(50); // 控制更新頻率if (pcar->x == g_xres - CAR_LENGTH){xSemaphoreGive(g_xSemTicks); // 到達屏幕邊緣時釋放信號量并結束任務vTaskDelete(NULL);}}}
}
// 主游戲函數,初始化并啟動任務
void car_game(void)
{g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();// 創建信號量,開始計數為2,最大計數為3g_xSemTicks = xSemaphoreCreateCounting(3, 2);// 創建每輛汽車的任務xTaskCreate(CarTask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(CarTask, "car2", 128, &g_cars[1], osPriorityNormal, NULL);xTaskCreate(CarTask, "car3", 128, &g_cars[2], osPriorityNormal, NULL);
}
可以通過改變g_xSemTicks = xSemaphoreCreateCounting(3, 2);的參數,來改變有多少張票。