2012-05-21的我突然想做一下ucos在cortex-m0平台上的移植,所以就看cortex-m0的内核架构和指令集,接着就开始着手移植了。先说明一下,我的开发板是新唐(Nuvoton)的NuTiny-EVB M051,芯片是M0516LAN。
可以分两步走。第一步,移植ucos的底层代码。第二步,建立新任务。
第一步:移植ucos就是修改这么几个文件:os_cpu_c.c , os_cpu.h , os_cpu_a.asm
其中,os_cpu.h主要是定义数据类型的定义,和有关处理器的一些设置,如栈的生长方向,进入临界代码段的方式,数据存储的大小端设置等等。
1. os_cpu.h:
1.1 定义好数据类型。这个简单,所以不用怎么说。
1.2 定义进行临界代码段的方式,我定义为方式3。即进入时保存PSR,然后关中断,出来是恢复PSR的值。
对应的函数为:OS_CPU_SR OS_CPU_SR_Save(void), void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)
1.3 定义栈的生长方向。我定义为从高向下生长的方向。
1.4 函数的声明。这里有5个函数需要声明。分别是:
void OSCtxSw(void)
void OSIntCtxSw(void)
void OSStartHighRdy(void)
OS_CPU_SR OS_CPU_SR_Save(void)
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)
由于OSIntCtxSw和OSCtxSw代码可以一致,所以只定义OSCtxSw。
1.5 具体代码如下:
- #ifndef OS_CPU_H
- #define OS_CPU_H
- #ifdef OS_CPU_GLOBALS
- #define OS_CPU_EXT
- #else
- #define OS_CPU_EXT extern
- #endif
- #ifndef OS_CPU_EXCEPT_STK_SIZE
- #define OS_CPU_EXCEPT_STK_SIZE 1 /* Default exception stack size is 128 OS_STK entries */
- #endif
- typedef unsigned char BOOLEAN;
- typedef unsigned char INT8U; /* Unsigned 8 bit quantity */
- typedef signed char INT8S; /* Signed 8 bit quantity */
- typedef unsigned short INT16U; /* Unsigned 16 bit quantity */
- typedef signed short INT16S; /* Signed 16 bit quantity */
- typedef unsigned int INT32U; /* Unsigned 32 bit quantity */
- typedef signed int INT32S; /* Signed 32 bit quantity */
- typedef float FP32; /* Single precision floating point */
- typedef double FP64; /* Double precision floating point */
- typedef unsigned int OS_STK; /* Each stack entry is 32-bit wide */
- typedef unsigned int OS_CPU_SR; /* Define size of CPU status register (PSR = 32 bits) */
- #define OS_CRITICAL_METHOD 3
- #if OS_CRITICAL_METHOD == 3
- #define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
- #define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
- #endif
- #define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory on ARM */
- #define OS_TASK_SW() OSCtxSw()
- #if OS_CRITICAL_METHOD == 3
- OS_CPU_SR OS_CPU_SR_Save (void);
- void OS_CPU_SR_Restore (OS_CPU_SR cpu_sr);
- #endif
- void OSCtxSw (void);
- void OSStartHighRdy (void);
- #define OSIntCtxSw() OSCtxSw();
- #endif
2. os_cpu_c.c
2.1 定义函数OSTaskStkInit(),对于其它的函数可以不用理,因为如果你不需要用到钩子函数的话,是不用实现的。
OSTaskStkInit()主要初始化各个任务的栈。
2.2 由于cortex-m0的架构决定,当产生异常(包括中断)时,进栈的顺序如下:(这是Full descending的模式)
XXX <---SP before exception
xPSR
PC
LR
R12
R3
R2
R1
R0 <--SP after before exception
2.3 代码如下:
- OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
- {
- OS_STK *stk;
- (void)opt; // 'opt' is not used, prevent warning
- stk = ptos; // Load stack pointer
- /* Registers stacked as if auto-saved on exception */
- *(--stk) = (INT32U)0x01000000L; // xPSR
- *(--stk) = (INT32U)task; // Entry Point (PC)
- *(--stk) = (INT32U)0xefefefefL; // LR
- *(--stk) = (INT32U)0x12121212L; // R12
- *(--stk) = (INT32U)0x03030303L; // R3
- *(--stk) = (INT32U)0x02020202L; // R2
- *(--stk) = (INT32U)0x01010101L; // R1
- *(--stk) = (INT32U)p_arg; // R0 : argument
- /* Remaining registers saved on process stack */
- *(--stk) = (INT32U)0x07070707L; //R7
- *(--stk) = (INT32U)0x06060606L; // R6
- *(--stk) = (INT32U)0x05050505L; // R5
- *(--stk) = (INT32U)0x04040404L; // R4
- *(--stk) = (INT32U)0x11111111L; // R11
- *(--stk) = (INT32U)0x10101010L; // R10
- *(--stk) = (INT32U)0x09090909L; // R9
- *(--stk) = (INT32U)0x08080808L; // R8
- return (stk);
- }
3. os_cpu_a.asm
这个文件是对头文件 os_cpu.h所定义的函数进行实现。都是汇编代码,不过不是很难懂。
- ;2011-05-23 xiaoben
- EXTERN OSRunning
- EXTERN OSPrioCur
- EXTERN OSPrioHighRdy
- EXTERN OSTCBCur
- EXTERN OSTCBHighRdy
- EXTERN OSIntNesting
- EXTERN OSIntExit
- EXTERN OSTaskSwHook
- EXPORT OS_CPU_SR_Save
- EXPORT OS_CPU_SR_Restore
- EXPORT OSStartHighRdy
- EXPORT OSCtxSw
- EXPORT OSIntCtxSw
- AREA os_cpu_asm, CODE, READONLY, ALIGN=2
- THUMB
- REQUIRE8
- PRESERVE8
- OS_CPU_SR_Save
- MRS R0, PRIMASK
- CPSID I
- BX LR
- OS_CPU_SR_Restore
- MSR PRIMASK, R0
- BX LR
- OSStartHighRdy
- LDR R0, __OSTaskSwHook ; Call OSTaskSwHook()
- BLX R0
- LDR R0, __OSRunning ; OSRunning=1
- MOVS R1, #0x01
- STRB R1, [R0]
- LDR R0, __OSTCBHighRdy ; SP = OSTCBHighRdy->OSTCBStkPtr
- LDR R1, [R0]
- LDR R2, [R1]
- MSR MSP, R2
- ; Restore registers from new task stack
- POP {R0-R7} ; Restore R4-R7, R8-R11
- MOV R11, R3
- MOV R10, R2
- MOV R9, R1
- MOV R8, R0
- ADD SP, #0x10 ; Restore PSR, PC, LR, R12
- POP {R0-R3}
- MOV R12, R0
- MOV LR, R1
- PUSH {R2} ; Push PC into stack
- MSR PSR, R3
- SUB SP, #0x1C ; Restore R0-R3
- POP {R0-R3}
- ADD SP, #0x0C
- CPSIE I ; Enable interrupts. NOTE: must not omit!
- POP {PC} ; Jump to the task and execute, not return.
- OSCtxSw
- CPSID I
- ; Save registers into current task stack
- SUB SP, #0x20 ; Save R4-R7
- PUSH {R4-R7}
- ADD SP, #0x30 ; Save PSR, PC, LR, R12, R0-R3
- MRS R7, PSR
- MOV R6, LR ; NOTE!
- MOV R5, LR
- MOV R4, R12
- PUSH {R0-R7}
- SUB SP, #0x10 ; Save R8-R11
- MOV R3, R11
- MOV R2, R10
- MOV R1, R9
- MOV R0, R8
- PUSH {R0-R3}
- ; Save SP in current stask stack
- MRS R0, MSP ; OSTCBCur->OSTCBStkPtr = SP
- LDR R1, __OSTCBCur
- LDR R2, [R1]
- STR R0, [R2]
- LDR R0, __OSTaskSwHook ; Call OSTaskSwHook()
- BLX R0
- LDR R0, __OSPrioCur ; OSPrioCur = OSPrioHighRdy
- LDR R1, __OSPrioHighRdy
- LDRB R2, [R1]
- STRB R2, [R0]
- LDR R0, __OSTCBCur ; OSTCBCur = OSTCBHighRdy
- LDR R1, __OSTCBHighRdy
- LDR R2, [R1]
- STR R2, [R0]
- LDR R0, [R2] ; SP = OSTCBHighRdy->OSTCBStkPtr
- MSR MSP, R0
- ; Restore registers from new task (with high priority) stack
- POP {R0-R7} ; Restore R4-R7, R8-R11
- MOV R11, R3
- MOV R10, R2
- MOV R9, R1
- MOV R8, R0
- ADD SP, #0X10 ; Restore PSR, PC, LR, R12
- POP {R0-R3}
- MSR PSR, R3
- PUSH {R2}
- MOV LR, R1
- MOV R12, R0
- SUB SP, #0X1C ; Restore R0-R3
- POP {R0-R3}
- ADD SP, #0X0C
- CPSIE I ; Enable interrupts! NOTE: must not omit!
- POP {PC} ; Jump to task and execute, not return
- NOP
- __OSRunning
- DCD OSRunning
- __OSTaskSwHook
- DCD OSTaskSwHook
- __OSIntExit
- DCD OSIntExit
- __OSIntNesting
- DCD OSIntNesting
- __OSPrioCur
- DCD OSPrioCur
- __OSPrioHighRdy
- DCD OSPrioHighRdy
- __OSTCBCur
- DCD OSTCBCur
- __OSTCBHighRdy
- DCD OSTCBHighRdy
- END
第二步: 到了这里,整个ucos内核就算移植好了。接下来就要建立任务了。对于任务的建立,与头文件 os_cfg.h有关。我们要在os_cfg.h设置一下有关任务的东西。
1. os_cfg.h里面有很多开关量,对于任务里用不到的东西,比如说信号量、定时器之类的,我们可以把它关掉,以减少系统的内存占有率。对于最低优先级OS_LOWEST_PRIO和最大任务数OS_MAX_TASKS要注意一下。还有OS_TICKS_PER_SEC,关于系统的节拍,也要设置一下,一般100到200之间就可以了。
2. 对于任务时钟的节拍,采用cortex-m0的SYSTick来作为节拍源,所以还要加上SysTick的异常处理函数!!!!我在main函数里面添加。
3. main函数里面任务的建立。我们先建立一个闪烁的LED任务。代码如下:
- #include <stdio.h>
- #include "M051Series.h"
- #include "Driver/DrvGPIO.h"
- #include "Driver/DrvSYS.h"
- #include "ucos_ii.h"
- /* LED 任务相关 */
- #define LED_STK_SIZE 64
- #define TASK_LED_PRIO 10 //不要大于OS_LOWEST_PRIO
- OS_STK TaskLEDStk[LED_STK_SIZE]; //任务栈
- void task_led(void *pdata);
- /* 与开发板相关的函数定义 */
- void init_target(void);
- void init_sys_clock(void);
- void init_led(void);
- void led_on(void);
- void led_off(void);
- /* SysTick Exception ISR */
- void SysTick_Handler(void)
- {
- OSIntEnter();
- OSTimeTick();
- OSIntExit();
- }
- /* main */
- int main()
- {
- __disable_irq();
- init_target();
- OSInit();
- OSTaskCreate( task_led, (void*)0, TaskLEDStk[LED_STK_SIZE-1], TASK_LED_PRIO );
- OSStart();
- return 0;
- }
- void task_led(void *pdata)
- {
- (void)pdata;
- while(1)
- {
- led_on();
- OSTimeDlyHMSM(0, 0, 1, 0);
- led_off();
- OSTimeDlyHMSMj(0, 0, 1, 0);
- }
- }
- void init_target(void)
- {
- init_sys_clock();
- init_led();
- /* Note: Important */
- SysTick_Config(DrvSYS_GetHCLKFreq()/OS_TICKS_PER_SEC-1); //Init system tick
- }
- void init_sys_colock(void)
- {
- UNLOCKREG(); // Unlock the protected registers
- DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1); // Enable the 12MHz oscillator oscillation
- while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1); // Waiting for 12M Xtal stable
- DrvSYS_SelectHCLKSource(0);
- LOCKREG(); // Lock the protected registers
- DrvSYS_SetClockDivider(E_SYS_HCLK_DIV, 0); // HCLK clock frequency = HCLK clock source / (HCLK_N + 1)
- }
- void init_led(void)
- {
- DrvGPIO_Open(E_PORT3, E_PIN6, E_IO_OUTPUT);
- DrvGPIO_SetBit(E_PORT3, E_PIN6); //led 接P3.6引脚
- }
- void led_on(void)
- {
- DrvGPIO_ClrBit(E_PORT3, E_PIN6);
- }
- void led_off(void)
- {
- DrvGPIO_ClrBit(E_PORT3, E_PIN6);
- }
到此,整个UCOS在cortex-m0就移植成功并且可以运行任务啦。。。从晚上跑到天亮,都非常稳定。
第二天,我又结合之前做过的TXT阅读器,在UCOS上也做了一个TXT电子书阅读器,也挺流畅,也很稳定。感觉很不错!