Почему мой ESP32-S3 выдает core dump при использовании очередей FreeRTOS для передачи данных температуры между задачами?
Я пытаюсь передавать показания температуры от задачи сенсора DHT20 к задаче мигания светодиода с помощью очереди, но система аварийно завершается с ошибкой Guru Meditation. Вот моя реализация:
Задача сенсора DHT20:
#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 миллисекунд
}
}
}
Задача мигания светодиода:
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:
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
- Проблемы с обработкой данных с плавающей точкой
- Проблемы конфигурации размера стека
- Преполнение очереди и проблемы синхронизации
- Управление памятью и повреждение
- Пошаговые решения
- Профилактические меры
- Техники отладки
- Заключение
Общие причины ошибок Guru Meditation с очередями FreeRTOS
Ошибка “Двойное исключение” указывает на то, что ESP32-S3 столкнулся с критической проблемой, кратко восстановился, а затем столкнулся с другим исключением. Этот типичный паттерн указывает на фундаментальные проблемы в реализации вашей задачи или обработке данных. Согласно документации Espressif, ошибки Guru Meditation возникают, когда “выполненная инструкция не была допустимой инструкцией”, часто из-за переполнения стека, повреждения памяти или неправильного использования аппаратных возможностей.
Проблемы с обработкой данных с плавающей точкой
Основная проблема в вашем коде - использование данных с плавающей точкой в очередях FreeRTOS. Как упоминается в ответе на Stack Overflow, “в ESP32 мы не можем использовать float в прерывании, потому что это будет использовать FPU, что не разрешено внутри обработчика прерываний”. Чтение сенсора DHT20, вероятно, вызывает контекст прерывания I2C, что приводит к неправильному доступу к FPU.
Решение:
Преобразуйте данные с плавающей точкой в целочисленное представление для передачи в очереди:
// Преобразование температуры в целочисленное представление
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 подтверждает это распространенное заблуждение.
Рекомендуемые размеры стека:
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, что указывает на проблемы управления очередью.
Решение:
Реализуйте правильный контроль потока данных в очереди:
// В задаче 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 упоминает аналогичные проблемы с ошибками утверждений в операциях с очередями.
Решение:
Включите обнаружение переполнения стека и мониторинг кучи:
// В 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. Добавьте обработку ошибок
// В setup():
if(xTaskCreate(Task_DHT20, "DHT20 Task", 8192, NULL, 2, NULL) != pdPASS) {
Serial.println("[FATAL][Setup]: Не удалось создать задачу DHT20");
while(1);
}
5. Используйте правильные задержки задач
Удалите задержку в 10 мс после отправки в очередь и полагайтесь на основную задержку цикла в 500 мс.
Профилактические меры
- Используйте статическое выделение очередей: Как упоминается на форуме ESP32, статические очереди могут предотвратить проблемы динамического выделения:
StaticQueue_t queue_cb;
uint8_t queue_storage[10 * sizeof(sensor_data_t)];
gTempQueue = xQueueCreate(10, sizeof(sensor_data_t), queue_storage, &queue_cb);
-
Включите таймеры сторожа: Используйте аппаратные таймеры сторожа для восстановления зависаний.
-
Реализуйте приоритеты задач: Обеспечьте правильные отношения приоритетов между задачами.
-
Используйте мьютексы для общих ресурсов: Защищайте общие данные мьютексами при необходимости.
Техники отладки
При возникновении ошибок Guru Meditation используйте следующий подход к отладке:
-
Проанализируйте трассировку стека: Ваша трассировка стека показывает
0x40384b9b:0x3fcedfd0, что указывает на ядро FreeRTOS. Это указывает на проблему на уровне ядра. -
Включите анализ дампа ядра: Как упоминается в документации Espressif, используйте функции дампа ядра для точного определения местоположения ошибки.
-
Используйте последовательную отладку: Добавьте больше отладочных выходов для отслеживания потока выполнения задачи.
-
Мониторьте системные ресурсы: Отслеживайте использование стека, фрагментацию кучи и загрузку ЦП.
Заключение
Ошибка Guru Meditation в вашей реализации FreeRTOS для ESP32-S3 в основном вызвана неправильной обработкой данных с плавающей точкой в очередях и недостаточными размерами стека. Преобразуя данные с плавающей точкой в целочисленное представление, регулируя размеры стека из слов в байты, реализуя правильный контроль потока данных в очереди и добавляя комплексную обработку ошибок, вы можете устранить эти сбои. Всегда помните, что ESP32-S3 имеет специфические требования к реализации FreeRTOS, которые отличаются от стандартного FreeRTOS, особенно в отношении использования FPU и спецификаций размера стека. Реализуйте предлагаемые изменения методично и тщательно тестируйте каждый компонент для обеспечения стабильной работы.
Источники
- Анализ ошибки Guru Meditation с очередями FreeRTOS в ESP32 - Stack Overflow
- Документация по ошибкам Guru Meditation в ESP32 - Espressif
- Форум ESP32: Ошибка Guru Meditation в uxListRemove
- Stack Overflow: Float в обработчике прерывания ESP32
- Документация по фатальным ошибкам ESP32-C3
- ESP-Techpedia: Руководство по отладке ошибки Guru Meditation