使用 FreeRTOS 进行基于事件的任务管理

Event-based task management using FreeRTOS

我正在尝试使用 esp32 学习 C。在查看 FreeRTOS 的确切工作原理时,我发现了以下关于如何使用任务和最佳实践等的页面。

https://www.freertos.org/implementing-a-FreeRTOS-task.html

根据此页面,为防止饥饿,任务应基于事件。关于我要实现的目标,我将尝试提供一个非常简化的示例。

背景

我有一个 LCD 屏幕,它应该显示来自传感器的数据。 LCD 上显示的数据将使用一项任务完成,根据文档,该任务不应退出,并且应由事件驱动以防止饥饿。

我有一种方法可以控制液晶屏上显示的数据,那就是旋转编码器。可以点击这个编码器,这应该会刷新传感器的数据。

问题

在此特定上下文中,我将如何实施 FreeRTOS 页面上描述的基于事件的任务?我查看了他们 github 上的文档和“简单”示例项目,但作为 C 语言和嵌入式领域的初学者,它们非常令人难以抗拒。

简单的演示代码

void update_sensor_task(void *pvParameters)
{
    // Ensure the task keeps on running
    for( ; ; )
    {

        if(event_update_sensor) // How would I be able to notify the task that this should be run?
        {
            // update the data
        }
        
    }

    // Tasks should not be returning, but if they happen to do so, ensure a clean exit
    vTaskDelete(NULL);
}

void screen_temperature_task(void *pvParameters)
{
    for(; ;)
    {
        if(event_sensor_updated)
        {
            // Update the lcd screen with the new data
        }
    }

    vTaskDelete(NULL);
}

void on_rotary_clicked(void *pvParameters)
{
    // Notify the sensor task that it should be updating?
} 

编辑:

通过使用被标记为正确答案的内容,我已经通过以下方式实现它:

/* Queue used to send and receive the data */
QueueHandle_t xStructQueue = NULL;

/* Struct which shall be used to hold and pass around the data for the LCD screen*/
struct LcdData
{
    int current_temp;
    int current_humidity;
} xLcdData;

void initialize_queues(void)
{
    xLcdData.current_humidity = 0;
    xLcdData.current_temp = 0;

    xStructQueue = xQueueCreate(
        /* The maximum number of items the queue can hold*/
        5,
        /* The size of each struct, which the queue should be able to hold */
        sizeof( xLcdData )
    );

    if(xStructQueue == NULL)
    {
        ESP_LOGE(TAG, "Queue has not been initialized successfully");
    }
}

void screen_temperature_task_simplified(void *pvParameters)
{
    int counter = 0;
    for(; ;)
    {
        struct LcdData xReceivedStructure;

        BaseType_t result;
        result = xQueueReceive(xStructQueue, &xReceivedStructure, ( TickType_t ) 10);

        if(result == pdPASS)
        {
            counter = counter + 1;

            char snum_current_counter[12];
            sprintf(snum_current_counter, "%d", counter);

            i2c_lcd1602_clear           (lcd_info);
            i2c_lcd1602_write_string    (lcd_info, snum_current_counter);
        }
    }

    vTaskDelete(NULL);
}

void update_sensor_struct(void)
{
    xLcdData.current_temp = DHT11_read().temperature;
    xLcdData.current_humidity = DHT11_read().humidity;

    // Log the results in the console
    printf("Temperature is %d \n", xLcdData.current_temp);
    printf("Humidity is %d\n", xLcdData.current_humidity);
    ESP_LOGI(TAG, "Data has been updated");
}

void on_rotary_clicked_simplified()
{
    ESP_LOGI(TAG, "Rotary encoder has been clicked!");

    // Update the struct which holds the data
    update_sensor_struct();

    /* Send the entire struct to the queue */
    xQueueSend(
        /* The handle of the queue */
        xStructQueue,
        /* The adress of the struct which should be sent */
        (void *) &xLcdData,
        /* Block time of 0 says don't block if the queue is already full.
        Check the value returned by xQueueSend() to know if the message
        was sent to the queue successfully. */
        ( TickType_t ) 0
    );
} 

我使用 FRTOS 和事件驱动开发。

这里的典型流程是:

for(;;)
{
    BaseType_t result;
    result = xQueueReceive(LCD_Event_Queue, &someLCDEvent, QUEUE_TIMEOUT);
    if (result == pdPASS)
    {
        /* We have new event data in someLCDEvent; Use that data to update the LCD */
    }
    else
    {
        /* No new event, do some brief idle-time processing if necessary */
    }
}

简而言之,等待新事件到达 QUEUE_TIMEOUT 时间。
如果新事件在该时间范围内成功到达,则处理该事件中的数据并更新您的屏幕。
如果新事件没有到来,您还有机会做一些其他的维护工作。

设计和定义someLCDEvent的结构类型,并将数据放入队列是一个很大的话题,具体取决于你的具体项目。