“Embedded system design requires not only an understanding of the hardware, but also an understanding of how the software functions and how it interacts with it. Designing hardware requires a certain paradigm that may be the exact opposite of designing software. Here are a few tips that hardware engineers should keep in mind when moving from hardware designs to designs that include software.
Embedded system design requires not only an understanding of the hardware, but also an understanding of how the software functions and how it interacts with it. Designing hardware requires a certain paradigm that may be the exact opposite of designing software. When moving from a hardware design to a design that includes software, hardware engineers should keep the following tips in mind:
Tip 1: Flowchart first, implementation second
When an engineer first steps into software development, there is a strong temptation to jump right in and start coding. Such stereotyped thinking is equivalent to trying to design a printed circuit board (PCB) before the circuit logic diagram is complete.
Resisting the urge to write code is critical when embarking on software development, starting with a flowchart to develop a software architecture diagram. Such an approach would allow the developer to develop a concept of the different parts and components required by the application, just as a circuit logic diagram can tell the engineer which hardware elements are needed. This ensures that the program as a whole is built on good organization and thought, reducing program debugging time. It also saves time and hassle in the long run.
Tip 2: Use a state machine to control program flow
You know, the state machine is one of the greatest software inventions of the 20th century. An application can often be divided into multiple state machines, each of which controls a specific part of the application. These state machines each have their own internal states and state transitions, which give insight into how the software interacts with various stimuli. Using state machines to design software simplifies software development, making it modular, maintainable, and easy to understand. Extensive resources are currently available to demonstrate the theory and algorithms of state machines.
Tip 3: Avoid using global variables
In the age of functional programming, functions preceded forms, and the programmer’s only goal was to make programs work as quickly as possible, regardless of program structure or reusability. This programming paradigm makes use of global variables without concern, which can be modified by any function in the program. The result is an increased chance of the variable being corrupted or the variable being misused.
In the newly recommended object-oriented paradigm, variables should be defined and encapsulated in the smallest scope to prevent misuse or corruption by other functions. Therefore, it is recommended that you limit the number of variables used in the global scope, which can be identified in C with external keywords.
Tip 4: Take advantage of modularity
Which part of the project is most likely to be delivered late and over budget, no matter which engineer you ask? The answer is software! Software tends to be complex and difficult to develop and maintain, especially when the entire application exists in a single file or loosely related files.
To alleviate maintainability, reusability, and complexity, programmers are strongly encouraged to take full advantage of the modularity of modern programming languages by breaking common functionality into modules. Breaking down the code in this way allows programmers to start building libraries of functions and features, and then reuse them in one application after another, improving code quality through continuous testing, while also reducing time and development costs.
Tip 5: Keep Interrupt Service Routines Simple
The interrupt service routine is used to interrupt the execution of the current code branch by the processor to handle the peripheral device that just triggered the interrupt. Whenever an interrupt is executed, a certain amount of overhead is required to save the current program state, run the interrupt, and then return the processor to the original program state. Modern processors are much faster than processors from years ago, but this cost still needs to be considered. In general, programmers want to minimize interrupt runtime to avoid interfering with the main code branch. This means, interrupts should be short and simple, and functions should not be called in interrupts.
Also, if interrupts start to become too complex or time-consuming, you should only use them for minimal work when necessary. For example, load data into a buffer and set a flag, then let the main branch process the incoming data. Doing so ensures that most processor cycles are spent running the application, not handling interrupts.
Tip 6: Use the processor sample code to experiment with peripherals
When designing hardware, it is always beneficial to prototype test circuits to ensure that engineers have a proper understanding of the circuit before making board layouts. The same applies to design software. Silicon manufacturers often have example code that can be used to test various parts of a microprocessor so engineers can determine how well that part works. This approach provides insight into how the software architecture should be organized, as well as any potential problems that may arise.
Identifying potential roadblocks early in the design phase is better than discovering them in the final hours before the product is delivered. This is a great way to pre-test code snippets. However, it should be reminded that the manufacturer code is often not modular, and it is not convenient for practical application without major modification. This limitation has changed over time, and perhaps one day chip suppliers will have production-ready code.
Tip 7: Limit Functional Complexity
There is an old word in engineering called “KISS” – keep it simple and direct. When dealing with any kind of complex job, the easiest way is to break it down into smaller, simpler, and more tractable tasks.
As a job or function becomes more complex, it becomes more difficult for people to record all the details accurately. When writing a function, its complexity seemed moderate at the time, yet consider how an engineer would look at the code during a six-month maintenance window. There are many ways to measure the complexity of functions (such as the complexity of loops), and there are tools that can automatically calculate the cyclomatic complexity of a function. A rule of thumb suggests that it is ideal to keep the cyclic complexity of a function below 10.
When dealing with any kind of complex job, the easiest way is to break it down into more manageable tasks.
Tip 8: Use source code repositories and commit frequently
People make mistakes, and they make mistakes when writing code. That’s why it’s so important for developers to use source code repositories. Source code repositories allow developers to “check in” a good version of code and describe the modifications made to that code. This step not only allows developers to restore or trace back to older versions of the code, but also to compare differences between older versions. If a series of changes made by the developer breaks the system, a good version of the code can be restored with just one click!
Just remember: if you don’t commit code frequently, the repository will not serve its intended purpose. If you make irreversible changes, commit the code after two weeks, and then restore it, it will cause a lot of work and time loss!
Tip 9: Code Detailing
In the heat of software development battles, it’s easy for developers to focus on writing and coding and thus ignore the need for detailed explanations. Under pressure, illustrating work is often the finishing touch on a project because the developer considers it to be the last piece of work.
However, it is crucial to explain in detail while the code is still fresh in your head, so that the developer or yourself can read the comments and understand how the code works.
If a series of changes made by the developer breaks the system, a good version of the code can be restored with just one click!