Implementation of uCOS-II real-time operating system on Linux
2026-04-06 05:11:55··#1
Abstract: uCOS-II is a small yet powerful real-time embedded operating system. This operating system is excellently explained in the book *Micro/uCOS-II THE REAL-TIME KERNEL (Second Edition)*, written by Jean J. Labrosse and translated by Professor Shao Beibei. This book is a classic textbook on uCOS-II and provides four examples of porting uCOS-II to the Windows environment. This article presents a port of the first example to Red Hat Linux 9.0. The porting work focuses on three main aspects, which will be detailed in three chapters below, along with the code. Finally, the article introduces my experimental platform and demonstrates my experimental results. 1 Introduction uCOS-II is a small yet powerful real-time embedded operating system. The book *Micro/uCOS-II THE REAL-TIME KERNEL (Second Edition)*, written by Jean J. Labrosse and translated by Professor Shao Beibei, provides an excellent explanation of this operating system. This book is a classic textbook on the uCOS-II operating system and also provides four examples of porting uCOS-II to the Windows environment. This article presents a port of the first example to Red Hat Linux 9.0. The porting work mainly focuses on three aspects, which will be detailed in three chapters below, along with the code. The article concludes by introducing my experimental platform and demonstrating my experimental results. 2. String Display The string display function PC-DispStr is located in the file pc.c, which is not part of uCOS-II itself. Its main function is to establish a series of functionalities to leverage the powerful capabilities of the PC and be called by the test code. 2.1 Setting the Foreground and Background Colors We use statements similar to prinf("33[30m")) to set the colors. An escape sequence is a control instruction that allows the shell to execute a specific step. Escape sequences usually begin with ESC (hence the name). In the shell, it's represented as ︿[. This notation takes a little getting used to; you can also use 33 to accomplish the same thing (ESC's ASCII code in decimal is 27, which is 33 in octal). 33 declares the start of the escape sequence, followed by [ to define the color. Now we'll choose the foreground color (here, 32, representing green). The background color 40 represents black. If you don't want the text after the prompt to also be green, we use 33[0m to disable the escape sequence; 33[0m is the shell's default color). There are 8 available choices for both the foreground and background colors. Available colors: red, green, yellow, blue, magenta, cyan, and white. Their corresponding color codes are: 30 (black), 31 (red), 32 (green), 33 (yellow), 34 (blue), 35 (magenta), 36 (cyan), 37 (white). Set the background color using the same method, but replace the first number "3" with "4", for example, 40, 41, 42, 43, 44, 45, 46, 47. Although we could modify the macros for the foreground and background colors defined in pc.h according to the correspondence described above to make the correspondence clearer (note: the second digit represents the foreground color, and the first digit represents the background color), our design here aims to avoid modifying the code in the original book as much as possible. Therefore, we directly use a switch statement in the function implementation to set the corresponding foreground and background colors. (Linux shell only supports the above colors) switch (color&0xF0) /* Check foreground color */ { case DISP_FGND_BLACK: printf("33[30m"); break; …… } switch (color&0x0F) /* Check background color */ { case DISP_BGND_BLACK: printf("33[40m"); break; …… } 2.2 Tracking the cursor position I use printf("33[%u;%uH",y+1,x+1) to track the cursor position. 33 declares the start of the escape sequence, as introduced above, and will not be repeated. [y;xH is the format for setting the cursor position. x and y represent the horizontal and vertical axes, respectively. 3 Keyboard input The keyboard input function PC_GetKey is easily implemented in the Windows environment because of the library function kbhit which returns the most recently pressed key. In a Linux environment, we need to construct our own kbhit. Reference 2 provides a ready-made implementation (this method blocks the read function and is not applicable in this paper). Here, we use another implementation method, and the implementation code is given below. `int kbhit(void) { struct timeval tv; fd_set readFd; struct termios newKbdMode; if (!inited) { newKbdMode.c_lflag&=~(ICANON | ECHO); newKbdMode.c_cc[VTIME]=0; newKbdMode.c_cc[VMIN]=1; tcsetattr(0,TCSANOW,&newKbdMode); atexit(rekbd); inited=1; } tv.tv_sec=0; tv.tv_usec=0; FD_ZERO(&readFd);` FD_SET(STDIN_FILENO, &readFd); select(1, &readFd, NULL, NULL, &tv); if(FD_SET(STDIN_FILENO, &readFd)) return 1; else return 0; } 3.1 Console Initialization First, the global variable `inited` is used here, which is a flag indicating whether or not initialization is performed. Because the function `kbhit` will be called multiple times, while initialization only needs to be done once, once `inited` is set to 1, repetitive initialization work will not be performed. If `inited` is 0, the console (keyboard) needs to be initialized. Here, a variable `newKbdMode` of type `termios` in the kernel structure is defined. We need to initialize two members of this structure, `c_lflag` and `c_cc`. In the code, setting `c_lflag` indicates that the terminal is in non-standard mode without echo. `c_cc[VTIME]=0` and `c_cc[VMIN]=1` indicate that the read function will wait until one keyboard input appears. (For a detailed analysis of this structure, please refer to Chapter 5 of Reference 2). Then, call tcsetattr to write the set value. Finally, the function atexit will be described in detail in Section 3.3. 3.2 Detecting Keyboard Input Here we use the macro FD_ZERO to clear the kernel structure readFd to 0. Use the macro FD_SET to associate the standard input file descriptor STDIN_FILENO with readFd, and then use the select function to monitor input. It only focuses on one descriptor, so the first parameter is 1, the second parameter is readFd above, the last two parameters indicate whether to focus on the standard output and error file descriptors, which we do not need, so they are set to 0. The last parameter represents the timeout, which we do not need, so it is set to 0. After the above processing, if there is input, the macro FD_ISSET will return a non-zero value. We know that there is input on the keyboard. 3.3 System Exit In the Windows environment, the paired functions PC_DOSSaveReturn() and PC_DOSReturn are used. The former saves the DOS state, and the latter is called before exiting to restore the saved DOS state. In Linux, it appears that I only used the `exit()` function to exit directly without any save-and-restore process. However, in Linux, we actually call the function `atexit(function)` to set the function to be called before the program terminates normally. When the program returns by calling `exit()`, the function specified by the parameter `function` will be called first, and then `exit()` will actually terminate the program. `function` will specify the function `rekbd` (the function implementation is shown in the code below). This function clears the screen and removes all previously set attributes. `33` declares the beginning of the escape sequence, followed by `[2J`, indicating clearing the screen. `[0m` indicates closing all attributes. `void rekbd(void) { prinf("33[0m"); prinf("33[2J"); }` 4. Writing the Makefile In Jean J. Labrosse's original book, the Boland C compiler was used. However, we use the GCC compiler in Linux. Due to the change in compiler, the Makefile needs to be rewritten. To simplify Makefile writing, I provide a very simple method: place all the uCOS-II source code (SOFTWARE uCOS-IISOURCE), configuration header files, test functions (SOFTWARE uCOS-IIEX1_x86LBC45SOURCE), and the pc.c and pc.h files written above in the Linux root directory, assuming it's /test78. The Makefile can then be simplified as follows: `UCOS_SRC=/test78 UCOS_PORT=/test78 UCOS_PC=/test78 all: gcc -I$(UCOS_SRC) -I$(UCOS_PORT) -I$(UCOS_PC) test.c $(UCOS_SRC)/uCOS_II.C $(UCOS_PC)/pc.c $(UCOS_PORT)/os_cpu_c.c -o test` `all` is a pseudo-target. It's not a file, just a label, and its characteristic is that it's always executed. The purpose is to ensure the compiler generates a new target each time. `-o test` specifies the output file as `test`. The `-I` option specifies the search directory. Note: Putting all source files in one directory might not be a good approach, as it makes the entire project messy, especially for large projects. This approach is not feasible. However, it's only used here to simplify Makefile writing and provide a feasible method. Therefore, I defined several macros at the beginning of this Makefile; if several files need to be compiled are in a specific path, only the path needs to be specified. 5. Conclusion The innovations of this article are mainly reflected in: 1. A self-built keyboard input function. Because the implementation in (Beginning.Linux.Programming) blocks the `read` function, this article uses an improved method to implement keyboard input, see Section 3 for details. 2. The `MAKEFILE` file. Due to changes in the compiler, we need to rewrite the Makefile; this article provides a very simple method for writing it, see Section 4 for details. My experimental platform is as follows: I installed Red Hat Linux 9.0 on Virtual PC 2004 and compiled and debugged it under Linux. References: [1] Jean J. Labrosse (translated by Shao Beibei) Embedded Real-Time Operating System UC/OS-II (2nd Edition) [2] John Wiley. Sons. Beginning. Linux. Programming, Third Edition [3] Werner Zimmermann uCOS-II-Port for the LINUX Operating System [4] Shi Shaoying, Zhang Pi, Luo Shitu. Research on Virtual Instrument Display Technology of Tank Based on Embedded Operating System VxWorks [J]. Microcomputer Information, 2005, 4: 136-137 [5] Qian Chen, Xu Ronghua, Wang Qinruo. Device Driver Development Based on Linux Operating System, Microcomputer Information, 2004, 9