I know this would generate a lot of confusion among embedded software engineers whenever they hear that there's a difference between Firmware and Embedded Software. Firmware are set of codes that communicate directly with a particular MCU and its peripherals, but Embedded software are application codes that are written for a specific device irrespective of the MCU, they take care of the high-level operations and features while firmware takes care of the low-level task.
As we all know that one of the most famous guys when it comes to clean code design or architecture is Robert C. Martins (majorly known as Uncle Bob), In chapter 29 of his book, Clean Architecture, he stated the relationship between Software, Firmware and Hardware, quoted below:
Software is a thing that can have a long useful life, but firmware will become obsolete as hardware evolves.
Although Software does not wear out, it can be destroyed from within by unmanaged dependencies on firmware and hardware.
In this article, I have stated three practices that would make your embedded software reusable, which are:
Good embedded design and architecture.
Code Refactoring
Document as you code.
Reusable embedded software must have a good embedded design and architecture
One of the reasons why many of us moved from Assembly to C language decades ago was because .asm is very difficult to reuse, maintain and scale. We only had the choice of writing firmware and not software, which means the code won't be resuable.
Basically, a long lasting embedded code architecture must have at least three (3) layers which are Software, Firmware and Hardware.
I would assume you already know what software, firmware and hardware means, so I won't spend time explaining them in detail.
Embedded software engineers need to develop habit for a three layer architecture so that they can easily reuse and port code from one hardware to another. My embedded codes are always known to have the Driver layer (firmware) and the Application layer (software). The application layer has nothing to do with the hardware but fully depends on the driver layer. With this kind of architecture, your co-programmers or end-users don't need to learn any MCU datasheet or reference manual, they only have to focus on a specific device to write their code.
Creating a layered architecture for the code is not just revelant but it's a MUST thing to do.
Let's assume we have a board called Leo developed inhouse by a company, the board featuring different colors of LEDs, the end users doesn't need to know what the MCU pins are, they only need to learn how the API works and the capabilities of the device but not the MCU internals.
Code example for the Software
led.write(BLACK);
led.toggle(RED);
Code example for the Firmware
case RED:
io.write(LED_G_GPIO_Port, LED_G_Pin, SET);
io.write(LED_B_GPIO_Port, LED_B_Pin, SET);
io.write(LED_R_GPIO_Port, LED_R_Pin, RESET);
break;
case GREEN:
io.write(LED_G_GPIO_Port, LED_G_Pin, RESET);
io.write(LED_B_GPIO_Port, LED_B_Pin, SET);
io.write(LED_R_GPIO_Port, LED_R_Pin, SET);
break;
case BLUE:
io.write(LED_G_GPIO_Port, LED_G_Pin, SET);
io.write(LED_B_GPIO_Port, LED_B_Pin, RESET);
io.write(LED_R_GPIO_Port, LED_R_Pin, SET);
break;
Please don't get me wrong, sometimes I write firmware only if the hardware isn't going to be used for a long time or large scale, just a quick code to test the MCU functionality.
Code Refactoring
Yes, this is a topic that I've been meaning to talk about for a long time. As a programmer, code readability is very important, other programmers or end-users should be able to read and understand your code with fewer or lesser comments in it, and all this depends on your structured, meaningful naming conventions and cross-referencing. Variable and function declarations should be explanatory enough for programmers to understand its purpose without diving into the called function.
Refactoring produces clean code that is easy to read, understand, maintain and increases the reliability and quality of the software.
I've once been in a situation where existing code was difficult to modify due to dirty codes that occurred because of tight deadlines, it took me so much time to clean and refactor before adding more functionalities. It will be very difficult to add more features to a dirty code because it will always end up being a mess, that's why it highly recommended to refactor your code first.
Below is an example of a function that explains itself, it saves end-user time to read through the code.
if(isTemperatureValueExceeded()) {
// do something
}
The naming convention makes it easier and faster to read and understand what the function does, your code has to be well structured and flexible than the existing code.
Document as you code
Only few programmers takes documentation seriously, they believe nobody is going to understand their code except them.
Let's assume you wrote a code and you picked up the same code after five years to read, no doubt that you won't remember everything in the code if there isn't a proper documentation.
We must take documentation as serious as writing the code. I know programmers prefers to write comments in their code to explain what a particular line does but it won't be as effective as a proper documentation. A tool like Doxygen helps to interpret declarations and specially-formatted comments to generate HTML documentation to ease you of the stress of documentating every time changes are made in your code.
Documenting as you code improves the correctness of your documentation and also reduces the effort it takes to generate the documentation.
Like or comment below if you find this helpful.
Top comments (1)
Hey there! Great insights on reusable embedded software in your post. It's crucial for efficiency and scalability. If you're looking to hire talented embedded software engineers to implement these practices, check out this link for some useful tips: cleveroad.com/blog/hire-embedded-s.... Thanks for sharing your expertise!