Embedded software developers have grown used to working at the lowest, nitty gritty hardware level within a microcontroller based system. Twiddling and manipulating bits and bytes is what embedded software developers were born to do. The embedded software industry is changing and that change is requiring developers to start working at higher levels of abstraction which requires designing and creating API’s that allow software to be reused. Let’s examine seven tips for developing great API’s.
Tip #1 – Make it an iterative process
When designing an API or a HAL, don’t assume that everything is going to go right the first time. There is no such thing as one API to rule them all and expecting to create such an API especially on a single try is a setup for failure. Instead, start with a draft API with the expectation that it will change slightly in future iterations. Usually over the course of three or four projects, the API will stabilize to the point where further changes are minor to non-existent.
Tip #2 – Examine more than one microcontroller datasheet
If the plan is to create an API that can be used across multiple microcontroller vendors, developers must look at more than a single microcontroller datasheet. Developers should examine the same peripheral for multiple microcontrollers and make a list for all the common and uncommon features. The common features should be rolled up into the API since they are undoubtedly industry standard features while the uncommon features can be implemented in an API extension only if those features are required.
Tip #3 – Use no more than 10 interfaces per module
The human mind can only consistently remember approximately 10-12 pieces of information that belong together. Developers should aim to keep their API’s with no more than approximately ten interfaces. Expanding well beyond this number will make it difficult to remember the calls and can also make the interface look complex and may even obfuscate understanding. Find ways to refactor interfaces by using control and configuration structures.
Tip #4 – Test pre-conditions and post-conditions
A great API implementation will not assume that the calling function or application has done everything that it is supposed in order for the function to work correctly. Developers should use assertions within the API to test that all pre-conditions are met and even to test post-conditions to ensure that the API has successfully performed its function. Don’t assume, as many developers do, that everything has been setup correctly and will execute properly. Design and implement the interface defensively!
Tip #5 – Logical Naming Conventions
A great API will have logical naming conventions that allow developers to easily recognize and recall the API interfaces. Using cryptic letters at the front of API’s will often make developers scratch their heads and question what that symbol meant again. Be explicit in the naming convention and follow best practice recommendations such as starting the naming convention from the general and working towards the specific.
Tip #6 – Provide a method for extending the interface
The goal is to create a great clean interface that is easy to understand and use but also contains the most common elements that developers need during their development. There are times when there may be uncommon features that developers will want to use in the microcontroller, such as a GPIO hardware debounce that every microcontroller may not have. In these cases, developers will want to make sure that they have a mechanism built into their API’s that allow the interface to be extended. This can be done by allowing a pointer to a new interface structure or by creating register accesses into an API that allow low level manipulation and higher level interfaces to be created.
Tip #7 – Build interrupt handling into the API
In order to make sure that interrupts are handled properly, it is easier for the API developer to handle interrupts internal to the API so that interrupts are a black box to the API user. This means that there needs to be a mechanism used to assign functions to interrupt handlers in the higher-level application code. One way to do this is to add callback registration into the API so that interrupts can be assigned their executable code in the application code. This allows the API to ensure that interrupts are handled properly, but also allow developers to override those defaults with their own custom code.
Developing API’s and HAL’s are nearly a requirement now if developers want to decrease costs and time to market. Microcontrollers have reached a level of complexity, performance and capability that are comparable to early x86 microprocessors. With such complexity in systems today, designing API’s to maximize code reuse is a must. These seven tips should help developers looking to reuse their own code more able to developer their own API’s and HAL’s.
To learn more about API and HAL design, consider attending Jacob’s free DesignNews Digikey Continuing Education Course “Designing API’s and HAL’s for Real-time Embedded Systems” located here.
Also check-out Jacob’s book on Reusable Firmware Development: A Practical Approach to APIs, HALs, and Drivers