将uCOSii 加入到工程,编译,如果报以下错误错误:
compiling ucos_ii.c...
linking...
.\out\test.axf: Error: L6200E: Symbol OSEventTbl multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSFlagTbl multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSTaskStatStk multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSRdyTbl multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSTaskIdleStk multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSTCBPrioTbl multiply defined (by ucos_ii.o and os_core.o).
.\out\test.axf: Error: L6200E: Symbol OSTCBTbl multiply defined (by ucos_ii.o and os_core.o).
.... 还有很多
解决方法:将ucos_ii.c 从工程中移除。
如果出现以下未定义错误:
.\out\test.axf: Error: L6218E: Undefined symbol OSCtxSw (referred from os_core.o).
.\out\test.axf: Error: L6218E: Undefined symbol OSIntCtxSw (referred from os_core.o).
.\out\test.axf: Error: L6218E: Undefined symbol OSStartHighRdy (referred from os_core.o).
.\out\test.axf: Error: L6218E: Undefined symbol OS_CPU_SR_Restore (referred from os_core.o).
.\out\test.axf: Error: L6218E: Undefined symbol OS_CPU_SR_Save (referred from os_core.o).
是因为 os_cpu_a.s 没有加入工程,将其加入工程即可解决问题。
------------------------------------------------------------------------
移植步骤:
1、修改OS_CPU.H 文件
1、重定义与编译器、uCOS II 有关的数据类型
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) */
2、定义中断的实现方式,一般是为了实现临界区代码保护
#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
3、定义栈的生长方式
/* 堆栈1是从上往下长的,0-从下往上的生长方式 */
#define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory on ARM */
4、宏定义 优先级任务切换 函数,用于现场保护以及现场恢复实现任务切换
#define OS_TASK_SW() OSCtxSw() // 任务级任务切换
5、定义开、关中断的函数,在保护临界区代码时会用到。
#if OS_CRITICAL_METHOD == 3 /* See OS_CPU_A.ASM */
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);#endif
6、声明函数 这些函数都是要自己实现或修改
void OSCtxSw(void); // 任务级切换 触发PendSV异常
void OSIntCtxSw(void); // 中断级切换 触发PendSV异常
void OSStartHighRdy(void); // 运行最高优先级的任务
void OS_CPU_PendSVHandler(void); // 发生PendSV异常时被触发,OSCtxSw()、OSIntCtxSw() 最终实现 即任务切换
void OS_CPU_SysTickHandler(void); /* See OS_CPU_C.C */
void OS_CPU_SysTickInit(void); // 系统时钟节拍初始化 用于任务切换
INT32U OS_CPU_SysTickClkFreq(void); /* See BSP.C OS_CPU_SysTickInit(void);用来获得硬件的时钟频率,这里是直接指定并没有用上。*/
OS_CPU_PendSVHandler(void)、OS_CPU_SysTickHandler(void) 需要在 startup_LPC11xx.s 定义

