In this lesson, you will learn how to leverage timer auto-reload registers to trigger consistent, periodic interrupts that act as a heartbeat for your background tasks.
Moving Beyond Busy-Waiting
Up until now, you have used hardware timers to create precise delays, often blocking the CPU while waiting for a counter to finish. However, in complex systems, you cannot afford to have your main program loop stuck in a delay loop. Instead, we use Periodic Interrupts to offload repetitive tasks—like reading a sensor or blinking an LED—to a background process.
The Auto-Reload Mechanism
The core of this technique lies in the Auto-Reload Register (ARR). When the timer counter reaches the value stored in the ARR, the hardware does two things automatically:
- It triggers an Interrupt Request (IRQ) to the processor.
- It resets the timer counter to zero and restarts the count, creating a seamless, repeating cycle.
By setting the prescaler and the ARR, you define the exact frequency at which the timer reaches its limit and fires an interrupt. Inside the resulting Interrupt Service Routine (ISR), you can perform time-critical actions without cluttering your main() loop.
Practical Implementation
To set this up, configure your timer so that the interrupt is enabled (Update Interrupt Enable). Your code will look conceptually like this:
// Concept: Configure timer for periodic interrupt
void Setup_Timer_Periodic(uint32_t frequency) {
// 1. Configure Prescaler for desired clock speed
Timer->PSC = (SystemClock / TargetClock) - 1;
// 2. Set Auto-Reload Register for the period length
Timer->ARR = (TargetClock / frequency) - 1;
// 3. Enable the Timer Update Interrupt
Timer->DIER |= TIMER_UPDATE_INTERRUPT_ENABLE;
// 4. Start the timer
Timer->CR1 |= TIMER_ENABLE;
}
// ISR runs automatically every time the ARR is reached
void Timer_IRQHandler(void) {
// Clear interrupt flag to prevent infinite re-entry
Timer->SR &= ~TIMER_UPDATE_FLAG;
// Perform periodic task here (e.g., polling sensors)
Execute_Background_Task();
}
By decoupling your periodic tasks from the main() loop, you ensure that even if your main logic becomes complex or slow, your background timing remains rock-solid and predictable.
Key Takeaway: By using the Auto-Reload Register to trigger hardware interrupts, you shift from blocking, synchronous code to an event-driven model that executes tasks with surgical timing precision.