5 Tips for driver design
Every embedded software application must at some point access the lowest levels of firmware and control the hardware. The design and implementation of drivers is critical to ensuring a system is able to meet its real-time requirements. Below are five tips every developer should consider when designing a driver.
Tip #1 – Use a design pattern
A design pattern is a solution to a problem that occurs repeatedly in software. A developer could reinvent the solution from scratch, wasting precious time and budget, or instead can open his toolbox of solutions and pick the one that best fits the problem. Low level drivers have been around since the dawn of the microprocessor and are a well understood problem. So why not utilize existing solutions?
Driver design patterns generally fall within four categories; bit bang, polling, interrupt driven and direct memory access (DMA). Developers choose bit bang pattern when a microcontroller either does not have an internal peripheral to perform the function or when all of those internal peripherals have been used up and one more is required. Bit bang solutions can be efficient but usually require a fair amount of software overhead in order to implement the capability. A bit bang pattern literally has the developer manually implementing a communication protocol or external behavior.
Polled patterns simply monitor for an event in a round-robin fashion. A polled pattern is great for simple systems but many modern applications require interrupts. Interrupts provide the ability to handle the event the moment it occurs rather than waiting for code to manually check for it. DMA patterns allow for another peripheral to handle the data transfer needs and let the driver be hands-off.
Tip #2 – Understand the real-time behavior
The ability of a real-time system to meet its deadlines starts with its drivers. Poorly written drivers will be inefficient and provide the potential for an unsuspecting developer to compromise their system's performance. Drivers come in two flavors that a designer should consider; blocking and non-blocking. A blocking driver prevents any other software from executing until the driver has completed its work. For example, a USART driver might put a character into the transmit buffer and, rather than move on, wait for the end-of-transmission flag before proceeding.
A non-blocking driver, on the other hand, will most often utilize interrupts to perform its functions. The use of interrupts prevents the driver from blocking software execution while it waits for an event to occur. The USART driver may put a character in the transmit buffer but then the main software moves on to its next instruction. The setting of the end-of-transmission flag causes an interrupt to fire, allowing the driver to then take its next action.
Regardless of type, in order to keep real-time performance and help prevent system glitches developers must understand the average and worst case execution times of their drivers. The integrity of the system is potentially at stake and perhaps much more if the system is safety critical.
Tip #3 – Design for reuse
Time and budgets are short so why reinvent the wheel? Reuse, portability, and maintainability are critical requirements in driver design. Many of these features can be accounted for through the design and use of a hardware abstraction layer.
A hardware abstraction layer (HAL) provides developers with a way to create a standard interface to control a microcontrollers’ peripherals. The abstraction hides the implementation details and instead provides visible functions such as Usart_Init and Usart_Transmit. The idea is that any USART, SPI, PWM or other peripheral has common features that all microcontrollers support. The use of the HAL hides the low level, device-specific details, allowing application developers to focus on the application requirements and not how the low level hardware works. At the same time the HAL provides a vessel for reuse.
Tip #4 – Reference the datasheets …. yes all of them
Microcontrollers have gotten a little complicated over the last few years. Once upon a time everything that one might want to know about the microcontroller was contained within a single datasheet consisting of 500 or so pages. Today's 32-bit microcontrollers often contain datasheets consisting of a part datasheet, a family datasheet, and hundreds of pages for each peripheral, in addition to all of the errata. A developer has a few thousand pages of documentation to go through if they really want to completely understand the part.
Unfortunately, all of these datasheets are needed to really implement a driver properly. A developer at the start should collect and sort each datasheet and the information contained within. Usually each one of them will need to be consulted to get a peripheral up and running. Critical information is sprinkled (and hidden) in each type of datasheet.
Tip #5 – Beware peripheral faults
I recently had the opportunity to port some drivers from one family of microcontrollers to another. The manufacturer and the datasheets all suggested that the PWM peripheral was identical between the two families. Running the PWM driver, on the other hand, showed that despite this assertion there was something very different between the two. The driver worked on the original part and not on the new one.
After painstakingly reviewing the datasheets, I discovered a footnote in a completely unrelated datasheet that the PWM peripheral at power-on was in a fault state and that a single bit hidden away in an obfuscated register needed to be cleared.
At the start of the driver implementation, identify the peripheral faults and any seemingly unrelated fault registers.
Driver design and implementation is a critical component to embedded system development. To further explore driver design patterns and how to build an embedded system that can access the internet, consider attending my next CEC Course from EDN's sister publication, Design News, on “Design Patterns and the Internet,” starting the week of October 19, 2015. We’ll be covering driver design for an I2C device on the STM32 along with creating an internet connected weather station using the Electric Imp.
Jacob Beningo is a Certified Software Development Professional (CSDP) whose expertise is in embedded software. He works with companies to decrease costs and time to market while maintaining a quality and robust product. Feel free to contact him at email@example.com, at his website www.beningo.com, and sign-up for his monthly Embedded Bytes Newsletter here.