信号量
1 信号量用于共享资源的访问:
2 信号量用于任务同步:
为什么一直说在中断服务函数中,不能够做太多的事情?
在进入中断服务函数时,低优先级的中断就不能响应,同类型的中断也无法响应,所以就要求ISR一定要短,快进快出。
最好的解决方案时,在中断服务函数中发送一个信号量,在任务中等待信号量,实现任务同步。
二值信号量
二值信号量简介:
二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值的吗? 任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步。
二值信号量执行流程:
创建信号量:
动态创建二值信号量:
实际上,信号量就是通过队列来实现的,源码如下:
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ((uint8_t)0U)
#if (configSUPPORT_DYNAMIC_ALLOCATION == 1)
#define xSemaphoreCreateBinary() xQueueGenericCreate((UBaseType_t)1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE)
#endif
实际上,就是调用创建队列的函数。
信号量的创建方式如下所示:
释放信号量:
获取信号量:
获取函数参数说明:
中断中获取信号量:
二值信号量测试:
串口中断服务函数中接收数据,接收完成后(IDLE)释放二值信号量,任务中获取信号量,根据串口数据来响应。
程序如下所示:
uint8_t RX_BUFFER[USART_BUFFER_LEN]; // 串口接收缓冲区
SemaphoreHandle_t binary_semaphore = NULL; // 二值信号量句柄
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
binary_semaphore = xSemaphoreCreateBinary(); // 创建信号量
if (binary_semaphore != NULL)
{
printf("信号量创建成功!\n");
}
else
{
printf("信号量创建失败!\n");
}
// 创建任务1
xTaskCreate((TaskFunction_t )task1_task,
(char * )"task1_task",
(uint16_t )TASK1_TASK_SIZE,
(void * )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t * )&task1_task_Handle);
// 创建任务2
xTaskCreate((TaskFunction_t )task2_task,
(char * )"task2_task",
(uint16_t )TASK2_TASK_SIZE,
(void * )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t * )&task2_task_Handle);
taskEXIT_CRITICAL();
// 删除开始任务
vTaskDelete(start_Task_Handle);
}
void task1_task(void *pvParameters)
{
for (;;)
{
printf("task1 running\r\n");
vTaskDelay(1000);
}
}
void task2_task(void *pvParameters)
{
uint16_t count = 0;
BaseType_t err_state;
for (;;)
{
if (binary_semaphore != NULL)
{
// 一直等待,直到获取到二值信号量
err_state = xSemaphoreTake(binary_semaphore, portMAX_DELAY);
if (err_state == pdTRUE)
{
if (strcmp((char *)RX_BUFFER, "LED_ON") == 0)
{
LED_RED;
}
if (strcmp((char *)RX_BUFFER, "LED_OFF") == 0)
{
LED_ALL_OFF;
}
if (strcmp((char *)RX_BUFFER, "BEEP_ON") == 0)
{
BEEP_ON;
}
if (strcmp((char *)RX_BUFFER, "BEEP_OFF") == 0)
{
BEEP_OFF;
}
memset(RX_BUFFER, 0, USART_BUFFER_LEN);
}
}
printf("we get %d times binary\n", ++count);
vTaskDelay(20);
}
}
void USART1_IRQHandler(void)
{
static uint16_t i = 0;
uint32_t temp;
BaseType_t pxHigherPriorityTaskWoken;
BaseType_t error_state;
// 保存接收数据到RX_BUFFER缓冲区
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
RX_BUFFER[i++] = huart1.Instance->DR;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
}
// 串口数据接收完成,串口空闲时,释放信号量
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
{
i = 0;
if (binary_semaphore != NULL)
{
error_state = xSemaphoreGiveFromISR(binary_semaphore, &pxHigherPriorityTaskWoken);
if (error_state != pdTRUE)
{
printf("二值信号量释放失败!\n");
}
// 判断是否需要进行任务切换
if (pxHigherPriorityTaskWoken == pdTRUE)
{
taskYIELD();
}
}
// 清除空闲中断(依次读取SR DR)
temp = huart1.Instance->SR;
temp = huart1.Instance->DR;
}
}
测试结果: