找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6196|回复: 5
打印 上一主题 下一主题
收起左侧

STC单片机uCOS-II移植教程

  [复制链接]
跳转到指定楼层
楼主
前言
       其实关于uCOS-II的51单片机移植教程和例子网上已经有很多了,但是大部分都是基于Proteus仿真外扩内存的,下载之后也不能直接在硬件上使用,也没有具体的移植教程。这对于一个想学习操作系统而又无从下手小白的来说简直就是噩梦。
       由于51内核的特殊性和keil编译器原因(51的系统堆栈指针和Keil编译后仿真堆栈指针增长的方向是相反的)带来移植的困难。网上的例程处理堆栈的方式有两种(至于不太懂的同学可以移步看看这篇帖子)https://blog.csdn.net/s111sw/article/details/6012720,一种是用小模式Small,另外一种是大模式Large。Small模式是把系统堆栈数据和仿真堆栈数据一起复制到用户堆栈XDATA区,这种方式编译代码小但是任务切换速度慢。而Large模式在编译的时候Keil默认是把仿真堆栈数据设置在XDATA区,所以在任务切换的时候只需要把系统堆栈和仿真堆栈的当前地址保存到用户堆栈就行,这样的方式编译代码大但任务切换速度快(现在都是用这种方式,STC12C5A60S2在22.1184MHz晶振下任务切换时间37us)。可是,前面已经说了51的系统堆栈指针是向上增长的,而Keil编译的仿真指针是向下增长的,这就导致了一个 问题---uCOS操作系统的堆栈检测函数OSTaskStkChk没办法使用。上面链接帖子里面用的方法是修改uCOS的内核函数实现堆栈检测功能的。正是因为我不想修改内核函数的原因所以才有了我现在移植的uCOS的版本。
                                                                                                                                                                                                         
图1

虽然51的系统堆栈指针只能向上增长,但是在代码里面我们可以人为的把里面的数据按照自己的意愿存放到用户堆栈里。下面是我移植的堆栈结构,把系统堆栈增长方向和仿真堆栈统一起来就可以实现堆栈连续存放数据了。
                                                                                                                                                                                                         
                                                                                                                   图2
开始移植
准备工具
1、    电脑一台(废话!)
2、    Keil4
3、    下载配套的代码一份,我移植的是比较经典的版本uCOS-II 2.52

51_uCOS-II V2.52.zip (376.83 KB, 下载次数: 96)
STC uCOS-II移植教程.pdf (252.53 KB, 下载次数: 78)
下载的代码已经是移植好STC12C5A60S2的例程
功能就是两个任务用堆栈检测函数OSTaskStkChk检测当前自己堆栈使用情况,然后串口发出
波特率115200

下面开始讲解怎么把uCOS移植到不同型号51单片机
打开工程\51_uCOS-IIV2.52\Project\ uCOS.uvproj

第1步:把keil配置为大模式,就是让Keil把默认变量定义到XDATA,下图3                                                                                           图3
第2步:打开STARTUP .A51启动文件修改一些启动参数来对应单片机资源
    根据单片机内部资源作调整,因为STC12C5A60S2有1280字节的SRAM,包括内部0-0xFF 256字节和外部0-0x3FF 1024字节
图4把IDATALEN改成100H(内部256),XDATALEN改成0x03FF(外部内存根据不同型号芯片的大小配置,最大是0xFFFF),这样单片机初始化的时候就会把相应的RAM清0。

                      图4  
接着把图5的XBPSTACK设置为1使能图6的仿真堆栈初始化代码,XBPSTACKTOP设置成 0x03FF,就是单片机外部RAM的末尾地址。
                                    
       图5

                                                                                                            
                       图6
这样就配置好启动文件了。
第3 步:配置uCOS文件
     uCOS系统需要一个时钟节拍,节拍频率10Hz-1000Hz。我用的是T0定时器每10ms中断一次。打开OS_CPU_C.C文件找到void InitHardware(void)  T0定时器初始化函数,配置成想要的中断时间,然后修改中断函数void OSTickISR(void) interrupt 1里面TL0和TL1的初值,如果是有 自动重装功能的定时器就可以注释掉这两句,最后配置OS_CFG.H文件里最下面的宏定义#define  OS_TICKS_PER_SEC,这个就是1秒钟的节拍数,例如10ms中断一次就是100,20ms中断一次就是50。
