Many beginners start learning from bare metal but often do not understand the difference between bare metal and operating systems. Some even believe that using interrupts can replace multitasking. Do you agree?
Bare Metal Systems
1. Polling System
A polling system is when programming in bare metal, the relevant hardware is initialized first, and then the main program runs in an infinite loop, sequentially doing various tasks. The pseudocode is shown in the code listing:
int main(void)
{
/* Hardware initialization */
HardWareInit();
/* Infinite loop */
for (;;) {
/* Handle task 1 */
DoSomething1();
/* Handle task 2 */
DoSomethingg2();
/* Handle task 3 */
DoSomethingg3();
}
}
A polling system is a very simple software structure, usually suitable for tasks that only need to execute code sequentially without requiring external events. In code listing 1-1, if the goal is just to implement LED toggling, serial output, LCD display, etc., then using a polling system would be perfect. However, if key operations are added, which require detecting external signals to simulate emergency alarms, the real-time response capability of the entire system will not be as good.
Assuming DoSomethingg3 is key scanning, when an external key is pressed, it acts as an alarm. At this moment, an immediate response is required for urgent processing, while the program happens to be executing DoSomethingg1, which takes a long time to complete. If DoSomethingg1 takes longer than the time the key is released, then when it gets to DoSomethingg3, the event will be missed. This illustrates that polling systems are only suitable for sequential execution of functional code; when external events are driven, the real-time performance decreases.
2. Foreground-Background System
Compared to the polling system, the foreground-background system adds interrupts on top of the polling system. The response to external events is completed within interrupts, while event handling returns to the polling system. Here, we refer to interrupts as the foreground, and the infinite loop in the main function as the background. The pseudocode is as follows:
int flag1 = 0;
int flag2 = 0;
int flag3 = 0;
int main(void)
{
/* Hardware initialization */
HardWareInit();
/* Infinite loop */
for (;;) {
if (flag1) {
/* Handle task 1 */
DoSomething1();
}
if (flag2) {
/* Handle task 2 */
DoSomethingg2();
}
if (flag3) {
/* Handle task 3 */
DoSomethingg3();
}
}
}
void ISR1(void)
{
/* Set flag */
flag1 = 1;
/* If event handling time is short, handle in interrupt
If event handling time is long, return to background for processing */
DoSomething1();
}
void ISR2(void)
{
/* Set flag */
flag2 = 2;
/* If event handling time is short, handle in interrupt
If event handling time is long, return to background for processing */
DoSomething2();
}
void ISR3(void)
{
/* Set flag */
flag3 = 1;
/* If event handling time is short, handle in interrupt
If event handling time is long, return to background for processing */
DoSomething3();
}
When the background program is executed sequentially, if an interrupt occurs, the interrupt will interrupt the normal execution flow of the background program, turning to execute the interrupt service routine, marking the event in the interrupt service routine. If the event handling is very short, it can be handled within the interrupt service routine; if it is longer, it returns to the background program for processing.
Although the response and handling of events are separated, the handling of events is still executed sequentially in the background. However, compared to the polling system, the foreground-background system ensures that events are not lost, and with the ability of interrupts to be nested, this greatly improves the real-time response capability of the program. In most small to medium-sized projects, a well-utilized foreground-background system can mimic the effects of an operating system.
Multithreading System
When an urgent event is marked in the interrupt, if the corresponding thread’s priority is high enough, it will respond immediately. Compared to the foreground-background system, the real-time performance of the multithreading system is further improved.
The pseudocode for a multithreading system is as follows:
int flag1 = 0;
int flag2 = 0;
int flag3 = 0;
int main(void)
{
/* Hardware initialization */
HardWareInit();
/* OS initialization */
RTOSInit();
/* OS starts, begins multithreading scheduling, does not return */
RTOSStart();
}
void ISR1(void)
{
/* Set flag */
flag1 = 1;
}
void ISR2(void)
{
/* Set flag */
flag2 = 2;
}
void ISR3(void)
{
/* Set flag */
flag3 = 1;
}
void DoSomething1(void)
{
/* Infinite loop, cannot return */
for (;;) {
/* Thread entity */
if (flag1) {
}
}
}
void DoSomething2(void)
{
/* Infinite loop, cannot return */
for (;;) {
/* Thread entity */
if (flag2) {
}
}
}
void DoSomething3(void)
{
/* Infinite loop, cannot return */
for (;;) {
/* Thread entity */
if (flag3) {
}
}
}
Compared to the sequential execution of the program body in the foreground-background system, in the multithreading system, we divide the program body into independent small programs that run in an infinite loop and cannot return, which we call threads.
Each thread is independent, does not interfere with each other, and has its own priority, managed by the operating system. With the introduction of the operating system, our programming becomes easier. We no longer need to meticulously design the execution flow of the program and worry about interference between functional modules.
With the operating system, our programming becomes simpler. The only extra overhead the system incurs is the small amount of FLASH and RAM occupied by the operating system. Nowadays, the FLASH and RAM of microcontrollers are increasing, more than enough to accommodate the overhead of RTOS.
Differences between polling, foreground-background, and multithreading system software models:
Leave a Comment
Your email address will not be published. Required fields are marked *