How to delay microseconds on ESP32 under a Free RTOS task

I have some code running as a FreeRTOS task on my ESP32. I would like to toggle an output pin in the order of microseconds so use the function delayMicroseconds. However, this crashes my ESP32 every time. Even a simple loop causes it to crash:

for ( int i = 0 ; i < 10000 ; i++ )
{
   delayMicroseconds( 1000 );
}

This code results in:

ELF file SHA256: 0000000000000000

Backtrace: 0x4008860c:0x3ffbf8f0 0x40088889:0x3ffbf910 0x401300bc:0x3ffbf930 0x40086f31:0x3ffbf950 0x40084241:0x3ffccd50 0x40084eaf:0x3ffccd70 0x400811d3:0x3ffccd90 0x40081213:0x3ffccdb0 0x400d0c7f:0x3ffccdd0 0x400d146d:0x3ffcce40 0x4008989a:0x3ffcce60

Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 271414342, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8

I would really appreciate any advice on how to reliably delay in microseconds under a task.

Andy

To what is this backtrace decoded when you activate debug build and the exception decoder per SW_CPU_RESET on esp32 (Boot Loop) - #2 by maxgerhardt?

Are you sure this won’t make the watchdog bite if you don’t feed it in between?

@maxgerhardt I am not exactly sure where I am supposed to put

build_type = debug
monitor_filters = esp32_exception_decoder

in platformio.ini but wherever I put those lines, the exception message is still the same as I originally posted :frowning:

I was not aware a watchdog was enabled and not sure how you go about kicking it if it is enabled.

Andy

What’s your current platformio.ini?

@maxgerhardt

My current platformio.ini is:

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[platformio]
default_envs = view

[base_config]
platform = espressif32@3.4
framework = arduino
monitor_speed = 115200
monitor_flags = 
	--eol=CRLF
	--echo
	--filter=esp32_exception_decoder
lib_deps =

build_flags =
  -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG

;
; This line is required when using LILYGO® T-Micro32 Plus 8MB flash 2MB Psram ESP32
; to make use of the extended FLASH. Make sure you copy lilygo_app.csv from the root
; of this project to here:
; C:\Users\mail\.platformio\packages\framework-arduinoespressif32@3.10006.210326\tools\partitions
;
;board_build.partitions = lilygo_app.csv

[env:view]
extends = base_config
; platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
; platform_packages =
;    framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#master
board = esp32doit-devkit-v1
lib_deps =
  ${base_config.lib_deps}

build_flags =
  ${base_config.build_flags}
 
  ; Reduce loop task stack size (only works on newer IDF Arduino core)
  ; -DARDUINO_LOOP_STACK_SIZE=2048

  ; Modify the default unusable pin mask to allow GPIO 7 (allowed to use on ESP32-PICO-V3-02)
  ; Unusable bits: 6, 8, 9, 10, 20
  ; (0ULL | _FL_BIT(6) | _FL_BIT(8) | _FL_BIT(9) | _FL_BIT(10) | _FL_BIT(20))
  -DFASTLED_UNUSABLE_PIN_MASK=0x100740LL
  
  ; 0~39 except from 24, 28~31 are valid
  ; (0xFFFFFFFFFFULL & ~(0ULL | _FL_BIT(24) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31)))
  -DSOC_GPIO_VALID_GPIO_MASK=0xFF0EFFFFFF
  ; GPIO >= 34 are input only
  ; (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | _FL_BIT(34) | _FL_BIT(35) | _FL_BIT(36) | _FL_BIT(37) | _FL_BIT(38) | _FL_BIT(39)))
  -DSOC_GPIO_VALID_OUTPUT_GPIO_MASK=0x30EFFFFFF

[env:handheld_tdisplay]
extends = base_config
board = esp32doit-devkit-v1
lib_deps =
  ${base_config.lib_deps}

build_flags =
  ${base_config.build_flags}

Thanks for your interest in my problem :+1:

Andy

This is ancient. Your problem persists on 5.1.1?

Instead of this, write

