uCosii移植51_STC12LE5A60S2
前言
这几天花了三天时间,把uCosii移植到了增强版51_STC12LES5A60S2上。很感谢在网上提供相关代码和移植方法的人,让我学到了很多知识,也少走了很多弯路。在这里我把我在移植中遇到的问题和方法记录下来,供大家学习和参考。
uCosii是实时操作系统,移植性很好可以移植在多种cpu上。移植51作为入门学习很简单。但是普通51的存储空间有限,不能发挥实时操作系统优势,没有使用价值。所以选择增强版51_STC12LES5A60S2是个不错的选择。STC12LES5A60S2有2K的RAM和64K的ROM。而我移植的最小uCosii可以裁剪到5K远远小于64K的存储空间。可见系统可以流畅的运行,并且具有实时操作系统的实用价值。
在看这篇文档前建议,先看看卢有亮的《嵌入式实时操作系统——uC/OS原理与实践》,学习一下uCosii。大概花一个星期就可以学会这个操作系统。只有了解了uCosii的工作原理,才能发挥它的最大潜力。(百度云盘:http://pan.baidu.com/s/1jGMj7qI)
正文
-
新建一个文件夹,目录解构如下图,其中的STC12C5A.h是51单片机的头文件可以直接在提供的文件夹下找到。
-
用keil在该文件夹下新建一个工程,如下图。 这里的单片机可以随便选一个。但是生成的STARTUP.A51可能不能用,可以用提供的STARTUP.A51代替。
-
修改Project->Option…下的相关配置,如下图
-
添加相关文件到工程中,如下图。需要注意的不要掉了STARTUP.A51和os_cpu_a.asm
-
移植ucos-II 涉及三个文件OS_CPU.H、OS_CPU_A.ASM、OS_CPU_C.C,分别作如下修改:
OS_CPU.H:
定义临界段宏
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() EA=0 //关中断
#define OS_EXIT_CRITICAL() EA=1 //开中断
定义任务切换宏:
定义数据类型:
OS_CPU_C.C:
初始化任务堆栈:
OS_STK*OSTaskStkInit (void (*task)(void *pd) reentrant, void *ppdata, OS_STK *ptos,INT16U opt) reentrant
{
OS_STK *stk;
ppdata = ppdata;
opt = opt; //opt没被用到,保留此语句防止告警产生
stk = ptos; //用户堆栈最低有效地址
*stk++ = 15; //用户堆栈长度
*stk++ = (INT16U)task & 0xFF; //任务地址低8位
*stk++ = (INT16U)task >> 8; //任务地址高8位
*stk++ = 0x0A; //ACC
*stk++ = 0x0B; //B
*stk++ = 0x00; //DPH
*stk++ = 0x00; //DPL
*stk++ = 0x00; //PSW
*stk++ = 0x00; //R0
//R3、R2、R1用于传递任务参数ppdata,其中R3代表存储器类型,R2为高字节偏移,R1为低字节位移。
//通过分析KEIL汇编,了解到任务的void *ppdata参数恰好是用R3、R2、R1传递,不是通过虚拟堆栈。
*stk++ = (INT16U)ppdata & 0xFF; //R1
*stk++ = (INT16U)ppdata >> 8; //R2
*stk++ = 0x01; //R3 因为我用的全是XDATA,所以存储器类型固定为1,见C51.PDF第178页说明。
*stk++ = 0x04; //R4
*stk++ = 0x05; //R5
*stk++ = 0x06; //R6
*stk++ = 0x07; //R7
//不用保存SP,任务切换时根据用户堆栈长度计算得出。
*stk++ = (INT16U) (ptos+MaxStkSize)>> 8; //?C_XBP 仿真堆栈指针高8位
*stk++ = (INT16U) (ptos+MaxStkSize) &0xFF; //?C_XBP 仿真堆栈指针低8位
return ((void *)ptos);
}
系统时钟初始化
voidInitHardware(void) reentrant //该函数在main函数中在初始化后被调用
{
TMOD &= 0xF0;
TMOD |= 0x01; //定时器0:模式1(16位定时器),仅受TR0控制;定时器1:波特率发生器
TH0 = 0xB8; //定义Tick=50次/秒(即0.02秒/次)
TL0 = 0x00; //OS_CPU_C.C中定时器中断响应也要设置,OS_CFG.H中OS_TICKS_PER_SEC也有关系
TR0 = 1;
OS_CPU_A.ASM
OSStartHighRdy:在OSStart中被调用执行当前优先级最高的就绪任务
OSCtxSw:即OS_TASK_SW()宏,在OS_Sched()函数结束时被调用,完成任务切换;
OSIntCtxSw:OSIntExit中被调用,完成中断退出后的任务切换
参考地址:http://www.cnblogs.com/Fredric-2013/archive/2013/02/19/2917805.html
-
把所以任务函数设置成reentrant,除了os_cpu_c.c文件下的void OSTickISRuser(void)和void UserTickTimer(void)。这两个是时钟中断服务函数,不能设置成重入。
-
修改内核文件中头部的包含为
#ifndef __INCLUDES__
#include "source\includes.h"
#endif
并且删除ucos_ii下的#define OS_GLOBALS
防止产生链接时的错误*** ERROR L104: MULTIPLE PUBLIC DEFINITIONS
注意os_core.c文件中修改为
#ifndef __INCLUDES__
#define OS_GLOBALS
#include "source\includes.h"
#endif
-
到main文件中编译整个工程。这时会出现错误
*** ERROR L102: EXTERNAL ATTRIBUTE MISMATCH
这是因为os_cpu_a.asm文件这的
EXTRN IDATA (OSTCBCur)
EXTRN IDATA (OSTCBHighRdy)
EXTRN IDATA (OSRunning)
EXTRN IDATA (OSPrioCur)
EXTRN IDATA (OSPrioHighRdy)
这5个变量在ucos_ii文件中没有定义成idata类型,这5个变量涉及到系统的任务调度所以要定义在内部的ram中。如下图所示
-
修改os_cfg.h文件中的开关,关闭不用的功能。需要注意的是,可以打开钩子函数的开关,这样可以减少一些编译警告。
-
回到main函数中编译整个工程,这是会编译通过,但会产生警告
*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
这是因为没有定义了函数没有掉用调用。通过添加条件编译语句,就可以消除这些警告。
这样可以消除所以的警告和错误,下载到STC12LES5A60S2看到P1.0和P1.3轮流闪烁的现象,那么恭喜你,可以享受跑实时操作系统的单片机。把那些繁琐的任务调度都交给操作系统去做。发挥单片机最大的潜能。
参考资料:
-
卢有亮的《嵌入式实时操作系统——uC/OS原理与实践》
-
http://www.cnblogs.com/Fredric-2013/archive/2013/02/19/2917805.html