The simplest way to produce regular interrupts from a timer like TIM3 on the STM32 family of processors is to set up the Auto Reload Register (ARR) to generate an update event. This article shows you how to correctly set up the clock source, prescaler and ARR to get regular events over a wide range of frequencies.
This is the second in a series of articles on using the general purpose timer, TIM3, available on all the STM32 family processors. I will look at the simplest configuration with the counter,CNT, set to auto increment at a frequency chosen by you. Once CNT counts up to the value in the reload register, ARR, an update event can be made to trigger an interrupt. By setting ARR to different values, with appropriate choice of clock prescaler, the interval between interrupts can be set to any value in a very large range.
Code here will use the Standard Peripheral Libraries for the STM32F4, running on an STM32F4 Discovery board. Similar code for the HAL libraries may be made available at a later date. Use is made of the SysTick setup described in an earlier article. Specifically, by comparing the output from the TIM3 code with the pattern of LED flashing under SysTick control, it is possible to check the correct functioning of the timer code.
The timer clock
Everything starts with the input clock to the timer peripheral. This clock is derived from the main system clock. This extract from the clock tree diagram in the reference manual shows that the actual frequency of the clock depends upon the value in the AHB and APBx prescalers. The diagram is ambiguous since it is not clear what determines the input frequency to the APBx prescalers. Nor is it clear which APBx timer clock any particular clock peripheral is connected to.
APBx timer clocks
To clear up which APBX clock is used – the memory and bus architecture section (section 2) of the STM32F4 reference manual, DM0090, has Table 1 which shows that TIM3 is connected to APB1. For other members of the STM32 family it might be a good idea to check the clock source. Operation of the timer, once it has a clock, will be the same.
APBx Prescaler
in the system_stm32f4xx.c file, all the basic clock configuration is done and in my project the APB1 prescaler is set to 2. Now it is possible to see from the diagram that the timer clock will be twice the APB1 clock frequency. It would be better to have the setup code work all this out for me where possible.
Find The Timer Clock Input Frequency
Referring again to the setup in system_stm32f4xx.c, I can see that the AHB prescaler is set to 1 and so I can determine that the actual frequency of the clock that drives the timer will be equal to twice the APB1 clock frequency.
There is an easy way to find the APB1 clock frequency by using the Peripheral Library function RCC_GetClocksFreq(). This function fills a structure with the values for several key clock frequencies. In this case, the one I want is PCLK1_Frequency.
There is no library function call that lets you get the value of the APB1 prescaler. However, all I need to know is whether or not it is set to one. For that I can just compare the values of SYSCLK_Frequency and PCLK1_Frequency. If they are the same, the prescaler is set to one. Now I can calculate the correct frequency for the timer clock. Here is the code fragment
1 2 3 4 5 6 7 8 9 |
RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq (&RCC_Clocks); uint32_t multiplier; if (RCC_Clocks.PCLK1_Frequency == RCC_Clocks.SYSCLK_Frequency) { multiplier = 1; } else { multiplier = 2; } uint32_t TIM3CLK_Frequency = multiplier * RCC_Clocks.PCLK1_Frequency; |
Note that there is no magic number for any of these frequencies and that I do not need to know how it is all set up in the clock configuration code. Thus, this method of finding the timer clock should work regardless of how I have the clocks configured. What will change are the considerations below about he range of values that are reasonable to expect from the timer.
Set the Clock Prescaler
Refer again to the simplified diagram of TIM3:
Before the timer clock signal gets to the counter, it must pass through the prescaler, PSC. This is a 16 bit counter that simply counts up to the value in the PSC register and wraps around. The overflow from that is what drives the actual counter, CNT. A value of zero in PSC effectively passes the input clock straight to the counter. It is safe to change the prescaler at any time since writes are buffered and the new value will be written at the next overflow.
The prescaler then can divide the input clock by any value from 1 to 65536 Suppose the input clock frequency to the prescaler is 72MHz. It would be possible to get the counter input frequency down to as little as 1098 Hz. Note that it would not be possible to get all the way down to 1 kHz from 72 MHz but with a more modest SYSCLK frequency of less than 65 MHZ, it should be perfectly possible if desired. Exact divisions, of course will be subject to integer rounding and a lot of specific frequencies may not be possible without some juggling of oscillator values.
The value written into PSC is the prescale_value – 1. Thus, to get a division by 10, the PSC register must get the value 9; to get a division by 720, the PSC register gets the value 719.
Set ARR to generate specific intervals
The Auto Reload Register, ARR, is also a 16 bit register. In normal operation, the counter, CNT, counts up until it reaches the value in ARR and is then set back to zero. Optionally, an event can be triggered that fires off an interrupt.
The longest interval comes when ARR is loaded with the value 65535. Then it takes 65536 counts (don’t forget the zero) before the counter overflows.
Taken with the maximum prescale value of 65536, it is possible to generate events as infrequently as once per TIM3CLK_Frequency/(65536 * 65536). With a 72MHz input clock, that amounts to once every 59 seconds or so. Irritatingly, it is just short of once every minute.
Clearly, there are several pairs of PSC and ARR values for any given final frequency and so more than one way to get the exact frequency desired.
An example
Suppose I have my STM32F4Discovery running with a TIM3CLK frequency of 72MHz and I want to generate a TIM3 event at 40kHz. That is an event every 25us. Perhaps I want to update a Direct Digital Synthesiser or sample some sensors. Or something. It often makes sense to factor the final frequency into a pair of numbers. One value will go in the prescaler and on in the reload register.
I am free to pick any values that work for PSC and ARR so what do I pick? In this case the numbers are pretty easy. I can divide the 72MHz clock by 72 to give me 1MHz into the counter – an interval of 1us – and then set the ARR to generate an event after 25 counts. Remembering to subtract 1, I get PSC = (72-1) = 71 and ARR = (25 – 1) = 24;
Using these values in this case makes extra sense because it makes it easier to understand what is happening and easier to make changes later. I could, for instance have left the prescaler at 1 and set the ARR to generate the event after 180 counts. The result would be the same but it is no longer easy to think in terms of intervals as multiples of 1us. Now if the requirement changed to one event every 30us, the first set of values makes this much more simple.
How do decide the register values
Frequency domain
If the problem statement is given in terms of an event frequency it might be better to stay in that domain unless the frequency is some nice simple value like the 40kHz above. Otherwise, calculate the total division factor needed to get the final frequency from the timer input clock and then decide how or whether to factor is out into multiple values. If the divisor is in the range 1 to 65536, you can set the prescaler to 1 and just put the divisor into the ARR. Otherwise look for easy factors. Of course, you could write a function to calculate appropriate values for you. This is an exercise left to the reader. Here are a couple of examples:
EVENT_Frequency = 18000Hz
divisor = TIM3CLK_Frequency/EVENT_Frequency = 72000000/18000 = 4000
This is within the range I can handle with the ARR register alone so I might choose to have PSC=0 and ARR=3999
EVENT_Frequency = 80Hz
divisor = TIM3CLK_Frequency/EVENT_Frequency = 72000000/80 = 900000 = 900 * 1000
This time I cannot use only one register so I might choose
PSC = (900-1) = 899 and ARR = (1000 – 1) = 999
Time domain
If the problem is stated in terms of the interval between events, it may be better to stick with calculations in the time domain. Again, if I have a requirement for an event every 20us, it would be easiest to think in terms of arranging for a 1MHz click into the counter and then to set the ARR for the number of 1us counts I need.
This example would give me a value of PSC = (72-1) = 71 and ARR = (20 – 1) = 19.
Given an event interval of 3.4ms, I might choose to have the counter run at 10kHz so that each count corresponds to 0.1ms and I can have an interval of 34 such counts.
prescale = TIM3CLK_Frequency/10000 = 7200
Thus PSC = (prescale-1) = 7199 and ARR = (34 – 1) = 33
Example TIM3 setup
For the example code here I will configure the timer to run with rollovers at 40kHz. As explained above, I will clock the main counter at 1MHz and set the ARR register to match after 25 counts. Here is the timer setup code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* make sure the peripheral is clocked */ RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3, ENABLE); RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq (&RCC_Clocks); uint32_t multiplier; if (RCC_Clocks.PCLK1_Frequency == RCC_Clocks.SYSCLK_Frequency) { multiplier = 1; } else { multiplier = 2; } uint32_t TIM3CLK_Frequency = multiplier * RCC_Clocks.PCLK1_Frequency; uint32_t TIM3COUNTER_Frequency = 1000000; uint16_t prescaler = (TIM3CLK_Frequency / TIM3COUNTER_Frequency) - 1; uint16_t reload = (25) - 1; // Tevt = 25 us TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* set everything back to default values */ TIM_TimeBaseStructInit (&TIM_TimeBaseStructure); /* only changes from the defaults are needed */ TIM_TimeBaseStructure.TIM_Period = reload; TIM_TimeBaseStructure.TIM_Prescaler = prescaler; TIM_TimeBaseInit (TIM3, &TIM_TimeBaseStructure); |
Notice that the Peripheral library does not refer to the ARR. Instead, the value given to TIM_Period is the one that gets placed into the ARR.
Using the Interrupt Event
Now I have the timer registers set up, I have to start the timer, arrange for it to generate interrupts and write an interrupt service routine. Be aware that if you start the timer and then enable the interrupts, there may be an interrupt event pending if the timer manages to roll over before you are ready. In many cases this will not be a problem but it is worth remembering. Here I will write the service routine, enable the interrupt and then start the timer.
Interrupt Service Routine (ISR)
For this simple case, I will be using the TIM3 update event interrupt. All that is required in a typical development environment is to write an appropriately named function and the linker will correctly connect it to the interrupt vector. When writing in C++, you must remember to declare this as a C function of the linking will not happen and your program will hang. Inside the ISR, I will have to check that it really is the timer interrupt flag that caused the event, perform whatever task is required and then clear the flag. If the flag is not cleared, the ISR will be immediately called after exit. In my example, I am going to use the timer interrupt to increment a counter and then use that counter value to blink another LED. Also in my code I have another LED blinking once per second with timing controlled by SysTick. If I arrange for the TIM3 controlled LED blinking to be at the same rate as my reference LED, I can easily see if I have the constants right. Here is the ISR:
1 2 3 4 5 6 7 8 9 10 11 12 |
extern "C" void TIM3_IRQHandler (void) { static uint32_t count = 0; if (TIM_GetITStatus (TIM3, TIM_IT_Update) != RESET) { count++; if (count > (20000 - 1)) { GPIO_ToggleBits (RED_LED); count = 0; } TIM_ClearITPendingBit (TIM3, TIM_IT_Update); } } |
ISR functions take no arguments and return no values. They should be small, fast and self-contained. Be wary of how you use globally defined variables inside the ISR. In this example, the counter is declared to be static so that its value will not be lost after the function exits. The magic number 20000 represents 20000 counts of the main counter which I am running at 40kHz. I will expect the red LED to blink on and off once per second.
Start the Timer
Starting a timer is a single call to a Peripheral library function. I have placed this inside another function so that I can more easily adapt the code later to use different libraries or different timers.
1 2 3 4 |
void timer_start (void) { TIM_Cmd (TIM3, ENABLE); } |
Enable the Interrupt
Again, this is a simple library call that I have wrapped up in another function for flexibility.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void timer_interrupt_enable (void) { /* * It is important to clear any pending interrupt flags since the timer * has been free-running since we last used it and that will generate * interrupts on overflow even though the associated interrupt event has * not been enabled. */ TIM_ClearITPendingBit (TIM3, TIM_IT_Update); /* put the counter into a known state */ TIM_SetCounter (TIM3, 0); TIM_ITConfig (TIM3, TIM_IT_Update, ENABLE); } |
Observing the Results
Once the timer is set up and turned loos, I will expect to see two LEDs flash on the Discovery board. They may flash together or they may flash in any overlapping pattern. With the example code, I expect them to flash alternately. It does not matter much but they MUST flash synchronously without the synchronization drifting off. So long as that is the case, I can be reasonably sure that the timing constants are correct.
Once the basic behaviour is confirmed, it is time to write the actual ISR code for the application or to experiment with the timing and counter values to be sure your have understood what is going on.
TIM3 Full code listing:
Here is the full code listing for this application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
#include "stm32f4xx.h" #include "systick.h" #define MHz 1000000L #define KHz 1000L // The LED indicators on the STM32F4Discovery board #define LED_PORT GPIOD #define LED_PORT_CLOCK RCC_AHB1Periph_GPIOD #define GREEN_PIN GPIO_Pin_12 #define ORANGE_PIN GPIO_Pin_13 #define RED_PIN GPIO_Pin_14 #define BLUE_PIN GPIO_Pin_15 #define ALL_LED_PINS GREEN_PIN | ORANGE_PIN | RED_PIN | BLUE_PIN #define GREEN_LED LED_PORT,GREEN_PIN #define ORANGE_LED LED_PORT,ORANGE_PIN #define RED_LED LED_PORT,RED_PIN #define BLUE_LED LED_PORT,BLUE_PIN #define ALL_LEDS LED_PORT,ALL_LED_PINS void timer_interrupt_init (void) { NVIC_InitTypeDef NVIC_InitStructure; RCC_ClocksTypeDef RCC_Clocks; /* Enable the timer global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init (&NVIC_InitStructure); } void timer_init (void) { RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq (&RCC_Clocks); uint32_t multiplier; if (RCC_Clocks.PCLK1_Frequency == RCC_Clocks.SYSCLK_Frequency) { multiplier = 1; } else { multiplier = 2; } uint32_t TIM3CLK_Frequency = multiplier * RCC_Clocks.PCLK1_Frequency; uint32_t TIM3COUNTER_Frequency = 1000000; uint16_t prescaler = (TIM3CLK_Frequency / TIM3COUNTER_Frequency) - 1; uint16_t reload = (25) - 1; /* make sure the peripheral is clocked */ RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* set everything back to default values */ TIM_TimeBaseStructInit (&TIM_TimeBaseStructure); /* only changes from the defaults are needed */ TIM_TimeBaseStructure.TIM_Period = reload; TIM_TimeBaseStructure.TIM_Prescaler = prescaler; TIM_TimeBaseInit (TIM3, &TIM_TimeBaseStructure); } void timer_start (void) { TIM_Cmd (TIM3, ENABLE); } void timer_stop (void) { TIM_Cmd (TIM3, DISABLE); } void timer_interrupt_enable (void) { /* * It is important to clear any pending interrupt flags since the timer * has been free-running since we last used it and that will generate * interrupts on overflow even though the associated interrupt event has * not been enabled. */ TIM_ClearITPendingBit (TIM3, TIM_IT_Update); /* put the counter into a known state */ TIM_SetCounter (TIM3, 0); TIM_ITConfig (TIM3, TIM_IT_Update, ENABLE); } void timer_interrupt_disable (void) { TIM_ITConfig (TIM3, TIM_IT_Update, DISABLE); } // for C++, ensure the interrupt handler is linked as a C function #ifdef __cplusplus extern "C" { #endif void TIM3_IRQHandler (void) { static uint32_t count = 0; if (TIM_GetITStatus (TIM3, TIM_IT_Update) != RESET) { count++; if (count > (20000 - 1)) { GPIO_ToggleBits (RED_LED); count = 0; } TIM_ClearITPendingBit (TIM3, TIM_IT_Update); } } #ifdef __cplusplus } #endif // these are the LEDs on the STM32F4Discovery void board_leds_init (void) { GPIO_InitTypeDef GPIO_InitStructure; // always do this with an auto structure as it is undefined GPIO_StructInit (&GPIO_InitStructure); RCC_AHB1PeriphClockCmd (LED_PORT_CLOCK, ENABLE); GPIO_StructInit (&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RED_PIN + GREEN_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init (LED_PORT, &GPIO_InitStructure); GPIO_ResetBits (LED_PORT, RED_PIN + GREEN_PIN); } void flash_green_led_forever (void) { while (1) { GPIO_SetBits (GREEN_LED); delay_ms (500); GPIO_ResetBits (GREEN_LED); delay_ms (500); } } int main (void) { systickInit (1000); timerInit(); timerInterruptInit(); timer_start(); timer_interrupt_enable(); board_leds_init(); flash_green_led_forever(); return 0; // there is no going back but keep the compiler happy } |
The systick.h file contains
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#ifndef SYSTICK_H #define SYSTICK_H #ifdef __cplusplus extern "C" { #endif // configure systick service to run with given frequency void systickInit (uint16_t frequency); // software timing delay. Not to be relied upon if the system clock changes void delay_us (uint32_t t); // a busy/wait delay based on counting interrupts from systick void delay_ms (uint32_t t); // global function to return time since last reset in milliseconds uint32_t millis (void); #ifdef __cplusplus } // extern "C" #endif #endif |
And systick.c contains
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <stdint .h> #include <stdbool .h> #include "stm32f4xx.h" #include "hardware.h" #include "systick.h" volatile bool crashed = false; static volatile uint32_t ticks; void systickInit (uint16_t frequency) { RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq (&RCC_Clocks); (void) SysTick_Config (RCC_Clocks.HCLK_Frequency / frequency); } // TODO: magic number for timing. This value correct for STM32F4 at 72MHz #define COUNTS_PER_MICROSECOND 24 inline void delay_us (uint32_t microseconds) { uint32_t count; count = microseconds * COUNTS_PER_MICROSECOND - 2; __ASM volatile ( " mov r0, %[count] \n\t" "1: subs r0, #1 \n\t" " bhi 1b \n\t" : : [count] "r" (count) : "r0" ); } void delay_ms (uint32_t t) { uint32_t start, end; start = millis(); end = start + t; if (start < end) { while ( (millis() >= start) && (millis() < end)) { // wait } } else { while ( (millis() >= start) || (millis() < end)) { // wait }; } } // return the system clock as milliseconds inline uint32_t millis (void) { return ticks; } void SysTick_Handler (void) { ticks++; } |
Finally, the hardware.h file looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#ifndef HARDWARE_H #define HARDWARE_H #ifdef __cplusplus extern "C" { #endif #define MHz 1000000L #define KHz 1000L // The LED indicators on the STM32F4Discovery board #define LED_PORT GPIOD #define LED_PORT_CLOCK RCC_AHB1Periph_GPIOD #define GREEN_PIN GPIO_Pin_12 #define ORANGE_PIN GPIO_Pin_13 #define RED_PIN GPIO_Pin_14 #define BLUE_PIN GPIO_Pin_15 #define ALL_LED_PINS GREEN_PIN | ORANGE_PIN | RED_PIN | BLUE_PIN #define GREEN_LED LED_PORT,GREEN_PIN #define ORANGE_LED LED_PORT,ORANGE_PIN #define RED_LED LED_PORT,RED_PIN #define BLUE_LED LED_PORT,BLUE_PIN #define ALL_LEDS LED_PORT,ALL_LED_PINS // The serial port #define USART_BAUD 115200 #define USART USART1 #define USART_CLK RCC_APB2Periph_USART1 #define USART_CLK_CMD RCC_APB2PeriphClockCmd #define USART_GPIO GPIOA #define USART_GPIO_CLK RCC_AHB1Periph_GPIOA #define USART_GPIO_CLK_CMD RCC_AHB1PeriphClockCmd #define GPIO_AF_USART GPIO_AF_USART1 #define USART_TX_PIN GPIO_Pin_9 #define USART_RX_PIN GPIO_Pin_10 #define USART_TX_AF_SRC GPIO_PinSource9 #define USART_RX_AF_SRC GPIO_PinSource10 #ifdef __cplusplus } // extern "C" #endif #endif |
Happy Coding
I have some questions that I hope you can answer:
1) What is the difference between using systick and TIM3 for interrupts? Why one and not the other?
2) If I want to have a delay in the microseconds range, do i set this to be from the systick or TIM3?
3) Assuming that one is used for delays and the other is used for an interrupt, will they interfere with each other when their interrupt events are called? For example the 1us interrupt that increments the system time will be called while the 1ms ISR route is running or even during ADC conversion. Won’t this have negative impacts on the performance of the 1ms ISR and possibly cause other problems (to ADC and I2C etc)as well?
Thank you,
Patrick
systick is part of the processor core and so will be present across a range of ARM processors. Timer3 is specific to the STM32 processors. systeick does not do other things in the way that Timer 3 so if you want the Timer 3 functionality you can still have it.
For your microsecond resolution delay, are you happy to busy-wait, eating up the processor or do you want to be doing something else? The ARM core has a cycle counter which can be used to generate accurate delays in the microsecond range or time events with sub microsecond accuracy. Timers can be used for this but you may not want to give up the other functions they can perform.
interrupts get queued up. The highest priority interrupt source can interrupt a lower priority ISR and the routine will return to the othe ISR when done. If a low priority interrupt occurs during the servicing of a high priority interrupt, its ISR will execute after the high priority ISR. YEs, if you rely on this kind of thing for timing events, they can interfere with each other. It is possible to have timers trigger events without needing intervention from code and it is possible to use DMA to let the hardware do data transfers without code.
Whatever you actually want to do, there is probably a away.
hi
i tried compiling the code but ran into some issues
— it wont compile
#include “systick.h”
— fatal error “systick.h” file not found
Am i missing a library?
My apologies. I think I have now included the missing file contents on the page. Please let me know how you get on.
Pingback: TIM3 on the STM32 - an introduction - Micromouse Online
Hi,
Thank you for in detail descriptions.
In time domain method if I want 1 ms or 1us interrupts then ARR = 0 and continuously update event occurrence.
I think you should this exception in your method, or should say ARR correct range.
Thanks
There are many solutions for any given time delay an the one you choose may be determined by other factors like available clock frequencies or the need to have other channels running from the same timer clock. Each case needs a little thought to get the most appropriate solution.
This question may sound very very simple, but I can not figure out the solution. How do you get TIM3CLK_Frequency to be 72MHz. Is it only an example or it is the actual value that you get out from your function?
Sorry for the delay in answering. The 72MHz comes out from the initial clock setup. For that I rely on the clock configuration code generated by ST’s utility – now part of the STM32CubeMX package.
Thanks a lot !!, Randomly ended up here but glad I did !!
Hi.
Thank you for sharing this. It helped me fix a bug in my code.
The solution was to check, in the handler, that the UIF was set in TIMx_SR[0] and to clear it after the LED is toggled. However, while I get that it is necessary to clear the UIF afterwards, it is not clear why it is necessary to check the UIF before. In my mind, for the update event to have raised the IRQ, the UIF must have been set. Unfortunately, it is not clear from the reference manual, or I am missing something.
Do you have any idea about this?
The timer can generate 8 different interrupts but they all share the same vector so you have to work out which was the actual source of the interrupt.
Thanks. That was really useful.
I am using vector 25 (TIM1 Update and TIM10 Global Interrupt) on the Nucleo F446RE, so you’re right, it makes sense to check the source of the interrupt.