Abstract: This article details the transplantation of UC / OS-II in 51 single-chip microcomputer, re-entry implementation method, hardware simulation, curing, man-machine interface and other key content. Keywords: embedded real-time multi-task operating system, UC / OS-II, C51 introduction: With the complexity of various application electronic systems, the improvement of system real-time demand, and accompanying application software to develop in systematic direction The embedded real-time operating system is widely used in the 16-bit / 32-bit microcontroller. However, there is a large number of 8-bit microcontrollers in actual use, from economical considerations, for some applications, using operating systems on 8-bit MCUs. From the perspective of learning operating system, UC / OS-II FOR 51 is simple and comprehensive, and learning is low, it is worth promoting. Conclusion: μC / OS-II has the advantages of free, simple, reliable, and good real-time performance, but there are also shortcomings such as facilitating development environments, especially if commercial embedded systems are widely used and continuous research updates. But openness has made developers to cut and add the required functions, and play a unique role in many applications. Of course, whether to embed the item developed in the microcontroller system, it is not necessary to use an embedded operating system for some simple, low-cost projects.
UC / OS-II Principle: UCOSII includes four parts: Task scheduling, time management, memory management, resource management (semaphore, mailbox, message queue), no file system, network interface, input output interface. Its transplant is only related to 4 files: assembled files (OS_CPU_A.ASM), processor related C files (OS_CPU.H, OS_CPU_C.C) and configuration files (OS_CFG.H). There are 64 priorities, and the system occupies 8, users can create 56 tasks, and no time slice rotation is not supported. Its basic idea is that "approximately always makes the highest-priority ready task in operation". In order to ensure this, it always performs a scheduling algorithm at the end of the system API function, the interrupt end, and the timing interrupt is always executed. The original author simplifies the amount of operation by calculating data in advance, and the delay can be predictable by carefully designed the description table structure. The switching of the task is achieved by simulating an interrupt. The core principle of UCOSII work is: approximation allows the highest priority ready task to run. The operating system will perform task scheduling in the case: call the API function (user active call), interrupt (system occupied time slice interrupt OSTIMETICK (), the interrupt used by the user). The scheduling algorithm is very clear, I mainly tell the overall idea. (1) When the API function is called, it is possible to cause blocking. If the system API function is not satisfied, you need to switch to call the OsSched () scheduling function, this process is automatically completed, the user is not involved. OsSched () determines whether to switch, if you need to switch, this function calls OS_TASK_SW (). This function simulates an interrupt (there is no soft interrupt in 51, I am using the subroutine to call the simulation, the effect is the same as the interrupt, in fact, the intention of the OS deliberately manufactured, the purpose is to switch to task. Since it is an interruption, the return address (ie, the PC address of the next assembly instruction next to OS_TASK_SW () is automatically pressed into the stack, then saves the CPU register (PUSHALL) in the interrupt program .... The stack structure is not arbitrary, but strictly in accordance with UCOSII specification. OS each switch will save and restore all field information (POPAL), and then return to the task breakpoint with RETI to continue execution. This breakpoint is the PC address of the next assembly instruction next to OS_TASK_SW () in the OsSched () function. The entire process of switching is that the user task program calls the system API function. The API calls OsSched (), and the OSSched () calls soft interrupt OS_TASK_SW () osctXsw, return the address (PC value) stack, enter the osctxswinter interrupt processing subroutine inside. Conversely, the switching program calls RETI returns the PC address of the next assembly instruction next to OS_TASK_SW (), and then returns to OSSChed () Next, then return to the API, ie the user program breakpoint. Therefore, if the task is running from running until running, it is running from the breakpoint before the schedule. (2) The interrupt will trigger the conditional change, and task schedules must be performed before exiting. UCOSII requires the interrupt stack structure to comply with the specifications to correctly coordinate interrupt exit and task switching. As mentioned earlier, the task switch is actually simulating an interrupt event, and the simulation in the real interruption (it is interrupt). As long as the interrupt stack structure is specified, the correct switch can be performed in the interrupt. Task Switching occurs before the interrupt exits, there is no return point in this time. Carefully observe the last two sentences of the interrupt procedures and switching procedures, they are exactly the same, popall reti.
That is, if you either directly from the interrupt program, return breakpoints; or save the site to the TCB, wait until the site is restored to the original interrupt breakpoint (due to the interrupt and switching function follow the common stack structure, the exit operation is the same, The effect is the same.). The interrupt subroutine written by the user must be written in accordance with the UCOSII specification. The task scheduling occurs before the interrupt exits, it is very timely and will not wait until the next time film is processed. The OsintctXsw () function makes simple adjustments to the stack pointer to ensure that the stack structure of all suspend tasks looks the same. (3) In UCOSII, the task must be written in two forms ("UCOSII Chinese" P99 page). In some RTOS development environments, there is no explicit call ostaskdel () because the development environment is automatically handled, and the actual principles are the same. UCOSII's development depends on the compiler, there is currently no dedicated development environment, so these inconveniences can be understood. Transplantation process: (1) After copying the book, the contents of the disc Sourcecode directory is included in C: / YY, delete unnecessary files and ex1l.c, leaving only P187 ("ucosii") listed. (2) Rewriting the simplest OS_CPU.H data type settings to see c51.pdf page 176. Note that Boolean wants to define a Unsigned Char type because the bit type is unique to the c51, and cannot be used in the structure. EA = 0 off interrupt; EA = 1 open interrupt. This definition reduces the number of lines, and avoids the crash caused by exiting the critical area. The MCS-51 stack increases from bottom to top (1 = down, 0 = up), OS_STK_GROWTH defines 0 # define os_task_sw () osctxsw () because the MCS-51 has no soft interrupt instruction, so use program call replacement. The stack format of the two is the same, the RETI command resets the interrupt system, and RET is not. Practice shows that for MCS-51, the use of subroutine calls into the stack, using interrupted return instruction RETI out is no problem, and it is not possible to break the stack RET out of the stack. In summary, for the stack, the subroutine call is the same as the interrupt call effect, which can be mixed. The reset interrupt system does not affect the system's normal operation without interruption. See Chapter 8 of "UC / OS-II" Chapter 8, 193, Chapter 12 (3) Remove OS_CPU_C.C I design The stack structure is shown below:
Ostcbstkptr in the TCB structure always points to the minimum address of the user stack. The user stack length is stored in the address space, and the upper space storage system stack image is: the user stack space size = system stack space size 1. SP is always plus 1 to save data, so the SP initially points to the system stack start address (OSSTKStart). Obviously system stack storage space size = SP-OSSTKSTART. When the task is switched, save the current task stack content first. The method is: use SP-OSSTKStart to give the number number, write it into the minimum address of the user stack, with the minimum address of the user stack, and the system stack is stored by osstkstart, and copy data from the system stack to the user stack. Cyclic sp-OSSTKSTART times, each stack pointer will increase 1 first. Second, restore the highest priority task system stack. The method is to obtain the highest priority task user stack minimum address, remove "length", with the highest priority task user stack at the address, with OsStkStart, from the system stack, by the user stack to the system stack copy data, loop The number of "length" value indicates, and each stack pointer will increase each time the copy is first. The user stack is initially saved from the downward: the user stack length (15), PCL, PCH, PSW, ACC, B, DPL, DPH, R0, R1, R6, R3, R4, R5, R6, R7. Do not save the SP, task is calculated according to the length of the user stack. The Ostaskstkinit function always returns the minimum address of the user stack. Operating System Tick Clock I use a 51 single-chip T0 timer, and its initialization code is written in this document with C. There are still a few things that must be noted. Originally, we don't have to modify the code-independent code, but due to the specificity of the Keil compiler, these codes will still be more modified. Because Keil is default, the code is not re-entered, and the multitasking system requires concurrent operation to lead to re-entry, so you have to mark the Reentrant keyword after each C function and its declaration. In addition, "PDATA", "DATA" is used in UCOS to do some functions, but it is also the keyword of Keil, which will cause compilation errors, I change "PDATA" to "PPDATA", "Data" Change to "DDATA" to solve this problem. Ostcbcur, Ostcbhighrdy, OSRUNNING, OSPRIOCUR, OSPRIOHIGHRDY These variables are used in the assembler. In order to use ri access without DPTR, you should define them in the internal RAM with keil extension keyword IDATA. (4) Reword OS_CPU_A.ASMA51 macro assembly is as follows: Name module name; independent of the file name; defined the relocation section must be defined according to the C51 format, compiling the C51 specification. Segment name format is:? PR? Function name? Module name; declaration reference global variable and external subroutine Note keyword "EXTRN" without 'e' global variable name Direct reference None parameter / no register parameter function FUNC with register parameter function _Func re-entry function _? Func; allocated stack space only cares for size, the starting point of the stack is determined by Keil, and the SP start point assigned by Keil can be obtained by the label. Don't assign your stack up, just use the DS to inform Keil reserved stack space.
• The Stack paragraph is the same as the segment name in Startup.a51, which means that Keil will spell together between the two with the same name when Link, I reserve 40h bytes, startup.a51 reserved 1 byte The total length of the stack segment is 41h after LINK. View YY.M51 KEIL will set the stack at 21h, in length 41h, in the internal RAM. Define macro macro macro entity endm; subroutine OSSTARTHRDYOSRSRSERIALISREND; declare that the wisdom source file ends the general pointer for 3 bytes. 0 Type 1 High 8-bit data 2 low 8-bit data See c51.pdf P.178 Low address saving 8-bit value, high address deposit 8-bit value. For example, 0x1234, base number 0: 0x12 base number 1: 0x34 (5) Portfront serial driver before I wrote interrupt serial driver, including print byte / word / long word / string, read serial port Initialize the serial / buffer. Use it to change it into the re-entry function. The display function provided by the system is concurrent. It is not directly displayed to the serial port, but before outputting to the memory, the user does not have to worry about the IO slow operation impact program operation. The serial entry input also uses the same technique, which allows the user that can make a blind input command when the CPU is busy with other tasks. (6) Writing the test program Demo (YY.C) Demo program created three tasks A, B, C priority 2, 3, 4, a once each second, B is displayed once every 3 seconds, C every 6 Seconds display once. From the display result, 1 B is displayed after 3 A. Displayed 1 C after 6 A and 2 B, and the results are obviously correct. Display results as follows: AAAAAA111111 is activeAAAAAA111111 is activeAAAAAA111111 is activeBBBBBB333333 is activeAAAAAA111111 is activeAAAAAA111111 is activeAAAAAA111111 is activeBBBBBB333333 is activeCCCCCC666666 is activeAAAAAA111111 is activeAAAAAA111111 is activeAAAAAA111111 is activeBBBBBB333333 is activeAAAAAA111111 is activeAAAAAA111111 is activeAAAAAA111111 is activeBBBBBB333333 is activeCCCCCC666666 is activeDemo program after Keil701 compiled code amount of 7-8K, can be simulated directly on Keilc51. Add OS_CPU_C.C, UCOS_II.C, OS_CPU_A.ASM, YY.C, OS_CPU_A.ASM
$ Nomod51ea bit 0a8h.7sp Data 081HB Data 0F0HACC Data 0E0HDPH DATA 083HDPL DATA 082HPSW DATA 0D0HTR0 BIT 088H.4TH0 DATA 08chtl0 Data 08ah
NAME OS_CPU_A; module name; define the relocation segment PR OSStartHighRdy OS_CPU_A SEGMENT CODE PR OSCtxSw OS_CPU_A SEGMENT CODE PR OSIntCtxSw OS_CPU_A SEGMENT CODE PR OSTickISR OS_CPU_A SEGMENT CODE????????????
? PR _ serial OS_CPU_A SEGMENT CODE;???? Statement cited global variables and external subprograms EXTRN IDATA (OSTCBCur) EXTRN IDATA (OSTCBHighRdy) EXTRN IDATA (OSRunning) EXTRN IDATA (OSPrioCur) EXTRN IDATA (OSPrioHighRdy) EXTRN CODE (_ OSTaskSwHook ???) EXTRN CODE (_ serial) EXTRN CODE (_ OSIntEnter) EXTRN CODE (_ OSIntExit) EXTRN CODE (_ OSTimeTick);? external statement four non-reentrant functions PUBLIC OSStartHighRdyPUBLIC OSCtxSwPUBLIC OSIntCtxSwPUBLIC OSTickISR; PUBLIC SerialISR; allocating stack space . Only the size of the stack is determined by Keil, and the SP start point assigned by Keil can be obtained by the label. ? STACK SEGMENT IDATARSEG STACKOSStack: DS 40HOSStkStart IDATA OSStack-1; push the stack defined macro PUSHALL MACROPUSH PSWPUSH ACCPUSH BPUSH DPLPUSH DPHMOV A, R0; R0-R7 stack PUSH ACCMOV A, R1PUSH ACCMOV A, R2PUSH ACCMOV A, R3PUSH ACCMOV? A, R4PUSH Accmov A, R5PUSH Accmov A, R6PUSH Accmov A, R7PUSH ACC; PUSH SP; does not have to save SP, the task switch is adjusted by the corresponding program for the corresponding program; does not have to save the SP, the task switch is adjusted by the corresponding program POP ACC R0-R7 out of the stack MOV R7, APOP ACCMOV R6, APOP Accmov R5, APOP Acmov R4, APOP Accmov R3, APOP Acmov R2, APOP Accmov R1, APOP Acmov R0, APOP DPHPOP DPLPOP BPOP ACCPOP PSWENDM; --- -------------------------------------------------- -------------------- RSEG? PR? OSSTARTHRDY? OS_CPU_AOSSTARTHRDY: Using 0; After power-on, 51 auto-off interrupt, here does not have to use CLR EA instructions, because here If there is no interrupt, the program is exited, and the interrupt is interrupted. LCALL _? OstasksWhook
OSCTXSW_IN:; ostcbcur ===> DPTR Gets the current TCB pointer, see c51.pdf 178 MOV R0, # low (Ostcbcur); get the OSTCBCUR pointer low address, pointer accounts for 3 bytes. 0 type 1 high 8-bit data 2 low 8-bit data INC R0MOV DPH, @ r0; global variable OSTCBCUR in Idata Inc R0MOV DPL, @ r0; ostcbcur-> ostcbstkptr ===> DPTR Get User Stack Pointer INC DPTR The pointer occupies 3 bytes. 0 type 1 high 8-bit data 2 low 8-bit data MOVX A, @ dptr; .ostcbstkptr is Void pointer MOV R0, AINC DPTRMOVX A, @ DPTRMOV R1, AMOV DPH, R0MOV DPL, R1; * UserStkptr === > R5 user stack start address content (ie, user stack lengths) See Document Description Pointer Details C51.PDF Page 178 MOVX A, @ DPTR; User Stack is Unsigned CHAR Type Data MOV R5, A ; R5 = user stack length; recovery site stack contents MOV R0, # OSStkStartrestore_stack: INC DPTRINC R0MOVX A, @ DPTRMOV @ R0, ADJNZ R5, restore_stack; restore stack pointer SPMOV SP, R0; OSRunning = TRUEMOV R0, # LOW (OSRunning) Mov @ r0, # 01popallsetb EA; open interrupt RETI; -------------------------------------- ---------------------------------- RSEG? PR? OSCTXSW? OS_CPU_AOSCTXSW: PushallosintctXsw_in :; Get Stack Length and Wage Site Mov A, SPCLR CSUBB A, # OSSTKSTARTMOV R5, A; Get the length of the stack; ostcbcur ===> DPTR Get the current TCB pointer, see c51.pdf 178 MOV R0, # low (Ostcbcur); get an OSTCBCUR pointer Low address, the pointer accounts for 3 bytes. 0 type 1 high 8-bit data 2 low 8-bit data INC R0MOV DPH, @ r0; global variable OSTCBCUR in Idata Inc R0MOV DPL, @ r0; ostcbcur-> ostcbstkptr ===> DPTR Get User Stack Pointer INC DPTR The pointer occupies 3 bytes.
0 type 1 high 8-bit data 2 low 8-bit data MOVX A, @ dptr; .ostcbstkptr is Void pointer MOV R0, AINC DPTRMOVX A, @ DPTRMOV R1, AMOV DPH, R0MOV DPL, R1; Save Stack length MOV A , R5MOVX @ DPTR, AMOV R0, # OSStkStart; get a stack from the site save_stack: INC DPTRINC R0MOV A, @ R0MOVX @ DPTR, ADJNZ R5, save_stack; call the user program LCALL _ OSTaskSwHook; OSTCBCur = OSTCBHighRdyMOV R0, # OSTCBCurMOV R1, #? Ostcbhighrdymov A, @ r1mov @ r0, AINC R0INC R1MOV A, @ R1MOV @ R0, AINC R0INC R1MOV A, @ R1MOV @ R0, A; OSPRIOCUR = OSPRIOHRDY Using these two variables The main purpose is to make pointer to becomes byte comparison To save time. MOV R0, # ospriocurmov r1, # ospriohighrdymov A, @ r1mov @ r0, aljmp osctxsw_in; --------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------ RSEG? PR? OSINTXSW? OS_CPU_AOSINTCTXSW: ; Adjust the SP pointer to the osintexit (), the excess content of the stack during the itintctxsw (); sp = SP-4
Mov A, SPCLR CSUBB A, # 4MOV SP, Aljmp OsintctXsw_in; -------------------------------------------------------------------------------------------------------------------- ------------------------------------- CSEG AT 000bh; ostickismrljmp ostickisr; use timer 0RSEG? PR ? Ostickisr? OS_CPU_A
OSTickISR: USING 0 PUSHALLCLR TR0MOV TH0, # 70H; defined Tick = 50 times / second (i.e., 0.02 seconds / times) MOV TL0, # 00H; OS_CPU_C.C and OS_TICKS_PER_SECSETB TR0LCALL _ OSIntEnterLCALL _ OSTimeTickLCALL _ OSIntExitPOPALL RETI; -??? -------------------------------------------------- -------------------- CSEG AT 0023H; serial port interrupt LJMP Serialism; working in system state, no task switching. RSEG? PR? _? Serial? OS_CPU_ASERIALIALISR: USING 0 PUSHALLLR EALCALL _? Serial setb eapall Reti; ----------------------------------------------------------------------------------------------------------------------- ----------------------------------------- -------------------------------------------------- -------------------
File name: OS_CPU_C.C
Void * ostaskstkinit (Void * Pd), Void * Ppdata, Void * Ptos, INT16U OPT) Reentrant {os_stk * STK;
PPDATA = PPDATA; OPT = OPT; // OPT is not used, retain this statement to prevent alarms from generating STK = (os_stk *) ptos; // User Stack Minimum Effective Address * STK = 15; // User Stack Length * STK = (INT16U) Task & 0xFF; // Task Address Low 8-bit * STK = (INT16U) Task >> 8; // Task Address Height 8-bit * STK = 0x00; // PSW * STK = 0x0a; // ACC * STK = 0x0b; // b * STK = 0x00; // DPL * STK = 0x00; // DPH * STK = 0x00; // r0 * STK = 0x01; // r1 * STK = 0x02; // r2 * STK = 0x03 ; // r3 * STK = 0x04; // R4 * STK = 0x05; // r5 * STK = 0x06; // r6 * STK = 0x07; // r7 // Do not save the SP, task is calculated according to the user stack length inferred. Return ((void *) ptos);} # if os_cpu_hooks_envoid ostaskcreatehook (OS_TCB * PTCB) Reentrant {PTCB = PTCB; / * Prevent Compiler Warning * /}
Void OstaskDelhook (OS_TCB * PTCB) Reentrant {PTCB = PTCB; / * Prevent Compiler Warning * /}
Void OstimetickHook (Void) Reentrant {} # Endif
// Initialization timer 0void inittimer0 (void) Reentrant {TMOD = TMOD & 0xF0; TMOD = TMOD | 0x01; // mode 1 (16-bit timer), only by TR0 control TH0 = 0x70; // Define Tick = 50 times / sec (Ie 0.02 seconds / time) TL0 = 0x00; //OS_CPU_A.ASM and OS_TICKS_PER_SECET0 = 1; // Allow T0 interrupt TR0 = 1;}
File name: yy.c
#include
#define max_stk_size 64
Void Taskstartyya (Void * YYDATA) Reentrant; Void Taskstartyc (Void TaskStartyco) Reentrant;
OS_STK TaskStartStkyya [MAX_STK_SIZE 1]; // Note: I set it in the ASM file? The STACK space is 40h, 64, do not exceed the range. Os_stk taskstartstkyb [max_stk_size 1]; // User Stack Multiple Byte Save Length OS_STK TaskStartStkyyc [MAX_STK_SIZE 1];
Void main (void) {osinit (); initTimer0 (); INITSERIAL (); INITSERIALBUFFER (); ostaskcreate (taskstartyya, (void *) 0, & taskstartstkya [0], 2); ostaskcreate (taskstartyleb, (void *) 0, & TaskStartStkyyb [0], 3); OSTaskCreate (TaskStartyyc, (void *) 0, & TaskStartStkyyc [0], 4); OSStart ();} void TaskStartyya (void * yydata) reentrant {yydata = yydata; clrscr (); PrintStr ( "/ n / t / t ******************************* / n"); PrintStr ("/ t / t * Hello! The world. * / N "); PrintStr (" / t / t ***************************** / N / N / N "); for (;;) {PrintStr (" / taaaaa111111 is active./n" );OstiMedly (OS_TICKS_PER_SEC);}}
Void Taskstartyyb (void * yydata) reentrant {yydata = yydata; for (;;) {printstr ("/ tbbbbbb333333 is active./n" );Ostimedly (3*OS_TICKS_PER_SEC);}}
Void Taskstartyyc (Void * yydata) Reentrant {yydata = yydata; for (;;) {printstr ("/ tccccc666666 is active./n" );OstiMedly (6*OS_TICKS_PER_SEC);}}
Solution to the issue: If the reentrant key is used in the task function, if the reentrant key is used to cause re-entry, from the content of the c51.pdf 129-131, the content of the function is to be saved, the meticulous and local variables must be saved. In the stack, since the 51 hardware stack is too small, Keil will simulate the stack according to the memory mode according to the memory mode (the growth direction is downward, opposite the hardware stack). For large-mode compilation, the function return address is saved in the hardware stack, and the metall parameters and local variables are placed in the simulation stack, and when the stack pointer is? C_xbp, XbpStack = 1, the start value is initialized to FFFFH 1 in STARTUP.A51. The simulation stack is low, Keil recommends try not to use it, but must be used in order to re-entry. Keil can be mixed using three simulation stacks (large, medium, small mode), in order to improve efficiency, recommend unified use of large-mode compilation for 51. In order to support re-entry, the stack structure is redesigned (as shown below). Added data structures for saving the simulation stack pointer? C_xbp and stack content. The corresponding changes are: OS_CPU_A.ASM, OS_CPU_C.C, OS_CPU.H, YY.C. As can be seen from the figure, the simulation stack saved in the user stack is grown to the hardware stack, and the intermediate is idle interval, apparently the stack detection function of UCOSII. The save recovery of the hardware stack is detailed. Save the Save of the Simulation Stack, like the 8086 transplant, the OS only provides a stack space and only the stack pointer, does not perform memory copies, and the efficiency is relatively high. It is recommended to use a unified fixed size stack space, although the UCosii original author looks like a different space to use different spaces as an advantage, but in order to effectively achieve task in 51, it is not necessary to use this advantage for 51 people. The size of the user stack space can be accurately calculated. User Stack Space = Hardware Stack Space Simulation Stack Space. Hardware Stack Use the internal RAM, the internal RAM executes high, and if the stack space is too large, it will affect the program performance of Keil compilation. If the stack space is small, the system crashes when the interrupt nesting and program calls are interrupted. Comprehensive consideration, I set the hardware stack space to 64 bytes, and the user can set it according to the actual situation. The simulation stack size depends on the type and number of partial variables and the number of local variables, can be accurately calculated. Because all user stacks use the same space size, the space size of the task function with the maximum space is used as the space size of the simulation stack. This allows the user's stack space. I define the user stack space with macro in the os_cfg.h file, macro named MaxStksize. 51 SP is only 8, which cannot be moved freely in 64K space, and there is only a stupid approach to copy all hardware stack content. 51 It was weak, so the shortcomings were more obvious. In fact, introducing OS will inevitably pay the price, general OS should take up the load capacity of CPU10% -20%, please weigh the pros and cons. The switching frequency determines the cost of the CPU, the higher the frequency, the greater the cost, and it will change the stronger CPU to a certain extent. I chose 50 Hz switching frequency, not high, and the user can determine itself as needed. I took several measures to increase efficiency when spentable. RET and RETI mixing reduction code; 2. IE, SP is not in the stack, solved by additionally; 3. The global variables used in assembly are declared with IDATA keywords, and the DPTR operation is Ri operation; 4. Design stack structure, simplified algorithm; 5. Let the serial input output work in the system state, do not occupy the task TCB and priority, increase the elastic buffer, reduce the waiting.
In the 51 single-chip hardware simulation UCOS51: Zyware User 2002/11/22 Letter to ask the hardware simulation problem on the single-chip microcontroller, the specific situation is "using the UCOS51 core on 51, and some components, KEILC simulation, with WAVE After the hardware simulation process, the Wave Simulation has no problem with the previous procedure, and I don't know what it is. " Since my OS program has actually been tested on the KEIL software emulation and hardware, it is impossible to be a program. Possible reasons can only be a hardware simulation software setting problem. I use the MedWin software, debug on Insight, running like the UCOS51 compile test program. Even if you add a modified Startup.a51 (see "Curing the UCOS51 on the 51 single-chip microcontroller") is not normal. I found that MedWin didn't seem to compile Startup.a51 because it adds this file to the other files directory instead of the source files directory, so I guess that only the files in the Source Files directory are compiled. By observing, the files that are suffixed by .c and .asm are placed under this directory and compiled. So I immediately changed Startup.a51 into Startup.asm and joined the project compilation, and the results were normal. Don't worry about the Startup rename, Keil will automatically handle the rename segment when linking the target file. This directory has high priority (I am so understanding, the specific principle is unclear, this is just based on the practice of practice, I hope to understand this Friends who have dealt with the process can be grateful.). The specific practices are as follows: 1. Press "Description of the UCOS51 on the 51 single-chip microcontroller" to modify Startup.a51 and rename it to Startup.ASM. 2. Join the item compilation of Startup.asm, YY1.C, OS_CPU_C.C, UCOS_II.c, OS_CPU_A.ASM. 3. The description of the UCOS51 is running on the 51 single-chip microcontroller: Recently, I received a number of netizens to tell the UCOS51 in the cured problem on the 51 single-chip microcomputer. It is: why OS can operate on the keilc51, but burn it in the CPU But can't you work? In theory, the program is burn it on the hardware after the software simulation, and the hardware debugging should be successful. Many netizens also have this experience, but why can't I invalid when debugging UCOS51? Is it very special? In fact, the problem is in the introduction of the re-entry function. The original Keilc51 software simulation does not modify the startup.a51 file, the lack of 64K external RAM, it puts all the 0000h-fffh all simulates the readable RAM, and the user's hardware system may not be used as large RAM space For example, only 8K / 16K / 32K, or the user maps some address space to other devices, such as 8019AS, etc. Before you call OrstaskCreate, the function defined as the Reentrant will simulate the stack top pointer with FFE0H, and this is not RAM in the user's system, resulting in a program running. For example, on my subscriber board, part of the Fe00H-FFFFH space is used to 8019AS. If the DEMO program is prescribed, burn directly to 51, it will not be able to run. The solution is to modify the startup.a51 file according to the system RAM, and join the project compile, as shown below:
Xbpstack EQU 1; Set to 1 if Large Reentrant Is Used.xbpstacktop EQU 07FFFH 1; Set Top of Stack To Highest Location 1.
After this modification, you can run normally on the system with 32K external RAM. Users can modify the Startup.a51 related parameters according to the actual configuration of our XRAM, and add it to the project. Don't pay attention to the same name file in the Keil / C51 / Lib directory, the Startup.a51 is highly priority, and Keil will compile the project according to the configuration of this file. This also explains some netizens asked, "Why add the Reentrant keyword, correct when the software is simulated, burning on the chip to crash, remove the reentrant, and the two are normal." Since most people rarely use the re-entry function, they often do not understand this detail. Here, please pay attention to you. The cause of UCOS51 does not work is also possible because the serial baud rate and OS_TICKS_PER_SEC and TL0 setting are incorrect. The DEMO program uses 22.1184 MHz crystals, 19200 baud rate, and the switching frequency is 50 Hz. To this end, 1. Set "TL1 = 0xFD; TH1 = 0xFD;" enables the baud rate to 19200; Set "TH0 = 0x70; TL0 = 0x00 in OS_CPU_C.C and OS_CPU_A.ASM;" makes the clock tick = 50 times / sec; 3. Set OS_TICKS_PER_SEC in OS_CFG.H to 50Hz. The user should modify these parameters accordingly according to the actual situation, otherwise it is incorrect.
Timer initial value setting:
Timer 0 for clock beat generator // ********************************************* ************************************************ // initial value calculation formula: // (2 ^ 16-x) * f = fosc / 12 // wherein: f = clock beat frequency Tick; FOSC = crystal or crystal frequency; x = initial value; // This example, f = 50; FOSC = 21.1184 MHz; So X = 0x7000. // ******************************************************** ****************************
Timer 1 for baud rate generator // ********************************************* ************************************************* // Input calculation formula: // TH1 = 256- (2 ^ SMOD / 32 * FOSC / 12 * 1 / Bound) // wherein: SMOD = 0, 1; fosc = crystal or crystal frequency; bound = baud rate // This example, sm= 0; FOSC = 21.1184 MHz; bound = 19200, so th1 = 0xFD. // ******************************************************** ****************************
In the Demo program project, the startup is correct after I'm rewritten as above, running correctly on my userboard hardware.
Increase the shell interface for UCOS51: Ucosii only provides operating system kernels, users have to add file processing, man-machine interface, network interface, etc. important parts. Where the shell (man-machine interface) provides an interface to the machine's interaction, it is an important part of the system is an important part of the system. Modern OS, such as UNIX, DOS, and VxWorks provide a friendly command line interface. Windows is also a GUI. Most people know that OS is starting here. UCOS51 also has a shell, which is transferred from the previously written front and back programs. The working principle of the command line shell is relatively simple. The main idea is that the single-chip microcomputer receives the user keyboard input to the command buffer, and return to the screen. When the user presses the Enter key, trigger the software state machine state change, from input state transfer To the command interpretation state, then call the associated subroutine according to the user command to perform the corresponding operation, return to the input state after execution. I feel very good, the procedure is not long, but the details should be repeated repeatedly to stabilize. For example: the command line is the protection of the left and right boundaries, the process of retracting, the design of the word meter, and more. The shell program is composed of the associated subroutine (see the source program list) by the word meter, the word subroutine, the state machine frame program (input echo and command). The word surface structure is shown in the list of procedures, the number of words, the number of the left, the number, the number, and the specific information (length, string) of each word (length, string). The left and right brackets are used for parentheses matching checks; the number of words is used for program loop; the specific information of the word is used as an input parameter of the interpretation / execution program. The word subroutine extracts the word from the command line statement and stores the word meter and simultaneously match the test and lexical analysis. The default characters are: 0-9, a-z, a-z, '.'; Delimiter is: space, comma, left / right bracket. It is recommended that the user adds the default character set (? / / -) to achieve a more flexible grammar. Note: The current version of the shell is only checked, and there is no priority and syntax meaning of the number of parentheses. Enter the echo program loop to check the user keyboard input. If you enter a carriage return, the program status transfer is explained; if the input back (8) is returned, the retracter, the retracter, the simulation delete character, and the input buffer clear the corresponding byte, before clearing the left boundary Whether it is off. If the crimp is called alarm and does not perform the clear operation; other character inputs are stored directly into the input buffer and return it, and before the right boundary overflow, if the overflow sounds the alarm and abandon the characters you just entered. Command Interpreter Calling Take a Frany Program Analysis User Command Line Input, according to the first word of the word meter to call the corresponding subroutine processing command, if there is no such word in the scavenging table, print "Bad Command ! " This sentence is also printed when the error indication is returned. Command Interpretation The program is incorporated into the corresponding command related subroutine, and the specific execution is determined by the user. After returning by the command-related subroutine, return to the command input state, complete the full process of execution. This process is performed in the week.
The command of the shell interface is divided into the following groups: 1. Operating System Related Commands: View Ready Task LT / Substation Kill / Recovery Task Executes the Call / CPU Usage Usage / Verification Ver / Table Task Information (TCB, Stack Content) LT View Switching Time LTS2. Network Related Command: Display Configuration Mac Address MacAdr / Display Configuration Host IP Address HOST / Display Configuration Submunication Mask Mask / Display Configuration Default Gateway Gateway Display Network Configuration Total LC / Connection Test Command PING / User Data News Send UDP / Telnet Command Tel / Related Application Command ** Displays the ARP Cache Address LS / Display Send Buffer Information LTI3. Screen display related command: Qing screen CLR / Help HELP / function key F3, F7 processing / combination key Ctrl C, Ctrl B processing 4. Peripheral (flash disk X5045 and I / O port) related commands: read flash drive RDX / read I / O port RDP / write flash disk WDX5. Security related command: identity authentication password permission USR, pass6. Application Related Commands: The user defines the case sensitive to the user command, and the program will unify the command string into small-write form. Various parameters in the program (eg, the maximum word length, the number of words ...) define a macro in a header file, ready to modify the configuration, which is convenient. The shell is operated outside the kernel as a task, occupying a task number.
Source: Cictionnation Typedef struct {int Num; int LEFTCURVENUM, RIGHTCURVENUM; STRUCT {Int length; unsigned char str [Maxlenword 1]; / * for '/ 0' * /} wt [MaxlenwordTable];} WordTable;
Take the word bit GetWord (unsigned char * combuf, wordtable * wordtable) {INT i = 0; / * Combuf string pointer * / int J = 0; / * Length of word * / int K = -1; / * the Number of Wordtable * / int strflag = 0; / * there "0-9 / az / az" before ", ()" * / int successENCEENDFLAG = 0; / * SENTENCE END * / CHAR CH;
WordTable-> Num = 0; WordTable-> LEFTCURVENUM = 0; WordTable-> Rightcurvenum = 0;
CH = Combuf [0]; While (! SENTENCEENDFLAG && i
Else Return 0;
}
Else {
Return 0;
}
}
Enter Election and Command Explanation Execution Void Yyshell (Void * YYDATA) Reentrant {yydata = yydata; clrsCr (); PrintStr ("/ t / t ****************************** **************************** / N "); PrintStr (" / t / t * welcom to use this program * / n " ); PrintStr ("/ t / t * author: yangyi 20020715 * / n"); PrintStr ("/ t / t ******************** ************************ / N / N / N "); / * login & password * /
Printstr ("%"); while (! Shellend) {
Switch (state) {copy statinputcom: {if (yygetch (& ch)) {if (CH == 13) / * Enter return key * / {printstr ("/ n"); Combuf [i 1] = '/ 0 '; if (i 1 == 0) PRINTSTSTR ("%"); else state = statexecom;} else {i = i 1; if ((i> = maxlencombuf) && (ch! = 8)) {Printchar (7); i = maxlencombuf-1;} else {IF (CH == 8) {i = I-2; IF (i <-1) {i = -1; printchar (7);} else {printchar 8); printchar (''); printchar (8);}} else {printchar (ch); Combuf [i] = ch;}}} Break;}}} Break;}}} Break;} else {// Ostimedly (10); Break;}} Case Statexecom: {IF (getWord (Combuf, & WordTable) == 1 && WordTable.Num! = 0) {yyStrLwr (WordTable.wt [0]. Str); for (TEM = 0; Tem