我设置了一个中断,将布尔值更改为true,在void loop()中,我经常检查布尔值是否为true,如下所示:
TTGOClass *ttgo;
bool irq = false;
void setup()
{
Serial.begin(115200);
ttgo = TTGOClass::getWatch();
ttgo->begin();
ttgo->openBL();
ttgo->tft->fillScreen(TFT_BLACK);
ttgo->tft->drawString("T-Watch AXP202", 25, 50, 4);
ttgo->tft->setTextFont(4);
ttgo->tft->setTextColor(TFT_WHITE, TFT_BLACK);
pinMode(AXP202_INT, INPUT_PULLUP);
attachInterrupt(AXP202_INT, [] {
irq = true;
}, FALLING);
//!Clear IRQ unprocessed first
ttgo->power->enableIRQ(AXP202_PEK_SHORTPRESS_IRQ | AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_CHARGING_IRQ, true);
ttgo->power->clearIRQ();
}
void loop()
{
if (irq) {
irq = false;
ttgo->power->readIRQ();
if (ttgo->power->isVbusPlugInIRQ()) {
ttgo->tft->fillRect(20, 100, 200, 85, TFT_BLACK);
ttgo->tft->drawString("Power Plug In", 25, 100);
}
if (ttgo->power->isVbusRemoveIRQ()) {
ttgo->tft->fillRect(20, 100, 200, 85, TFT_BLACK);
ttgo->tft->drawString("Power Remove", 25, 100);
}
if (ttgo->power->isPEKShortPressIRQ()) {
ttgo->tft->fillRect(20, 100, 200, 85, TFT_BLACK);
ttgo->tft->drawString("PowerKey Press", 25, 100);
}
ttgo->power->clearIRQ();
}
delay(1000);
}t-watch 2020 (一款esp32智能手表)使用电池运行,这种在示例草图中发现的奇怪方法(上面的代码是示例,而不是实际代码,因为它相当混乱)浪费了相当多宝贵的电池电量。因此,我尝试将void loop()中的if代码直接放入attachInterrupt(AXP202_INT, []中,但是...我需要执行冗长的命令(我认为这就是问题的原因),比如Serial.print()和delay() (它们是绝对必要的),esp32崩溃返回以下错误:
16:18:32.486 -> Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
16:18:32.486 -> Core 0 register dump:
16:18:32.486 -> PC : 0x4009947b PS : 0x00060233 A0 : 0x8009894f A1 : 0x3ffbd150
16:18:32.486 -> A2 : 0x3ffba5a8 A3 : 0x3ffbd2dc A4 : 0x00000001 A5 : 0x00000001
16:18:32.486 -> A6 : 0x00060223 A7 : 0x00000000 A8 : 0x00000000 A9 : 0x3ffba5a8
16:18:32.486 -> A10 : 0x3ffba5a8 A11 : 0x00060023 A12 : 0x00060021 A13 : 0x3ffc0910
16:18:32.486 -> A14 : 0x00000003 A15 : 0x00060023 SAR : 0x00000000 EXCCAUSE: 0x0000001c
16:18:32.519 -> EXCVADDR: 0x00000004 LBEG : 0x40093948 LEND : 0x40093964 LCOUNT : 0x00000000
16:18:32.519 ->
16:18:32.519 -> ELF file SHA256: 0000000000000000
16:18:32.519 ->
16:18:32.519 -> Backtrace: 0x4009947b:0x3ffbd150 0x4009894c:0x3ffbd170 0x4009726f:0x3ffbd190 0x400972fd:0x3ffbd1b0 0x40098fbe:0x3ffbd1d0 0x4009909f:0x3ffbd210 0x40096b06:0x3ffbd240
16:18:32.519 ->
16:18:32.519 -> Rebooting...代码如下:
TTGOClass *ttgo;
bool axpIrq = false; //axpIrq for button press and power plug in/remove events.
bool lenergy = false;
bool BLaudio = false;
bool keepAwake = false;
//some initializations
ttgo = TTGOClass::getWatch();
ttgo->begin();
ttgo->lvgl_begin();
pinMode(AXP202_INT, INPUT_PULLUP);
attachInterrupt(AXP202_INT, [] {
axpIrq = true;
ttgo->power->readIRQ();
if (ttgo->power->isPEKShortPressIRQ()) {
//ttgo->power->clearIRQ();
Serial.println("button pressed");
low_energy();
}
ttgo->power->clearIRQ();
}, FALLING);低能量在另一个文件中定义:
void low_energy() {
//portENTER_CRITICAL(&synch);
if (!keepAwake) {
if (ttgo->bl->isOn()) {
//Serial.println("backlight on, turning off");
ttgo->closeBL();
ttgo->stopLvglTick();
ttgo->bma->enableStepCountInterrupt(false);
ttgo->displaySleep();
//lenergy = true;
gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL);
gpio_wakeup_enable ((gpio_num_t)BMA423_INT1, GPIO_INTR_HIGH_LEVEL);
esp_sleep_enable_gpio_wakeup ();
if (!BLaudio) {
setCpuFrequencyMhz(20);
esp_light_sleep_start();
//Serial.println("BLaudio is off, light sleep starts");
} else {
//Serial.println("BLaudio is on, light sleep won't start");
}
//Serial.println("screen off");
} else {
//Serial.println("Waking up");
setCpuFrequencyMhz(160);
ttgo->startLvglTick();
ttgo->displayWakeup();
ttgo->rtc->syncToSystem();
lv_disp_trig_activity(NULL);
ttgo->openBL();
ttgo->bma->enableStepCountInterrupt();
displayTime(true);
}
}
//portEXIT_CRITICAL(&synch);
}我尝试用volatile bool axpIrq = false;替换bool axpIrq = false;,取消对portENTER_CRITICAL(&synch);和portEXIT_CRITICAL(&synch);的注释,仍然没有结果。如果我是正确的,当我认为冗长的命令是问题,我如何执行命令,而CPU继续在无效循环正常(使回调在第二个核心上执行?我不知道)?如果我是错的,那么实际问题是什么,我如何解决它?
发布于 2021-10-26 15:21:39
这里有两个问题。
首先,需要使用IRAM_ATTR属性定义中断处理程序,以确保它们已经加载到指令存储器(IRAM)中。可以理解,ESP32不喜欢为了服务中断而必须将代码从闪存加载到内存中。您需要确保它已经存在。如果不这样做,您看到的正是您所看到的错误。指令RAM也是一种极其有限的资源;您不希望占用超过绝对必要的资源。
您指定了一个lambda表达式作为中断处理程序。我不确定您是否可以将IRAM_ATTR与lambda一起使用。可以肯定的是,将中断处理程序分解为一个使用IRAM_ATTR正确定义的函数
void IRAM_ATTR handle_interrupt() {
axpIrq = true;
ttgo->power->readIRQ();
if (ttgo->power->isPEKShortPressIRQ()) {
//ttgo->power->clearIRQ();
Serial.println("button pressed");
low_energy();
}
ttgo->power->clearIRQ();
}
...
attachInterrupt(AXP202_INT, handle_interrupt, FALLING);
...其次,你在你的中断处理程序中做了太多的事情。我在上面引用了您现有的代码,但这是行不通的。除非您真的知道自己在做什么,否则您的处理程序除了设置volatile boolean flag变量并返回之外,不应该做更多的事情。在本例中,您将这个TTGO库称为Serial,可能是蓝牙?
您需要确保您调用的每个函数,以及这些函数调用的每个函数,等等,都是使用IRAM_ATTR定义的。你不会想这样做的,因为你需要修改很多第三方代码,而且它会占用很多--可能比现在更多的--指令RAM。
您也不知道从中断处理程序调用的这些函数中的任何一个都是可重入的。你不知道他们是否会锁定中断。除非你确实知道这一点,否则调用它们是不安全的-当它们被中断并重新进入时,它们的内部数据结构可能处于不一致的状态。对于任何高级库,最好的假设是它们不会被中断处理程序调用。
最后,中断的目的是快速处理,以便恢复正常的代码流。
您的第一段代码不是“奇怪的方法”--这是在ESP32上用Arduino代码处理中断的正常方式,也是您如何编写中断驱动的代码,这些代码所做的工作远远超出了在中断服务例程中所能做的安全工作。这是在ESP32 Arduino程序中执行此操作的“正确”方法,这就是示例显示此类代码的原因。
你真正的问题似乎是如何做到这一点,而不是耗尽你的电池。这是一个更大的问题集;您将需要了解ESP32 sleep modes和ESP-IDF/FreeRTOS任务,以及如何创建一个任务并从中断处理程序中唤醒它。这一切都取决于手表代码的其余部分,以及它是如何设计来节省电力的。如果你想做得更进一步,最好的办法就是回来,就你在尝试工作时遇到的具体问题发表问题。
https://stackoverflow.com/questions/69724249
复制相似问题