TIM3 ARR Gives Regular Interrupts on the STM32F4

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.

STM32F4 timer clock selection

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

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:

TIM3 timer diagram

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:

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:

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.

Enable the Interrupt

Again, this is a simple library call that I have wrapped up in another function for flexibility.

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:

The systick.h file contains

And systick.c contains

Finally, the hardware.h file looks like this:


Happy Coding
This entry was posted in ARM, Hardware, STM32 TIM3. Bookmark the permalink.

9 Responses to TIM3 ARR Gives Regular Interrupts on the STM32F4

  1. Patrick John says:

    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

  2. Peter Harrison says:

    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.

  3. Emmanuel Jammeh says:

    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?

  4. Peter Harrison says:

    My apologies. I think I have now included the missing file contents on the page. Please let me know how you get on.

  5. Pingback: TIM3 on the STM32 - an introduction - Micromouse Online

  6. Karim says:

    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

  7. Peter Harrison says:

    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.

  8. Matthieu says:

    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?

  9. Peter Harrison says:

    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.

Leave a Reply