问题描述
问题
我有一个带有 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
已更改,但我更喜欢维护一个标志的清晰性,表明有事情要做。