Fix display freeze: move I2C work out of interrupt context #8

Open
claude-fable wants to merge 2 commits from claude-fable/metronom:fix/i2c-delay-in-isr-freeze into master
3 changed files with 30 additions and 12 deletions

View File

@@ -18,11 +18,11 @@ void buttonISRstate() {
else if (countdown == 0) { else if (countdown == 0) {
// TODO: restart metronome timer to align countdown // TODO: restart metronome timer to align countdown
countdown = COUNTDOWN; countdown = COUNTDOWN;
updateTube(); displayNeedsUpdate = true;
} else { } else {
countdown = -1; countdown = -1;
digitalWrite(BUTTON_LED_PIN, LOW); digitalWrite(BUTTON_LED_PIN, LOW);
updateTube(); displayNeedsUpdate = true;
// TODO: disable metronome timer // TODO: disable metronome timer
} }
} }

View File

@@ -37,18 +37,19 @@ volatile float sensorValue[SENSORS] = {};
volatile uint displaySensor = TEMPERATURE; volatile uint displaySensor = TEMPERATURE;
volatile bool displayNeedsUpdate = false; volatile bool displayNeedsUpdate = false;
volatile bool tubeRotated = false; volatile bool tubeRotated = false;
volatile bool sensorsDue = false;
int tubeTimerID; int tubeTimerID;
/* I2C (BME280 reads, tube writes) must never run in interrupt context:
both libraries call delay(), which inside an ISR can spin forever
because SysTick (millis/micros) is blocked. ISRs only raise flags;
loop() does the actual bus work. */
void TasksHandler(void) { void TasksHandler(void) {
TasksISRs.run(); TasksISRs.run();
if (displayNeedsUpdate || tubeRotated)
updateTube();
if (tubeRotated) {
TasksISRs.changeInterval(tubeTimerID, SENSOR_TIMESHARE[displaySensor]);
tubeRotated = false;
} }
void scheduleSensors() {
sensorsDue = true;
} }
void setup() { void setup() {
@@ -62,7 +63,7 @@ void setup() {
TaskTimer.attachInterruptInterval_MS(TASK_HANDLER_INTERVAL, TasksHandler); TaskTimer.attachInterruptInterval_MS(TASK_HANDLER_INTERVAL, TasksHandler);
TasksISRs.setInterval(METRONOME_INTERVAL, runMetronome); TasksISRs.setInterval(METRONOME_INTERVAL, runMetronome);
TasksISRs.setInterval(SENSOR_INTERVAL, readSensors); TasksISRs.setInterval(SENSOR_INTERVAL, scheduleSensors);
tubeTimerID = TasksISRs.setInterval(SENSOR_TIMESHARE[displaySensor], rotateTube); tubeTimerID = TasksISRs.setInterval(SENSOR_TIMESHARE[displaySensor], rotateTube);
} }
@@ -79,4 +80,17 @@ void runMetronome() {
} }
} }
void loop() {} void loop() {
if (sensorsDue) {
sensorsDue = false;
readSensors();
}
if (displayNeedsUpdate || tubeRotated)
updateTube();
if (tubeRotated) {
TasksISRs.changeInterval(tubeTimerID, SENSOR_TIMESHARE[displaySensor]);
tubeRotated = false;
}
}

View File

@@ -6,7 +6,11 @@ char tubeText[5] = "";
void initTube() { void initTube() {
// Wire initialized by BME280 sensor // Wire initialized by BME280 sensor
tube.setTubeType(TYPE_4, TYPE_4_DEFAULT_I2C_ADDR); tube.setTubeType(TYPE_4, TYPE_4_DEFAULT_I2C_ADDR);
tube.setBrightness(15); // Brightness drives LED current. The freeze correlates with '8' on the last
// multiplexed digit (most segments lit -> peak current) at full brightness 15,
// which points at a supply/I2C-bus glitch. Lowered to probe that hypothesis;
// raise back toward 15 if the display is too dim and freezes don't return.
tube.setBrightness(4);
tube.setBlinkRate(BLINK_OFF); tube.setBlinkRate(BLINK_OFF);
} }