Timers - Output Compare and Interrupts

📚 Lesson: Output Compare, Interrupts, and RCR.

  • In our previous lessons, we used Basic Timers to generate Update Events when a timer’s counter (CNT) reached its maximum value (ARR) and rolled over. This is great for triggering periodic interrupts.

  • What if we want to change the state of an output pin precisely when a specific time has passed, or generate multiple different timing events from a single timer? For this, we use Output Compare (OC) combined with hardware interrupts, and we can further manipulate update events using the Repetition Counter Register (RCR).

1️⃣ Concept: Output Compare (OC)

  • Output Compare allows the hardware to automatically control an output pin when the timer’s Counter (CNT) matches a specific value you set in the Capture/Compare Register (CCRx).

  • When CNT == CCRx, a “Match Event” occurs. The hardware can be configured to automatically perform one of the following actions on the assigned GPIO pin:

  • Set the pin HIGH.

  • Reset the pin LOW.

  • Toggle the pin state.

  • Among other features, Output Compare can also generate an interrupt when a match occurs, allowing your software to respond to the event. For more details, check the reference manual, particularly the TIMx_CCMRx and TIMx_CCER registers.


2️⃣ Output Compare with Interrupts

  • Toggling a pin automatically is powerful, but if the timer keeps counting up to its maximum value (e.g., 0xFFFF), the next toggle won’t happen until the counter rolls over and reaches the CCR value again.

  • To create a continuous, specific frequency (e.g., toggling a pin every 50 µs), we must advance the target CCR value immediately after every match. Instead of trapping the CPU in a while(1) polling loop to check the status register, we use Output Compare Interrupts.

Generating Multiple Toggles with One Timer

  • A timer like TIM15 has multiple channels. We can configure Channel 1 to toggle every A µs and Channel 2 to toggle every B µs, all running off the exact same timer counter.

  • For instance, if the timer tick is configured for 1[us] Ticks (e.g., a 50 MHz system clock with a prescaler of 50-1), then 500 µs requires 500 ticks.

  • The Interrupt Handler: When the hardware toggles the pin and fires the interrupt, we clear the flag and advance the CCR by the exact tick amount.

void TIM15_IRQHandler(void)
{
  // Channel 1 Match: Advance by 200 ticks (20 µs)
  if(TIM15->SR & TIM_SR_CC1IF)
  {
    TIM15->SR &= ~TIM_SR_CC1IF;  
    TIM15->CCR1 += 200; 
  }
  
  // Channel 2 Match: Advance by 500 ticks (50 µs)
  if(TIM15->SR & TIM_SR_CC2IF)
  {
    TIM15->SR &= ~TIM_SR_CC2IF;
    TIM15->CCR2 += 500; // Note: Always add the exact interval amount to avoid jitter
  }
}

3️⃣ The Update Interrupt and Repetition Counter Register (RCR)

  • Alongside Output Compare, we can still use the timer’s Update Interrupt (UIF) to trigger events when the counter overflows (reaches ARR).

  • The RCR (Repetition Counter Register): Available on advanced and some general-purpose timers (like TIM1, TIM15, TIM16, TIM17), the RCR acts as a prescaler for the Update Event.

  • Instead of generating an Update Event on every overflow, the hardware delays the event until the counter has overflowed RCR + 1 times (up to the RCR maximum value, which is 255).

RCR Math Example

  • Suppose TIM15 is ticking every 1us, and ARR is left at its default maximum 16-bit value (0xFFFF or 65535). A single overflow takes:
\[65536 \times 1 \text{ us} = 65.536 \text{ ms}\]
  • If we want to manually toggle a pin in the Update Interrupt approximately every 393 ms, we can calculate the required number of overflows:
\[\text{Overflows Required} = \frac{393 \text{ ms}}{65.536 \text{ ms}} \approx 6.00\]
  • Since the event fires every RCR + 1 overflows, setting TIM15->RCR = 5; yields exactly 6 overflows per interrupt, resulting in a perfectly timed 393.216 ms pulse width.
if(TIM15->SR & TIM_SR_UIF)
  {
    TIM15->SR &= ~TIM_SR_UIF;
    GPIO_Toggle(GPIOA, 5); // Toggles ~every 393ms thanks to RCR=5
  }

4️⃣ Brief Review: Output Compare vs. Hardware PWM

  • As we’ve seen, managing pin toggles with Output Compare Interrupts is highly flexible, allowing multiple different asymmetric intervals on the same timer. However, it requires the CPU to fire an interrupt and manually execute code (like TIM15->CCR1 += 200;) for every single edge transition.

  • Pulse Width Modulation (PWM) is a specialized extension of Output Compare designed to remove the CPU from the equation entirely.

When to use which?

  • Output Compare + Interrupts: Use this when you need multiple channels doing different things at different intervals, when you need to execute C code alongside the pin toggle (e.g., reading a sensor exactly when the pin goes HIGH), or when generating highly asymmetric and shifting waveforms.

  • Hardware PWM: Use this when you need a continuous, stable square wave (like dimming an LED or driving a motor) where the frequency (ARR) and duty cycle (CCR) remain relatively constant. In PWM, the hardware resets the counter at ARR and toggles the pin at CCR automatically, meaning your while(1) loop and your CPU remain 100% free.

  • No interrupts are required for PWM, and the CPU can sleep or perform other tasks while the hardware handles the waveform generation.


5️⃣ Summary of Timer Roles

Register Purpose in OC / PWM Analogy
PSC Sets the speed of the timer “tick”. How fast the clock’s second hand moves.
ARR Sets the Total Period (T) / Overflow. How many ticks make up one full wave or rollover.
CCR Sets the Match Event trigger point. The exact tick where the signal switches state.
RCR Sets the Update Event multiplier. Skipping daily alarms to only ring every $N$ days.