Interrupt-Driven or Polled I/O?
When designing an embedded system, a fundamental decision is the type of I/O scheme. Should we use interrupt-driven or polled I/O? We'll explore how this important decision has an impact not only on the firmware but on the hardware as well.
Fishing for I/O
Polled I/O is much like fishing: there is a lot of waiting. In a polled I/O scheme, software is written to check the status of an I/O device. If there is no data to be received (Rx) or if the device is not ready to transmit (Tx), the software must wait. This technique is called busy-waiting. After waiting for some time, eventually the software can receive or transmit some data, and the rest of the software can execute (see Diagram 1).
Diagram 1: Flowchart for polled I/O
If this sounds inefficient, that's because it is. It's a very simple way of dealing with I/O with no surprises for the developer, but a lot of CPU time is spent waiting for data to transfer.
Interrupt-driven I/O uses a little hardware to aid in efficiency. When the I/O device has already received some data or the I/O device is ready to transmit, an interrupt is generated, and the appropriate interrupt service routine (ISR) handles reading or writing some data. Interrupts add a level of complexity, and can be very difficult to debug, but the resulting system wastes no time waiting for data to transfer (see Diagram 2).
Diagram 2: Flowchart for interrupt-driven I/O
Yes, ISRs can be this simple. The above diagram shows separate ISRs for the receiver and transmitter if there are two separate interrupts. Sometimes there is only one interrupt for both functions, so the ISR is a little more complicated.
Development Time
From the simple example above, it may appear that interrupts and ISRs are very easy to implement. In many cases they are, but they can also be very complicated. The development cost of ISRs is generally higher than polled I/O. Consider that you're not only developing the functionality of the polled I/O routines, but you've also got to deal with how interrupts work in your particular system. Polled I/O occurs at very specific points in the firmware (i.e. whenever the polled I/O routines are called). Interrupts can happen at anytime. An interrupt bug can cause anything from simple system crashes to really frustrating intermittent data loss and unreliable system performance.
Which Is Best?
The decision to use polled or interrupt-driven I/O is usually pretty easy. If you've got the interrupt, use it! In the long run, even though it may take more time to develop, interrupt-driven I/O leads to a solid design, predictable performance, and holds up well to modifications of application code. However, if you've got a very simple design, using a straightforward polled I/O scheme may be preferable. There are a few factors which can influence your design.
Throughput. If your system throughput requirements are very high, you must use interrupt-driven I/O. Polled I/O won't work because the CPU efficiency won't be high enough to move the data and do everything else. However, if you've got a specialized system with extremely high data rate requirements, it may actually be faster to use polled I/O. That's because polled I/O doesn't incur any interrupt overhead or interrupt latency (see below).
Interrupt latency. Interrupt latency is the time it takes for the processor to respond to an interrupt event. These times range from 100's of nanoseconds to 10's of microseconds. In very rare cases, if interrupt latency is long or unpredictable, it may be preferable to poll for an event in very controlled conditions to ensure quick capture of an event.
Interrupt availability. Most of today's microcontrollers have several external interrupts. If a hardware interrupt is not available, you will have no choice but to poll for I/O (see Simulating Interrupt-Driven I/O below). It's best to work with the electrical engineers to make sure that the microcontroller you choose works for both the electrical and firmware designs.
By the way, there is no reason that you can't incorporate both types of I/O in a system. For example, perhaps your Ethernet driver uses interrupts and your serial port driver doesn't. It's all a matter of using a suitable technique for each situation.
Simulating Interrupt-Driven I/O
So what if you've got a system where there are no more external interrupts and you want some interrupt-driven I/O? You can simulate interrupt-driven I/O by using a dedicated task in an RTOS. While you can't really address interrupt latency with this technique, it gives CPU time to other tasks when there is no data to receive or transmit. An example task is shown below which handles receiving and transmitting characters on a UART.
Diagram 3: Flowchart of UARTTask
Conclusion
Both polling and interrupt-driven techniques should be in the embedded designer's toolbox. While we recommend using interrupt-driven I/O whenever possible, it doesn't really matter which technique you use as long as the final design meets product requirements, is easy to understand, and is easy to maintain.