Introduction
In embedded software development, including microcontroller development, software architecture is a critical consideration for developers.
Software architecture is vital for the overall stability and reliability of the system. A suitable software architecture is not only clearly structured but also facilitates development.
I believe that most developers adopt a simple sequential execution architecture in the early stages of embedded or microcontroller software development (I did the same). In embedded software development, program architecture is mainly divided into three types, which this article will elaborate on.
Significance of Software Architecture
A good program architecture can be seen as a dividing line between an experienced engineer and a beginner. Software architecture is friendly to developers; you can decide which tasks to execute first, which tasks to execute next, or which tasks to synchronize with certain events, etc. The specific methods to solve these problems vary under different software architectures.
The greatest help of software architecture to developers is that it helps them grasp the framework of the entire project. Once you are proficient in a particular program architecture, you will be able to quickly locate and resolve bugs that arise in the system. Of course, I recommend choosing a suitable software architecture based on your needs, and the specific reasons will be discussed later in the article.
In-depth Introduction to Three Different Program Architectures
The three commonly used software architectures are: sequential execution front-and-back system, time-slice polling system, and multitasking operating system. To provide a clearer understanding, I will introduce an example using these three software architectures. This example includes four tasks: key scanning, sound and light alarm, display refresh, and ultrasonic distance measurement. The specific function of this example is to set a threshold for measuring distance through a key. When the measured distance falls below the set threshold, it triggers a sound and light alarm and displays the measured distance in real-time on the screen (this application is a specific embodiment of a car reversing radar).
1. Sequential Execution Front-and-Back System
In the sequential execution front-and-back system, I will place the keyboard scanning in a querying manner within while(1), while using interrupts for display refresh and ultrasonic distance measurement. The measured distance is displayed after being obtained in the interrupt service function, while key detection and sound and light processing are handled in the main function’s loop. Thus, the entire program executes in a synchronized manner using variable flags in the main loop and background interrupts. The corresponding program code is shown in the figure below:
The above code is the main function of the sequential execution front-and-back system.
The above code is the interrupt service function of the sequential execution front-and-back system.
The advantage of this architecture is its simplicity and ease of understanding, while the disadvantage is that if each task occupies too much CPU time, it can lead to poor real-time performance, such as in key detection.
2. Time-Slice Polling System and Multitasking Operating System
The time-slice polling method typically appears in operating systems, meaning it belongs to the operating system. However, what is discussed here is time-slice polling based on the front-and-back system.
The essence of the time-slice polling method is to select a timer, increment the count value each time a timer interrupt occurs, and execute tasks in the main loop based on this count value, which represents the time slice for task polling.
In this example, if a time-slice polling system is used, we first select any timer from the main control chip, and the timer’s timing period is determined by us. To ensure real-time performance and operational efficiency, this value is usually set to 10ms, 30ms, or 50ms. I would set the key scanning polling value to 20ms, as the key bounce duration is generally around 20ms. This approach achieves debouncing while ensuring key detection is not missed.
The display refresh is set to 30ms; if you feel the refresh response is slow, you can modify this polling value for improvement. The ultrasonic distance measurement polling value is set to 100ms, meaning it triggers distance measurement once every 100ms, which is sufficient for most situations.
The program code is as follows:
The above code is the main function of the time-slice polling system.
The above code is the timer interrupt function of the time-slice polling system.
It can be seen that the time-slice polling method has significant advantages over sequential execution, combining the benefits of both sequential execution and some advantages of operating systems.
3. Multitasking Operating System
The operating system itself is a relatively complex entity, and the management and scheduling of tasks at the underlying level is quite complicated and challenging.
However, we generally regard the operating system itself as a tool or platform; our goal is to utilize its functions rather than develop an operating system.
I have used small real-time operating systems like uCOS and FreeRTOS, as well as large operating systems like Linux. With an operating system, both the stability of the program and the efficiency of development improve significantly.
When using an operating system, we need to learn and understand its scheduling and communication methods.
In fact, very few people can truly use an operating system; most run bare-metal systems, which also relates to specific product requirements. Many simple systems only require bare-metal to meet their needs.
Here, I will not delve too deeply into the operating system itself, as it is indeed quite complex. The code in the diagram below shows the program structure for controlling an LED with a key in FreeRTOS, which you can compare:
The above is the main function in the FreeRTOS multitasking system.
The above is the task callback function in the FreeRTOS multitasking operating system. In fact, there are also some STM32 frameworks that can implement operating system functions, as referenced in this article: A STM32 Framework Implementing Partial Functions of RTOS.
How to Choose the Right Software Architecture
I have used various MCUs for project development, such as STM32, STC15, Nuvoton, etc., and have encountered complex design requirements, such as automotive intelligent systems and smart homes. I have also worked with operating systems like uCOS, FreeRTOS, and Linux. When returning to bare-metal development, I inevitably think about the design issues of the complete system’s software architecture, and I believe that most readers also engage in bare-metal development.
I believe there is no best software architecture (program architecture), only the most suitable one. Different application scenarios require different program designs, and simply comparing which program architecture is the best is not practically meaningful.
Next, let’s analyze specific application scenarios:
-
In some systems with clear logic and single functions, it is very suitable to choose a sequential execution front-and-back architecture. This software architecture can often meet most of our needs, such as rice cookers, induction cookers, and voice-controlled light bulbs;
-
In cases where resources are scarce in microcontrollers and high reliability is required, this method is very suitable because the system consumes relatively little, sacrificing only one timer. However, choosing this program architecture requires careful consideration of time slice allocation;
-
Finally, in systems with complex functions and difficult logical control, it is suitable to choose a multitasking operating system, such as video surveillance systems, drones, and other application scenarios.
As embedded software engineers, mastering these three software architectures is essential. They provide us with more choices and considerations when designing programs, and each different program architecture has its own advantages and disadvantages, which we need to practice diligently to appreciate their intricacies.