Tick Tick – ARM Cortex-M SysTick timer

Introduction

In this post, let me demonstrate the SysTick timer provided in ARM Cortex-M core. I will show how to use the SysTick timer for a simple use case – yes you guessed it right – blinking LED. I will use STM32F4 Discovery board and the ARM assembler along with Keil uVision IDE.

In an earlier post, we had explored assembly code to blink LEDs. The purpose of that post was to get introduced to ARM assembly. The main loop in the code from that post was an inefficient way of using the processor. Bulk of the processor’s time is wasted in waiting for the 500msec interval to get over. Instructions which actually switch on/off LEDs barely take around a few hundreds of nanoseconds. So there is a minuscule amount of time the processor is doing something useful. A better idea is to use hardware timers and push the 500msec waiting as a background task so the processor can do something useful and be more efficient.

The SysTick timer

Scanning through the datasheet of STM32F407, we can see that there are a whopping 17 timers available including the watchdog, RTC etc. We can use one of these basic timers on the as well. However, let’s stick to using SysTick timer for now. The one thing distinguishing about SysTick timer is that it is a feature of the ARM Cortex-M core and not the microcontroller that implements the core. The SysTick timer is integrated as a part of the NVIC (Nested Vector Interrupt Controller) on all of the ARM Cortex-M devices. This timer is intended to be used as a periodic interrupt needed in operating systems. This enables the OS to invoke the kernel regularly and carry out task and context switching. The biggest advantage of this is that your operating system code becomes portable across all ARM Cortex-M devices. If an OS is not used then this timer can be used as a simple timer peripheral to generate delay or periodic interrupts for certain tasks. We will use this for a periodic interrupt generation and use the interrupt to toggle LEDs.

Let’s start

With this background, let’s delve into the details. You will need to have a STM32F4 Discovery board to run this code.  I presume that you are familiar with Keil MDK-ARM and have MDK-Lite or a higher edition of the tools installed on your PC. You may also find the following manuals and documents handy to refer throughout this post. All these documents are also available from within the Keil window.

I have provided entire source code along with Keil project files on GitHub. All you need to do is clone the GitHub repo, open the project in Keil, build and download the code to STM32F4 Discovery board. The README in GitHub repo details the steps involved. I will provide the code explanation here.

Hardware

The STM32F4 discovery board provides 4 user controllable LEDs which are connected to PD12 to PD15 (GPIO port D pins 12, 13, 14 and 15). The anodes of LEDs are directly connected to port pins through a resistor with no external driving circuitry. We need to switch these pins high / low to switch on / off the LEDs respectively. Accordingly we need to configure these port pins in output mode. If you would like to know more details about the hardware, go through the STM32F4 Discovery board user manual.

Firmware

The project uses startup code from the STM32F4 software pack provided by ST Micro. The application logic is split into two files – main.s and isr.s.

Keil Project Window

Keil Project Window

The file main.s initializes various registers related to GPIO-D port pins and configures the SysTick timer to generate an exception every 500msec. It then sits up idle in the main loop. The exception handler code in isr.s toggles the LEDs thus effectively blinking them at 500msec intervals.  All of the register definitions are contained in a single file reg_stm32f407xx.inc which is then included in both the isr.s and main.s with assembly directive GET . Let’s now get into more details of the code.

Application Initialization

The function SystemInit called after startup takes care of configuring various registers required for our project. The tasks carried out by this function are:

  1. Enable clock supply for peripheral GPIO port D
  2. Configure GPIO port D pins 12 to 15 as output in push-pull mode
  3. Configure SysTick timer for 500msec interrupt generation and enable the SysTick exception.

I will skip some details of the configuration in this post such as “How to find address of a register” or the details of GPIO-D setup. These have been covered in the earlier blinking LEDs blog post. So in this post let’s avoid duplicating the content and focus on SysTick timer.

Systick timer configuration

The SysTick timer is a simple 24-bit down counter and runs on processor clock frequency or an on-chip reference clock signal. When the count reaches zero, it generates an exception (if enabled) and also reloads count with the reload value. Let’s go through the SysTick timer configuration registers and code to configure them.

