Description
Hardware:
Board: ESP32 Dev Module
Core Installation version: 1.0.6 as well as 2.0.0-alpha1 and current master 21947eb
IDE name: Platform.io
Flash Frequency: 40Mhz
PSRAM enabled: no
Upload Speed: 921600
Computer OS: Windows 10
Description:
I am attempting to replicate the tutorial Hardware Interrrupts here, specifically the code here. The sketch uses functions from esp32-hal-timer.c
to setup timer 0 with an alarm that will trigger the execution of an ISR in which a pin is toggled at a rate of 1Hz.
When uploading this sketch in PlatformIO, using all default settings (meaning also core version 1.0.6), this does not work. There is no blinking going on the defined LED pin at all.
The following sketch was used, which additionally prints some debug information on the Serial
. A LED is connected to IO2.
#include <Arduino.h>
// Settings
static const uint16_t timer_divider = 80;
static const uint64_t timer_max_count = 1000000;
// Pins (change this if your Arduino board does not have LED_BUILTIN defined)
static const int led_pin = 2;
// Globals
static hw_timer_t *timer = NULL;
//*****************************************************************************
// Interrupt Service Routines (ISRs)
// This function executes when timer reaches max (and resets)
void IRAM_ATTR onTimer() {
// Toggle LED
int pin_state = digitalRead(led_pin);
digitalWrite(led_pin, !pin_state);
}
//*****************************************************************************
// Main (runs as its own task with priority 1 on core 1)
void setup() {
Serial.begin(115200);
Serial.println("Firmware start");
// Configure LED pin
pinMode(led_pin, OUTPUT);
// Create and start timer (num, divider, countUp)
timer = timerBegin(0, timer_divider, true);
// Provide ISR to timer (timer, function, edge)
timerAttachInterrupt(timer, &onTimer, true);
// At what count should ISR trigger (timer, count, autoreload)
timerAlarmWrite(timer, timer_max_count, true);
// Allow ISR to trigger
timerAlarmEnable(timer);
}
void loop() {
Serial.println("Timer seconds: " + String(timerReadSeconds(timer)));
Serial.println("Timer alarm seconds: " + String(timerAlarmReadSeconds(timer)));
Serial.println("Timer is alarm enabled: " + String(timerAlarmEnabled(timer)));
Serial.println("Timer is started: " + String(timerStarted(timer)));
delay(250);
}
platformio.ini
[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600
The serial output clearly shows the problem: The alarm in the timer is not enabled.
Firmware start
Timer seconds: 0.00
Timer alarm seconds: 1.00
Timer is alarm enabled: 0
Timer is started: 1
Timer seconds: 0.00
Timer alarm seconds: 1.00
Timer is alarm enabled: 0
Timer is started: 1
Timer seconds: 0.25
Time keeps advancing as expected for the timer though.
I was able to fix this issue by going into the C:\Users\<user>\.platformio\packages\framework-arduinoespressif32\cores\esp32\esp32-hal-timer-c
file and adding volatile
declaration to the timer registers struct.
arduino-esp32/cores/esp32/esp32-hal-timer.c
Lines 50 to 54 in 21947eb
to
typedef volatile struct {
//...
Adding this single keyword immediately got the LED blinking.
So okay I think, the Arduino-ESP32 has a bug regarding compiler optimizations where a write to the alarm-enable register is not executed in order (or, at all) due to the developers forgetting that the registers need to be marked volatile
. That fixes it for the 1.0.6 core.
Note that the same fixing effect can be achieved by not declaring the struct volatile
but instead placing a call to function between timerAlarmWrite()
and timerAlarmEnable()
in setup()
, e.g. a simple delayMicroseconds(0);
. This strongly hints at me to the compiler optimizing (away) or reordering something in the sequence of writes to the timer registers.
However, I then also cross-checked with the latest master version of the core and compiler.
[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600
platform_packages =
toolchain-xtensa32 @ ~2.80400.0
framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#master
platformio/tool-esptoolpy @ ~1.30100
Pulling the latest compiler and Arduino core (without any source code changes to the core or the sketch) and re-uploading showed again that the blinky was not working (kinda expected).
However, the behavior on the Serial monitor shows a different story.
Firmware start
Timer seconds: 0.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 1.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 1.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 1.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 1.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 2.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 2.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 2.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 2.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 3.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 3.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 3.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 3.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 4.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 4.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 4.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 4.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 5.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 5.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 5.50
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 5.75
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 6.00
Timer alarm seconds: 6.42
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 6.25
Timer alarm seconds: 6.42
Timer is alarm enabled: 0
Timer is started: 1
Timer seconds: 0.08
Timer alarm seconds: 6.42
Timer is alarm enabled: 0
Timer is started: 1
Timer seconds: 0.33
Timer alarm seconds: 6.42
Timer is alarm enabled: 0
You can see that right off the bat, the alarm seconds
value returns is suddenly.. 6.42? Which makes no sense since it should be 1.0. The timer seconds keep counting up until hitting 6.42, and then the alarm goes from enabled to disabled.
Adding the volatile
keyword to the struct definition as shown above does change the output.
Timer seconds: 0.00
Timer alarm seconds: 1.00
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.25
Timer alarm seconds: 1.00
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.50
Timer alarm seconds: 1.00
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.75
Timer alarm seconds: 1.00
Timer is alarm enabled: 1
Timer is started: 1
Timer seconds: 0.00
Timer alarm seconds: 1.00
Timer is alarm enabled: 0
Timer is started: 1
Timer seconds: 0.25
Now the timer seconds to read correctly as 1.00
but after 1 elapsed second, the alarm is just disabled and never continues...
However from there, no matter where I add volatile
in the register struct definition, I can not get it to function correctly. There might be additional issues somewhere else.
In any case, I see this is a bug. For 1.0.6 it's a bug reproducable with PlatformIO and also fixable as I've shown, As for 2.0.0-alpha1 and the current master version, I see the same bug source (still no volatile
on timer regs) but that is not the only needed thing to keep it going.
Are you able to reproduce the issue in PlatformIO?
Magically per the linked video, Arduino IDE seems to work with some versions though? Can anyone test this with the latest master?