uC/OS-II在C8051F020单片机上的移植


http://blog.sina.com.cn/s/blog_51bd458a0100crwo.html 原文地址


1 uC/OS-II的版本和C8051F020单片机的集成开发环境

µC/OS-II内核使用了V2.52版本。虽然Silicon Lab公司免费提供C8051F系列单片机的集成开发环境,由于使用习惯,笔者还是选择了µVision2 V2.38A版本,C编译器版本是C51.exe V7.06,汇编器的版本是A51.exe V7.07。适配器使用Silicon Lab公司的EC2,需要说明的是,要在Keil uVision2 IDE中调试C8051F系列单片机,必须安装动态链接库。
2 uC/OS-II在C8051F020单片机上的移植
移植工作就是更改OS_CPU.H、OS_CPU_C.C、OS_CPU_A.ASM这 个文件。在说明这几个文件之前,先说明两点注意事项。(1)可重入函数。单片机内部堆栈空间有限,C51提供一种压缩栈的方式,当递归调用这个函数时, 会导致变量被覆盖,所以在实时应用中,要用关键字reentrant将函数声明成可重入函数,把每次函数调用时的局部变量单独 保存起来。但函数中不可以使用BOOL变量,因为在LARGE编译模式下,KEIL默认将所有变量定位到外部RAM的最高处,而MCS-51系列的MCU中只有内部20H-2FH的地址可以位寻址。(2)C51的关键字和uC/OS -II定义变量的矛盾。“pdata”、“data”在uC/OS-II中用做一些函数的形参,但它同时又是C51的关键字,在编译时导致语法错误,通过把“pdata”改成“ppdata”,“data” 改成“ddata”解决了此问题。
2.1 OS_CPU.H文件
这个文件主要是与处理器相关的宏定义和数据类型声明。如前面提到的,不能使用bit型变量,把BOOLEAN型定义成unsigned char型。另外8位MCU数据宽 度和堆栈宽度都是8位,分别将OS_STK和OS_CPU_SR定义成unsigned char型。uC/OS-II提供了3种处理临界代码的方法,这里使用第一种,即通过对寄存器EA开关中断。MCS-51系列MCU的堆栈是从下向上递增的,定义OS_STK_GROWTH = 0。
2.2 OS_CPU_C.C文件
这个文件主要是完成OSTaskStk Init()。在uC/OS-II中,任务是一个无限循环,任务之间也不会互相调用,但是uC/OS-II总是执行优先级最高的任务,假定当前有一个更高优先级的任务进入就绪状态,为了保证原来低优先级任务的完整性,uC/OS-II为每个任务建立了任务堆栈,就相当于函数调用时保存返回地址和参数一样,用来保存当前任务的状态,保证任务切换能和函数调用一样正确。只不过函数调用时函数堆栈的操作过程是编译器自动完成的,而任务切换时需要模拟一个和编译器类似的任务堆栈的操作过程。实际上,uC/OS-II的移植工作主要就是解决这个问题,OSTaskStkInit()完成任务栈初始化,后面介绍的OS_CPU_A.ASM文件完成3种不同条件下的任务切换操作。
图1列出了应用任务堆栈和MCS-51单片机的系统堆栈结构,左边是MCS-51单片机的系统堆栈,右边是应用任务的堆栈。MCS-51单片机系统堆栈是满递增栈,定义MCS51Sp为系统堆栈指针,定义MCS51SpTop为系统堆栈在内部RAM块的起始地址。初始化时把堆栈指针指向0x60H地址,即MCS51Sp = MCS51SpTop = 0x60H。需要保存的寄存器共有13个,应用任务入栈操作具体过程如下:(1)、把所有的寄存器压入系统堆栈;(2)、计算系统堆栈的深度(SP- MCS51SpTop),并且保存到任务堆栈的栈顶;(3)把系统堆栈所有的内容保存到任务堆栈,这里既包括在(1)中入栈的13个寄存器也包括程序运行时需要保存的参数(斜线填充部分)。应用任务出栈操作具体过程如下:(1)、从任务堆栈的栈顶得到要进行出栈的深度;把任务堆栈所有内容恢复到系统堆栈;(2)、把13个寄存器出栈(还剩下程序指针PC);(3)、执行RETI命令,把系统堆栈中的地址恢复到程序指针PC。
错误!链接无效。                图1任务堆栈和系统堆栈
OSTaskStkInit()的函数原型为 OS_STK *OSTaskStkInit(void (*task)(void *pd),void *pdata, OS_STK *ptos, INT16U opt);其中task指向应用任务的起始地址,pdata是应用任务传递的参数指针,ptos是任务堆栈的栈顶指针,需要说明的是,MCS-51单片机的堆栈是递增的,所以这个指针应该指向任务堆栈的低地址,在OSTaskCreat()中调用这个函数时opt没有意义,将opt设置为0。OSTaskStkInit()中,主要保存新建任务栈的起始地址(2个字节),也就是在图1右半部分的虚线部分,由于任务还没有被调度,MCU的状态字并不具有实际意义。
2.3 OS_CPU_A.ASM文件
这个文件包括4个汇编语言函数。OSStartHighRdy();OSCtxSw();OSIntCtxSw();OSTickISR()。Keil编译器不支持插入行汇编代码,需要写这4个汇编函数,如果编译器支持插入行汇编代码,就可以将所有与处理器相关的代码放到OS_CPU_C.C文件中(ARM和DSP的移植就是这样实现的)。
2.3.1 OSStartHighRdy()
OSStart()函数调用OSStartHighRdy(),使就绪态任务中优先级最高的任务开始执行,OSStartHighRdy()移植代码如下:
   RSEG ?PR?OSStartHighRdy?OS_CPU_A    ; RSEG 选择已经在SEGMENT定义的段              
