STM32F4: Timer

This shows how to make a simple interval timer that toggles an LED (the orange LED on the discovery board) everytime the counter overflows. It uses Timer 2 and busy waits to see if the overflow flag has been set. If set, then it toggles the LED. Thus the frequency of the LED is half the frequency of overflow.

The main loop is straightforward: the timer is first started, then the LEDs on the GPIO pins. It then enters an infinite loop, toggling PD13 whenever the flag is raised.

int main(void)
{
  INTTIM_Config();
  /* LED Configuration */
  LED_Config();
  while (1)
  {
    if (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
    {
      TIM_ClearFlag(TIM2, TIM_IT_Update);
      GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
    }
  }
}

Once you have had a go at reading the insanely long specification documents, it is quite easy to set up Timer2. The STM32 timers are incredibly flexible in terms of their prescaling – a lot of microcontrollers only give you a few options, whereas here you can divide by any integer (16bit?). You then set the period to dictate how often it rolls over – here we first divide down to 1MHz using the prescaler and then by using a period of 100, we get down to 10kHz. If you want to visibly see the LED flash, change this from 100 to 10000 or something similar. I was on the scope, so no issue with watching flashing lights.

void INTTIM_Config(void)
{
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 1 MHz down to 10 KHz (0.1 ms)
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // Down to 1 MHz (adjust per your clock)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}

Download the complete file here.

15 thoughts on “STM32F4: Timer

    1. The timer can run down to 10’s of nanoseconds (clocked at 168 MHz) in the STM32F405xx. So microsecond range will be no problem. And there are two 32 bit timers if you need wider timers than the standard 16 bit (which will overflow quite quickly when clocked at full tilt).

  1. Thanks for the reply. I believe I have a timer set up for microseconds, how would you suggest checking the length of a delay function? I was considering attempting hooking an oscilloscope up to one of the GPIO pins and sending a signal or setting up another time to count the length of the delay function call. Think either of these would provide accurate results?

    Using something like this for the second timer counter
    TIM_SetCounter(TIM1,0x0000);
    Delay(xx);
    Spent_time = TIM_GetCounter(TIM1);

    1. I think your first approach sounds the most reasonable – to use the GPIO pins to indicate when the timer overflows. Off the top of my head (untested!), if you set:
      TIM_TimeBaseStructure.TIM_Period = 84 – 1; // 1 MHz
      TIM_TimeBaseStructure.TIM_Prescaler = 0; // No prescaler (running at main clock speed)

      This should give you TIM2 ticking at 1MHz (microsecond).

      Rather than using GPIO, a more elegant way of testing would be to use the timer overflow to trigger a CCP output (PWM). See the post on PWM to get an idea of how to do this.

  2. You actually make it seem so easy with your presentation but I find this topic to be actually something which I think I would never understand. It seems too complex and extremely broad for me. I’m looking forward for your next post, I will try to get the hang of it!

  3. Hi ,

    Can you kindly explain how to use a 32 bit timer as there seem to be no setting enable the use 32 bit timer.

    Regards,
    sean

    1. Hi Sean,
      You are right, there is no “setting” to use the 32 bit timers, as you cannot control the resolution of the timer. Timers 2 and 5 are 32 bit timers. Timers 3 and 4 are 16 bit timers. With this example code, we are using Timer 2 and it is thus possible to set a full 32 bit period. For example, if you set the prescaler to 1:

      TIM_TimeBaseStructure.TIM_Prescaler = 1;

      the clock will now tick at the full bus rate (84 MHz). You can now set the period to a 32 bit integer – e.g. to see the LEDS toggle every second:

      TIM_TimeBaseStructure.TIM_Period = 84000000 - 1;

      Hope this makes sense

  4. Hi Andrew and thanks for the tutorial. These STM32F4 boards are complex beasts.
    I understand the configuration and your main loop but I was wondering if you could answer a question for me.. I would like to know how to change the period and/or the prescaler value using a for loop. Reason being I want to get the Peripherals library DAC signals generation example making siren noises rather than single tones. Can I access the timer period from outside of the tim config function or do I have to use interrupts? I’m not sure and I am a total noob to these devices. 😉
    If you could give me a pointer I’d be V.V grateful.
    Thanks and thanks again,
    Steve.

    1. Steve, apologies for the (very) slow reply. To generate a siren noise, you will need to frequency modulate the timer. There are a number of ways to do this, and this depends on what kind of frequency range you are aiming for. For what you are doing, it might be simplest just to alter the prescaler value periodically. You can either do this inside the current interrupt block (i.e. every N calls to the block you change the prescaler) or you can have another interrupt timer which alters the prescaler for the sine wave timer. If you look at the CMSIS library, you will find a function called
      TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)
      which you can use to update the prescaler.

      You could also just update the period (autoreload) periodically, it just depends on your application as to which method is best.

  5. Hi,

    TIM_TimeBaseStructure.TIM_Prescaler = 84 – 1; // Down to 1 MHz (adjust per your clock)

    You are dividing your clock down to 0.5 MHz, because the maximum Speed at APB1 is 42 MHz, isnt that right?

    Interesting question, because somehow my timer is alsoways too fast.

    I used:
    TIM4_Init_Structure.TIM_Prescaler = 4200 – 1; /* 42MHz down to 10 kHz */
    TIM4_Init_Structure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM4_Init_Structure.TIM_Period = 10000 – 1;

    to get down to 1s toggle duration, ie. a frequency of 0.5 Hz, but actually Im getting 1 Hz. I dont no why?

    Thank you for your article and probably your help.

      1. Just to save anyone else the trouble of figuring this out, since it took me a while: if you look at page 114 of the reference pdf (this is the large timer diagram), right where it says “APBx timer clocks” is a box saying “if APBx presc = 1 x1, else x2″… in other words, even though the APB1 peripheral clock is 48MHz, the APB1 *timer* clock is 84MHz, since the prescaler isn’t 1. At least, if I’m understanding all of this correctly!

Leave a Reply