As early as the 1960s, people began researching and developing embedded operating systems. However, it is only recently that it has received increasing attention in China, with its growing importance in fields requiring real-time processing, such as communications, electronics, and automation, attracting more and more attention. However, discussions often focus on well-known commercial kernels such as VxWorks and PSOS. These commercial kernels offer superior performance but are expensive, primarily used in 16-bit and 32-bit processors. For the 51 series 8-bit microcontrollers used by most users in China, the free uC/OS-II option is available.
Features of uC/OS-II
1. uC/OS-II is an open-source kernel written by Mr. Labrosse, its most prominent feature being its open-source nature. This presents both advantages and disadvantages for users. The advantages are that it's free and users can modify it according to their needs. The disadvantages are a lack of necessary support and powerful software packages; users typically need to write their own drivers, especially if they are using less common microcontrollers, in which case they must also write their own porting programs.
2. uC/OS-II is a preemptive kernel, meaning that a high-priority task that is ready can preempt a running low-priority task from using the CPU. This feature makes its real-time performance better than non-preemptive kernels. Typically, we put high-priority tasks into a ready state (e.g., by sending a signal) within an interrupt service routine. After exiting the interrupt service routine, a task switch occurs, and the high-priority task is executed. Taking the 51 microcontroller as an example, a comparison reveals the advantages of this approach. If we need to collect and process a batch of data using interrupts, traditional programming methods cannot perform complex data processing within interrupt service routines because this would result in excessively long interrupt disabling times. Therefore, a common method is to set a flag and then exit the interrupt. Since the main program executes in a loop, it always has a chance to detect this flag and jump to the data processing routine. However, because it's impossible to determine exactly where the program is executing when the interrupt occurs, it's also impossible to determine how long it will take for the data processing routine to execute. The interrupt response time is uncertain, and the system's real-time performance is weak. When using uC/OS-II, simply setting the priority of the data processing program to a higher level and putting it into a ready state within the interrupt service routine will ensure the data processing program is executed immediately after the interrupt ends. This limits the interrupt response time to a certain range, which is essential for systems with strict requirements on interrupt response time. However, it should be noted that this approach may not be suitable if the data processing program is simple. This is because uC/OS-II requires the use of the OSINTEXIT function at the end of the interrupt service routine to determine whether a task switch should be performed, which takes some time.
3. Unlike well-known time-sharing operating systems like Linux, uC/OS-II does not support round-robin scheduling. uC/OS-II is a priority-based real-time operating system where each task must have a different priority. Analyzing its source code reveals that uC/OS-II uses task priority as an identifier; if tasks have the same priority, they cannot be distinguished. The highest-priority task that enters the ready state gains CPU access first, and other tasks can only be executed after it relinquishes CPU access. Therefore, it can only be described as multitasking, not multiprocessing, at least not in the way we are familiar with. Obviously, if only real-time performance is considered, it is better than a time-sharing system, as it can guarantee that important tasks always have priority over the CPU. However, in a system, the number of important tasks is limited, which makes prioritizing other tasks a complex and challenging problem.
Furthermore, alternating the execution of some tasks is actually more beneficial to the user. For example, when controlling two small displays with a microcontroller, both the programmer and the user would prefer them to work simultaneously, rather than displaying information from one display after the other. In this case, it would be more suitable if uC/OS-II supported both priority-based and time-slice rotation methods.
4. uC/OS-II provides protection mechanisms for shared resources. As mentioned above, uC/OS-II is a multitasking operating system. A complete program can be divided into several tasks, each performing different functions. Thus, a task is equivalent to a submodule in a modular design. When adding code to a task, as long as it's not a shared resource, there's no need to worry about mutual interference. For shared resources (such as serial ports), uC/OS-II also provides a good solution. Generally, semaphores are used. Simply put, a semaphore is created and initialized. When a task needs to use a shared resource, it must first acquire the semaphore. Once acquired, the semaphore is only released after the resource is used. During this process, even if a higher-priority task enters the ready state, it cannot use the resource because it cannot acquire the semaphore. The benefits of this feature are obvious. For example, if an interrupt occurs while the display is showing information, and the interrupt service routine needs to display other information, the original information may be corrupted after exiting the interrupt service routine. In uC/OS-II, when using semaphores, new information can only be displayed after the existing information has been fully displayed, thus avoiding this phenomenon. However, this method comes at the cost of sacrificing system real-time performance. If displaying the existing information takes a significant amount of time, the system has to wait. This effectively prolongs interrupt response time, which is fatal, especially when the undisplayed information is an alarm message. In uC/OS-II, this situation is called priority inversion, where a high-priority task must wait for a low-priority task to complete. In the above situation, priority inversion between two tasks is unavoidable. Therefore, when using uC/OS-II, a thorough understanding of the system being developed is essential to determine whether to use semaphores for a particular shared resource.
Some features of uC/OS-II in microcontroller applications
1. Embedding uC/OS-II into a microcontroller system enhances system reliability and simplifies debugging. Traditional microcontroller development often encounters programs crashing or getting stuck in infinite loops. While watchdog timers can address crashes, the latter, especially those involving complex mathematical calculations, requires setting breakpoints and expending considerable time analyzing the code. Embedding uC/OS-II simplifies the process considerably. The program can be divided into many independent tasks, each with a timeout function. Once the timeout expires, the task must relinquish CPU usage. Even if one task fails, it won't affect the execution of other tasks. This improves system reliability and simplifies debugging.
2. Embedding uC/OS-II in a microcontroller system will increase system overhead. Currently used 51 microcontrollers generally refer to the 87C51 or 89C51, which have on-chip RAM and ROM. For some simple programs, traditional programming methods eliminate the need for external memory. If uC/OS-II is embedded, external ROM is also unnecessary if only task scheduling, task switching, semaphore handling, and delay or timeout services are required; however, external RAM is still necessary. Since uC/OS-II is a customizable operating system, its RAM requirements depend on the number of operating system functions. For example, uC/OS-II allows users to define the maximum number of tasks. Since each task requires a corresponding data structure TCB, which occupies a significant amount of memory, the actual needs must be considered when defining the maximum number of tasks. Setting it too high will inevitably lead to unnecessary waste. The total RAM requirement after embedding uC/OS-II can be derived from the following expression:
Total RAM requirement = Application RAM requirement + Kernel data area RAM requirement + (Task stack requirement + Maximum interrupt nesting stack requirement) × Number of tasks
Fortunately, uC/OS-II allows defining the stack space size for each task, enabling developers to allocate stack space according to the actual needs of the task. However, with limited RAM, care should still be taken when using large arrays, data structures, and functions; remember, function parameters are also pushed onto the stack.
3. Porting uC/OS-II is also a task that requires careful attention. If there are no existing porting examples, you must write the porting code yourself. Although it only requires modifying two files, a good understanding of the corresponding microprocessor is still necessary, and it's best to refer to existing porting examples. Furthermore, even if there are porting examples, it's advisable to read them before programming, as they involve stack operations. When writing interrupt service routines, the order in which registers are pushed onto the stack must correspond to the order in the porting code.
4. Unlike some other well-known embedded operating systems, uC/OS-II has a simpler boot process in microcontroller systems. Unlike some operating systems that require compiling the kernel into an image file and writing it to ROM, then loading the file from ROM into RAM after power-on reset before running the application, uC/OS-II's kernel and application are compiled together into a single file. Users only need to convert this file to HEX format and write it to ROM. After power-on, it will run like a regular microcontroller program.
Conclusion
As can be seen from the above introduction, uC/OS-II has advantages such as being free, easy to use, highly reliable, and having good real-time performance. However, it also has disadvantages such as difficulty in porting and a lack of necessary technical support, and it is not as widely used or continuously researched and updated as commercial embedded systems. However, its openness allows developers to customize and add the required functions, playing a unique role in many application areas. Of course, whether to embed uC/OS-II into a microcontroller system depends on the project being developed. For some simple, low-cost projects, there is no need to use an embedded operating system.