Ads

Get STM32 tutorial using HAL at $10 for a limited time!

Sunday, January 10, 2016

ARM Cortex-M3 (STM32F103) Tutorial - System Timer - Delay Function

In this tutorial, I will share how to use system timer (SysTick) for making delay functions. SysTick is a basic countdown timer. SysTick can be used by an operating system to ease porting from another platform. SysTick can be polled by software or can be configured to generate an interrupt. To use SysTick we can reload a value to the reload value register. SysTick reload value register supports value between 0 - 0x00FFFFFF (24-bit).

SysTick can be configured through the 4 registers:


To configure SysTick easily, we can use this function (you can see in core_cm3.h):
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks);
This function will initialize the SysTick timer and its interrupt, then start the SysTick. The counter is in free running mode to generate periodic interrupts. The input parameter of this function is the number of ticks between two interrupts.

In this tutorial, I will make 2 delay function (us delay and ms delay). First, to make 1us delay, I cofigure the SysTick interrupt to triggered every 1us. By using SysTick_Config() function you can initialize SysTick to trigger the interrupt every 1us.

There is a variable called usTicks that holds value of ticks in us. Every we call DelayUs() function, we reload this variable with the value of delay in us and then we poll this variable until reach 0. This variable will be decrement by 1 every 1us by SysTick_Handler() function.

To make DelayMs() function, we can reload the value of ms, then decrement it by 1 every 1000us using DelayUs() function.

This is the code for the delay library:
#ifndef __DELAY_H
#define __DELAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f10x.h"

void DelayInit(void);
void DelayUs(uint32_t us);
void DelayMs(uint32_t ms);

#ifdef __cplusplus
}
#endif

#endif
#include "delay.h"

// For store tick counts in us
static __IO uint32_t usTicks;

// SysTick_Handler function will be called every 1 us
void SysTick_Handler()
{
    if (usTicks != 0)
    {
        usTicks--;
    }
}

void DelayInit()
{
    // Update SystemCoreClock value
    SystemCoreClockUpdate();
    // Configure the SysTick timer to overflow every 1 us
    SysTick_Config(SystemCoreClock / 1000000);
}

void DelayUs(uint32_t us)
{
    // Reload us value
    usTicks = us;
    // Wait until usTick reach zero
    while (usTicks);
}

void DelayMs(uint32_t ms)
{
    // Wait until ms reach zero
    while (ms--)
    {
        // Delay 1ms
        DelayUs(1000);
    }
}
To use that delay functions don't forget to call DelayInit() function first. You can see my complete code with example in here.

For STM32 tutorial with HAL library, you can click here.

6 comments :

  1. I want to see your complete code. But the link is break now. Would you relink your complete code?

    ReplyDelete
    Replies
    1. The link for complete code has been fixed. Thank you.

      Delete
    2. Thank you, Yohanes Erwin.

      Delete
  2. Hi Yohanes,
    The idea that the processor gets interrupted 1.000.000 times per second bothered me. The clock on my STM32F103 is 72MHz so there are only 72 cycles available between interrupts. Some are used by the interrupt handler leaving even less for the rest of the program. I did some very unscientific research and concluded that 25% of processor time is spent on the systick interrupt only, when you run it every 1 uS.
    So after some googling I found out about yet another timer, called DWT. This timer counts at the processor clock speed (72Mhz in my case) and is often used in a microsecond delay.
    I adapted your code (only the delay.c) which I will post below this. If that becomes unreadable I will put it somewhere else. :-)


    /**
    ******************************************************************************
    * @file delay.c
    * @author Yohanes Erwin Setiawan
    * @date 10 January 2016
    ******************************************************************************
    */

    #include "delay.h"

    // For store tick counts in ms
    static __IO uint32_t msTicks;
    static uint32_t Clk_Cyc_Per_uS;

    void DelayInit()
    {
    // Update SystemCoreClock value
    SystemCoreClockUpdate();

    // Configure the SysTick timer to overflow every 1 ms
    SysTick_Config(SystemCoreClock / 1000);

    // DWT_Init
    Clk_Cyc_Per_uS = SystemCoreClock/1000000;

    if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk))
    {
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    }
    }


    // SysTick_Handler function will be called every 1 ms
    void SysTick_Handler()
    {
    if (msTicks > 0) msTicks--;
    }


    void DelayMs(uint32_t ms)
    {
    // Reload ms value
    msTicks = ms;
    // Wait until usTick reach zero
    while (msTicks);
    }

    void DelayUs(uint32_t us)
    {
    int32_t tp = DWT->CYCCNT + us * Clk_Cyc_Per_uS;
    while (((int32_t)DWT->CYCCNT - tp) < 0);
    }




    /********************************* END OF FILE ********************************/
    /******************************************************************************/

    ReplyDelete
    Replies
    1. Hi Wilko,
      That's a good idea. Thank you for sharing your work.

      Delete
  3. I added .h and .c file in user folder but facing below issue. Please suggest.

    *** Using Compiler 'V5.06 update 4 (build 422)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
    Build target 'Target 1'
    compiling delay.c...
    User\delay.h(18): warning: #1-D: last line of file ends without a newline
    #endif
    User\delay.c(39): warning: #1-D: last line of file ends without a newline
    }
    User\delay.c: 2 warnings, 0 errors
    compiling main.c...
    User\delay.h(18): warning: #1-D: last line of file ends without a newline
    #endif
    User\delay.c(39): warning: #1-D: last line of file ends without a newline
    }
    User\main.c: 2 warnings, 0 errors
    linking...
    .\Demo.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by delay.o and main.o).
    .\Demo.axf: Error: L6200E: Symbol DelayInit multiply defined (by delay.o and main.o).
    .\Demo.axf: Error: L6200E: Symbol DelayMs multiply defined (by delay.o and main.o).
    .\Demo.axf: Error: L6200E: Symbol DelayUs multiply defined (by delay.o and main.o).
    Not enough information to list image symbols.
    Not enough information to list the image map.
    Finished: 2 information, 0 warning and 4 error messages.
    ".\Demo.axf" - 4 Error(s), 4 Warning(s).
    Target not created.
    Build Time Elapsed: 00:00:01

    ReplyDelete