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
There are several options available in FreeRTOS for exclusive access:
vTaskSuspendScheduler, taskDISABLEINTERRUPTS and taskENTERCRITICAL.
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?
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:
taskENTERCRITICAL on Cortex-M3/4/7 only masks interrupts up to configMAXSYSCALLINTERRUPTPRIORITY, 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 portDISABLEALLINTERRUPTS() asm volatile("cpsid i")
define portENABLEALLINTERRUPTS() 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,
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.