The ARM Cortex M0 is one of the most popular ARM processor cores used in microcontrollers and other embedded systems. It is an extremely low power and low cost 32-bit processor optimized for simple embedded applications. One common task in embedded programming is creating delays or timing loops. On the Cortex M0, this can be accomplished using the SysTick timer and the __WFI() instruction.
Purpose of Delays
Delays are used extensively in embedded systems programming. Some common examples include:
- Creating a delay in between program operations or state changes
- Generating timed pulses for PWM signals or communication protocols
- Implementing a simple real-time schedule or RTOS
- Debouncing inputs like buttons and switches
- Timing various events and operations
The Cortex M0 chip includes a number of peripherals and timers that can be used to create delays and handle timing requirements. However, the simplest approach is often best – using the SysTick timer and wait states.
The SysTick Timer
The SysTick timer is built into all Cortex M0 and Cortex M0+ processors. It is a simple 24-bit decrementing counter that runs off the system clock. The SysTick can be configured to trigger an interrupt when it reaches zero. It auto-reloads the start value after reaching zero and continues running. The start value is configurable up to 24 bits (16 million counts).
In most cases the SysTick is configured to interrupt at a known rate that is used for the RTOS or other system timing. For delays, we simply need to read the current SysTick value, compare to the desired delay, and wait for it to count down. This turns the SysTick into a simple microsecond counter that we can use for creating delays and timing events in the program.
Wait For Interrupt Instruction
The Cortex M0 includes a special WFI instruction that allows the processor to enter a low power state until the next interrupt occurs. This can be used in conjunction with the SysTick to create efficient and accurate delays.
The __WFI() intrinsic generates the WFI instruction inline. By calling this while waiting for the SysTick to expire, we put the processor to sleep. This reduces power consumption compared to simply polling the SysTick value in a loop.
Delay Function Implementation
Here is an example implementation of a delay microsecond function using the Cortex M0 SysTick and WFI instruction: void Delay_Microsecond(uint32_t micros) { uint32_t start = SysTick->VAL; while (micros > 0) { if ((SysTick->VAL – start) >= micros) { break; } __WFI(); // Enter sleep mode } }
This function accepts the number of microseconds to delay as a parameter. It starts by reading the current SysTick counter value and saving it. It then enters the delay loop, checking the current SysTick value against the desired delay time. If the elapsed time is greater than or equal to the delay time, it will exit the loop.
Inside the loop, the __WFI() instruction puts the processor into a low power state. This reduces power consumption significantly compared to standard busy-wait polling. When the SysTick interrupt triggers, it will wake the processor to execute the next loop iteration.
Calibrating Delay Accuracy
The accuracy of the delays depends on the SysTick reload value and system clock speed. For example, with a 48 MHz clock and the default SysTick reload value of 48,000, each SysTick count is 1 microsecond.
For different clock speeds, the reload value must be adjusted to maintain timing accuracy. A higher reload value also improves resolution for short delays. Delay accuracy can be verified by using an oscilloscope or logic analyzer to measure output waveforms.
Calibrating the SysTick reload value should be done during system initialization by configuring the register directly or using a chip-specific CMSIS function. Most vendors also provide macros to convert milliseconds into the required SysTick value for a given clock speed.
Implementing Longer Delays
The SysTick counter and variable in the previous delay function are 32-bit, allowing delays up to a few seconds depending on clock speed. For longer delays, a common approach is to call the microsecond delay function in a loop. void Delay_Millisecond(uint32_t ms) { while(ms–) { Delay_Microsecond(1000); } }
This simple function calls the microsecond delay in a loop to implement millisecond delays. The same approach can be extended to create delays up to many minutes or hours if required.
Other Considerations
When using the SysTick and WFI for delays, there are some limitations and factors to keep in mind:
- Interrupts disabled during WFI will prevent delay exit. Floating point operations also prevent WFI from completing.
- Accuracy is dependent on constant clock speed. Changes to the clock will alter delay times.
- Higher priority interrupts can disrupt timing and cause longer delays than expected.
- Nested calls to delay functions can cause compounding errors in delay length.
- Delay functions should never be called from an ISR, only thread mode.
For embedded applications requiring very precise delays or timing, a dedicated timer/counter peripheral may be preferable to SysTick. However, for most common tasks and non-critical timing, the SysTick delay approach provides a simple and sufficient solution on Cortex M0.
Example Projects
Here are some example projects demonstrating Cortex M0 SysTick delays:
- Blink LED with 1 Second Interval – Blinks an LED on and off using SysTick delays
- Generate 38kHz Infrared PWM – Uses SysTick delays to output a 38kHz modulated IR PWM signal
- Simple Microsecond Timer – Implements a basic microsecond timer with start/pause/reset functions
- Debounce an Input Button – Debounces a button press using SysTick delays
These provide simple, real-world examples of using the Cortex M0 SysTick and WFI to generate delays and handle timing tasks in embedded projects.
Conclusion
The Cortex M0 SysTick timer and WFI instruction provide an easy way to generate basic microsecond delays and timing functions directly on the processor. While not suitable for very high precision timing, SysTick delays work well for common tasks like state timing, debouncing, PWM signals, and non-critical scheduling.
With a few simple functions, delays from microseconds to hours can be implemented. Accuracy requires proper configuration based on clock speed. Limitations include interrupt disruption and decreased precision over longer delays. Overall, SysTick delays give a straightforward delay option on Cortex M0 without requiring additional hardware timers.