我有在ISR(中断模式)中执行深度睡眠和i2c通信的问题。
我正在使用这个库在Arduino IDE中编码:
https://github.com/espressif/arduino-esp32
https://techtutorialsx.com/2017/09/30/esp32-arduino-external-interrupts/
当我在void loop()函数中运行它时,它对i2c(例如打开LED)工作正常,但当我将它移植到中断时它不起作用。
与深度睡眠相同,我无法在中断模式下执行它。我绕过它的方式是我在中断模式下设置一个标志,以显示我想要深度睡眠,然后在void loop()函数中执行它。
有没有人有任何解决方案如何使这项工作? (代码仅适用于i2c和esp32)
#include <Wire.h>
#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
#define Serial SerialUSB
#endif
// Interrupt Setup - TIMER
hw_timer_t * timer = NULL; //configure the timer, need pointer to a variable type of hw_timer_t
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // used to sync main loop and ISR
RTC_DATA_ATTR bool should_sleep = false;
// Setting ADC Properties - BATTERY
int voltage_amplifier = 0;
int battery_percentage = 0;
// Set i2c Address - I/O EXPANDER
const int address = 0x20;
uint16_t led_status = word(B11111111,B11111111);
// INTERRUPT MODE - INSERT INBETWEEN portENTER and portEXIT
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
// led_battery(); led doesn't update if used here
portEXIT_CRITICAL_ISR(&timerMux);
}
void led_battery(){
voltage_amplifier = analogRead(34);
Serial.println(voltage_amplifier);
int bit_max = 4096;
int battery_percentage = voltage_amplifier*100/bit_max;
// If battery is below 20%
if (battery_percentage <= 20){
led_status &= word(B00111111,B11111111); // clearing the bits that we want to change whilst preserving the other unchanged bits
led_status |= ~word(B11000000,B00000000); // setting up the bits that we want to change
pf575_write(led_status);
}
else if (battery_percentage <= 40){
led_status &= word(B00011111,B11111111); // clearing the bits that we want to change whilst preserving the other unchanged bits
led_status |= ~word(B11100000,B00000000); // setting up the bits that we want to change
pf575_write(led_status);
}
else if (battery_percentage <= 60){
led_status &= word(B00001111,B11111111); // clearing the bits that we want to change whilst preserving the other unchanged bits
led_status |= ~word(B11110000,B00000000); // setting up the bits that we want to change
pf575_write(led_status);
}
else if (battery_percentage <= 80){
led_status &= word(B00000111,B11111111); // clearing the bits that we want to change whilst preserving the other unchanged bits
led_status |= ~word(B11111000,B00000000); // setting up the bits that we want to change
pf575_write(led_status);
}
else if (battery_percentage <= 100){
led_status &= word(B00000011,B11111111); // clearing the bits that we want to change whilst preserving the other unchanged bits
led_status |= ~word(B11111100,B00000000); // setting up the bits that we want to change
pf575_write(led_status);
}
}
void ioexpander_setup(){
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\n Blinker Ready");
Wire.begin();
}
void pf575_write(uint16_t data) {
Wire.beginTransmission(address);
Wire.write(lowByte(data));
Wire.write(highByte(data));
Wire.endTransmission();
}
void timer_setup(){
// Base Clock Frequency = 80MHz ; Timer Frequency = 1MHz | Clock Cycle = 1us [in this case]
timer = timerBegin(0,80,true); // return a pointer to a structure of type hw_timer_t
// Timer binded to a handling function
timerAttachInterrupt(timer, &onTimer, true); // Parameter : (timer_initialization, address_interrupt,flag_to_activate - true(edge)/false(level))
// Specify the counter value in which the timer interrupt will be generated (set every 10 ms)
timerAlarmWrite(timer, 10000, true); // Parameter : (timer_initialization, when_to_interrupt (us), flag_to_reload)
// Enable the timer
timerAlarmEnable(timer);
}
void setup() {
Serial.begin(115200);
// IO Expander
ioexpander_setup();
// Timer
timer_setup();
}
void loop() {
led_battery(); //led update if used here
}
当你从中断处理程序调用led_battery()
时,你在那里做了太多的工作。
中断可以中断任何没有中断锁定的内容。
假设您的代码使用Serial输出内容并发生定时器中断。现在你的代码在Serial内的某个地方运行代码,你再次调用Serial ...而软件和硬件可能处于不一致的状态。
从中断处理程序执行的每个子例程和硬件访问都是这种情况。防止这种情况的唯一方法是在代码可能访问硬件或可能修改数据结构时禁用中断。
不幸的是,禁用中断很容易出错 - 如果你忘了这么做,你就会发生神秘的崩溃。如果您忘记重新启用它们,那么您将遇到大麻烦 - 您的网络,计时器和Serial将全部停止工作。它还会给代码增加很多开销。它会降低整体系统性能 - 它会延迟或导致您错过网络和计时器事件。您可以从串行端口删除字符。而且您可以确定Arduino Core中的所有代码都不会为您执行此操作。
所以,长话短说,锁定中断以便你可以在中断处理程序中做很多事情是不切实际的。
您还希望最小化在中断处理程序中花费的时间,因为这会抢占网络堆栈,计时器,串行和其他硬件处理,并可能阻止其他
你在原帖中指出了我们处理这个问题的方法:在中断处理程序中设置一个标志(确保它是volatile
)并在任务中处理它。除非你真的,真的知道你在做什么,以及你的系统中所有软件是如何工作的,否则这是解决这个问题的唯一实用方法。如果您尝试完成大量工作并从中断处理程序调用您正在调用的内容,则程序将出现故障并崩溃。