ESP32 无法通过 Timer ISR 在 OLED 上显示文本

问题描述

问题

我有一个带有 I2C OLED 的 ESP32 T-call SIM800L,使用 Adafruit_SSD1306 库连接。在设置或循环中调用 displayText() 函数时,它工作得很好,尽管我将定时器中断设置为 1Hz,我尝试在 OLED 上显示一条消息,但它不起作用。 ISR 本身确实有效,我有一个计数变量,它会在每次中断时递增,然后将其打印到串行监视器(我知道您不应该在 ISR 中真正执行串行操作,但它仍然有效),因此 ISR 肯定会中断正确。奇怪的是,我发誓我以前使用过它(显示在 ISR 中),所以我确信这是可能的。我尝试删除 ISR 中的所有其他内容,只包括显示功能,但没有成功。

非常感谢任何帮助

代码

#include <Adafruit_SSD1306.h> //OLED
#include <Adafruit_GFX.h>

#define SCREEN_WIDTH 128 // OLED display width,in pixels
#define SCREEN_HEIGHT 32 // OLED display height,in pixels

// Declaration for an SSD1306 display connected to I2C (SDA,SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire,OLED_RESET);

volatile int count = 0;

void setupdisplay () 
{

  if(!display.begin(SSD1306_SWITCHCAPVCC,0x3C)) // Address 0x3C for 128x32
  
  { 
    Serial.println(F("SSD1306 allocation Failed"));
    for(;;); // Don't proceed,loop forever

  }
}

void displayText(int count) 
{

  display.cleardisplay();
  display.setTextSize(1); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.print(count);
  display.display();      // Show initial text

}

///* create a hardware timer */
hw_timer_t * timer = NULL;

void IRAM_ATTR onTimer()
{
count++;
Serial.println(count);
//displayText(count);


  display.cleardisplay();
  display.setTextSize(1); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.print("display from ISR");
  display.display();      // Show initial text

}

void setup() 
{
  Serial.begin(115200); 

  setupdisplay();
  displayText(count);

  timer = timerBegin(0,80,true);
  timerAttachInterrupt(timer,&onTimer,true);
  timerAlarmWrite(timer,1000000,true);
  timerAlarmEnable(timer);
  Serial.println("start timer");
}

void loop()
{

}

串行监视器打印输出

start timer
1
2
3
4
5
6
7
8
9
10

解决方法

您在中断处理程序中做得太多了。

中断处理程序不应该调用高级函数,除非您知道它们是可重入的——即它们在操作硬件或数据结构时锁定中断以保持一致——并且它们可用于在 RAM 中执行(已从闪存中复制,这是 IRAM_ATTR 修饰符保证的内容)。

中断处理程序也应该运行尽可能短的时间,因为它们会阻止 ESP32 为其他中断服务。

如果您不执行这些操作,您的程序有时可能会运行。当您对其进行细微更改时,它也可能会随机停止工作或开始崩溃或表现出我们喜欢称之为“未定义行为”的情况。

不要调用 Serial.println() 或您的任何 display 方法,而是设置 volatile boolean 标志以指示有工作要完成,并在 loop() 中完成工作。

试试这个:

volatile boolean work_to_be_done = false;

void IRAM_ATTR onTimer()
{
count++;
work_to_be_done = true;
}

void loop()
{
  if(work_to_be_done)
  {
    Serial.println(count);
    //displayText(count);


    display.clearDisplay();
    display.setTextSize(1); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0,0);
    display.print("Display from ISR");
    display.display();      // Show initial text

    work_to_be_done = false;
  }
}

您也可以编写 loop() 来检测 count 已更改,但我更喜欢维护一个标志的清晰性,表明有事情要做。