ESP32 上的 FreeRTOS - 任务处理、优先级、中断

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

免责声明:我珍惜每个人的时间,欣赏这个社区,并且不期望人们为我编写代码。我正在寻找有关根据我的申请最好做什么的指导。

我正在处理的项目遇到问题。主要问题是如何充分利用 FreeRTOS。

我在 FireBeetle 2 ESP32-E 上使用 PlatformIO。

附件是我认为对于我寻求帮助的代码中最有用的部分。如果有帮助的话,我可以分享所有 .h 和 .cpp 文件。

我想要实现的是:

  1. ReadSensorsTaskcode 在计时器上运行

    1. 如果正在运行,永远不会被中断
    2. 如果尚未开始运行,则应等待其他任务完成。
  2. FanCleaningSEN55代码在 ISR(ESP32 上的触摸针)触发时运行

    1. 如果正在运行,永远不会被中断
    2. 如果尚未开始运行,则应等待其他任务完成。
  3. UpdateRGBLEDTaskcode 在计时器上运行。

    1. 如果正在运行,永远不会被中断
    2. 如果尚未开始运行,则应等待其他任务完成。
  4. ControlDisplayTaskcode 在 ISR(ESP32 上的触摸针)触发时运行。

    1. 如果正在运行,永远不会被中断
    2. 如果尚未开始运行,则应等待其他任务完成(UpdateRGBLED 除外)。

我没有使用互斥量、信号量或任何类型的队列,因为经过多次尝试,我还没有弄清楚哪一个是正确的方法。二进制信号量? 任务固定在核心上。这个可以吗?使用两个核心有什么好处吗?我计划实现 WiFi 功能:与之相关的任何东西会“阻止”核心 0 吗?

非常感谢任何类型的反馈。

/************************************************************************************
 ********  FreeRTOS tasks
 **********************************************************************************/

#include "global_defs.h"
#include "leds.h"
#include "display.h"
#include "sensirionSCD41.h"
#include "sensirionSEN55.h"
#include "time.h"
#include "gsheet.h"
#include "tasks.h"

TaskHandle_t ReadSensorsTask;
TaskHandle_t FanCleaningSEN55Task;
TaskHandle_t UpdateRGBLEDTask;
TaskHandle_t ControlDisplayTask;

// Settings
static const TickType_t dim_delay = 5000 / portTICK_PERIOD_MS;

// Pins (change this if your Arduino board does not have LED_BUILTIN defined)
static const int led_pin = LED_BUILTIN;

// Globals
static TimerHandle_t one_shot_timer = NULL;

// ISR function
void IRAM_ATTR gotTouchSEN55cleaning(){
    // Resume tasks
    vTaskResume(FanCleaningSEN55Task);
}

// ISR function
void IRAM_ATTR gotTouchDisplayCarousel(){
    // Resume tasks
    buttonDisplayCount++;
    vTaskResume(ControlDisplayTask);
}

//  Triggered by TIMER
void ReadSensorsTaskcode( void * pvParameters ){

    /* DEBUG Inspect our own high water mark on entering the task. */
        //uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
        
    for (;;) {
    
        // Run rightaway on first boot
        if (readingCounter == 0){
            const TickType_t xFrequency = (1000 / portTICK_PERIOD_MS);
            vTaskDelay(xFrequency );
        }
        else{
            const TickType_t xFrequency = (delayMinutesReadSensors * 60000 / portTICK_PERIOD_MS);
            vTaskDelay(xFrequency );
        }

        //vTaskSuspend(UpdateRGBLEDTask);
        
        Serial.println("** SCD41 and SEN55 Get Readings **");
        Serial.print("running on core ");
        Serial.println(xPortGetCoreID());

        lightFadeInPurpleLED();
        displayReading();
        getSCD41readings();
        getSEN55readings();
        if (readingSensorsDataflagOK == true) 
        {
            displayOKcircleThick();
        }
        else 
        {
            displayNGcircleThick();
        }
        readingSensorsDataflagOK = false;
        lightFadeOutPurpleLED();
        readingCounter++;

        // Show CO2 value if above dangerous levels
        if (co2 > dangerousValueCO2) 
        {
            displayCO2();
        }

        // Show latest sensors readings
        else
        {
            displayCO2();
            delay(3000);
            displayTempHumid();
        }

        Serial.print("Value set in memory for dangerous CO2: ");
        Serial.println(dangerousValueCO2);
        Serial.print("Value set in memory for high CO2: ");
        Serial.println(highValueCO2);
        Serial.print("Value set in memory for max CO2 gauge: ");
        Serial.println(maxco2);
        Serial.print("Value set in memory for max PM2.5 gauge: ");
        Serial.println(maxpm25);
        Serial.print("Value set in memory for max VOC gauge: ");
        Serial.println(maxVOC);

        readingCounter++;

        /* debug */
        //uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
        
    }
    
}

