TIM3 Output Compare on the STM32 Family

By | February 4, 2016

TIM3 is a general purpose timer found on all the STM32 family processors. Among other features, it has four capture compare channels that can be used to generate regular interrupts. In this article I will show you how to set up simple interrupt events based on these features.

This is part of a series of articles about the TIM3 general purpose timer. In the preceding parts I introduced the TIM3 timer features and showed you how to identify the timer clock and set up the prescaler and reload register.You can see all the STM32 TIM3 posts here. Code is written using the Standard Peripheral Library for the STM32F4Discovery board. Timer related portions should run directly on other STM32 family members since they all have a TIM3

In this part, I will explain the basic configuration of the capture compare registers, describe how they can be used to generate regular interrupts and give you sample code to demonstrate the facility.

Capture Compare Registers

Here is a modified view of the TIM3 timer taken from the reference manual RM0090.

TIM3 timer diagram

 

You can see that there are four registers associated with the timer TIM3. These are referred to as CCR1, CCR2, CCR3 and CCR4 or CCRx in general. Each channel is actually a pretty complex affair I am only going to concern myself with its behaviour in output compare modes. The reference manual RM0090 has this diagram of the output configuration:

Output stage of a output compare module in the STM32 general purpose timers

 

A look at the reference manual reveals that in basic Output Compare mode, I can program the output mode controller to one of four different states on a match. It can be:

  • frozen – that is, nothing happens
  • set active (forced high)
  • set inactive (forced low)
  • toggled

Other results are possible in other modes such as PWM.

In this article though, I just want to look at triggering an interrupt event. Although there are four Output Compare channels, and each can generate its own interrupt, they all get handled by the same Interrupt Service Routine (ISR). That means a series of tests will be needed in the ISR to determine exactly which channel caused the interrupt. I will just use two channels to illustrate how all this works.

Example Problem Statement

For this example, I want to set up two Output Compare channels on TIM3. Each channel will simply generate an interrupt. The ISR for that channel will toggle an LED on the target board. Each channel gets its own LED. Channel 1 will look after the blue LED and flash it  4 times per second. Channel 2 will look after the orange LED and flash it 2 times a second. These are simple numbers and the interrupt rate is quite low so that it is easy to see what is happening. Adapt the code for your needs.

Set up the Timer Prescale and Auto Reload

As with most timer problems, the first task is to set up the basic counter activity. This problem needs counts that are some handy but fairly small fraction of a second. Since the problem statement mentions numbers like 4 times per second and 8 times per second, it seems reasonable to try and arrange the counting frequency to be a convenient power of 2. Something like 256Hz or 128kHz of 3200Hz. Whatever, some number that lets me easily get binary fractions.

I have already established that the input frequency to the prescaler is 72MHz. This is an inconvenient value since there are no divisors that give me the kind of counter frequency that I was looking for. After a bit of fiddling with some of the factors of 72,000,000 I discovered that if I divide it by 28,125 I get 2560. Now that is a handy value for the counter since I can easily get many multiples of 2 and 5 out of it. Multiples of 3 would be more tricky but that is not part of my problem. The actual frequency for the counter is not critical in this case so long as it is high enough to give me the resolution I require.

Note that it is not sufficient to just pick a frequency. You must choose an value that divides exactly into the clock frequency if the results are to be accurate. the TIM3 counter frequency gets stored in a variable so that we can use it when calculating the intervals for the CCRx registers.

Now I need to set the prescaler to (28125-1) = 28124 so that my counter will increment at 2560Hz. The prescaler value is calculated from the timer input clock frequency (TIM3CLK_Frequency) and the desired TIM3COUNTER_Frequency.

When using the Output Compare channels, I need the CNT register to wrap around completely for reasons that should be clear shortly. To do this, I need to load the ARR register with 65535.

The update event interrupt is not needed so basic timer configuration looks like this:

Once the timer is started, it will free run at the specified frequency and then I have to think about how to configure the Output Compare registers.

Configure the Output Compare Function

Along with all the other configuration, I have to tell the timer to use the CCRx registers in output compare mode. The Standard Peripheral Library makes all this super easy:

Each channel is configured separately and I need only configure the channels I need. They are completely independent. The initial interval must be loaded into the register when using the library code but that value is only good for the first match and assumes that the CNT register starts at zero. When a match between CNT and the CCRx register is made, the CNT register carries on counting. That leaves me with the problem of how to update the CCRx registers after each match. The value used for the interval will be the number of CNT counts between interrupts. That gets calculated depending on the problem requirements.

Updating the Output Compare Registers

There is nothing in the Output Compare that will reset the CNT register. Even if there was, it would be no good to me because I want to use the CNT register contents to match against up to four CCRx registers. So, how can I get recurring intervals?