#define  OS_TICKS_PER_SEC100  /* Set the number of ticks in one second */
----------------------------------移植完毕---------------------------------------

堆栈结构解释
                               图7
        任务创建之后堆栈指针一直指向用户栈顶,图7左是堆栈初始化之后里面的数据结构,用户堆栈的最高3个字节一直固定保存“系统堆栈长度”和“?C_XBP(仿真堆栈指针)”因为任务初始化的时候仿真堆栈还没有使用,所以?C_XBP指向的堆栈下一个地址就是空闲堆栈,紧跟着就是系统堆栈数据。
        在启动任务调度置后仿真堆栈被使用之后变成图7右的结构,任务切换时从?C_XBP指向的下一个地址开始保存系统堆栈数据,保存的数据长度由“系统堆栈长度” 决定 ,这样就实现了堆栈向下连续增长而不需要修改uCOS的堆栈检测函数。

//堆栈初始化函数
OS_STK *OSTaskStkInit(void (*task)(void *pd) reentrant, void *p_arg, OS_STK *ptos, INT16U opt)reentrant
{
    OS_STK *stk;

    p_arg =p_arg;
    opt    = opt;                      //opt没被用到,保留此语句防止告警产生   
    stk    =ptos;                     //用户堆栈最低有效地址
    *stk-- =15;                       //系统堆栈长度
*stk-- =(INT16U)(ptos-3) >> 8; //?C_XBP 仿真堆栈指针高8位
   *stk-- = (INT16U)(ptos-3) &0xFF; //?C_XBP 仿真堆栈指针低8位 最高3个字节一直被占
//用所以减3
    *stk-- =0x07;                          //R7
    *stk-- =0x06;                          //R6
    *stk-- =0x05;                          //R5
    *stk-- =0x04;                          //R4
    *stk-- =0x01;                          //R3
    *stk-- =0x02;                     //R2
    *stk-- =0x01;                    //R1
    *stk-- =0x00;                          //R0
    *stk-- =0x00;                          //PSW
    *stk-- =0x00;                          //DPL
    *stk-- =0x00;                          //DPH
    *stk-- =0x0B;                          //B  
*stk-- =0x0A;                          //ACC
    *stk-- =(INT16U)task >> 8;             //任务地址高8位  
    *stk-- =(INT16U)task & 0xFF;           //任务地址低8位

stk    = ptos;//堆栈指针一直指向栈顶

    return stk;
}


;*****************************************************************************************
;*                                              uC/OS-II
;*                                               实时内核
;*
;*                        (c) Copyright1992-1998, Jean J. Labrosse, Plantation, FL
;*                                               版权所有
;*
;*                                           MCU-51 专用代码
;*                                           KEIL C51大模式编译
;*
;* 文件名 : OS_CPU_A.ASM
;* 作者  : Jean J. Labrosse
;*****************************************************************************************


;声明:本代码仅供学习研究uCOS-II使用,如用作其他用途出现问题本人概不负责。

;伪指令详细用法请查A51.PDF文件
;程序结构详见《uC/OS-II》193-198页

;不用此语句!!! $CASE    ;标号和变量名区分大小写

$NOMOD51
EA  BIT     0A8H.7
SP  DATA    081H
B   DATA    0F0H
ACC DATA    0E0H
DPH DATA    083H
DPL DATA    082H
PSW DATA    0D0H
TR0 BIT     088H.4
TH0 DATA    08CH
TL0 DATA    08AH

        NAMEOS_CPU_A    ;模块名

;定义重定位段
?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

;声明引用全局变量和外部子程序
        EXTRNDATA  (?C_XBP)     ;仿真堆栈指针用于重入局部变量保存,为V2.51能被C使用定义在本模块中

        EXTRNIDATA (OSTCBCur)
        EXTRNIDATA (OSTCBHighRdy)
        EXTRNIDATA (OSRunning)
        EXTRNIDATA (OSPrioCur)
        EXTRN IDATA (OSPrioHighRdy)
     EXTRNDATA  (EA_Nesting)

;        EXTRNCODE  (OSTaskSwHook)
        EXTRNCODE  (OSIntEnter)
        EXTRNCODE  (OSIntExit)
        EXTRNCODE  (OSTimeTick)