//  Called by ISR
void FanCleaningSEN55code( void * pvParameters ){

  for(;;){
    
    Serial.print("FanCleaningSEN55 running on core ");
    Serial.println(xPortGetCoreID());
    
    //---------------------------------------------------------------------
    //--- USER ACTION ----- SEN55 fan cleaning -----------------

    int touchValue1 = touchRead(touchPinSEN55cleaning);

    if (sen55initflagOK == true && touchValue1 < threshold);

    if (sen55initflagOK == true && touchValue1 < threshold) 
    {
            displaySEN55Cleaning();
            sen55FanCleaning();
            lightFadeInOutYellowLEDfast();
    }
    else if(sen55initflagOK == false && touchValue1 < threshold)
    {
        displaySEN55InitError();
        displayNGcircleThick();
        delay(2000);
    }
    
    // Suspend tasks
    vTaskSuspend(FanCleaningSEN55Task);
    
  }
}

//  Triggered by TIMER
void UpdateRGBLEDTaskcode( void * pvParameters ){

    for(;;){
    
        const TickType_t xFrequency = (delaySecondsCO2LED * 1000 / portTICK_PERIOD_MS);
        vTaskDelay(xFrequency );

            //Serial.println("** Update RGB LED color based on CO2 **");
            //Serial.print("running on core ");
            //Serial.println(xPortGetCoreID());
            //---------------------------------------------------------------------
            //--- ONBOARD LED #2 ----- Update based on CO2 readings -----------------
            if (useOnboardLEDUser == true)
            {
                if (co2 >= dangerousValueCO2) 
                {
                    lightFadeInOutRedLED();
                }
                else if (co2 >= highValueCO2) 
                {
                    lightFadeInOutOrangeLED();
                }
                else
                {
                    lightFadeInOutGreenLED();
                }
            }
            

    }
}

//  Called by ISR
void ControlDisplayTaskcode( void * pvParameters ){

    for(;;){
    
    Serial.print("ControlDisplay running on core ");
    Serial.println(xPortGetCoreID());
    Serial.println(buttonDisplayCount);

    int touchValue2 = touchRead(touchPinDisplayCarousel);
    
    if (buttonDisplayCount > 1 && touchValue2 < threshold) 
    {
            buttonDisplayCount = 0;
    }

    if (buttonDisplayCount == 0 && touchValue2 < threshold) 
    {
            displayTempHumid();
    }
    else if (buttonDisplayCount == 1 && touchValue2 < threshold) 
    {
            displayCO2();
    }
    
    // Suspend tasks
    vTaskSuspend(ControlDisplayTask);
    
    }

}


void initializeFreeRTOStasks() {

    xTaskCreatePinnedToCore(
                        ReadSensorsTaskcode,   /* Task function. */
                        "ReadSensorsTask",     /* name of task. */
                        10000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        10,           /* priority of the task */
                        &ReadSensorsTask,     /* Task handle to keep track of created task */
                        1);          /* pin task to core 0 */                  
    delay(500);

    touchAttachInterrupt(touchPinSEN55cleaning, gotTouchSEN55cleaning, threshold);
    xTaskCreatePinnedToCore(
                        FanCleaningSEN55code,   /* Task function. */
                        "FanCleaningSEN55Task",     /* name of task. */
                        5000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        3,           /* priority of the task */
                        &FanCleaningSEN55Task,     /* Task handle to keep track of created task */
                        1);          /* pin task to core 0 */                  
    vTaskSuspend(FanCleaningSEN55Task);
    delay(500);

    xTaskCreatePinnedToCore(
                        UpdateRGBLEDTaskcode,   /* Task function. */
                        "UpdateRGBLEDTask",     /* name of task. */
                        5000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        8,           /* priority of the task */
                        &UpdateRGBLEDTask,     /* Task handle to keep track of created task */
                        1);          /* pin task to core 0 */                  
    delay(500);

    touchAttachInterrupt(touchPinDisplayCarousel, gotTouchDisplayCarousel, threshold);
    xTaskCreatePinnedToCore(
                        ControlDisplayTaskcode,   /* Task function. */
                        "ControlDisplayTask",     /* name of task. */
                        10000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        3,           /* priority of the task */
                        &ControlDisplayTask,     /* Task handle to keep track of created task */
                        1);          /* pin task to core 0 */                  
    vTaskSuspend(ControlDisplayTask);
    delay(500);

}

