en.radzio.dxp.pl

Lesson 4. Blinking with timer interrupts
In lesson 3 we use timer TIM3 to measure time for blinking the LED, but shown method was not perfect due to polling timer update flag. In this lesson we learn how to use timer interrupts for blinking LED.

STM32 interrupts basics
Interrupt system of STM32 microcontrollers are described in section 8.1 of RM0041 document. Now look into startup_stm32f10x_md_vl.s file. At line 61 (or one of thier neighborhood) is defined vector table. This table consist from sequence of DCD (Define Constant Double word) directive. Each of this directives defines address of handler for all interrupts on STM32 microcontroller, except the first element in this table, which is initial value of stack pointer. First sixteen elements are reserved for vectors specific for Cortex-M3 core. Remaining positions can be used by microcontroller vendor for thier own interupts vector. Now look at line 196. There are defined DefauldHandler procedure, some exports functions name with WEAK keyword and similar some of functions label. This is way to define 'default' functions, which are overrided by definie it in other place of application. At line 281 there is only one assembler instruction :

  B .

This is branch instruction to current line. It make endless loop. Now, if we don't define any interrupt handler, all interrupt vectors will be pointed to this branch instruction. This prevents from jumps to undefined locations when not handled interrupt occurs. Thanks to this, we can define vectors only for used interrupts, without modyfing vector table each time. How to define our own interrupt handler? It is very simple :

void TIM3_IRQHandler(void)
{
// code of handler
}

When we define our handler, it's address will be placed on proper location in interrupts vectors table. Define vector is one of things to make interrupt working. Next thing is enable interrupt in NVIC (Nested Vectored Interrupt Controller). How do it? Like as this :

NVIC_EnableIRQ(TIM3_IRQn);

The last thing is enable peripheral to requests interupt. For our timer we want enable update event to generate an interrupt :

TIM3->DIER = TIM_DIER_UIE;

Now, we can place in interrupt handler our code for check flag and toggle LED state :

void TIM3_IRQHandler(void)
{
if(TIM3->SR & TIM_SR_UIF) // if UIF flag is set
  {
  TIM3->SR &= ~TIM_SR_UIF; // clear UIF flag
  LED_BLUE_GPIO->ODR ^= (1 << LED_BLUE_PIN); // toggle LED state
  }
}

Remember, interrupt flag must be cleared after enter to interrupt handler. Otherwise, interrupt handler will be still executed.

Complete source code :

//=============================================================================
// STM32VLDISCOVERY tutorial
// Lesson 4. Blinking with timer interrupts.
// Copyright : Radoslaw Kwiecien
// http://en.radzio.dxp.pl
// e-mail : radek(at)dxp.pl
//=============================================================================
#include "stm32f10x.h"
#include "antilib_gpio.h"
//=============================================================================
// Defines
//=============================================================================
#define LED_BLUE_GPIO GPIOC
#define LED_BLUE_PIN 8
//=============================================================================
// main function
//=============================================================================
int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

#if (LED_BLUE_PIN > 7)
  LED_BLUE_GPIO->CRH = (LED_BLUE_GPIO->CRH & CONFMASKH(LED_BLUE_PIN)) | GPIOPINCONFH(LED_BLUE_PIN,     GPIOCONF(GPIO_MODE_OUTPUT2MHz, GPIO_CNF_OUTPUT_PUSHPULL));
#else
  LED_BLUE_GPIO->CRL = (LED_BLUE_GPIO->CRL & CONFMASKL(LED_BLUE_PIN)) | GPIOPINCONFL(LED_BLUE_PIN,     GPIOCONF(GPIO_MODE_OUTPUT2MHz, GPIO_CNF_OUTPUT_PUSHPULL));
#endif

TIM3->PSC = 23999;         // Set prescaler to 24 000 (PSC + 1)
TIM3->ARR = 1000;           // Auto reload value 1000
TIM3->DIER = TIM_DIER_UIE; // Enable update interrupt (timer level)
TIM3->CR1 = TIM_CR1_CEN;   // Enable timer

NVIC_EnableIRQ(TIM3_IRQn); // Enable interrupt from TIM3 (NVIC level)

while (1) {}
}
//=============================================================================
// TIM3 Interrupt Handler
//=============================================================================
void TIM3_IRQHandler(void)
{
if(TIM3->SR & TIM_SR_UIF) // if UIF flag is set
  {
  TIM3->SR &= ~TIM_SR_UIF; // clear UIF flag
  LED_BLUE_GPIO->ODR ^= (1 << LED_BLUE_PIN); // toggle LED state
  }
}
//=============================================================================
// End of file
//=============================================================================

As we see, CPU execute endless loop, that do nothing. But LED is blinking. Each every 1000 miliseconds state of LED is changing in the interrupt handler routine.

 

 
(c) Radosław Kwiecień