二值信号量常用于互斥和同步。
二值信号量和互斥量非常相似,但是又有区别。互斥量有优先级继承机制,二值信号量没有。
二值信号量更适合用于同步(任务与任务之间,任务与中断之间)。
互斥量更适合用于简单的互斥访问。
当一个任务尝试读取信号量的时候,如果信号量无效,那么可以制定一个阻塞时间,在这个时间内任务进入阻塞状态。如果有多个任务阻塞在同一个信号量,当信号量有效时,最高优先级的任务先解除阻塞状态。
二值信号量可以被认为是只有一个项的队列,这个队列只有“空”或“满”两种状态(因此叫做二值)。任务和中断使用的时候不需要关心队列里面是什么,只需要关心这个队列是空还是满,这种机制被用于任务和中断之间的同步。
一个很典型的应用,串口中断接收数据,接收完成后,接收完成标志置1,然后再主循环里面轮询判断这个标志,然后处理接收到的数据。使用系统后,我们只需设置二值信号量,然后在任务里面阻塞判断这个二值信号量。相比不用系统,省去了轮询判断标志的过程,这个时间cpu可以去干其他事情了。
另一个典型应用:按键动作(外部中断)发生后,设置设置信号量,然后在任务里面获取这个信号量,阻塞任务。省去了轮询判断标志的过程,这个时间cpu可以去干其他事情了。
typedef void * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;//信号量句柄,从这里也可以看到源码实现还是用的队列
SemaphoreHandle_t xSemaphoreCreateBinary( void )//创建二值信号量
xSemaphoreGive( SemaphoreHandle_t xSemaphore ) //给一个信号量
xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)//获取信号量
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken)
//给一个信号量,用于中断函数里面
总体设计:3个任务,2个信号量
lcdtask:lcd显示当前计数并串口打印,当计数到50的时候,给TaskToTaskSemaphore一个信号量;
totalcounttask:获取TaskToTaskSemaphore信号量,如果获取到了,计数一次,并打印总共获取到的次数;
serialtask:获取TaskToIrqSemaphore信号量,当串口空闲中断产生的时候,这个信号量会在串口中断中给一个信号量。
创建任务和信号量
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#define LCD_TASK_PRIO 1 //任务优先级
#define LCD_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t LCDTaskHandler; //任务句柄
void LCDTaskFunc(void *pvParameters); //任务函数
#define TOTAL_COUNT_TASK_PRIO 2 //任务优先级
#define TOTAL_COUNT_TASK_STK_SIZE 50 //任务堆栈大小
TaskHandle_t TotalCountTaskHandler; //任务句柄
void TotalCountTaskFunc(void *pvParameters); //任务函数
#define SERIAL_TASK_PRIO 4 //任务优先级
#define SERIAL_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t SerialTaskHandler; //任务句柄
void SerialTaskFunc(void *pvParameters); //任务函数
SemaphoreHandle_t TaskToIrqSemaphore;
SemaphoreHandle_t TaskToTaskSemaphore;
void OtherTest(void )
{
BaseType_t ret;
BoardInitMcu();
BoardInitPeriph();
TaskToIrqSemaphore=xSemaphoreCreateBinary();//创建一个二值信号量
TaskToTaskSemaphore=xSemaphoreCreateBinary();
ret=xTaskCreate((TaskFunction_t )LCDTaskFunc,
(const char* )"lcdtask",
(uint16_t )LCD_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )LCD_TASK_PRIO,
(TaskHandle_t* )&LCDTaskHandler);
ret=xTaskCreate((TaskFunction_t )TotalCountTaskFunc,
(const char* )"totalcount",
(uint16_t )TOTAL_COUNT_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )TOTAL_COUNT_TASK_PRIO,
(TaskHandle_t* )&TotalCountTaskHandler);
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 LCDTaskFunc(void *pvParameters)
{
char string[21] = {0};
static uint8_t i=0;
for(;;)
{
i++;
sprintf(string, "%03d ", i);
printf("current count :%d\r\n",i);
dis_string(1,0,(uint8_t *)string,1);
if(i==50)
{
xSemaphoreGive(TaskToTaskSemaphore);
i=0;
}
vTaskDelay(500);
}
}
void TotalCountTaskFunc(void *pvParameters)
{
static uint16_t i=0;
for(;;)
{
if(xSemaphoreTake(TaskToTaskSemaphore,portMAX_DELAY))
{
i++;
printf(" total count:%d\r\n",i);
}
else
{
vTaskDelay(10);
}
}
}
void SerialTaskFunc(void *pvParameters)
{
for(;;)
{
if(xSemaphoreTake(TaskToIrqSemaphore,portMAX_DELAY))
{
printf("serial:%s\r\n",SerialFrame.Buff);
SerialFrame.Len=0;
memset(SerialFrame.Buff,0,255);
}
else
{
vTaskDelay(10);
}
}
}
运行结果: