НейроАгент

Исправление дампа ядра ESP32-S3 с очередями FreeRTOS

Узнайте, как исправить ошибку Guru Meditation на ESP32-S3 при использовании очередей FreeRTOS для передачи данных температуры. Найдите решения для обработки float, настройки стека и синхронизации очередей.

Почему мой ESP32-S3 выдает core dump при использовании очередей FreeRTOS для передачи данных температуры между задачами?

Я пытаюсь передавать показания температуры от задачи сенсора DHT20 к задаче мигания светодиода с помощью очереди, но система аварийно завершается с ошибкой Guru Meditation. Вот моя реализация:

Задача сенсора DHT20:

c
#include "DN_Task_DHT20.h"

DHT20 sensor_dht20(&Wire); // Объект сенсора DHT20
float temperature_dht20 = 0.0f; // Значение температуры
float humidity_dht20 = 0.0f;    // Значение влажности

void Task_DHT20(void *pvParameters) {
    float temp_temperature = 0.0f;
    float temp_humidity = 0.0f;
    sensor_dht20.begin();
    vTaskDelay(1000 / portTICK_PERIOD_MS); // Начальная задержка для стабилизации сенсора

    while (1){
        int error_code = sensor_dht20.read();
        if (error_code == DHT20_OK) {
            temp_temperature = sensor_dht20.getTemperature();
            temp_humidity = sensor_dht20.getHumidity();
            Serial.printf("[INFO][DHT20]: Temperature: %.2f C, Humidity: %.2f %%\n", temp_temperature, temp_humidity);
            temperature_dht20 = temp_temperature;
            humidity_dht20 = temp_humidity;
            if(gTempQueue) {
                if(xQueueSend(gTempQueue, &temperature_dht20, (TickType_t)0) == pdTRUE) {
                    // Успешно отправлена температура в очередь
                    Serial.println("[INFO][DHT20]: Temperature sent to queue");
                } else {
                    Serial.println("[ERROR][DHT20]: Failed to send temperature to queue");
                }
                vTaskDelay(10 / portTICK_PERIOD_MS);
        } else {
            Serial.printf("[ERROR][DHT20]: ERROR ON READING SENSOR: %d\n", error_code);
        }
        vTaskDelay(500 / portTICK_PERIOD_MS); // Задержка 500 миллисекунд
        }
    }
}

Задача мигания светодиода:

c
void Task_LEDBlink(void *pvParameters) {
    pinMode(LED_BUILTIN_PIN, OUTPUT);
    vTaskDelay(2000); // Ожидание стабилизации системы
    // Значение по умолчанию до первого чтения
    float currentTemperature = 25.0f;
    float receivedTemperature = 25.0f;
    uint16_t  blinkMs = tempToFreqByRange(currentTemperature); // полный период в мс
    while(1){
        Serial.println("[DEBUG][LED BLINK] Waiting for temperature data...");
        if(xQueueReceive(gTempQueue, &receivedTemperature, portMAX_DELAY) == pdTRUE) {
            Serial.printf("[DEBUG][LED BLINK] Received Temperature: %.2f C\n", receivedTemperature);
            currentTemperature = receivedTemperature;
            blinkMs = tempToFreqByRange(currentTemperature);
        }
        Serial.printf("[INFO][LED BLINK] Current Temperature: %.2f C, Blink Frequency: %d ms\n", currentTemperature, blinkMs);
        digitalWrite(LED_BUILTIN_PIN, HIGH);
        vTaskDelay(blinkMs);
        digitalWrite(LED_BUILTIN_PIN, LOW);
        vTaskDelay(blinkMs);
    }
}

Функция Setup:

c
void setup() {
  Serial.begin(115200); // UART @ 115200 bps
  initQueues();
  Wire.begin(11, 12);
  delay(500);
  // Задержка для инициализации Serial Monitor
  xTaskCreate(
    Task_DHT20,          // Функция задачи
    "DHT20 Task",       // Имя задачи
    4096,               // Размер стека (в словах)
    NULL,               // Входной параметр задачи
    2,                  // Приоритет задачи
    NULL                // Дескриптор задачи
  );

  xTaskCreate(
    Task_LEDBlink,      // Функция задачи
    "LED Blink Task",   // Имя задачи
    2048,               // Размер стека (в словах)
    NULL,               // Входной параметр задачи
    1,                  // Приоритет задачи
    NULL                // Дескриптор задачи
  );
}

Лог ошибок:

Rebooting...
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x29 (SPI_FAST_FLASH_BOOT)
Saved PC:0x4037b4dd
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2820,len:0x116c
load:0x403c8700,len:0xc2c
load:0x403cb700,len:0x3108
entry 0x403c88b8
[INFO][Queue] gTempQueue created successfully
[    20][I][esp32-hal-i2c-ng.c:112] i2cInit(): Initializing I2C Master: num=0 sda=11 scl=12 freq=100000
[   530][W][Wire.cpp:300] begin(): Bus already started in Master Mode.
[INFO][DHT20]: Temperature: 25.37 C, Humidity: 65.91 %
[INFO][DHT20]: Temperature sent to queue
[DEBUG][LED BLINK] Waiting for temperature data...
Guru Meditation Error: Core  0 panic'ed (Double exception). 

Core  0 register dump:
PC      : 0x40384b9e  PS      : 0x00040b36  A0      : 0x803821c4  A1      : 0x3fcedfd0  
A2      : 0x00040b36  A3      : 0x00040026  A4      : 0x00000004  A5      : 0x00001800  
A6      : 0x3fc984bc  A7      : 0x3fce9710  A8      : 0x3fcee090  A9      : 0x00001800  
A10     : 0x00000084  A11     : 0x00001800  A12     : 0x3fcee070  A13     : 0x00000084  
A14     : 0x00000002  A15     : 0x3fc9ad00  SAR     : 0x0000001d  EXCCAUSE: 0x00000002  
EXCVADDR: 0x00001800  LBEG    : 0x400556d5  LEND    : 0x400556e5  LCOUNT  : 0xffffffff  


Backtrace: 0x40384b9b:0x3fcedfd0 0x403821c1:0x3fcee000 0x40380df9:0x3fcee020 0x403765af:0x3fcee040 0x403765c9:0x3fcee070 0x403762d5:0x3fcee090

Что может вызывать этот core dump при использовании очередей FreeRTOS на ESP32-S3, и как это можно исправить?

НейроАгент

Ошибка Guru Meditation с “Двойным исключением” в ESP32-S3 при использовании очередей FreeRTOS

Ошибка Guru Meditation с “Двойным исключением” в вашем ESP32-S3 при использовании очередей FreeRTOS обычно вызвана одной или несколькими из следующих проблем: неправильная работа с данными с плавающей точкой, недостаточные размеры стека, переполнение очереди или повреждение памяти. На основе вашего кода и анализа ошибок, вот конкретные причины и решения:

Содержание

Общие причины ошибок Guru Meditation с очередями FreeRTOS

Ошибка “Двойное исключение” указывает на то, что ESP32-S3 столкнулся с критической проблемой, кратко восстановился, а затем столкнулся с другим исключением. Этот типичный паттерн указывает на фундаментальные проблемы в реализации вашей задачи или обработке данных. Согласно документации Espressif, ошибки Guru Meditation возникают, когда “выполненная инструкция не была допустимой инструкцией”, часто из-за переполнения стека, повреждения памяти или неправильного использования аппаратных возможностей.

Проблемы с обработкой данных с плавающей точкой

Основная проблема в вашем коде - использование данных с плавающей точкой в очередях FreeRTOS. Как упоминается в ответе на Stack Overflow, “в ESP32 мы не можем использовать float в прерывании, потому что это будет использовать FPU, что не разрешено внутри обработчика прерываний”. Чтение сенсора DHT20, вероятно, вызывает контекст прерывания I2C, что приводит к неправильному доступу к FPU.

Решение:

Преобразуйте данные с плавающей точкой в целочисленное представление для передачи в очереди:

c
// Преобразование температуры в целочисленное представление
typedef struct {
    int16_t temp_celsius;   // Температура в Цельсиях * 100
    int16_t humidity_percent; // Влажность в процентах * 100
} sensor_data_t;

// В задаче DHT20:
int16_t temp_int = (int16_t)(temp_temperature * 100);
int16_t hum_int = (int16_t)(temp_humidity * 100);
sensor_data_t data = {temp_int, hum_int};
xQueueSend(gTempQueue, &data, (TickType_t)0);

// В задаче LED:
sensor_data_t received_data;
if(xQueueReceive(gTempQueue, &received_data, portMAX_DELAY) == pdTRUE) {
    float temp = (float)received_data.temp_celsius / 100.0f;
    float hum = (float)received_data.humidity_percent / 100.0f;
}

Проблемы конфигурации размера стека

Ваши текущие размеры стека (4096 и 2048 слов) могут быть недостаточными. В ESP32 размеры стека FreeRTOS указываются в байтах, а не в словах, как в стандартных реализациях FreeRTOS. Обсуждение на форуме ESP32 подтверждает это распространенное заблуждение.

Рекомендуемые размеры стека:

c
xTaskCreate(
    Task_DHT20,          // Функция задачи
    "DHT20 Task",       // Имя задачи
    8192,               // Размер стека в байтах (было 4096 слов)
    NULL,               // Параметр ввода задачи
    2,                  // Приоритет задачи
    NULL                // Дескриптор задачи
);

xTaskCreate(
    Task_LEDBlink,      // Функция задачи
    "LED Blink Task",   // Имя задачи
    4096,               // Размер стека в байтах (было 2048 слов)
    NULL,               // Параметр ввода задачи
    1,                  // Приоритет задачи
    NULL                // Дескриптор задачи
);

Переполнение очереди и проблемы синхронизации

Ваша задача DHT20 отправляет данные каждые 500 мс, но задача LED может не обрабатывать их достаточно быстро. Короткая задержка в 10 мс после отправки не предотвращает потенциального переполнения очереди. Обсуждение на форуме ESP32 показывает аналогичные проблемы в uxListRemove, что указывает на проблемы управления очередью.

Решение:

Реализуйте правильный контроль потока данных в очереди:

c
// В задаче DHT20 - проверка доступности очереди перед отправкой
if(gTempQueue) {
    // Удаление старых данных для предотвращения переполнения
    xQueueReset(gTempQueue);
    
    if(xQueueSend(gTempQueue, &data, (TickType_t)10) == pdTRUE) {
        Serial.println("[INFO][DHT20]: Температура отправлена в очередь");
    } else {
        Serial.println("[WARNING][DHT20]: Очередь заполнена, данные отброшены");
    }
    vTaskDelay(500 / portTICK_PERIOD_MS); // Более длинная задержка
}

Управление памятью и повреждение

“Двойное исключение” часто указывает на повреждение памяти из-за переполнения стека или проблем с кучей. Обсуждение на форуме ESP32 упоминает аналогичные проблемы с ошибками утверждений в операциях с очередями.

Решение:

Включите обнаружение переполнения стека и мониторинг кучи:

c
// В setup():
// Включение обнаружения переполнения стека
vTaskSetApplicationTaskTag(NULL, (TaskFunction_t)1);

// Включение мониторинга кучи
heap_caps_check_integrity_all(true);

// Мониторинг использования стека
void Task_DHT20(void *pvParameters) {
    // ... существующий код ...
    while(1) {
        // Добавление мониторинга стека
        UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        if(stackHighWaterMark < 100) {  // Порог предупреждения
            Serial.printf("[WARNING][DHT20]: Низкий уровень стека: %u слов\n", stackHighWaterMark);
        }
        // ... остальной код ...
    }
}

Пошаговые решения

1. Исправьте обработку данных с плавающей точкой

Замените очереди с float на структурированные целочисленные данные, как показано выше. Это устраняет использование FPU в контекстах прерываний.

2. Отрегулируйте размеры стека

Преобразуйте ваши размеры стека из слов в байты и увеличьте их для безопасности:

  • Задача DHT20: 8192 байта
  • Задача LED: 4096 байт

3. Реализуйте контроль потока данных в очереди

Используйте xQueueReset() или реализуйте более сложную систему управления очередью для предотвращения переполнения.

4. Добавьте обработку ошибок

c
// В setup():
if(xTaskCreate(Task_DHT20, "DHT20 Task", 8192, NULL, 2, NULL) != pdPASS) {
    Serial.println("[FATAL][Setup]: Не удалось создать задачу DHT20");
    while(1);
}

5. Используйте правильные задержки задач

Удалите задержку в 10 мс после отправки в очередь и полагайтесь на основную задержку цикла в 500 мс.

Профилактические меры

  1. Используйте статическое выделение очередей: Как упоминается на форуме ESP32, статические очереди могут предотвратить проблемы динамического выделения:
c
StaticQueue_t queue_cb;
uint8_t queue_storage[10 * sizeof(sensor_data_t)];
gTempQueue = xQueueCreate(10, sizeof(sensor_data_t), queue_storage, &queue_cb);
  1. Включите таймеры сторожа: Используйте аппаратные таймеры сторожа для восстановления зависаний.

  2. Реализуйте приоритеты задач: Обеспечьте правильные отношения приоритетов между задачами.

  3. Используйте мьютексы для общих ресурсов: Защищайте общие данные мьютексами при необходимости.

Техники отладки

При возникновении ошибок Guru Meditation используйте следующий подход к отладке:

  1. Проанализируйте трассировку стека: Ваша трассировка стека показывает 0x40384b9b:0x3fcedfd0, что указывает на ядро FreeRTOS. Это указывает на проблему на уровне ядра.

  2. Включите анализ дампа ядра: Как упоминается в документации Espressif, используйте функции дампа ядра для точного определения местоположения ошибки.

  3. Используйте последовательную отладку: Добавьте больше отладочных выходов для отслеживания потока выполнения задачи.

  4. Мониторьте системные ресурсы: Отслеживайте использование стека, фрагментацию кучи и загрузку ЦП.

Заключение

Ошибка Guru Meditation в вашей реализации FreeRTOS для ESP32-S3 в основном вызвана неправильной обработкой данных с плавающей точкой в очередях и недостаточными размерами стека. Преобразуя данные с плавающей точкой в целочисленное представление, регулируя размеры стека из слов в байты, реализуя правильный контроль потока данных в очереди и добавляя комплексную обработку ошибок, вы можете устранить эти сбои. Всегда помните, что ESP32-S3 имеет специфические требования к реализации FreeRTOS, которые отличаются от стандартного FreeRTOS, особенно в отношении использования FPU и спецификаций размера стека. Реализуйте предлагаемые изменения методично и тщательно тестируйте каждый компонент для обеспечения стабильной работы.

Источники

  1. Анализ ошибки Guru Meditation с очередями FreeRTOS в ESP32 - Stack Overflow
  2. Документация по ошибкам Guru Meditation в ESP32 - Espressif
  3. Форум ESP32: Ошибка Guru Meditation в uxListRemove
  4. Stack Overflow: Float в обработчике прерывания ESP32
  5. Документация по фатальным ошибкам ESP32-C3
  6. ESP-Techpedia: Руководство по отладке ошибки Guru Meditation