Suspending FreeRTOS for precise timing

simonhaines wrote on Thursday, September 01, 2016:

My Cortex-M4 project uses a bank of WS2812 LEDs that require precise delays
in the nanosecond range when modulating a data pin. The typical approach
when using this part is to suspend all interrupts, pull the pin high, issue
a certain number of NOP instructions depending on the value to display,
then pull the pin low and issue another set of NOP instructions, repeating
the process until all values are displayed and finally re-enabling
interrupts.

There are several options available in FreeRTOS for exclusive access:
vTaskSuspendScheduler, taskDISABLE_INTERRUPTS and taskENTER_CRITICAL.
Considering my requirement is to have exclusive use of the chip for timing
(not access to a shared resource), which is the preferred approach? If I
use critical sections, will I need to set the priority of all interrupts to
be at least BASEPRI so they are masked? Will the PendSV and SysTick
interrupts still run, possibly throwing the timing out of whack?

Thanks, Simon.

xz8987f wrote on Thursday, September 01, 2016:

Hi Simon,
first off, I do not recommend to drive WS2812 with such a bit-banging approach you describe: this is doable for a system where you do only this, as this requires a lot of CPU cycles. The typical way to drive WS2812 is with hardware like SPI or DMA (see https://mcuoneclipse.com/2014/11/10/neoshield-ws2812-rgb-led-shield-with-dma-and-nrf24l01/ and the multi-part tutorial here: https://mcuoneclipse.com/2015/08/01/tutorial-adafruit-ws2812b-neopixels-with-the-freescale-frdm-k64f-board-part-1-hardware/). I’m sucessfully using the above approaches with FreeRTOS.

If you inisist on your bit-banging approach, then you need to make sure that your bit banging is not interrupted at all during the data transfer. If doing this from a task, you need to turn off all interrupts. You need to check your port how it deals with the interrupts:
taskENTER_CRITICAL on Cortex-M3/4/7 only masks interrupts up to configMAX_SYSCALL_INTERRUPT_PRIORITY, so other interrupts with higher urgency still run.
dito for taskDISABLE_INTERRUPTS: it uses BASEPRI for interrupt masking.

What you need to use is to disable all interrupts which usually is provided in the FreeRTOS port like this:

#define portDISABLE_ALL_INTERRUPTS()   asm volatile("cpsid i")
#define portENABLE_ALL_INTERRUPTS()   asm volatile("cpsie i")

I recently wrote an article series about Cortex-M interrupt system and how it is used in FreeRTOS, so this might be helpful for you:



I hope this helps,
Erich

simonhaines wrote on Saturday, September 03, 2016:

Thanks Erich, yes I’m an avid reader of your blog and have been following your interrupts series. I did not know you had already covered the particular requirements of the WS2812, and I’ll give the FTM/DMA approach a shot.
Simon.