2、修改OS_CPU_C.C 文件
1、修改OSTaskStkInt()函数
主要修改OSTaskStkInit()函数,其他的HOOK函数根据需要实现(将文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN设为1,设为0表示不使用这些函数)
用户创建任务时,OSTasKCreat()会调用OSTaskStkInt()函数初始化该任务的堆栈,并把返回的堆栈指针保存到该任务的TCB结构中
的最前面的参数OSTCBStkPtr中,当该任务要被恢复时,任务切换函数从其TCB块中取得其任务堆栈指针,依次将堆栈内容弹到处理器
对应的CPSR、r0、r1,…,r12,lr,pc的寄存器中,完成现场的恢复和程序指针PC的返回。
// 创建任务时被调用 初始化任务堆栈 具体实现不是很明白
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 保存任务函数地址 */
*(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/
*(--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)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
return (stk);
}
2、实现 void OS_CPU_SysTickInit (void)、void OS_CPU_SysTickHandler (void)
// 初始化SysTick 用于在固定时产生中断
void OS_CPU_SysTickInit (void)
{
INT32U cnts;
/* cnts = OS_CPU_SysTickClkFreq() / OS_TICKS_PER_SEC; */
cnts = 48000000 / OS_TICKS_PER_SEC; // OS_TICKS_PER_SEC 10ms 产生一次中断
OS_CPU_CM3_NVIC_ST_RELOAD = (cnts - 1);
OS_CPU_CM3_NVIC_ST_CURRENT = 0;
/* Set Priority of SysTick to 2 (0-3, 0 is highest) */
cnts = OS_CPU_CM0_NVIC_SHPR3;
cnts &= 0x00FFFFFF;
cnts |= 0x80000000;
OS_CPU_CM0_NVIC_SHPR3 = cnts;
/* Enable timer. */
OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;
/* Enable timer interrupt. */
OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;
}
// SysTick中断服务函数 用于产生系统节拍
void OS_CPU_SysTickHandler (void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
OSIntNesting++;
OS_EXIT_CRITICAL();
OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */
OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
}
3、修改OS_CPU_A.S 文件
1、定义寄存器地址
NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register.
NVIC_SCB_SHPR3 EQU 0xE000ED20
NVIC_PENDSV_PRI EQU 0x00FF0000
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
2、实现 OS_CPU_SR_Save()、OS_CPU_SR_Restore()、OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()、OS_CPU_PendSVHandler()
// 实现OS_CPU_SR_Save()、OS_CPU_SR_Restore()
// 通过保存中断状态来禁用、启用中断 用在
OS_CPU_SR_Save ; 保存中断状态 MRS R0, PRIMASK ; Set prio int mask to mask all (except faults) CPSID I BX LR
OS_CPU_SR_Restore ; 恢复中断状态 MSR PRIMASK, R0 BX LR
// 实现 OSStartHighRdy()
// 启动优先级最高的任务 OSStartHighRdy
; 设置PendSV 异常优先级为最低
ldr r0, =NVIC_SCB_SHPR3
ldr r1, [r0]
ldr r2, =NVIC_PENDSV_PRI
orrs r1, r1, r2
str r1, [r0]
; 初始化PSP设置为 0 MOVS R0, #0 ; Set the PSP to 0 for initial context switch call MSR PSP, R0 ; PSP为0 告诉上下文切换,这是第一次运行
; 设置 任务运行状态为 1 LDR R0, =OSRunning ; OSRunning = TRUE MOVS R1, #1 STRB R1, [R0]
; 触发PendSV 异常,实现任务切换 LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0]
; PendSV 异常处理函数会关闭中断,所以需要开中断 CPSIE I ; 使能中断 Enable interrupts at processor level
OSStartHang B OSStartHang ; Should never get here
[size=14.44444465637207px]// 实现 OSCtxSw() 任务级切换任务
// 触发PendSV 异常,实现任务切换
OSCtxSw LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR
[size=14.44444465637207px]// 实现 OSIntCtxSw() 中断级切换任务
// 触发PendSV 异常,实现任务切换
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;/* Pendsv 中断函数 */
;/* 用于实现时间片轮转法 中断级任务调度、任务级中断调度 切换上下文*/
; 进入PendSV时:
; xPSR、PC、LR、R12、R0~R3 已经在处理栈中被保存
; 处理模式切换到线程模式
; 栈是主堆栈
OS_CPU_PendSVHandler
CPSID I ; 关闭中断,避免上下文切换时发送中断。
MRS R0, PSP ; PSP is process stack pointer
;CBZ R0, OS_CPU_PendSVHandler_nosave ; Skip register save the first time
cmp r0, #0 ; 如果获取任务的SP 为0 则跳到 OS_CPU_PendSVHandler_nosave
beq OS_CPU_PendSVHandler_nosave
; 保存R3~R11和SP
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
stm r0!, {r4-r7}
mov r1, r8
mov r2, r9
mov r3, r10
mov r4, r11
stm r0!, {r1-r4}
subs r0, r0, #0x20
; 将当前的堆栈指针给当前进程的任务块
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
; 调用 OSTaskSwHook() 函数 用于扩展
mov r0, lr
push {r0}
ldr r0, =OSTaskSwHook
blx r0
pop {r0}
mov lr, 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]
; 得到新任务的SP和线程恢复 R4~R11
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
ldm r0!, {r4-r7}
ldm r0!, {r1-r3}
mov r8, r1
mov r9, r2
mov r10, r3
ldm r0!, {r1}
mov r11, r1
; 载入新的SP和返回
MSR PSP, R0 ; Load PSP with new process SP
mov r0, lr
movs r1, #0x04
orrs r0, r0, r1
mov lr, r0
CPSIE I ; 开启中断
BX LR ; Exception return will restore remaining context
b .
END
移植uCOS II 的难点在于:函数的实现。
总结:
OS_CPU_C.C:
void OS_CPU_SysTickInit (void)、void OS_CPU_SysTickHandler (void)
任务调度,产生系统时钟节拍,每一次节拍就切换一次当前就绪表中优先级最高的任务
OS_CPU_A.S:
OS_CPU_SR_Save()、OS_CPU_SR_Restore():
用于任务保存和恢复自身的中断状态。用于临界区代码。
OSStartHighRdy():
寻找就绪表中优先级最高的任务。
OSCtxSw()、OSIntCtxSw()、OS_CPU_PendSVHandler()
OSCtxSw()、OSIntCtxSw()都是通过触发PendSV异常进行任务切换
OSCtxSw():一般是在任务调用延时函数 如OSTimeDlyHMSM(0, 0, 0, 300); 时调用的。
OSIntCtxSw():一般用在系统时钟节拍中断时,会采用这种方式调度新的任务。
OS_CPU_PendSVHandler() 产生异常PendSV异常就进行一次任务切换
|