For a few more time, I participated in a SNMP mesh project, I was responsible for hardware design and board software development. The hardware of the plate consists of MCS51 RTL8019AS, with 64K Flash and 64K SRAM. Software section has operating systems and TCPIP protocol stacks. The hardware is relatively simple, it is done for a month, the protocol stack I refer to some of the procedures of the old ancient development board, and the SNMP source code is also completed, but it is found when testing the CPU using lower clock frequencies. (To reduce cost), since the ASN.1 codec is too large, my procedure is a large cycle, the Agent's response speed is seriously affected, and the user interface is also slow. A worse message is to run PPP and HTTP on the company in order to adapt to market needs. In that case, I have to use 40MHz AT89C51RD2 or people to break the program into several parts and then connect them again with the state machine. However, I don't want to increase the cost, and I don't want to mess with the program, I can't wait, I have to use the operating system. To be honest, I am not very confident, I don't know if the 51 Flash is installed so much code, I only have to do OS application development, I don't want to think about it. However, I still have some clues after searching for a while. I found a few OS source code (I like to use home-class), according to the code size, real-time, number of people, the criteria, etc., and finally selected UCOS2. I feel that it is guaranteed, the delay can be predicted, the code is said to be a small to 2K, and the people discussing this topic is also more, and its website has a transplant instance for Keil C51. After some look, I got 5 versions. 3 of them were compiled with keil. Originally, I would like to directly embed the OS code to the application, but then I found no one can be used directly. Some can't compile directly with Keil, and some need to modify the DLL to use it under the software simulation. And I need to enter the serial input and output, do not need to modify any unrelated software, real-time multi-task operating system that can be run on software simulation and hardware. There is no way, I have to pay my head to adapt. I analyzed my disadvantage: 1. Keil just started using, not familiar; 2. Mixed programming has never been made before; 3. Time is urgent, you have to get it within 1 month. And my advantage is that there are 5 transplant examples for reference, you can check the information. At first, I use "stack", "mixed programming", "assembly", "UCOS" and other keywords to retrieve relevant information on C51BBS and the old ancient forum and read it by, after reading, the idea in the mind is gradually clear. I learned that there is a 151.pdf and c51.pdf in Keil's HLP directory. It is very comprehensive to introduce assembly and C51. It is the Keil's authoritative user manual; SP initialization, memory clear 0 and other operations are implemented in Startup.a51 files, users It can be rewritten; the allocation information of Keil's variable, subroutine, etc. can be found in .m51 file; there is a solution to a lot of troubles in Keil's own forum ... By reading and thinking, solve the starting point of the stack, stack space Key issues such as settings. The problem in the forum is some of what I didn't think of, which made me find my own omissions. After getting a lot of information online, I started reading "ucosii" Chinese version, I have read 3 times. The first time is browsing, understand UCOSII, including 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. The second time is mainly to pass the entire work process in the mind, do not understand the local targeted investment, focusing on thinking about working principles and processes. I found that it is very simple. That is, "approximation always makes the task of the highest priority 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 third time focuses on the content of the transplanted part. For example, the specific implementation of the code was studied. In the early stage, I have been prepared for 20 days, and I really write code for only 1.5 days, and debugging for 2 days. The specific process is as follows: (1) After copying the book, the contents of the disc Sourcecode directory is included to C: / YY, remove unnecessary files and ex1l.c, only the files listed on P187 ("UCOSII"). (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 MCU-51 stack increases from bottom to top (1 = down, 0 = up), OS_STK_GROWTH defines 0 #define os_task_sw () osctXsw () because the MCU-51 does not have a soft interrupt command, so use program call instead. The stack format of the two is the same, the RETI command resets the interrupt system, and RET is not. Practice shows that for the MCU-51, the subprogrammation is used in the stack. It is not a problem with the interrupt return instruction RETI out, and it is not possible to break the stack RET outlet. 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 "UC / OS-II" Chapter 8 193 Page 120 (3) Change OS_CPU_C.C I design The stack structure is shown below: *************** *********************************************************** **************** ---------- * | Ostcbcur | * ---------- * | * | ---- ---------------------------- * / ----> | ostcbcur-> ostcbstkptr | SP ----> | | * -------------------------------- * | | * | ---------- - - -------- * | | | ---------- | |. | * | | ----- ----- | ---------- * | |. | Length | | 1 * | | | | ---------- * | | |Stack ----> | | 0 * | ------------------------------------------------------ - * | | | osstkstart ----> | Do not care | -1 low address * | ------------------------ * / ---- ----> | Length | Low Address System Stack * ---------- * User Stack Length = SP-OSSTKSTART ******************* *********************************************************** ************ Ostcbstkptr in the TCB structure always points to the minimum address of the user stack, and stores the user stack length in the address space, and the system stack image is stored on the space.
That is: 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) Rewriting OS_CPU_A.ASM A51 macro assembly is as follows: Name module name; independent of the file name; defined the relief segment 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 OSSTARTHRDY OSCTXSW OSINTURTXSW OSTICKISR SerialISR END; declaration that the went 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) Transplant serial port I wrote based on interrupt serial port drivers, including print bytes / word / long words / 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 active AAAAAA111111 is active AAAAAA111111 is active BBBBBB333333 is active AAAAAA111111 is active AAAAAA111111 is active AAAAAA111111 is active BBBBBB333333 is active CCCCCC666666 is active AAAAAA111111 is active AAAAAA111111 is active AAAAAA111111 is active BBBBBB333333 is active AAAAAA111111 is active AAAAAA111111 is active AAAAAA111111 Is active bbbbb333333 is active ccccc666666 IS Active Demo program After compiling by Keil701, the amount of code is 7-8k, which can be simulated directly on Keilc51. To join the OS_CPU_C.C, UCOS_II.C, OS_CPU_A.ASM, YY.C, and I have some experience of my transplant UCOS51, write it just let the peers who are ready to run the operating system on 51 and bend less and Enhance your confidence. I strongly recommend everyone to use UCOS this simple and practical operating system in their 51 system. Its size should not be a problem, and the improvement in performance is significant.
I hope that this article can help from friends, mistakes are inevitable, I hope you have prawn to correct, and the masters have laughed! Note: All source code can be trusted to be (asDjf@163.com), and the following is just a critical code section. File name: 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 declaration 4 non-re-renewable functions public osstarthighrdy public osctxsw public osintxsw public ostickism; public serialism Assign the 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 IDATA RSEG? Stackosstack: DS 40HOSSTKSTART IDATA OSSTACK-1; Define Stack Out Stack Macro Pushhall Macro Push PSW Push ACC Push B Push DPL Push DPH MOV A, R0; R0-R7 Fact Push Acc Mov A, R1 Push ACC MOV A, R2 PUSH ACC MOV A, R3 PUSH ACC MOV A, R4 PUSH ACC MOV A, R5 PUSH ACC MOV A, R6 PUSH ACC MOV A, R7 PUSH ACC; PUSH SP; Do not have to save SP, task switching Program adjustment ENDM POPALL MACRO; POP ACC; does not have to save sp, task switching POP ACC; R0-R7 outlet MOV R7, A POP ACC MOV R6, A POP ACC MOV R5, A POP ACC MOV R4, A POP ACC MOV R3, A POP ACC MOV R2, A POP ACC MOV R1, A POP ACC MOV R0, A POP DPH POP DPL POP B POP ACC POP PSW ENDM; Subprogram; ----------- --------------------------------------- ----------------------- RSEG? Pr? OSSTARTHRDY? OS_CPU_AOSSTARHRDY: using 0; 51 Automatic Off Interrupt after power-on, here does not have to use CLR EA instructions, Because it has not been interrupted here, this program is exited and interrupt. LCALL _? OstasksWhooksctxsw_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 incn, @ r0; global variable ostcbcur In iDATA Inc R0 MOV DPL, @ r0; ostcbcur-> ostcbstkptr ===> DPTR Get user stack pointer INC DPTR; pointer up 3 bytes.
0 type 1 high 8-bit data 2 low 8-bit data MOVX A, @ dptr; .ostcbstkptr is Void pointer MOV R0, A INC DPTR MOVX A, @ DPTR MOV R1, A MOV DPH, R0 MOV DPL, R1; * UserStkptr ===> R5 User Stack start address content (ie, user stack lengths) See Document Description Pointer Usage C51.pdf Page 169 MOVX A, @ DPTR; User Stack is a unsigned char Type Data MOV R5, A; R5 = User Stack Length; Restore Scene Stack Content MOV R0, # osstkstart restore_stack: Inc DPTR INC R0 MOVX A, @ DPTR MOV @ R0, A DJNZ R5, RESTORE_STACK; Restore Stack Pointer SP MOV SP, R0 Osrunning = true mov r0, # running) Mov @ r0, # 01 popall setb EA; open interrupt RETI; ---------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------- RSEG PR? Osctxsw: pushall osintxsw:; get the stack length and from the address MOV A, SP CLR C SUBB A, # OSSTKSTART MOV R5, A; Get the stack length; OSTCBCUR ===> DPTR Get the current TCB pointer, See c51.pdf p. 178 MOV R0, # low (Ostcbcur); get the OSTCBCUR pointer low address, the pointer accounts for 3 bytes. 0 type 1 high 8-bit data 2 low 8-bit data incn, @ r0; global variable ostcbcur In iDATA Inc R0 MOV DPL, @ r0; ostcbcur-> ostcbstkptr ===> DPTR Get user stack pointer INC DPTR; pointer up 3 bytes.
0 type 1 high 8-bit data 2 low 8-bit data MOVX A, @ dptr; .ostcbstkptr is Void pointer MOV R0, A INC DPTR MOVX A, @ DPTR MOV R1, A MOV DPH, R0 MOV DPL, R1; Save the stack length MOV A, R5 MOVX @ DPTR, A MOV R0, # osstkstart; get the stack from the site Save_Stack: Inc DPTR INC R0 MOV A, @ r0 MOVX @ DPTR, A DJNZ R5, SAVE_STACK; call User Program LCALL _? OstasksWhook Ostcbcur = ostcbhighrdy mov r0, # ostcbcur mov r1, # ostcbhighrdy mov A, @ r1 mov @ r0, a incc R0 INC R1 MOV A, @ R1 MOV @ R0, A INC R0 INC R1 MOV A, @ r1 MOV @ r0 , A; OSPriocur = OSPRIOHighrdy uses these two variables to make pointers to become byte comparisons in order to save time.
MOV R0, # ospRiocur Mov R1, # ospRiohighrdy Mov A, @ r1 mov @ r0, a ljmp osctxsw_in; --------------------------- ---------------------------------------------- RSEG? PR? OsintcTxsw? OS_CPU_A OSINTCTXSW:; Adjust the SP Pointer to the ostexit (), the excess content in the stack during the osintxsw (); sp = SP-4 MOV A, SP CLR C SUBB A, # 4 MOV SP, A LJMP Osintctxsw_in ; ------------------------------------------------- ------------------------ CSEG AT 000BH; Ostickisr Ljmp Ostickisr; Using Timer 0 RSEG? PR? OsticksR? OS_CPU_AOSTICKISR: Using 0 Pushall CLR TR0 MOV TH0, # 70H; Define Tick = 50 times / sec (ie 0.02 seconds / time) MOV TL0, # 00H; OS_CPU_C.C and OS_TICKS_PER_SEC SETB TR0 LCALL _? OSINTENTER LCALL _? OSINTEXIT INTICKALL_ 斯ISINTEXIT POPALL RETI; --- -------------------------------------------------- -------------------- CSEG AT 0023H; serial port interrupt LJMP Serialism; working in system state, no task switching.
RSEG? PR? _? Serial? OS_CPU_A Serialism: USING 0 PUSHALL CLR EA LCALL _? Serial Setb Ea Popall Reti; -------------------------- ------------------------------------------------ End; -------------------------------------------------- ----------------------- File Name: OS_CPU_C.CVOID * OSTASKSTKINIT (Void (* Task) (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, the task switch is calculated according to the length of the user stack.
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 // Initial Timer 0VOID INITTIMER0 (Void) Reentrant {TMOD = TMOD & 0XF0; TMOD = TMOD | 0x01; // Mode 1 (16-bit timer), only TR0 Control TH0 = 0x70; // Define Tick = 50 times / sec (ie 0.02 seconds / time) TL0 = 0x00; //OS_CPU_A.ASM and OS_TICKS_PER_SEC ET0 = 1; // Allow T0 interrupt TR0 = 1;} file name: YY .C # include