In this lesson, you will learn how to leverage transmit-empty interrupts to offload data streaming to hardware, freeing your CPU from the inefficiency of waiting for slow serial peripherals.
The Problem with Polling
Previously, we explored polling-based byte transmission, where the processor enters a loop, repeatedly checking a "Transmit Data Register Empty" (TXE) flag before sending the next byte. While simple, this strategy is wasteful. Because a UART peripheral operates at a much lower speed than the CPU, the processor spends the vast majority of its cycles doing absolutely nothing but waiting for the hardware to finish shifting out the previous character.
Leveraging Interrupts for Background Streaming
Instead of blocking execution, we can use an interrupt-driven approach. The core idea is to rely on the hardware to "ask" for data only when it is ready.
- Initialization: You load your data into a circular buffer. You then enable the "Transmit Interrupt" (TXIE) in the peripheral.
- First Trigger: You write the first byte to the transmit register manually.
- The Interrupt Loop: Once the hardware finishes sending that byte, the TXE flag triggers an interrupt. Your Interrupt Service Routine (ISR) pauses the main program, grabs the next byte from your circular buffer, and writes it to the transmit register.
- Completion: If the buffer is empty, the ISR disables the interrupt so it doesn't fire continuously, signaling that the transmission is complete.
This architecture allows the CPU to execute complex logic, handle sensors, or process math while the hardware handles the slow task of shifting bits onto the wire in the background.
void UART_TX_ISR(void) {
if (!Buffer_IsEmpty(&tx_buffer)) {
// Send next byte and clear the interrupt flag
UART_DR = Buffer_Pop(&tx_buffer);
} else {
// Disable interrupt so we don't trigger when idle
UART_DisableTXInterrupt();
}
}
By decoupling the transmission speed from the CPU clock, your system remains highly responsive, effectively multitasking by letting the hardware interrupt the flow only when it needs attention.
Key Takeaway: By using transmit-empty interrupts to stream data from a circular buffer, you transform a blocking, CPU-intensive wait process into a background task that executes only when the hardware is ready for more data.