OSStartHighRdy:                          
     USING      0                            ; 选择工作寄存器组
   LCALL _?OSTaskSwHook
     MOV R0, #LOW(OSRunning)  
     MOV @R0,#01                         ; OSRunning = TRUE
OSStartHighRdyRpt:        
MOV    R0,#LOW (OSTCBCur)             ; 取TCB指针OSTCBCur。在单片机C语言中,
INC    R0                            ; 指针占用3个字节,低字节是指针存储类
   MOV    DPH,@R0                         ; 型编码,第二、第三字节分别存放指针高     
   INC    R0                            ; 位、低位地址偏移量。  
   MOV    DPL,@R0
   INC    DPTR                            ; 装载OSTCBStkPtr地址到DPTR                                   
   MOVX A, @DPTR                 
   MOV    R1,A
   INC    DPTR
   MOVX A, @DPTR
   MOV    R0,A
   MOV    DPH, R1
   MOV    DPL, R0
   MOVX A, @DPTR                         ; 出栈第一个字节,即系统堆栈深度
   MOV    R4,A
   MOV R0,#OSStkStart
COPY_STK_TO_SYS:                         ; 把任务堆栈的数据恢复到系统堆栈
   INC    DPTR
   INC    R0
   MOVX A, @DPTR
   MOV    @R0, A
   DJNZ R4, COPY_STK_TO_SYS
   MOV    SP, R0                         ; 调整系统堆栈指针
   POPAll
RETI
2.3.2 OSCtxSw()
这个函数完成任务切换。包括3个步骤:(1)把系统堆栈保存到当前任务堆栈;(2) 找出优先级最高的任务;(3)把高优先级任务的堆栈恢复到系统堆栈。在写这部分代码时,只需写前两部分,第3部分的内容和OSStartHighRdy()几乎完全一样,跳转到OSStartHighRdyRpt开始的部分就可以了。
2.3.3 OSIntCtxSw()
OSIntExit()通过调用OSIntCtxSw(),在ISR中执行任务切换功能。因为OSIntCtxSw()是在ISR中被调用的,这时处理器寄存器已经被保存到任务堆栈。所以只要一条LJMP    指令跳转到OSCtxSw()函数的步骤(2)的入口地址就可以了。
2.3.4 OSTickISR()
   时钟节拍中断服务子程序OSTickISR()完成的操作和OSCtxSw()类似,只不过OSTickISR()是由硬件定时器溢出中断触发的,定时器使用了C8051F020单片机的T0,时钟节拍设置为每秒20次,外部的22.1184MHZ晶体经过2分频 作为T0基准,只要在退出这个函数之前加上如下2条指令即可。
   MOV    TH0, #0x2C
   MOV    TL0, #0x12
3 测试移植代码
创建的2个测试任务及源码如下:
OSTaskCreate(TestTransplantA, (void *)0, & TestTransplantAStk[0],2);
OSTaskCreate(TestTransplantB, (void *)0, & TestTransplantBStk[0],3);
void TestTransplantA(void *ddata) reentrant
{    ddata=ddata;
for(;;)
{    Uart0Send(0xAA);
      OSTimeDly(60*OS_TICKS_PER_SEC);  
   }
}
void TestTransplantB(void *ddata) reentrant
{    ddata=ddata;
for(;;)
{    Uart0Send(0xBB);
      OSTimeDly(30*OS_TICKS_PER_SEC);  
}
}
多任务调度开始后,通过超级终端接收的UART0的数据为:AA  BB BB AA BB BB AA BB BB AA BB BB AA BB BB AA BB BB AA ……。
   高优先级的任务TestTransplantA()能首先被调度运行,说明OSTaskStkInit()和OSStartHighRdy()函数是正确的。任务TestTransplantA()和任务TestTransplantB()由时钟节拍驱动而周期地被调度,说明OSCtxSw()、OSIntCtxSw()、OSTickISR()也是正确的。通过以上两点可以认为移植结果是正确的
4 结束语
在µC/OS-II平台下开发程序,首先要掌握内核。通过上述移植过程,能够对任务堆栈、任务调度有深刻理解。即使有已经移植成功的版本,还是建议自己动手移植,这样有助于更深刻的理解任务的调度过程,而且可以积累一些嵌入式软件调试的经验。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值