SysTick Reload Value Register

Reload value for SysTick timer is stored in the 24-bit wide register SYSTICK_RVR. When the timer reaches zero, this value gets loaded into the timer for next cycle. The reload value can range from 0x01 up to 0xFFFFFF. A starting value of 0x00 is possible but the timer will then roll over from 0x00 to oxFFFFFF and then count down to 0x00 to cause the interrupt and reload the value again. Note that the timer reloads the value and triggers an exception only when transitioning from 1 to 0 so a reload value of 0x00 does not trigger the exception immediately. Here are the details of this register.

STM32F407 SysTick Reload Value Register

STM32F407 SysTick Reload Value Register

For our use case, we want the SysTick timer to trigger an exception every 500 msec so we can blink the LED at 500msec interval. We are going to use the internal processor clock which is running at 16MHz by default. At this speed, one clock period is 62.5nsec.  To get a timer interval of 500msec, we therefore need a reload value of 500ms/62.5ns = 8000000 = 0x7A1200. As per ARM Cortex-M4 device user guide, to get a delay of n cycles, the timer should be programmed with a reload value of n-1. So We need a reload value of 0x7A11FF.  This is well within the maximum possible reload value (0xFFFFFF). Here is the code to configure the reload value.

The code follows a read-modify-write cycle. The pseudo instruction LDR  loads address of register SYSTICK_RVR in R1 . The value that we want to write in SYSTICK_RVR is copied to R2. Finally, the code simply moves the contents of R2 (reload value) to the address contained in R1 (SYSTICK_RVR) with the instruction STR R2, [R1] .

SysTick Control and Status Register

The control and status register SYSTICK_CSR provides 3 bits to control the SysTick timer operation. The diagram below explains these bits.

STM32F407 SysTick Control and Status Register

STM32F407 SysTick Control and Status Register

 

Bit 2 CLKSOURCE lets us select the clock source for the timer. The clock to this timer can be supplied from an internal clock (which defaults to 16Mhz on STM32F407) or a reference clock. The reference clock implementation is chip dependent. In our code we are going to use the internal clock so we need to set this bit to 1.