;对外声明4个不可重入函数
        PUBLICOSStartHighRdy
        PUBLICOSCtxSw
        PUBLICOSIntCtxSw

;        PUBLICOSTickISR
;        PUBLICSerialISR        


;分配堆栈空间,?STACK和STARTUP.A51中同名,编译器会将两个?STACK段合并,堆栈大小在STARTUP.A51中定义
?STACK    SEGMENT   IDATA
          RSEG    ?STACK
;-------------------------------------------------------------------------------
PUSHALL    MACRO ;定义压栈出栈宏
        PUSH ACC
        PUSH B
        PUSH DPH
        PUSH DPL
        PUSH PSW
     MOV  PSW,#0x00
        PUSH0x00   ;R0-R7入栈
     PUSH 0x01
     PUSH 0x02
     PUSH 0x03
     PUSH 0x04
     PUSH 0x05
     PUSH 0x06
     PUSH 0x07
        ENDM

POPALL    MACRO
        POP  0x07   ;R0-R7出栈
     POP  0x06
     POP  0x05
     POP  0x04
     POP  0x03
     POP  0x02
     POP  0x01
     POP  0x00
        POP  PSW
        POP  DPL
        POP  DPH
        POP  B
        POP  ACC
        ENDM
;-------------------------------------------------------------------------
;    启动任务------切换堆栈指针,恢复堆栈数据
;-------------------------------------------------------------------------
        RSEG?PR?OSStartHighRdy?OS_CPU_A
OSStartHighRdy:
        USING0    ;
;       LCALL OSTaskSwHook
CtxSw:
       ;OSTCBCur ===> DPTR  获得当前TCB指针,详见C51.PDF第178页
        MOV  R0,#LOW (OSTCBCur) ;获得OSTCBCur指针低地址,
        INC  R0                  ;指针占3字节。+0类型+1高8位数据+2低8位数据
        MOV  DPH,@R0        ;全局变量OSTCBCur在IDATA中
        INC  R0
        MOV  DPL,@R0

       ;OSTCBCur->OSTCBStkPtr ===> DPTR 获得用户堆栈指针
        INC  DPTR       ;指针占3字节。+0类型+1高8位数据+2低8位数据
        MOVXA,@DPTR     ;取用户栈顶指针OSTCBStkPtr
     MOV  R0,A
        INC  DPTR
        MOVXA,@DPTR

     ADD  A,#0FEH    ;DPTR-2指向保存?C_XBP低8位的地址
     MOV  DPL,A
     MOV  A,R0
     ADDC A,#0FFH
     MOV  DPH,A

     ;恢复仿真堆栈指针
     MOVX A,@DPTR
     MOV  ?C_XBP+1,A ;?C_XBP仿真堆栈指针低8位
     INC  DPTR
     MOVX A,@DPTR   
     MOV  ?C_XBP,A   ;?C_XBP仿真堆栈指针高8位

     INC  DPTR
        MOVXA,@DPTR    ;恢复系统堆栈长度
        MOV  R5,A      ;
     ;因为DPTR没有自减1指令,所以只能计算出系统堆栈的末尾地址然后用INC DPTR复制数据提高效率
     CLR  C          ;系统堆栈末尾地址 = 当前仿真堆栈地址-系统堆栈长度
     MOV  A,?C_XBP+1
     SUBB A,R5
     MOV  DPL,A
     MOV  A,?C_XBP
     SUBB A,#0
     MOV  DPH,A

        MOV  R0,#?STACK-1 ;获取堆栈起址

RestoreStk:          ;恢复现场堆栈内容
     INC DPTR        
        INC  R0
        MOVXA,@DPTR
        MOV  @R0,A
        DJNZR5,RestoreStk

        MOV  SP,R0 ;恢复堆栈指针SP

       ;OSRunning=TRUE
        MOV  R0,#LOW (OSRunning)
        MOV  @R0,#01

        POPALL

     MOVEA_Nesting,#0 ;切换任务清零EA嵌套
        SETBEA    ;开中断
        RETI
;-------------------------------------------------------------------------
;    任务级切换函数
;-------------------------------------------------------------------------
        RSEG?PR?OSCtxSw?OS_CPU_A
OSCtxSw:
        PUSHALL;任务堆栈进栈
