cancel
Showing results for 
Search instead for 
Did you mean: 

Update Interrupts & Race Conditions

Dennis Ai
Associate II
Posted on March 30, 2018 at 01:45

Hi,

I am using a STM32F767Z MCU at the moment, and wanted to understand better understand how update interrupts work.  Specifically:

  • I have configured TIM1 to create an update interrupt when the counter overflows (upcounting mode).  TIM1 is set to Master Mode, and TRGO is set to the update event.
  • TIM2 is set to Slave Mode - Combined Reset Trigger Mode, and has one PWM output on Channel 1.  I set TIM2_CCR1 to determine the duty cycle.
  • In my update interrupt, I would like to update the value of TIM2_CCR1 to modify the duty cycle of PWM Channel 1 on TIM2.

I have a few questions regarding this configuration.

  1. If I do not set the preload bit for TIM2 PWM Channel 1, and TIM2_CCR1 is already set to some small value (e.g. a value corresponding to 1 microsecond), is it possible that my update interrupt will fail to modify TIM2_CCR1 before the counter reaches the existing value in TIM2_CCR1?
  2. If I set the preload bit for TIM2 PWM Channel 1, then which happens first?  The modification of the TIM2_CCR1 preload register with the update interrupt, or the loading of the preload register into the shadow register due to the Reset Mode that TIM2 is configured to?

In other words, what type of overhead is associated with update interrupts, is it predictable, and can it cause a race condition in the scenario above?

1 ACCEPTED SOLUTION

Accepted Solutions
henry.dick
Senior II
Posted on March 30, 2018 at 17:12

for Q1, the compare match may be missed: TIM2 starts right away while you are still executing TIM1 ISR, setting the CCR1.

the right way to do it is to set up a TIM2 ISR at its end whereby you would update the CCR1, waiting for it to be trigger next time by TIM1.

View solution in original post

5 REPLIES 5
Posted on March 30, 2018 at 12:31

I don't understand what do you want to achieve, perhaps you may want to elaborate on that.

Generally, interrupts take dozens of system clock cycles - at least one dozen to enter, then the compiler generated prologue must be executed and then any code, subject to compiler optimizations. Add latencies and jitters given naturally by the processor core (uninterruptible multicycle instructions), plus those given by code memory latency partially mitigated by the various buffers and pipelines and accelerators, and bus matrix clashes with other masters. The picture is very complex.

Thus, interrupts are not good enough for control in the sub-microsecond realm, even on the multimegahertz system clocks. Depending on the priority of the interrupt in question and the way how it's written and where it is run from, in the best case I'd count on jitter in the range of half to one dozen of system cycles and  latency maybe two-three dozens of them.

Timer links are there for such control, but note that the preloaded value gets loaded into the CCRx at every update, i.e. at every beginning of cycle of the running timer, so you can't load a new value into it and then wait indefinitely until the other timer provides the trigger.

JW

Posted on March 30, 2018 at 14:35

Hi Jan, thanks for the helpful response.

Here's what I'm trying to accomplish.  I have TIM1 set to Encoder Mode, and TIM3 is connected to a light source I would like to trigger (e.g. a LED).  However, I would like to trigger TIM3 on a fractional encoder count basis.  For example, instead of triggering TIM3 every 7 encoder counts, I want to trigger it every 7.4 encoder counts.  I also know ahead of time the average time it takes for an encoder count to occur.

I configure TIM1 to Encoder Mode, set it to Master Mode, where TRGO is configured to the Update Event.  Then I configure TIM2 (which is responsible for the fractional encoder count) as Slave Mode - Combined Reset & Trigger (Internal Trigger: ITR0/TIM1), and set it to One Pulse Mode.  I setup one channel in Output Compare or PWM Mode 2, so that the compare register corresponds to the average time it takes for the fractional encoder count to occur.  Configure TIM3 (which is connected to the LED) to Slave Mode - Combined Reset & Trigger (Internal Trigger: ITR1/TIM2) and set it to One Pulse Mode.  So I see two ways of implementing this sub-encoder count feature.  Perhaps there is a 3rd that is better, I am all ears!

  1. I set the Period (ARR register) of TIM1 to be 7.  I set the Pulse (CCR register) of TIM2 to correspond to 0.4 encoder counts based on the average time it takes for an encoder count to occur.  Since I am not able to set TIM1 to Slave Mode, I can use the Compare Interrupt of TIM2 to reset the Counter of TIM1.  This may fail if the fractional encoder count is high (e.g. 0.9 encoder counts, where an encoder count is expected to take 3.us), as the interrupt may not reset TIM1 in time due to the overhead you mentioned.
  2. Since I know that I want to trigger TIM3 every 7.4 encoder counts, I can use a counter variable in memory to store the exact encoder count I am at.  This variable will be updated to 7.4, 14.8, 22.2, etc.  Since the number of whole encoder counts since the last trigger of TIM3 can either be 7 counts or 8 counts (for example, 7 - 0 = 7, 14 - 7 = 7, and 22 - 14 = 8), and the fractional encoder count value changes, I can on every Update Interrupt of TIM1, update the Period (ARR register) of TIM1, and the Pulse (CCR register) and Period (ARR register) of TIM2.  This way, I avoid resetting the counter of TIM1, and have plenty of time (up to 7 encoder counts or 20 us) to update the Period (ARR register) of TIM1.  However, I also need to be able to update the Pulse (CCR register) of TIM2.  This is why I was asking about the overhead of an interrupt relative to the reset action of Slave Mode - Reset Mode.  I was wondering if I preload the Pulse (CCR register) of TIM2 during the update interrupt, will it always happen after the slave mode reset loads the CCR preload register into the shadow register.

I am also open to other approaches, these just happen to be the two that I have thought of.

henry.dick
Senior II
Posted on March 30, 2018 at 17:12

for Q1, the compare match may be missed: TIM2 starts right away while you are still executing TIM1 ISR, setting the CCR1.

the right way to do it is to set up a TIM2 ISR at its end whereby you would update the CCR1, waiting for it to be trigger next time by TIM1.

Posted on March 30, 2018 at 15:58

Before we discuss this further, let's make this one clear:

I have TIM1 set to Encoder Mode,

Encoder mode is intended for... encoders. The virtue of encoders is 1. possibly changing direction; 2. varying rate; 3. possibly high frequency oscillations at around any point, including 0 (which then implies high update event rate in the timer). This makes encoder mode impractical for any timing-related application; it's good only for... erm... encoders.

So, are you absolutely sure you want to use Encoder mode? Why?

JW

Posted on March 30, 2018 at 16:04

Hi Jan, the reason I want to use encoders is that I have a motorized stage whose encoder I can tap into.  I would like to synchronize the triggering of my LED with the stage so that every time the stage moves a fixed spatial distance (e.g. 7.4 encoder counts), I trigger the LED.  We have done testing of the stage to show that even if we set it to run to constant velocity, it will deviate from that velocity, so it is better if we synchronize the triggering of the LED with the encoder of the stage to the best of our ability.