Next, bit 1 TICKINT enables or disables the exception for SysTick timer. When the exception is enabled, the processor triggers an exception (exception #15) when the timer counts down to 0 from 1. This leads the processor to execute the SysTick exception handler code.

Note that the terminology used in ARM documentation uses word ‘exception’ for system triggered interrupts such as SysTick or memory manage fault etc. For other interrupts generated by on-chip peripherals (such as GPIO or I2C etc.) the terminology used is ‘interrupt’. 

Finally bit 0 when set to 1, enables the SysTick timer.

So we need to set all the lower 3 bits of this register to enable the timer, make it run with an internal clock and also enable the SysTick exception. Here is the part of code which does this.

Using read-modify-write cycle, the code loads address of register SYSTICK_CSR into R1 and then reads the current value of the register in R0. It then sets the lower 3 bits of R0 and writes the new value back to SYSTICK_CSR register.

Once the SysTick timer is enabled it loads the value from SYSTICK_RVR register and starts counting down from that value until it reaches zero.

Recommended Sequence for SysTick timer configuration

Although not followed strictly in the code accompanying this post since I want to keep the code as simple as possible, the recommended sequence to configure SysTick timer is as below.

  1. Disable SysTick timer by clearing bit zero of SysTick Control and Status register. This is optional but a good practice for reusable code as the timer may have been enabled earlier.
  2. Write the reload value to SysTick Reload Value Register.
  3. Clear the SysTick Current Value Register. This register holds the current value of SysTick timer. Writing any value to this register clears the value to 0.
  4. Write to SysTick Control and Status register to configure the clock, enable the timer and optionally enable SysTick exception if required.
Other Initialization

One last piece of code in the SystemInit function is to initialize a flag variable which will help us toggle the LED . When the exception handler executes code to switch the LED on/off, there needs to be a way to store the current state of LED and then toggle the LED accordingly. Register R7 is used for this purpose which is initialized to 0. The exception handler then toggles bit 0 of R7  every time the LED is switched on/off.

Main loop

As you can see the main loop literally does nothing. It is only an idle loop with a branch instruction B . at the same address which means it keeps executing the branch instruction again and again.

You may wonder if this is really a better use of processor? The answer is NO. But having a background timer running makes the processor free to do some additional work in the main loop. This is quite useful when the application grows in complexity. Alternatively we can also make the processor go to sleep mode and reduce average power consumption. The processor can wake up at the SysTick timer exception, toggle the LED and then go to sleep again.

SysTick Exception Handler

The startup file provides a default implementation of the SysTick exception handler.

Notice that the SysTick_Handler is exported as [WEAK]  in the startup file. This allows us to redefine the exception handler in our own way without modifying the startup file. This way you can keep the same startup file for all your projects but have different implementations of exception handling in different projects. In our project, the exception handler for systick is redefined in the file isr.s.

To switch on/off a GPIO pin, we make use of the Bit Set Reset Register (BSRR) of GPIO port D is used. Let’s have a look at the register description.

STM32F407 GPIO-D bit set reset register

STM32F407 GPIO-D bit set reset register

This register is 32 bits wide where the upper 16 bits correspond to switching a GPIO pin low while the lower 32 bits correspond to switching on the GPIO pins. This means that writing a ‘1’ to one of the upper 16 bits in this register will switch the corresponding GPIO pin low. Simiarly writing a ‘1’ to one of the lower 16 bits in this register will switch on the corresponding GPIO pin high. Writing ‘0’ to any of the bits has no effect.

In our case the LEDs are connected to GPIO-D pins 12 to 15. To switch them ON, we write ‘1’ to bits 12 to 15 in the lower part of this register. To switch the LEDs off, we write ‘1’ to bits 12 to 15 in upper part of the register or in other words, we write ‘1’ to bits 28 to 31. These values therefore become 0x0000F000 for switching ON and 0xF0000000 to switch the LEDs off.

Using the BSRR register avoids the read-modify-write cycle and toggles the GPIO pins in only one instruction. This is called atomic operation. The advantage of this atomic operation is that no interrupt can occur during switching the pin. Though this won’t be much of a problem here for simple LED switching, this atomic behaviour is very useful in large complex applications.

Other than switching the LED, the exception handler toggles R7 flag with the instruction EOR R7, R7, #0x01 . The EOR  instruction stands for Ex-OR. When you Ex-OR a bit with ‘1’, effectively the bit value gets inverted. The LED is switched off if R7 is 0x00 and turned on otherwise.

Notice that the exception handlers returns with a simple  BX LR instruction. There is no special instruction when returning from interrupts in ARM Cortex-M architecture. This means that an exception handler doesn’t really need any special treatment and it can be programmed as a normal subroutine. This is useful when programming in high level languages such as C / C++. How the processor knows to differentiate between exception handler and normal subroutines deserves a separate post so let’s not get into the details right now.

Demonstration

Now that you have understood the code, let’s see the execution. The following video shows the code in action.

Conclusion

The SysTick timer is very useful and a provides a simple way to implement time scheduled tasks. As I mentioned earlier, it is widely used in RTOS implementations and helps to make the code portable. This post showed you a quick dirty way to use the timer and more importantly introduced you to the exception handling mechanism of ARM Cortex-M. In the later posts, we will explore more about the other exceptions and get a better idea of NVIC (Nested Vector Interrupt Controller) on ARM Cortex-M devices.

Do let me know your feedback and suggestions about this post. Have fun coding!

About Gopal

Gopal has rich experience of 20+ years on on Embedded systems, digital/analog design, Microcontrollers and programming. Long back he learned programming the classic 8051 Microcontroller in assembly language and now he is working on cutting edge technologies around Cloud, Voice and IoT. Outside the professional world, Gopal loves photography, listening to music and spending time with his family.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.