SysTick Timer : Blinking an LED without a delay

In previous two guides (Understanding GPIOs and Understanding the Systick Timer) we explained how to use GPIOs in both input mode and output mode and how to use SysTick timer to generate delay.

Delay is a function thats block the cpu for certain of time and can’t move beyond this point until the delay time is elapsed. This can be cause unpredictable system behavior in certain situations where the main loop must be as fast as possible and you want to toggle the led every second to inform the use that the main loop is working without any issue. Hence, we will be using the Systick Timer in interrupt mode to generate an interrupt every 1 milliseconds and increment an counter.

We will cover in this guid the following:

  • Configuring the GPIO PA5 as output.
  • Configure SysTick in interrupt mode.
  • Required parts.
  • Code.
  • Code explanation.

1. Configuring PA5 as output

Since STM32F411RE Nucelo has built-in LED which is connected to PA5 as mention in the reference manual of Nucelo-64. Hence we the Following steps:

  • Enable clock access to GPIOA
  • Set PA5 as Output

In order to enable clock access to GPIO Port A, we need to locate which bus is the GPIOA is connected to. This information can be found in the datasheet of the STM32F411RE.

STM32F411xC/xE block diagram

As the figure, the GPIO Port A is connected to AHB1 (Advanced High-performance Bus ) Bus. After that we need to enable the clock access to the port by heading to reference manual and check the clock enable of AHB1 register.

Then we need to configure pin PA5 as output. The pin can be set as output using MODER register which can be found in GPIO section.

We need to set MODER5 as output hence bit number 10.

Then we can use ODR (Output Data Register) to turn on/off the led

2. Configure SysTick Timer in Interrupt mode

In order to configure Systick Timer in interrupt mode, we need the following steps

  • Disable Systick Timer (SysTick->CTRL=0;).
  • load value 16000-1 for 1mS operation (SysTick->LOAD=16000-1;).
  • Select clock source to processor clock, enable interrupt and start counter (SysTick->CTRL=7;)

3. Required part

Here we only need STM32F411RE Nucleo-64 since it has built-in led to test the functionality.

4. Code

#include "stm32f4xx.h"                  // Device header
#include "stdint.h"
#define rate 1000
uint64_t previous; //variable to store the time
volatile uint64_t ms,rms; //ms current millis, rms return current ms when millis() function is called

uint64_t millis(void)
	{
	__disable_irq();
	rms=ms; //store current ms in rms
	__enable_irq();
	return rms;
	}
int main(void)
	{
	__disable_irq();
	
	SysTick->LOAD=16000-1;
	SysTick->VAL=0;
	SysTick->CTRL=7; //0b00000111;
	RCC->AHB1ENR |=RCC_AHB1ENR_GPIOAEN;
	GPIOA->MODER|= GPIO_MODER_MODER5_0; //PA5 as output
	__enable_irq();
	
	
	
	
while(1)
	{
	if(millis()-previous>rate) //if current time is elapsed, execute the if condition 
		{
		GPIOA->ODR^=GPIO_ODR_OD5;
		previous=millis();
		}
	
	}	
	
	
	}
	
	
void SysTick_Handler(void){
ms++;
}

Compile and upload the code to the Nucleo-64 board and you will see that the led is blinking every second.

5. Code Explanation

we starting by including the required header file

#include "stm32f4xx.h"                  // Device header
#include "stdint.h"

then declare some required variables and constants. The reason behind using volatile is to opt-out compiler optimization and this value can be changed during code execution .

#define rate 1000 //rate of toggling 
uint64_t previous; //variable to store the time
volatile uint64_t ms,rms; //ms current millis, rms return current ms when millis() function is called

Now the millis function to return the time in milliseconds

uint64_t millis(void)
	{
	__disable_irq(); //disable all interruot
	rms=ms; //store current ms in rms
	__enable_irq(); //enable all interrupt
	return rms; //return the value
	}

Main function

int main(void)
	{
	__disable_irq();
	SysTick->LOAD=16000-1;
	SysTick->VAL=0;
	SysTick->CTRL=7; //0b00000111;
	RCC->AHB1ENR |=RCC_AHB1ENR_GPIOAEN;//enable clock access to port a
	GPIOA->MODER|= GPIO_MODER_MODER5_0; //PA5 as output
	__enable_irq();
	
	
	
	
while(1)
	{
	if(millis()-previous>rate) //if current time is elapsed, execute the if condition 
		{
		GPIOA->ODR^=GPIO_ODR_OD5; //toggle the led
		previous=millis(); //store current time 
		}
	
	}	
	
	}
	

Lastly, SysTick interrupt handler

	
void SysTick_Handler(void){
ms++;
}

4 Comments

Add Comment

Your email address will not be published. Required fields are marked *