Drivers can be architected and implemented in many different ways but no matter the method there is one critical characteristic that needs to be considered up front; to block or not to block. This decision can impact not only the efficiency of the driver but also the real-time behavior of the system.
A blocking driver is by far the easiest to implement. The driver when executing in blocking mode runs without any interruptions or concerns for any other process that may require the CPU. Blocking drivers are called so because they don’t want to be interrupted. They take the CPU and run with it no matter what else might be going on in the system. A classic example of a blocking driver can be seen in Figure 1.
Figure 1 – Blocking Driver
This example shows a series of bytes that need to be transmitted on a serial interface. After loading a byte into the transmit buffer the driver polls the transmission complete flag continuously until transmission is completed. In effect, while data is being transmitted the driver is blocking any other code from using the microprocessor. This wastes CPU cycles and makes the blocking driver inefficient. Figure 2 shows an example of what a blocking driver’s sequence diagram would look like.
Figure 2 – Blocking Driver Sequence Diagram
Blocking drivers are simple and make sense when transmitting small amounts of data over high speed interfaces or when the driver is being used in a time independent application. Non-blocking drivers can be used when the driver execution time is consider too long and other portions of the application need access to the microprocessor. Non-blocking drivers are slightly more complicated than blocking drivers because they require the use of interrupts to signal when the driver needs access to the CPU. Figure 3 shows an example of what a non-blocking driver’s sequence diagram might look like.
There are a number of practical driver design patterns that would utilize a non-blocking architecture. For example, an analog to digital converter driver might interrupt when a channel has sampled. The interrupt would store the measured value and increment to the next channel to be sampled. This behavior allows the converter to sample analog signals in the background while still executing application code. The important point to understand is that by using interrupts in the driver, the developer is then able to execute other code on the microcontroller rather than waste precious computing cycles.
Both blocking and non-blocking drivers are perfectly legitimate design choices. Selecting the method to employ is determined by the requirements and needs of the system. In many applications a simple blocking driver is sufficient. Applications that are time critical or are short on computing cycles benefit greatly from a non-blocking methodology. For a comparison between the two methodologies refer to Table 1.