信号量概述
信号量的本质也是一个队列
信号量中传输的是一个数值,因此信号量只能表示资源可用的数量,而不能传输数据。具体的信号量模型如下图:
信号量有两种:计数型信号量、二进制信号量,它们之间的区别在于计数值的范围
- 计数型信号量可以取值0~任意值,在创建时可以设置当前的计数值
- 二进制信号量取值只能为0、1,在创建时自动设置为0
使用信号量的方法就如模型一样,只有3个步骤:
- 创建信号量
- 存入信号量,这就是让计数值+1
- 获取信号量,这就是让计数值-1
相关配置
在使用信号量之前,需要打开宏开关,具体的步骤如下:
信号量相关函数
创建计数型信号量
函数声明如下:
/* 这是一个宏,创建计数型信号量 */
xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
/* 宏定义 */
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
/* 实际调用函数 */
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
返回值:信号量句柄
uxMaxCount:计数值的最大值
uxInitialCount :计数值的初始值
创建二进制信号量
函数声明如下:
/* 这是一个宏,创建二进制信号量 */
xSemaphoreCreateBinary();
/* 宏定义 */
#define xSemaphoreCreateBinary() \
xQueueGenericCreate( ( UBaseType_t ) 1,
semSEMAPHORE_QUEUE_ITEM_LENGTH,
queueQUEUE_TYPE_BINARY_SEMAPHORE )
/* 实际调用函数 */
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType )
返回值:信号量句柄
存入(释放)信号量
函数声明如下:
/* 这是一个宏,存入(释放)信号量 */
xSemaphoreGive( xSemaphore )
/* 宏定义 */
#define xSemaphoreGive( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ),
NULL,
semGIVE_BLOCK_TIME,
queueSEND_TO_BACK )
/* 实际调用函数 */
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
返回值:成功返回pdPASS,在当前计数值=最大计数值时,会存入失败
xSemaphore :信号量句柄
获取信号量
函数声明如下:
/* 这是一个宏,获取信号量 */
xSemaphoreTake( xSemaphore, xBlockTime )
/* 宏定义 */
#define xSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
/* 实际调用函数 */
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,
TickType_t xTicksToWait )
xSemaphore :信号量句柄
xBlockTime :阻塞等待时间,portMAX_DELAY为死等
验证实验
使用二进制信号量来确保串口的输出不被打断。
具体代码如下:
QueueHandle_t SemaphoreHandleTest;/* 信号量句柄 */
void PrintFunction(void *param){
while(1){
xSemaphoreTake(SemaphoreHandleTest,portMAX_DELAY);/* 获取串口资源 */
printf("%s",(char*)param);
xSemaphoreGive(SemaphoreHandleTest);/* 释放串口资源 */
vTaskDelay(10);/* 必须释放CPU资源 */
}
}
int main( void )
{
TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask2;
prvSetupHardware();
SerialPortInit();
printf("UART TEST\r\n");
/* 创建二进制信号量,初始值为0*/
SemaphoreHandleTest = xSemaphoreCreateBinary();
/* 让信号量为1,代表串口资源可用 */
xSemaphoreGive(SemaphoreHandleTest);
xTaskCreate(PrintFunction,"Task1",100,(void*)"this is task1\r\n",1,&xHandleTask1);
xTaskCreate(PrintFunction,"Task2",100,(void*)"this is task2\r\n",1,&xHandleTask2);
vTaskStartScheduler();
return 0;
}
运行结果如下:
可以看到,这两句话都进行完整的打印,并没有出现打印到一半就被调度器切换到其他任务的情况。