Почему SSD1306 OLED-дисплей получает скремблированный текст при печати сообщений из отдельных задач в RTOS?
ESP32 с RTOS, тестовое приложение с 2 независимыми задачами. Каждый из них выводит сообщение на OLED-дисплей. Почему текст часто скремблируется? Мьютекс был использован для совместного использования ресурса отображения.
#define DEBUG_ESP //закомментируйте, чтобы деактивировать консоль отладки
#ifdef DEBUG_ESP
#define pDBGln(x) Serial.println(x)
#define pDBG(x) Serial.print(x)
#else
#define pDBG(...)
#define pDBGln(...)
#endif
//создайте дескриптор для мьютекса. Он будет использоваться для ссылки на мьютекс
SemaphoreHandle_t xMutex;
//*********************************OLED-дисплей SSD1306
#define I2C_SDA 14
#define I2C_SCL 15
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // ширина OLED-дисплея, в пикселях
#define SCREEN_HEIGHT 64 // высота OLED-дисплея, в пикселях
#define OLED_RESET 4 // Reset pin # (или -1 при совместном использовании Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//*****************************************************
void setup() {
// создайте мьютекс и назначьте ему уже созданный обработчик
xMutex = xSemaphoreCreateMutex();
Wire.begin(I2C_SDA,I2C_SCL);
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = генерировать напряжение дисплея от 3,3 В внутри
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
pDBGln("SSD1306 allocation failed - Halt");
for(;;); // Don't proceed, loop forever
}else{
// Показать начальное отображение содержимого буфера на экране --
// библиотека инициализирует это с помощью заставки Adafruit.
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.clearDisplay();
}
xTaskCreatePinnedToCore(
task01, /* Функция для реализации задачи */
"task01", /* Имя задачи */
10000, /* Размер стека в словах */
NULL, /* Входной параметр задачи */
1, /* Приоритет задачи */
NULL, /* Дескриптор задачи. */
0); /* Ядро, в котором должна выполняться задача */
xTaskCreatePinnedToCore(
task02, /* Функция для реализации задачи */
"task02", /* Имя задачи */
10000, /* Размер стека в словах */
NULL, /* Входной параметр задачи */
1, /* Приоритет задачи */
NULL, /* Дескриптор задачи. */
1); /* Ядро, в котором должна выполняться задача */
}
void loop() {}
void task01( void * parameter ){
while(1){
printToDisplay("Task01..............");
vTaskDelay(1500);
}
}
void task02( void * parameter ){
while(1){
printToDisplay("Task02..............");
vTaskDelay(2500);
}
}
void printToDisplay(String text){
bool myMutex;
while(1){
// take mutex
myMutex = xSemaphoreTake(xMutex, portMAX_DELAY);
if(myMutex){
static byte lineCounter = 0;
static String displayText[8];
if(lineCounter>7){
for(int i=0;i<7;i++){
displayText[i] = displayText[i+1];
}
displayText[7] = text;
display.clearDisplay();
display.setCursor(0, 0);
for(int i=0;i<8;i++){
display.println(displayText[i]);
}
}else{
displayText[lineCounter] = text;
display.println(displayText[lineCounter]);
if(lineCounter<8){lineCounter++;}
}
display.display();
// release mutex
xSemaphoreGive(xMutex);
break;
}
}
}
Приведенный ниже код не использует ОСРВ и работает отлично: (мой проект требует ОСРВ)
#define DEBUG_ESP //закомментируйте, чтобы деактивировать консоль отладки verbose
#ifdef DEBUG_ESP
#define pDBGln(x) Serial.println(x)
#define pDBG(x) Serial.print(x)
#else
#define pDBG(...)
#define pDBGln(...)
#endif
byte lineCounter = 0;
//*********************************SSD1306 OLED Display
#define I2C_SDA 14
#define I2C_SCL 15
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//*****************************************************
void setup() {
Wire.begin(I2C_SDA,I2C_SCL);
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
pDBGln("SSD1306 allocation failed - Halt");
for(;;); // Don't proceed, loop forever
}else{
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.clearDisplay();
}
}
void loop() {
pDBGln("Start Display Test...");
task01();
task02();
}
void task01(){
printToDisplay("Task01..............");
delay(500);
}
void task02(){
printToDisplay("Task02..............");
delay(500);
}
void printToDisplay(String text){
static byte lineCounter = 0;
static String displayText[8];
if(lineCounter>7){
for(int i=0;i<7;i++){
displayText[i] = displayText[i+1];
}
displayText[7] = text;
display.clearDisplay();
display.setCursor(0, 0);
for(int i=0;i<8;i++){
display.println(displayText[i]);
}
}else{
displayText[lineCounter] = text;
display.println(displayText[lineCounter]);
if(lineCounter<8){lineCounter++;}
}
display.display();
}
@Paulo Borges, 👍2
Обсуждение1 ответ
Это формальный ответ на вопрос, основанный на комментариях Паулу Борхеса и Мадженко.
ESP32 является двухъядерным, что означает, что он поставляется с 2 32-разрядными микропроцессорами Xtensa LX6: core 0 и core 1.
Проблема заключается не в ошибке в вашем коде, а в том, что вы запускаете свои потоки на ядре 0 вместо ядра 1.
В ESP ядро 0 используется для радиочастотной связи. Даже с защитой от мьютекса весьма вероятно, что ваши задачи имеют проблемы с синхронизацией.
Переместите Task01 и Task02 в ядро 1, и ваша программа будет работать без каких-либо проблем с отображением.
- Как отображать текст на OLED?
- esp32, platformio A fatal error occurred: Packet content transfer stopped (received 8 bytes) *** [upload] Error 2
- Как выбрать альтернативные контакты I2C на ESP32?
- Драйверы для чипа последовательного порта CH9102X
- Как преобразовать форматированный оператор print в строковую переменную?
- ESP32 - "Детектор Браунаута был активирован" при запуске Wi-Fi
- Питание esp32cam от аккумулятора
- Контакты RX и TX на esp32
На первый взгляд все выглядит нормально. Может быть, за кулисами есть что-то, что все портит. Возможно, вам следует поместить весь код отображения в один выделенный поток и передать данные в этот поток от других., @Majenko
Вы имеете в виду создание третьей задачи printToDisplay(текст строки)?, @Paulo Borges
Третья задача, которая выполняет инструкции по какому-либо другому маршруту (возможно, по трубе или чему-то подобному) из других задач и отображает информацию., @Majenko
Просто обнаружил, что если я помещу Task01 для запуска на том же ядре, что и Task02 (ядро 1), он отлично работает. И если я включу оба режима на core 0, беспорядок станет намного хуже. Проблема явно имеет какое-то отношение, когда задача, работающая на ядре 0, пытается выполнить запись на дисплей, даже защищенный мьютексом., @Paulo Borges
Ядро 0 используется для стека Wi-Fi и IP. Тогда, вероятно, есть какие-то проблемы со временем., @Majenko
Да, возможно. Все еще пытаюсь найти способ обойти это или придется с этим жить..., @Paulo Borges
Можете ли вы показать нам фотографию повреждения дисплея? Возможно, мы сможем увидеть закономерность, которая объяснила бы это., @Majenko
Пожалуйста, проверьте фотографии, которые я добавил вверху.Да, есть закономерность, которая повторяется, но я считаю, что это результат времени между выполнением задачи., @Paulo Borges
Итак, здесь происходит одно из двух событий, и в данный момент невозможно узнать, какое именно. Способ работы библиотеки заключается в том, что она имеет внутренний буферизатор кадров, который вы изменяете с помощью функций рисования/печати. Этот буферизатор кадров затем выводится полностью на дисплей одним махом с
.display()
. Либо этот внутренний буферизатор кадров поврежден во время рисования, либо связь с дисплеем прерывается при отправке. Я предлагаю, чтобы функция". display () " выполнялась только в потоке на ядре 1. Остальное может быть где угодно., @Majenko