Electronic – Bare metal timer interrupts on stm32f103 “bluepill”

armembeddedmicrocontrollerstm32

I am trying to implement a simple interrupt routine, blinky led using a timer on the stm32f103 "blue pill" board.

I have been reading the datasheet, reference manual, timer cookbooks and all but I'm not able to figure this part out.

I'm using the gnu-arm cross compiler and was successful in getting a timer a started and was able to blink an led by continuously checking the 'update event' bit inside the TIMx_SR register.

Up till now, I had a look at the NVIC section in reference manual but I can't figure out exactly how to work with the vector table…

Any suggestions or resources are welcome.

Best Answer

I will attempt to answer this fairly vague question by listing the high level steps to set up a timer interrupt, to help point you in the right direction.

  • Turn on the timer peripheral by enabling it's clock. This is done via the RCC subsystem. You will find this kind of register manipulation in any STM32 code example. If you are going to use the timer for e.g. PWM waveform generation, or to measure external signals, you will also need to enable the clock for the GPIO port you are using. I assume you are past the general "blinking LED" stage and will not go into more detail about GPIO ports :)
  • Configure the timer peripheral to generate interrupts in response to specific timer events. This is not covered in the NVIC section of the manual, but in the section about timers. For the STM32F103 and probably other STM32 chips, the register is called DIER. You will likely want to set the UIE bit in this register, however don't take my word for it. You should read the whole section of the manual to understand how the timer works and how to set it up.
  • Enable the correct IRQ in the NVIC. This hardware (the NVIC) is not specific to STM32, but is common to all ARM microcontrollers. Each interrupt line on the chip (there may be one dedicated to the timer you are using, or it might be shared by several peripherals) is referred to by an integer. I usually look in the device header file for this information, and I recommend getting comfortable with looking through the vendor code for answers about the chip you're working with.
  • Implement your interrupt handler function, often referred to as an ISR (Interrupt Service Routine). In C code, you declare a function which is already named elsewhere in the vendor's initialization code. When you declare a function with that name, the compiler/linker will know to place the address of that function into a known location (the vector table, another good thing to look up). This is the part of the code where you will toggle your LED. It is important to note that each peripheral that can generate an interrupt has a status bit that your ISR should clear, to acknowledge that the ISR has been executed. This becomes important when you have an IRQ shared by multiple peripherals, or if you want to handle more than one event generated by one peripheral. For example, if you enable the "timer update" interrupt via the UIE bit, you will need to then clear the corresponding UIF bit in the status register. This is the only way the peripheral knows that the interrupt has been handled. If you don't do this, the chip will lock up because it is continuously entering the interrupt handler.

Other general advice: the book "The Definitive Guide To ARM Cortex-M3 and Cortex-M4 Processors" helped me understand a lot of this stuff, but not as much as closely reading the chip manual and vendor-provided code.