IntCtxSw:
       ;OSTCBCur ===> DPTR  获得当前TCB指针,详见C51.PDF第178页
        MOV  R0,#LOW (OSTCBCur) ;获得OSTCBCur指针低地址,指针占3字节。+0类型+1高8位数据+2低8位数据
        INC  R0
        MOV  DPH,@R0  ;全局变量OSTCBCur在IDATA中
        INC  R0
        MOV  DPL,@R0

       ;OSTCBCur->OSTCBStkPtr ===> DPTR 获得用户堆栈指针
        INC  DPTR       ;指针占3字节。+0类型+1高8位数据+2低8位数据
        MOVXA,@DPTR     ;取用户栈顶指针OSTCBStkPtr
     MOV  R0,A
        INC  DPTR
        MOVXA,@DPTR

     ADD  A,#0FEH    ;DPTR-2指向保存?C_XBP低8位的地址
     MOV  DPL,A
     MOV  A,R0
     ADDC A,#0FFH
     MOV  DPH,A

     ;保存仿真堆栈指针?C_XBP
     MOV  A,?C_XBP+1 ;?C_XBP 仿真堆栈指针低8位
     MOVX @DPTR,A
     INC  DPTR
     MOV  A,?C_XBP   ;?C_XBP 仿真堆栈指针高8位
     MOVX @DPTR,A

        MOV  A,SP ;计算系统堆栈长度
        CLR  C
        SUBBA,#?STACK-1
        MOV  R5,A
     ;保存系统堆栈长度
     INC  DPTR
     MOVX @DPTR,A
     ;因为DPTR没有自减1指令,所以只能计算出系统堆栈的末尾地址然后用INC DPTR复制数据提高效率
     CLR  C          ;系统堆栈末尾地址 = 当前仿真堆栈地址-系统堆栈长度
     MOV  A,?C_XBP+1
     SUBB A,R5
     MOV  DPL,A
     MOV  A,?C_XBP
     SUBB A,#0
     MOV  DPH,A

        MOV  R0,#?STACK-1 ;获取堆栈起址
SaveStk:
     INC  DPTR   ;保存系统堆栈到任务堆栈
     INC  R0
     MOV  A,@R0
     MOVX @DPTR,A
     DJNZR5,SaveStk

;    LCALL OSTaskSwHook  ;调用用户程序

     ;获取新任务TCB指针
     MOV  R0,#OSTCBCur   ;OSTCBCur= OSTCBHighRdy
     MOV  R1,#OSTCBHighRdy
     MOV  A,@R1
     MOV  @R0,A  ;指针类型
     INC  R0
     INC  R1
     MOV  A,@R1
     MOV  @R0,A  ;指针高8位
     INC  R0
     INC  R1
     MOV  A,@R1  
     MOV  @R0,A  ;指针低8位

     ;获取新任务优先级
     MOV R0,#OSPrioCur  ;OSPrioCur =OSPrioHighRdy
     MOV  R1,#OSPrioHighRdy
     MOV  A,@R1
     MOV  @R0,A

     LJMP CtxSw
;-------------------------------------------------------------------------
;    中断级任务切换
;-------------------------------------------------------------------------
        RSEG?PR?OSIntCtxSw?OS_CPU_A
OSIntCtxSw:
        ;调整SP指针去掉在调用OSIntExit(),OSIntCtxSw()过程中压入堆栈的多余内容
        ;SP=SP-4
        MOV  A,SP
        CLR  C
        SUBBA,#4
        MOV  SP,A

        LJMPIntCtxSw

        END

评分

参与人数 3黑币 +60 收起 理由
zmc419 + 5 很给力!
admin + 50 共享资料的黑币奖励!
muzhi + 5 很给力!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏18 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:108624 发表于 2021-2-8 15:47 | 只看该作者
写的不错! 值得一试!
回复

使用道具 举报

板凳
ID:677393 发表于 2021-2-9 10:20 | 只看该作者
哥太牛了,51能上系统
回复

使用道具 举报

地板
ID:108624 发表于 2021-2-28 16:03 | 只看该作者
最近又测试了一下。非常好。发挥了STC的大XDATA. 值得一用!!!!!!!!
回复

使用道具 举报

5#
ID:576482 发表于 2022-11-8 10:14 | 只看该作者
哪天吃多了没事干的时候研究研究!!!
回复

使用道具 举报

6#
ID:983641 发表于 2022-11-9 21:25 | 只看该作者
太强大了。。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表