The CNT register, by default, will just count upwards at a fixed rate, wrapping around to zero again when it gets to the end. I just made sure of that in the timer setup. Each CCRx register is just compared with CNT for equality – it does not care what the actual value is. Every time the ISR runs then, I just need to add a fixed interval to the CCRx register. When the CNT value catches up with the CCRx again another interrupt will be generated. Overflows will not matter since both registers overflow in the same way and a match must eventually occur. Updating the CCR is now very simple. This code fragment deals with channel 1. The other channels are similar:

In the problem statement, I wanted the blue LED to flash 4 times a second using channel 1 and the orange LED to flash 2 times a second using channel 2. To get two flashes per second, I set the interval to a quarter second since, at each interrupt, I will toggle the LED pin. Similarly, I use 8 ticks per second for the blue LED

Variable Update Intervals

There is no reason why the same interval has to be used each time the interrupt fires. If I want to drive stepper motors, I can arrange to have a steadily diminishing interval as the motor accelerates. The values could be calculated or looked up in a table. Because I can have four channels and each is independent, I could easily drive four stepper motors from the same timer and have different motion profiles on each one.

Servicing the Output Compare Interrupt

Recall that several channels are all sharing the same ISR so in there I must poll each of the possible interrupt sources, act on any that are active and reset their flags ready for the next time. The entire ISR for my two channel problem looks like this.

The action associated with each channel here is just toggling an LED on and off. It could just as easily be setting the pattern of bits needed to drive the phases of a stepper motor. Just try to keep the ISR code as short and simple as you can. All that remains is to enable the interrupt and let it loose.

Enabling the Output Compare Interrupt

Although all the Output Compare channels share an ISR, each has its own interrupt enable flag so they can be turned on and off individually. Again, the Standard Peripheral Library makes this easy:

Once the timer is started, my ISR should run and the LEDs will flash in the required way.

Putting it Together

Here is a complete code listing demonstrating how to do all these things with the STM32F4 Discovery

Happy Coding




10 thoughts on “TIM3 Output Compare on the STM32 Family

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

  2. sasi

    I want my timer start from 0 to 2^32-1(free run timer) and generate an interrupt on every 16ms and the timer shoudnt reset to 0. is it possible to generate an free run timer interrupt?

  3. Peter Harrison Post author

    That is exactly what the example here does. The capture compare register just needs updating in each ISR.

  4. Joe Allen

    Hi, How does the math work out for 4 toggles every second for the blue led? I tried calculating it by hand I got 4 toggles every half second

    2560 Hertz = .3906 milliseconds
    2560 Hertz / 8 = 320(CCR1_interval)

    .3906 milliseconds * (320 *4) = roughly .5 second

  5. Peter Harrison Post author

    You divide by 8 to get the 320 microseconds but then only multiply by four which will give you 0.5 seconds. Remember also that the LED is toggled each tick to the flash frequency will be half the toggle frequency.

  6. Andrea

    Hello, I think to have some problem in undersanding the example. I was imagining that the LED toggle was handled directly by the comparison between the CNT and the CCRx registers configuring the CCMRx register in this way. Why do I nees an ISR that execute the GPIO_ToggleBits? Are the ports connected to the LED configured as simple GPIOs? Thank you very much in advance for your answer.

  7. Peter Harrison Post author

    Thanks for looking. The idea was only to illustrate how code can be called in response to the output compare. If you want to connect an output pin directly to the OC event, that is a slightly different issue.

  8. Joe Allen

    Hello, I am very confused why when I set the CCR1_Interval = TIM3COUNTER_Frequency, the led is on for 2 seconds and not 1 second. I thought when the CNT = CCR1, the led is toggled via the interrupt. So when CCR1_Interval = TIM3COUNTER_Frequency, the interrupt should be called every second to toggle the led right?

    Does it have to do with this statement in the previous comment: “Remember also that the LED is toggled each tick to the flash frequency will be half the toggle frequency.” ? Could you elaborate this with an simple example?

  9. Peter Harrison Post author

    The code given, on the STM32F4Discovery flashes the orange LED at 0.5Hz. If you are getting half that then I don’t really understand. Are you running on a different target? Perhaps I missed something about the way the clocks are set up. As for the toggle rate vs the flash rate. A full cycle has the LED on for half the time and off for half the time. So, if it changes state twice per second, the frequency is just 1Hz. That is exactly what the green LED should be doing.

  10. Joe Allen

    Hi Peter,

    I see now. I figured out the issue, it was that I wasn’t setting the prescaler variable correctly, I was using the System Clock which I have it set to 168MHz but if forgot the timers are located on a different bus, I think in mine case,since i have the System Clock max out to 168MHz, the TIM clock should run at 84MHz, once i used the correct value, everything worked as expected. thanks

Leave a Reply