uC/OS ii的移植要点:
uC/OS ii的移植相当LINUX来说是非常简单的,uC/OS ii的移植涉及到的代码很少;我们只需要修改与处理器相关的代码即可;
OS_CPU.H:设置与处理器与编译器有关的代码
OS_CPU_C.C:在这其中用C语言编写6个与操作系统相关的函数
OS_CPU.ASM:在这其中用汇编语言编写4个与处理器相关的函数
如上图,需要移植的部分就是Ports路径下的几个代码文件,其他的代码都不要移植(os_cfg.h只是用来配置系统的各个功能,做系统的裁剪而且,ucos_ii.h中提供系统所有的接口函数,os_cpu_a.asm就是OS_CPU.ASM)
1:修改OS_CPU.H
OS_CPU.H中定义了数据类型、处理器的堆栈数据类型的字长、堆栈增长方向、任务切换宏、临界区访问处理。
从方便移植的角度,uC/OS ii使用的数据类型,都是自己定义的那一套。
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned int OS_STK;
typedef unsigned int OS_CPU_SR;
M3使用的是向下生长的满栈,堆栈指针指向最后一个被压入堆栈的32位整数:
#define OS_STK_GROWTH 1
定义开关中断的宏:
#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
定义宏OS_TASK_SW执行任务切换:#define OS_TASK_SW() OSCtxSw()
2:在OS_CPU_C.C中要求用户编写10个简单的C函数【唯一必要修改的函数是OSTaskStkInit(),其余的函数必须声明,但并不一定要包含任何代码】
OSTaskStkInit():任务创建函数OSCreate()与OSCreateExt()通过调用OSTaskStkInit,初始化任务的栈结构,因此,任务的堆栈看起来就像刚发生了中断一样(所以的寄存器按一定的次序都保存在堆栈中,为什么要这样做?因为任务间的切换就是任务堆栈中保存的CPU现场的切换,所以任务被创建时,要初始化它的堆栈,为后面的切换做准备)
OSTaskCreateHook(ptcb):【系统提供给用户的钩子函数】任务创建时OSCreate()与OSCreateExt()调用OS_TCBInit,OS_TCBInit会调用OSTaskCreateHook,同时传递指向刚刚建立的任务的任务控制块的指针,这样OSTaskCreateHook就可以访问任务控制块结构的所有成员,这个函数的功能有限,不是必要的函数,且需要通过OS_APP_HOOKS_EN使能,且该函数调用时中断是开着的。
OSTaskDelHook(ptcb):OSTaskDel()会调用OSTaskDelHook(ptcb),即在将任务从uC/OS II的内部有效链表中删除之前被调用,同时传递一个指向被删除任务的任务控制块的指针,OSTaskDelHook可以用来检验TCB扩展部分是否已建立(一个非空指针),并可进行一些清0操作,OSTaskDelHook调用时,中断是关闭的,函数太长会影响中断响应时间。
OSTaskSwHook():在任务切换时会调用OSTaskSwHook,不论是在OSCtxSw()还是OSIntCtxSw(),都会调用该函数,OSTaskSwHook可访问全局变量OSTCBCur(指向将被切换出去的任务的任务控制块)、OSTCBHiggRdy(指向新任务的任务控制块) ,OSTaskSwHook被调用时,中断是关闭的,函数太长会影响中断响应时间。
OSTaskIdleHook():很多微处理器都允许执行相应的指令,将CPU置于低功耗模式,而当接收到中断信号时,CPU就会自动退出低功耗模式。OS_TaskIdle()函数可以调用OSTaskIdleHook()来实现CPU的这种低功耗模式【很好】。
OSTaskStatHook():该函数每秒都会被统计任务OS_TaskStat()【统计任务每秒执行一次】调用一次,可以通过该函数扩展统计任务的功能,如可跟踪并显示每个任务的执行时间、每个任务所用的CPU份额以及每个任务执行的频率等等,该函数没有任何参数。
OSTimeTickHook():该函数在每个时钟节拍都会被OSTimeTick()调用;systick中断服务程序SysTickHandler调用OSTimeTickHook,在这里面用户可以处理应急的事务。
OSInitHookBegin():进入OSInit()函数后,OSInitHookBegin就会被调用,添加这个函数的原因在于想把与OS有关的初始化代码也放在OSInti()函数中,这个函数使得用户可以将自己特定的代码也放在OSInit()函数中。
OSInitHookEnd():OSInitHookEnd与OSInitHookBegin相似,只是他在OSInit()函数返回之前被调用,添加这个函数的原因与OSInitHookBegin是一样的。
OSTCBInitHook(ptcb):任务创建时OSCreate()与OSCreateExt()调用OS_TCBInit,OS_TCBInit会在调用OSTaskCreateHook前调用 OSTCBInitHook ,用户可以在OSTCBInitHook函数中做一些与初始化控制块OS_TCB有关的处理,在OSTaskCreateHook中做一些与初始化任务有关的处理;OSTCBInitHook收到的ptcb参数指向新添加任务的任务控制块的指针,而这个新添加任务的任务控制块绝大部分已经初始化完成,但是还没有链接到已经建立任务的链表中。
OS_STK * OSTaskStkInit (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT16U opt)
{
OS_STK *stk;
(void)opt;
stk = ptos;
*(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);
}
3:在OS_CPU_A.ASM中用汇编写4个与处理器相关的函数
需要移植的函数有,如下所示,
OSStartHighRdy():在调用OSStart()函数时,使就绪态任务中优先级最高的任务开始运行
OSCtxSw():
OSIntCtxSw():
OSTickISR():
OS_CPU_SR_Save()
OS_CPU_SR_Restore():
OSCtxSw():可以参考学习笔记中的记录,M3中任务切换还需实现软中断的处理函数
OSCtxSw ;悬起PSV异常
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
OSIntCtxSw():在退出中断前调用OSIntCtxSw,在ISR中执行任务的切换功能,OSIntCtxSw与OSCtxSw基本相同,M3的是一模一样的。
OSPendSV 【软中断的处理函数】
MRS R0, PSP
CBZ R0, OSPendSV_nosave
SUBS R0, R0, #0x20
STM R0, {R4-R11}
LDR R1, __OS_TCBCur
LDR R1, [R1]
STR R0, [R1]
OSPendSV_nosave
PUSH {R14}
LDR R0, __OS_TaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, __OS_PrioCur
LDR R1, __OS_PrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, __OS_TCBCur
LDR R1, __OS_TCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2]
LDM R0, {R4-R11}
ADDS R0, R0, #0x20
MSR PSP, R0
ORR LR, LR, #0x04
BX LR
OSTickISR在这里即SysTickHandler函数
void SysTickHandler(void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL();
OSIntNesting++;
OS_EXIT_CRITICAL();
OSTimeTick(); //主要判断延时的任务是否计时到
OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务就绪了,则执行一次任务切换
}
OSStartHighRdy
LDR R0, =NVIC_SYSPRI2
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0] //这几句是设置PendSV优先级
MOVS R0, #0
MSR PSP, R0 //设置PSP的初始值0(在第一次PendSV中断服务程序中不用保存CPU的各寄存器)
LDR R0, __OS_Running
MOVS R1, #1
STRB R1, [R0] // OSRunning = TRUE,表明操作系统已经运行
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0] //触发PendSV中断,暂时挂起
CPSIE I //开总中断【一定要在这里才开总中断,如果在此之前任何一个地方开中断,系统一旦响应中断,很有可能造成系统因没有做出足够的准备而出错(比如如果在初始化systicks时就开启了中断,uC/OS此时还处于未知阶段,就会崩溃),所在在这里开启总中断才是安全的】
OS_CPU_SR_Save()
MRS R0, PRIMASK ;保存全局中断标志
CPSID I ;关中断
BX LR
OS_CPU_SR_Restore()
MSR PRIMASK, R0 ;恢复全局中断标志
BX LR
【到此移植完毕】
uC/OS-ii移植详解
最新推荐文章于 2024-04-24 23:48:58 发布