Over the years, as I have reviewed and analyzed embedded software, I have noticed a few common issues that even to this day remain. Issues that no matter the company size or how mature the development process always seem to pop-up. In order to help alleviate these common issues, there are 10 questions every developer should ask themselves when reviewing their code that can help find potential bug-ridden areas.
1. Does the program build without warnings?
Code cannot be loaded on a target without a successful compile. A successful compile involves the programmer diligently removing any syntax errors in order to make the compiler happy and have it create an output file. But a compiler can build an application without error yet still find other anomalies, such as an implicit cast, that it reports as a warning. A truly successful compilation of a program, then, should involve not just compiling with zero errors but also with zero warnings. This definition of successful compilation may seem obvious, but failure to resolve warnings is an issue that I continue to see from both green and senior engineers alike. The worst case I have seen had more than 1,000 warnings! The most disturbing case had just one: a simple implicit cast warning of an unsigned integer into a float.
2. Are there any blocking functions?
One primary purpose for a microcontroller (MCU), in my opinion, is to be able to handle real-time events. MCUs should be able to handle those events in a very deterministic manner that can be measured and proven. Yet one of the common mistakes that I often see is that a driver or some application code segment is written such that it enters a loop or calls a delay function for an extended period of time. But a loop or delay prevents any other code from running on the processor, potentially compromising determinism. Button debounce functions are a notorious example. Instead of polling the GPIO pin or setting up an interrupt, many debounce implementations read the pin, then enter a delay of 20, 30, maybe even 40 milliseconds before reading the pin again. Take a look at Figure 1 for an example. In an environment without an RTOS this delay is the death of a real-time system.
|Figure 1 – Blocking Button Debounce|
3. Are there any potential infinite loops?
Who in their right mind would put an infinite loop into their code on purpose? (Excluding of course those needed in tasks or the application’s main loop.) Yet, there is a lot of example code on the web and from microcontroller vendors that exhibit an infinite loop failure behavior. For example, code that writes data to flash or EEPROM will typically monitor a hardware flag for completion. Example code will create a while loop on the flag to reach a certain state before allowing code execution to continue. If the hardware fails and the flag never sets, the code becomes stuck in an infinite loop! An example of this can be seen in Figure 2.
|Figure 2 –|