信号量的特性
消息队列用于传输多个数据,但是有时候我们只需要传递状态,这个状态值需要用一个数值表示。套用队列笔记中的流水线例子,可以理解为流水线上工件的数量。
信号:起通知作用
量:还可以用来表示资源的数量
当"量"没有限制时,它就是"计数型信号量"(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);的参数,来改变有多少张票。