我尝试为每个任务使用优先级,但这不起作用,并且 MCU 有时仍然会恐慌。一个简单的yield()对我的应用程序有用吗?

esp32 freertos platformio
1个回答
0
投票

如果我们暂时忽略要求 4.2,那么您的愿望基本上相当于简单的协作多线程(即任务不能被抢占,除非它自愿让步)。 FreeRTOS 具有抢占式多线程功能,这意味着调度程序将在每次更新后重新评估正在运行的任务,以查看某个具有相同或更高优先级的线程是否已准备好运行。我会推荐优秀的 FreeRTOS 书,第 4 章作为参考。

不管怎样,你可以轻松模拟协作多线程。如果我想到解决您的问题的最简单的解决方案,那就是处理系统中出现的事件(中断或计时器滴答)的单个线程。第 1-4 步中的每一步都只是创建一个事件并将其推送到处理线程的队列中。

概念性解决方案,未经编译或测试:

QueueHandle_t ev_queue; // Queue for sending events to worker thread
TimerHandle_t timer;    // Timer for generating periodic events

// Event definitions
typedef enum {
    EV_READ_SENSORS,
    EV_FAN_CLEANING,
    EV_UPDATE_RGB_LED,
    EV_CONTROL_DISPLAY,
} event_t;

// ISR generates some event
void IRAM_ATTR gotTouchSEN55cleaning() {
    event_t ev = EV_FAN_CLEANING;
    xQueueSendFromISR(ev_queue, &ev, NULL);
}

// ISR generates some other event
void IRAM_ATTR gotTouchDisplayCarousel() {
    event_t ev = EV_CONTROL_DISPLAY;
    xQueueSendFromISR(ev_queue, &ev, NULL);
}

// Timer generates the periodic events
void on_timer(TimerHandle_t timer_h) {
    event_t ev;
    if (TIME_TO_READ_SENSORS()) {
        ev = EV_READ_SENSORS;
        xQueueSend(ev_queue, &ev, 0);
    }
    if (TIME_TO_UPDATE_RGB_LED()) {
        ev = EV_UPDATE_RGB_LED;
        xQueueSend(ev_queue, &ev, 0);
    }
}

// Single task handles all events in order of appearance
void worker(void* arg) {
    while (true) {
        event_t ev;
        if (xQueueReceive(ev_queue, &ev, portMAX_DELAY)) {
            switch (ev) {
            case EV_READ_SENSORS:
                ReadSensors(); // Your code in here, no FreeRTOS task management needed
                break;
            case EV_FAN_CLEANING:
                FanCleaningSEN55();
                break;
            case EV_UPDATE_RGB_LED:
                UpdateRGBLED();
                break;
            case EV_CONTROL_DISPLAY:
                ControlDisplay();
                break;
            }
        }
    }
}

void app_main() {
    ev_queue = xQueueCreate(10, sizeof(event_t));
    assert(ev_queue);
    BaseType_t ret = xTaskCreatePinnedToCore(worker, "Worker", 10000, NULL, 10, NULL, 1);
    assert(ret == pdPASS);
    timer = xTimerCreate("MyTimer", pdMS_TO_TICKS(100), pdTRUE, NULL, on_timer);
    assert(timer);
}
           

要求 4.2“UpdateRGBLED 除外”并不完全适合这个简单模型。我会把这个问题留给你去解决,因为我在 10 秒内没有想出一个明确的解决方案:)

如果同一核心上没有其他优先级为 10 或更高的任务运行,它应该或多或少是实时工作的。如果您计划使用 WiFi 或 BLE,那么它们往往会在核心 0 上创建高优先级任务,在这种情况下,在核心 1 上运行任务并不是一个坏主意。否则核心钉住没有什么区别。

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