TIM3 Output Compare on the STM32 Family

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

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

One Response to TIM3 Output Compare on the STM32 Family

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

Leave a Reply