如何通过 ESP32 FreeRTOS 使用 LED 矩阵渲染任务

问题描述 投票:0回答:1

我有一个 ESP32/Arduino 应用程序,可以驱动 LED 矩阵等。它使用SimpleFSM来实现处理用户交互、传感器输入等的状态机。有时LED矩阵显示静态“图标”,有时显示动画。这两个用例在代码级别概念上有所不同:

  • 静态:迭代所有 LED 并将它们设置为 0 或 1,即它是有限的。
  • 动态/动画:执行相同的操作,但处于无限循环中直到应用程序状态更改需要在 LED 矩阵上显示新信息

FreeRTOS 实现渲染部分的惯用方法是什么?

最初,我想在需要渲染新内容时开始一项新任务,但这只会带来痛苦。在新任务开始之前,必须先终止旧/上一个任务。此外,任务应该是长期运行的,最好是无限的。

因此,FreeRTOS 的方式似乎是启动一个无限运行的渲染任务。当状态发生变化时,主任务会告诉它停止当前正在做的事情并开始做其他事情。如何交换任务的“工作单元”?当任务渲染静态图标时,如何防止任务完成 - 除了一遍又一遍地渲染它?渲染任务是否应该使用它自己的小状态机?

c++ esp32 freertos arduino-esp32
1个回答
0
投票

渲染任务是否应该使用它自己的小状态机?

是的,[单个]渲染任务应该保持其自己的状态。

渲染任务应该有一个轮询的请求/工作队列。

主要任务是进行用户交互。如果它决定更改显示的内容,则会将请求排队到渲染任务。

渲染任务轮询请求队列。如果有新请求到达,它将启动该操作。否则,它将继续处理当前/现有的工作请求。


这是一些伪代码。将队列相关函数替换为 FreeRTOS 队列等效函数:

typedef enum {
    ACTION_IDLE,

    ACTION_ZERO,
    ACTION_ONE,

    ACTION_ANIM_CIRCLE,
    ACTION_ANIM_SQUARE,
    ACTION_ANIM_TRIANGLE,
} action_t;

typedef struct {
    action_t req_action;
    int req_state;
    // ...
} request_t;

void
render_task(void)
{
    request_t *reqcur = NULL;
    request_t *reqnew = NULL;

    while (1) {
        // new work to do?
        reqnew = dequeue_request_nowait(&render_task_work);

        // yes, release old and setup new work
        if (reqnew != NULL) {
            // release old request block to free queue
            if (reqcur != NULL)
                enqueue_request(&render_task_free,reqcur);

            // setup new request
            reqcur = reqnew;
            init = 1;
        }
        else
            init = 0;

        // handle current work
        if (reqcur != NULL) {
            switch (reqcur->req_action) {
            case ACTION_IDLE:
                break;

            case ACTION_ZERO:
                if (init)
                    display_zeros();
                reqcur->req_action = ACTION_IDLE;
                break;

            case ACTION_ONE:
                if (init)
                    display_ones();
                reqcur->req_action = ACTION_IDLE;
                break;

            case ACTION_ANIM_CIRCLE:
            case ACTION_ANIM_SQUARE:
            case ACTION_ANIM_TRIANGLE:
                if (init)
                    reset_animation(reqcur);
                display_animation_frame(reqcur);
                advance_animation_state(reqcur);
                break;
            }
        }

        // prevent infinitely fast rendering ...
        // NOTE: this could be done via a timeout on a _blocking_ dequeue
        // operation above
        wait_for_frame_start(timeout);
    }
}

上面是工作线程的线程池的简单版本。有关一些背景知识,请参阅我的答案:在 C 中,将数据存储在线程中的局部变量中是否类似于创建本地副本? AKA 这个线程池同步有意义吗?

© www.soinside.com 2019 - 2024. All rights reserved.