Microcontrollers only have so many resources, why do so many people want to use RTOS?
[Copy link]
Recently I saw many friends in the group discussing whether to use bare metal or RTOS for microcontrollers, so today I found some content to share some views.
For engineers who work with microcontrollers, especially engineers who use the 8051 series, when it comes to RTOS for microcontrollers, they often ask: "Why do we need RTOS? The microcontroller has so few resources. Can using RTOS guarantee efficiency?"
To this question, I would ask: "What is the purpose of using a microcontroller? Is it to program in C, assembly or even binary instructions?" In the 1980s, engineers used binary instructions to program Z80. Who still uses it now? There are still people who cling to assembly, but more and more engineers use C programming (I also used assembly at first). Why?
Because our goal is to complete the project with quality and quantity within a limited time or even insufficient time! What tools and methods are used is secondary (if your project puts cost first, then it is another matter, at this time, you also need to consider the development time). Time is money, and it is acceptable for a product to increase the cost of the microcontroller a little.
Moreover, when using the 8051 series of microcontrollers, the microcontroller resources are often surplus, and the CPU is usually just idling, which creates conditions for using RTOS. So, what are the benefits of using RTOS?
Let me give you an example. Suppose we write a serial communication program with the following communication protocol: the length of the data packet is NBYTE, the starting bytes are STARTBYTE1, STARTBYTE2, the last byte is the checksum, and the middle bytes cannot have STARTBYTE1 and STARTBYTE2 appearing consecutively.
The first method is to handle the protocol in the interrupt:
unsignedcharBuf[NBYTE-2];
bitGetRight=0;
voidcomm(void)interrupt4//"串行口中断"
{
staticunsignedcharSum,Flag=0,i;
unsignedchartemp;
if(RI==1)
{
RI=0;
temp=SBUF;
switch(Flag)
{
case0:
if(temp==STARTBYTE1)
{
Flag=1;
}
break;
case1:
if(temp==STARTBYTE2)
{
Sum=STARTBYTE1+STARTBYTE2;
i=0;
Flag=2;
break;
}
if(temp==STARTBYTE1)break;
Flag=0;
break;
case2:
if(temp==STARTBYTE1)
{
Flag=3;
break;
}
Sum+=temp;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=temp;
break;
case3:
if(temp==STARTBYTE2)
{
Sum=STARTBYTE1+STARTBYTE2;
Flag=2;
i=0;
break;
}
Sum+=STARTBYTE1;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=STARTBYTE1;
if(temp==STARTBYTE1)
{
break;
}
Sum+=temp;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=temp;
Flag=2;
break;
}
}
}
The second method uses the queue interrupt function:
voidcomm(void)interrupt4//"串行口中断"
{
if(RI==1)
{
RI=0;
SBUF入队;
}
}
Functions that are constantly called by the main program:
unsignedcharBuf[NBYTE-2];
unsignedcharReadSerial(unsignedchar*cp)
{
unsignedchari;
unsignedchartemp,Sum;
temp=队列中数据个数;
if(temp<(NBYTE))return0;
出队temp;
if(temp!=STARTBYTE1)return0;
temp=队列首字节;
if(temp!=STARTBYTE2)return0;
出队temp;
sum=STARTBYTE1+STARTBYTE2;
for(i=0;i<NBYTE-3;i++)
{
temp=队列首字节;
if(temp==STARTBYTE1)
{
temp=队列次首字节;
if(temp==STARTBYTE2)return0;
}
出队temp;
*cp++=temp;
Sum+=temp;
}
temp=队列首字节;
Sum+=temp;
if(Sum!=0)return0;
出队temp;
return1;
}
The third method uses the RTOS interrupt function:
voidcomm(void)interrupt4//"串行口中断"
{
OS_INT_ENTER();
if(RI==1)
{
RI=0;
OSIntSendSignal(RECIVE_TASK_ID);
}
OSIntExit();
}
The task with ID RECIVE_TASK_ID.
voidRecive(void)
{
unsignedchartemp,temp1,Sum,i;
OSWait(K_SIG,0);
temp=SBUF;
while(1)
{
while(1)
{
OSWait(K_SIG,0);
temp1=SBUF;
if((temp==STARTBYTE1)&&(temp1==STARTBYTE2))break;
temp=temp1;
}
Sum=STARTBYTE1+STARTBYTE2;
OSWait(K_SIG,0);
temp=SBUF;
for(i=0;i<NBYTE-3;i++)
{
OSWait(K_SIG,0);
temp1=SBUF;
if((temp==STARTBYTE1)&&(temp1==STARTBYTE2))
{
OSWait(K_SIG,0);
temp=SBUF;
i=-1;
Sum=STARTBYTE1+STARTBYTE2;
continue;
}
Buf[i]=temp;
Sum+=temp;
temp=temp1;
}
Sum+=temp1;
if(Sum==0)OSSendSignal(命令解释任务ID);
}
}
The following is a comparison of these methods in terms of readability and ease of programming.
The third method is the best (if goto statements are allowed, the program will be simpler and easier to read)
The second one is inferior (because it requires queuing programs)
The first one is relatively poor, and this aspect becomes more apparent if the protocol is more complex.
The program is simple and easy to read, so there is less chance of error.
In terms of RAM usage, the third method takes less, the second method takes the most (because the queue takes up a lot of space), and the first method takes the least.
In terms of interrupt execution time, the third method is the longest, the second is the shortest, and the first is the longest.
In terms of functionality, the third method is the strongest, as it can also handle timeouts (although the example program does not have this), while the other methods cannot.
If the data arrives too fast and the command handler has no time to process it, the three methods have different processing methods. The first and third methods are similar: discarding the previous data, while the second method discards the later data. Moreover, the second method must wait until the command handler is completed before processing the next data packet, while the first and third methods can process the next data packet only after the command handler receives the data. In other words, the first and third methods are processed in parallel with the command handler, while the second method is serial processing.
Now, generally speaking, development efficiency comes first, and execution efficiency (including execution time and resource usage) comes second. In this case, why not reduce a little efficiency in exchange for a greater improvement in development efficiency?
Moreover, the high efficiency of a single module does not mean the high efficiency of the entire program. For example, if the program needs to wait for a period of time, it is generally delayed by a program or a timer. Regardless of the method, the CPU will no longer handle other tasks, and the efficiency is very low. With RTOS, the CPU can handle other tasks while waiting, and the efficiency is improved.
The following is excerpted from "uC/OS-II--Real-time Embedded Operating System with Open Source Code"
"A real-time kernel is also called a real-time operating system or RTOS. Using it makes it easy to design and expand real-time applications. New features can be added without major changes. By dividing the application into several independent tasks, the RTOS greatly simplifies the application design process. When using a preemptive kernel, all time-critical events are handled as quickly and efficiently as possible. Through effective services such as semaphores, mailboxes, queues, delays, timeouts, etc., the RTOS makes better use of resources. "If the application project can afford the additional requirements, you should consider using a real-time kernel. These additional requirements are: the price of the kernel, additional ROM/RAM overhead, and 2 to 4 percentage points of additional CPU burden. "Another factor that has not been mentioned is the added price cost of using a real-time kernel. In some applications, price is everything, so much so that even the thought of using an RTOS is unthinkable."
All in all, the best is the one that works best. Don't reject RTOS. It works well when it is applicable.
Author: Chen Ming, compiled by bug.
|