monitor_eol = CRLF
monitor_echo = yes
build_type = debug
monitor_filters = esp32_exception_decoder

(see docs)

@maxgerhardt Sorry, I am really new to platformio :frowning:
Not sure where I got the platformio.ini file from - I now have:

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[platformio]
default_envs = view

[base_config]
platform = espressif32@5.1.1
framework = arduino
monitor_speed = 115200
monitor_eol = CRLF
monitor_echo = yes
build_type = debug
monitor_filters = esp32_exception_decoder
lib_deps =

build_flags =
  -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG

;
; This line is required when using LILYGO® T-Micro32 Plus 8MB flash 2MB Psram ESP32
; to make use of the extended FLASH. Make sure you copy lilygo_app.csv from the root
; of this project to here:
; C:\Users\mail\.platformio\packages\framework-arduinoespressif32@3.10006.210326\tools\partitions
;
;board_build.partitions = lilygo_app.csv

[env:view]
extends = base_config
; platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
; platform_packages =
;    framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#master
board = esp32doit-devkit-v1
lib_deps =
  ${base_config.lib_deps}

build_flags =
  ${base_config.build_flags}
 
  ; Reduce loop task stack size (only works on newer IDF Arduino core)
  ; -DARDUINO_LOOP_STACK_SIZE=2048

  ; Modify the default unusable pin mask to allow GPIO 7 (allowed to use on ESP32-PICO-V3-02)
  ; Unusable bits: 6, 8, 9, 10, 20
  ; (0ULL | _FL_BIT(6) | _FL_BIT(8) | _FL_BIT(9) | _FL_BIT(10) | _FL_BIT(20))
  -DFASTLED_UNUSABLE_PIN_MASK=0x100740LL
  
  ; 0~39 except from 24, 28~31 are valid
  ; (0xFFFFFFFFFFULL & ~(0ULL | _FL_BIT(24) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31)))
  -DSOC_GPIO_VALID_GPIO_MASK=0xFF0EFFFFFF
  ; GPIO >= 34 are input only
  ; (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | _FL_BIT(34) | _FL_BIT(35) | _FL_BIT(36) | _FL_BIT(37) | _FL_BIT(38) | _FL_BIT(39)))
  -DSOC_GPIO_VALID_OUTPUT_GPIO_MASK=0x30EFFFFFF

[env:handheld_tdisplay]
extends = base_config
board = esp32doit-devkit-v1
lib_deps =
  ${base_config.lib_deps}

build_flags =
  ${base_config.build_flags}

The code still crashes but gives marginally more information:

abort() was called at PC 0x400e112d on core 0


Backtrace:0x40083625:0x3ffbeacc |<-CORRUPTED




ELF file SHA256: 0000000000000000

Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 271414342, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13132
load:0x40080400,len:3036
entry 0x400805e4
[0$$HH+QW«Y.\&¦KZX¶K,\],'¦LMW®Y](\]£¹equencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz

Output is the same when you comment out this line?

Your test firmware is minimal with only src/main.cpp as

#include <Arduino.h>

void setup() {}

void loop() {
  for ( int i = 0 ; i < 10000 ; i++ )
  {
     delayMicroseconds( 1000 );
  }
}

?

Is freeRTOS still a part of the problem?
I only say this because the freeRTOS ‘tick’ is normally of the order of milliseconds which makes sub-millisecond timing problematic unless you put the code into a critical section that will suspend the task switcher.
Susan

@aussiesusan Yes, I suspect that it is down to FreeRTOS which is configured for a 1ms tick. I am not sure if FreeRTOS is working in pre-emptive or co-operative mode but in either case I would not expect a crash just that my timing goes all wonky if the task is switched out.

Andy

It’s pre-emptive. Your tasks will get interrupt by a timer. Further, if you don’t resume tasks for a prolonged period of times, the WiFi task will crash. But for short delays it should work to use critical sections.

If you can, try and push these timing requirements onto the hardware, not software. See if you can use a timer perpiheral and a quick ISR.