STM32F Timers – Simple Up/Down Counter Demo – Blinky LED

Keywords: Embedded systems, ARM, STM32F4, Timers, Counter, LED

Code Link: Source Code Github Keil- Bare-Metal

Code Link: Source Code Github Keil- HAL Libraries

In this tutorial we will go one step further into programming STM32 timers to blink an LED each time the timer count a preprogrammed number of counts. This tutorial is based on previous introductory tutorial of STM32 Timers. So if you haven’t gone through previous tutorial, it is highly recommended to go through the tutorial with the link given bellow.

In this tutorial the board used is STM32f4-Discovery(STM32F407VG MCU). The STM32F407VG has total 14 timers (including General Purpose timers, Basic Timers, and Advanced Timers). In this tutorial, we will program Time Base of Timer-2 (General-Purpose Timer) to count certain counts (resulting in total time elapse of 500msec). Upon completing the counting, an interrupt is generated. In Timer-2 ISR, GPIOD-15 (PD#15) is toggled. On STM32F4-Discovery, GPIOD-15 is connected to Onboard Blue LED so ultimately the LED blinks every second (toggle half a second).

Let’s jump into actual programming:

1. Enable Timer-2 Clock:

As can be seen from STM32F407 Datasheet, Timer-2 is connect to Advanced Peripheral Bus-1 (APB-1) i.e. Timer-2 is clocked from APB-1 bus which is subsequently by default clocked from HSI internal 16Mhz Oscillator unless reconfigured. Other options available to feed time base of Timers is explained in introductory Timers tutorial above.

Figure-1: Timer-2 Clock source – APB1

By default, timers are clocked from internal Oscillator (16-Mhz). To enable bus clock to Timer-2 from APB1, bit-0 in RCC_APB1ENR Register needs to be set, Figure-2.

Figure-2: RCC_APB1ENR Register
/* Enable clock to Timer-2 on AHB-1 Bus */
__setbit(RCC->APB1ENR, 0U);

2. Clock Prescale:

We will divide clock by a factor of 1000 before it is feed to timer base counter. Thus the actual frequency that the timer base counter will be receiving is 16Mhz/1000 = 16Khz. The input clock is divided by writing appropriate value to TIM2_PSC Register, Figure-3.

Figure-3: TIM2_PSC Register
/*
    Divide the timer-2 input frequency (16Mhz)
    by a factor of 1000 (16,000,000/1,000 = 16,000 = 16Khz) 
*/
TIM2->PSC = 1000;

3. Timer Counts:

Next thing is to configure Timer counts. As we need 500msec delay, we need timer-2 to count X number of cycles that can result into total time of 500msec. Timer-2 is receiving input frequency of 16kHz / 16000Hz after Prescaler division value of 1000. Thus:
1-Cycle ———-> 1/f seconds (Time Period)
How Many Cycles (X) —————-> 0.5sec (500msec)
Rearranging and after cross multiplication:
X = 0.5sec x f ; f = 16000
=> X = 8000
So we want the timer-2 to count 8000 counts. This value needs to be programmed into timer-2 Auto-Reload Register i.e. TIM2_ARR, Figure-4.

Figure-4: TIM2_ARR Register
/*
    We want the time count to be 500msec (half a second).
    As the input frequency is 16khz so the total
    counts required for 500msec delay:
        
    total counts = 500msec * f
                 = (.5 sec) * 16,000
                 = 8,000
                 = 0x1F40
*/
TIM2->ARR = 8000;

4. Up/Down Counter:

Next thing we need to do is to configure timer-2 to either count UP/DOWN. If bit-4 of TIM2_CR1 register is clear, the timer will count up otherwise it will count down, Figure-5.

Figure-5: TIM2_CR1 Register
/* SET UP_COUNTER TO 0 IF DOWN COUNTER IS REQUIRED */
#define     UP_COUNTER      1

#if (UP_COUNTER)
/* Up-Counter mode*/
__clearbit(TIM2->CR1, 4U);
#else
/* Down Counter*/
__clearbit(TIM2->CR1, 4U);
#endif

5. Enable Interrupt:

To get notification of timer overflow/underflow, we will enable timer-2 interrupt on timer-2 side via TIM2_DIER (Figure-6) and on NVIC side.

Figure-6: TIM2_DIER Register
/*
    Enable timer-2 Update interrupt to
    generate interrupt on counter overflow/underflow
*/
__setbit(TIM2->DIER, 0U);

/*
    Timer-2 interrupt is received on IRQ-6 on NVIC
    so enable IRQ-6 on NVIC for interrupt detection
*/
NVIC_EnableIRQ(TIM2_IRQn);

6. Interrupt ISR:

Next step is to implement Timer-2 ISR. From STM32F407 vector table/ STM32F4xx Startup file the ISR for Timer-2 is TIM2_IRQHandler.

The ISR implementation should include GPIOD.15 (BLUE LED) Toggle as per OUR application requirements. The ISR should also clear timer interrupt otherwise it will keep calling the timer-2 ISR thus starving the main application.

/*********************************************************
    Timer-2 Interrupt Handler
**********************************************************/

#ifdef   __cplusplus
    extern "C" {
#endif
void TIM2_IRQHandler (void) {
    
    /* clear timer interrupt */
    __clearbit(TIM2->SR, 0U);
    
    /* Toggle BLUE-LED PD#15 on timer interrupt*/
    __togglebit(GPIOD->ODR, 15);
}
#ifdef   __cplusplus
	}
#endif

7. Kick Timer-2:

The final step is to kick the timer for starting counter. This can be done by setting bit-0 in TIM2_CR1 register, Figure-6.

/* Start Timer-2 */
__setbit(TIM2->CR1, 0U);


8. LED Configuration:

LED Configuration tutorial is covered in the GPIO-Output Tutorial. The link is given bellow.

For complete source code refer to the Github links given at the start of this tutorial.

Video demonstration:

[1] – Reference manual-RM0090



20 thoughts on “STM32F Timers – Simple Up/Down Counter Demo – Blinky LED”

Leave a Reply

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