ESP8266/Arduino:为什么需要将 ICACHE_RAM_ATTR 宏添加到 ISR 和从那里调用的函数?
ESP8266/Arduino: Why is it necessary to add the ICACHE_RAM_ATTR macro to ISRs and functions called from there?
注意:自 esp8266/Arduino release 3.0.0 ICACHE_RAM_ATTR has been changed to IRAM_ATTR. For future readers, I updated the links to the ESP8266 Arduino Core docs 起,但问题的其余部分保持不变。
我读到 need to add the ICACHE_RAM_ATTR macro to interrup service routines (ISRs) and to every function that is called from there in my Arduino code for ESP8266 to prevent random crashes. I also found an explanation of what the macro ICACHE_RAM_ATTR does,虽然我不确定针对 Espressif ESP8266 SDK 的解释是否也适用于 ESP8266 上的 Arduino。而且我不明白为什么我需要将宏添加到ISR。
第一个问题:为什么我需要将 ICACHE_RAM_ATTR 宏添加到 ISR 以及从那里调用的所有函数?
下一个问题是,如果我强制内联一个从 ISR 调用的函数会发生什么:
inline void doStuff() __attribute__((__always_inline__)) { // <-- necessary to add ICACHE_RAM_ATTR here?
// no more function calls here
}
void ICACHE_RAM_ATTR handleInterrupt() {
doStuff();
}
第二个问题:强制内联的函数需要加ICACHE_RAM_ATTR宏吗?
ICACHE_RAM_ATTR 和 ICACHE_FLASH_ATTR 是链接器属性。一旦你编译了你的草图,你可以说这个函数应该存储在 RAM 还是闪存中(通常你不设置任何东西:没有缓存)。
ESP8266 是多任务处理,而 ESP32 有 2 个内核。因此您可以将代码作为多线程执行 - 因为它使用 RTOS。
现在的问题是:整个闪存都用于程序和存储。读取和写入闪存只能通过 1 个线程完成。如果您尝试通过 2 个不同的线程同时访问闪存,您的 ESP 可能会崩溃。
这是因为您可以将函数放在 RAM 而不是闪存中。因此,即使您在 EEPROM 或闪存中写入内容,也可以在不访问闪存的情况下调用此函数。
使用 ICACHE_RAM_ATTR
将函数放在 RAM 上。
对于ICACHE_FLASH_ATTR
,您将函数放在FLASH 上(以节省RAM)。
中断函数应该使用ICACHE_RAM_ATTR。经常调用的函数,不应该使用任何缓存属性。
重要提示:切勿在中断内访问您的闪存!中断可能发生在闪存访问期间,因此如果您尝试同时访问闪存,您将遇到崩溃(有时会在您使用设备后 1-2 小时后发生)。
因为你只有 32kb 的 IRAM(指令 RAM),你应该尽量只把中断函数放在 RAM 中,而不是所有的函数,即使这样做是可能的。
第二个问题:
不,绝对不! inline 是另一个编译器标志,因此编译器将尝试将您的整个函数放在调用函数中 => 将函数调用转换为 main 中的 c++ 代码。这并不意味着编译器会做,只是尝试一下。如果编译草图后函数不再存在,则不能要求将函数放入 RAM 中。
非常感谢 ICACHE_RAM_ATTR,我在代码的最顶部使用了它...当然还有一些引脚不能用作中断,例如在我的情况下我使用的是板WEMOS D1 Mini Pro 和引脚 D0 (GPIO 16) 不工作,直到我换到下一个引脚 (GPIO 14) 并且它完美地工作....
ICACHE_RAM_ATTR 适用于较新的库,过时的 2.5 库也可以在没有这段代码的情况下使用。
非常感谢!
const uint8_t interruptPin = 14;
volatile byte interruptCounter = 0;
int numberOfInterrupts = 0;
void ICACHE_RAM_ATTR handleInterrupt();
void setup() {
Serial.begin(9600);
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, CHANGE);
}
void handleInterrupt() {
interruptCounter++;
}
void loop() {
if(interruptCounter>0){
interruptCounter--;
numberOfInterrupts++;
Serial.print("An interrupt has occurred. Total: ");
Serial.println(numberOfInterrupts);
}
}
注意:自 esp8266/Arduino release 3.0.0 ICACHE_RAM_ATTR has been changed to IRAM_ATTR. For future readers, I updated the links to the ESP8266 Arduino Core docs 起,但问题的其余部分保持不变。
我读到 need to add the ICACHE_RAM_ATTR macro to interrup service routines (ISRs) and to every function that is called from there in my Arduino code for ESP8266 to prevent random crashes. I also found an explanation of what the macro ICACHE_RAM_ATTR does,虽然我不确定针对 Espressif ESP8266 SDK 的解释是否也适用于 ESP8266 上的 Arduino。而且我不明白为什么我需要将宏添加到ISR。
第一个问题:为什么我需要将 ICACHE_RAM_ATTR 宏添加到 ISR 以及从那里调用的所有函数?
下一个问题是,如果我强制内联一个从 ISR 调用的函数会发生什么:
inline void doStuff() __attribute__((__always_inline__)) { // <-- necessary to add ICACHE_RAM_ATTR here?
// no more function calls here
}
void ICACHE_RAM_ATTR handleInterrupt() {
doStuff();
}
第二个问题:强制内联的函数需要加ICACHE_RAM_ATTR宏吗?
ICACHE_RAM_ATTR 和 ICACHE_FLASH_ATTR 是链接器属性。一旦你编译了你的草图,你可以说这个函数应该存储在 RAM 还是闪存中(通常你不设置任何东西:没有缓存)。
ESP8266 是多任务处理,而 ESP32 有 2 个内核。因此您可以将代码作为多线程执行 - 因为它使用 RTOS。
现在的问题是:整个闪存都用于程序和存储。读取和写入闪存只能通过 1 个线程完成。如果您尝试通过 2 个不同的线程同时访问闪存,您的 ESP 可能会崩溃。
这是因为您可以将函数放在 RAM 而不是闪存中。因此,即使您在 EEPROM 或闪存中写入内容,也可以在不访问闪存的情况下调用此函数。
使用 ICACHE_RAM_ATTR
将函数放在 RAM 上。
对于ICACHE_FLASH_ATTR
,您将函数放在FLASH 上(以节省RAM)。
中断函数应该使用ICACHE_RAM_ATTR。经常调用的函数,不应该使用任何缓存属性。
重要提示:切勿在中断内访问您的闪存!中断可能发生在闪存访问期间,因此如果您尝试同时访问闪存,您将遇到崩溃(有时会在您使用设备后 1-2 小时后发生)。
因为你只有 32kb 的 IRAM(指令 RAM),你应该尽量只把中断函数放在 RAM 中,而不是所有的函数,即使这样做是可能的。
第二个问题: 不,绝对不! inline 是另一个编译器标志,因此编译器将尝试将您的整个函数放在调用函数中 => 将函数调用转换为 main 中的 c++ 代码。这并不意味着编译器会做,只是尝试一下。如果编译草图后函数不再存在,则不能要求将函数放入 RAM 中。
非常感谢 ICACHE_RAM_ATTR,我在代码的最顶部使用了它...当然还有一些引脚不能用作中断,例如在我的情况下我使用的是板WEMOS D1 Mini Pro 和引脚 D0 (GPIO 16) 不工作,直到我换到下一个引脚 (GPIO 14) 并且它完美地工作....
ICACHE_RAM_ATTR 适用于较新的库,过时的 2.5 库也可以在没有这段代码的情况下使用。
非常感谢!
const uint8_t interruptPin = 14;
volatile byte interruptCounter = 0;
int numberOfInterrupts = 0;
void ICACHE_RAM_ATTR handleInterrupt();
void setup() {
Serial.begin(9600);
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, CHANGE);
}
void handleInterrupt() {
interruptCounter++;
}
void loop() {
if(interruptCounter>0){
interruptCounter--;
numberOfInterrupts++;
Serial.print("An interrupt has occurred. Total: ");
Serial.println(numberOfInterrupts);
}
}