Skip to content

Timer functions do not work at all in PlatformIO #5337

Closed
@maxgerhardt

Description

@maxgerhardt

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.

typedef struct {
union {
struct {
uint32_t reserved0: 10;
uint32_t alarm_en: 1; /*When set alarm is enabled*/

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions