Home > On-Demand Archives > Talks >

Decoupling Drivers from the Hardware Platform

John Taylor - Watch Now - EOC 2025 - Duration: 34:10

Decoupling Drivers from the Hardware Platform
John Taylor

This talk is about how to design drivers that are decoupled from a specific hardware platform so they can be reused on different microcontrollers. Decoupled drivers also enable the creation of a functional simulator for your embedded application. Writing decoupled drivers does not require more effort or time to implement than a traditional platform-specific driver.

A decoupled driver requires a public interface that is defined without direct hardware dependencies and a mechanism (e.g., a Hardware Abstraction Layer) that decouples the driver implementation from a specific hardware platform.

There are numerous ways to create interfaces for a Hardware Abstraction Layer (HAL). The following list covers three ways to create abstract interfaces that decouple a driver from platform-specific dependencies. In this context, an “abstract interface” is an interface that defines a behavior with late binding.

  • Façade. Using link-time bindings.
  • Separation of concerns. Using compile-time bindings.
  • Polymorphic. Using run-time bindings.
M↓ MARKDOWN HELP
italicssurround text with
*asterisks*
boldsurround text with
**two asterisks**
hyperlink
[hyperlink](https://example.com)
or just a bare URL
code
surround text with
`backticks`
strikethroughsurround text with
~~two tilde characters~~
quote
prefix with
>

Prabo
Score: 0 | 1 week ago | no reply

Hi John,
This is an excellent presentation. Thank yo very much. I too am very passionate about best practices and clean architecting techniques for embedded systems. My approach so far had been slightly different (but pretty much the same concept) for decoupling. As we have several products that has same peripherals in the PCB (i.e., wireless modules, various sensors etc), my approach had been to decouple those sensor/wireless module interfaces from the application mainly using function pointers. I use function pointers to point to the board specific functions and set them in a separate manager module which has to be specific to each board. The design patterns you explained here are definitely going to make my future architecting cleaner. Thank you very much.

SteveM
Score: 0 | 2 weeks ago | 1 reply

Many of the products that I work on are 8 bit and run on coin cell batteries. Would this kind of decoupling be a good fit for these products or is it too heavy? A few years back STM8 were impossible to find so several product lines switched to AVR. It wasn't a huge lift, but could have been easier with better decoupling.

johnttaylorSpeaker
Score: 0 | 1 week ago | 1 reply

Yes, you can decouple your drivers on small 8bit platforms. The "facade" and the "separation of concerns" patterns I presented in my talk do not add any run-time or memory footprint overhead as part of the abstraction mechanisms. For example, the separation-of-concerns can be used to take full advantage of the preprocessor to replace function calls with macros.

SteveM
Score: 0 | 1 week ago | 1 reply

is the polymorphic method using C++ virtual classes going to be too much for an 8 bit MCU?

johnttaylorSpeaker
Score: 0 | 1 week ago | no reply

It depends. Can a 8bit MCU run C++ - absolutely. The Arduino framework is really C++ under the covers - and the framework does use virtual/polymorphic methods. C++ uses 'vtables' to implement virtual functions - where the vtable is just a table of function pointers. Vtables are not large - but take code space (per class not per instance) and additional pointer added the class's data footprint (per instance). Run time execution (CPU cycles and stack) of a virtual method - has the additional cost of a "functional calling a function".

It all comes down what are your specific requirements and hardware constraints. For example, you may have limited memory (code + data) - but your application is "small enough" that the extra overhead of C++ virtual functions is a don't care. Or your application is "too large" for your target HW platform and you are already "optimizing" to reclaim every byte/bit of code/data you can - so the introduction virtual functions is counter productive.

MO
Score: 0 | 1 week ago | no reply

Thank you. Excellent presentation. I learned something new !!!

datamstr
Score: 0 | 1 week ago | no reply

Excellent presentation!

MarkB
Score: 1 | 2 weeks ago | no reply

This is a great illustration of applying architecture principles to a very practical embedded development context (developing drivers for multiple platforms). Anyone whose appetite is whetted by this presentation should definitely read John's book "Patterns in The Machine". His LHeader pattern is covered in more detail in chapter 13 "More About Late Binding", as is the LConfig pattern.
I'm going to check out his new book now.

Burkhard
Score: 1 | 2 weeks ago | 1 reply

I love how you write the decoupled drivers for multiple platforms without the use of a single #ifdef.

johnttaylorSpeaker
Score: 2 | 2 weeks ago | no reply

Thank you. Many decades ago, I worked on a project where we had to port a commercial RTOS to our custom hardware. Wading through the RTOS's BSP code and the rat nest of #ifdef they use to support two dozen plus hardware platforms was an "anti-pattern" I have not forgotten.

Otzen
Score: 0 | 2 weeks ago | 1 reply

Hi
This looks a lot like how I have written drivers for many years, so we are in sync on that ;-)
My challenge is that it is relative simple with the basic cases, like your PWM (on/of/duty) but often when we want to utilize more advanced features of the HW it becomes harder to maintain a neutral interface.
For example ADC's. The simple version is getValue. but different ADC's have different features, like some ST ADC's have a set of four fast channels, that can interrupt other ongoing ADC conversions, to get those values fast, and can be setup to sample all four values in one call to HW. Some TI ADC's can be setup in chains so you just trigger them once and it will sample all values in that chain. Some ADC's can synchronize reads between several instances of ADC devices on the chip.
Not to forget the PWM, if you use those for motor control, it becomes even more complex.
And because we want to always use the cheapest microcontroller, that tend to be the smallest one, we need to squeeze all the performance we can, out of the HW. And thus we can't just ignore those special features.
But still the need for switching HW fast is there, sometimes because of cost down, sometime because of supply issues, that saw a lot in the tail of the Covid pandemic.
Any thoughts or advice on that topic?

johnttaylorSpeaker
Score: 2 | 2 weeks ago | no reply

Otzen,

Thank you for your question. Drivers can get very intertwined with the hardware especially when performance and bandwidth are principal concerns. For me, the best way to tackle these problems is on a case-by-case basis, i.e. be practical and there is no one-size-fits-all solution. That said my advice is:

  1. Some where in your driver stack - have a interface/layer that is totally decoupled from the platform. It is judgement call on where in the stack that layer is.

  2. When tight coupling with the HW is avoidable - still decouple the driver code so that it is not dependent on a specific BSP or application. A simple example: writing a ST32 F4 specific blocking, interrupt driven serial driver that is decoupled from a specific BSP (using a indirect header include mechanism that I discussed in my webinar). Yes the driver is limited in scope of its "reusability" - but at the same time most companies try to standardize on a small number of MCUs. And when the driver is not reusable on the 'next' HW - at least you have a reference design that defines the scope/functionality of the new driver.

  3. Don't try to build the end-all, be-all, universal driver. Built just what you have immediate requirements for. Once you got the an proven implementation - you can look at potential refactoring - but only if it make sense (i.e. there is good ROI for the refactoring work).

OUR SPONSORS & PARTNERS