FreeRTOS专栏12:二值信号量

信号量

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;
    }
}

测试结果:

发布了184 篇原创文章 · 获赞 100 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/dingyc_ee/article/details/104108665