最新版本uC-OS2-2.93.01已移植到STC最新的 1T 8051, STC8H8K64U上
===适用于STC8系列所有型号
===解决了网络上不同移植版本潜在的bug
===鼓励大家在STC官网 www.STCAIMCU.com 帮忙查错, 200元/每条错误, 感谢, 大家一起前行
许可改成 APACHE 2.0 也就是可以商业使用不用授权。具体可以查看APACHE 2.0内容。
声明:本移植版本参考了陈是知移植版本和STC网站上的原网友版, 另外测试例子使用杨为民老师移植的版本
一,提升速度
1. 重要的变量使用data修饰
2. 结构体指针变量使用xdata修饰
3. 使用DPTR自增方式
注意,使用DPTR自增方式,这部分代码不能硬件仿真,单独调试,可以全速运行。
os_cpu_a.A51文件,有测试信号。
P20 测试OSIntExit 运行时间 (有任务切换4us)(无任务2.83us)
P21 测试OSIntCtxSw 系统级切换任务时间 (8.6us)
P23 测试OSCtxSw 任务级切换任务时间 (8.7us)
P26 测试滴答定时器中断全过程时间 (有任务24.3us) (无任务13us)
P27 测试OSTimeTick 运行时间 (有任11.2us)(无任务9.3us)
以上测试,选择 STC8H8K64U 工作时钟是 40MHz
os_cpu_a.A51文件,有测试信号。
P20 测试OSIntExit 运行时间 (有任务切换4us)(无任务2.83us)
P21 测试OSIntCtxSw 系统级切换任务时间 (8.6us)
P23 测试OSCtxSw 任务级切换任务时间 (8.7us)
P26 测试滴答定时器中断全过程时间 (有任务24.3us) (无任务13us)
P27 测试OSTimeTick 运行时间 (有任11.2us)(无任务9.3us)
以上测试,选择工作时钟是40MHz
二,允许任务堆栈空间任意大小
修改了OSTaskCreate,增加stk_size入口
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT16U stk_size,
INT8U prio) large reentrant
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
对 OSTaskStkInit函数利用 opt 传入任务堆栈大小 实现不等长任务堆栈
三 ,临界区保护方法,支持方法2和方法3
#if OS_CRITICAL_METHOD==2
//执行OS_ENTER_CRITICAL()时,先将中断状态保存到堆栈,然后关中断;
//执行OS_EXIT_CRITICAL()时,再从堆栈中恢复原来的中断开/关状态。这种方法不会改变中断状态,避免前面的问题。
//但是,当用户使用的处理器有堆栈指针相对寻址模式时,可能出现严重错误。
//问题分析:OS_ENTER_CRITICAL()时导致中断切换任务的时候硬件堆栈多入了一个_push_(IE),
//解决方法:OSIntCtxSw()函数调整SP的时候应该多减1,原来SP=SP-4改为SP=SP-5
#include
#define OS_ENTER_CRITICAL()do{_push_(IE);EA = 0;}while(0) /* 利用堆栈保存中断状态,再关中断 */
#define OS_EXIT_CRITICAL()do{ _pop_ (IE);}while(0) /* 将IE从堆栈弹出,恢复IE值 */
#endif
#if OS_CRITICAL_METHOD==3
//获取当前中断状态的值,并将其保存在C函数局部变量之中
//问题分析:可重入函数的局部变量cpu_sr会入仿真栈。导致中断切换任务的时候,仿真堆栈出问题。
//解决方法:OSIntCtxSw()函数执行时需要对仿真堆栈指针++操作。
#define OS_ENTER_CRITICAL() do{ cpu_sr=IE;EA = 0;}while(0)
#define OS_EXIT_CRITICAL() do{ IE = cpu_sr; }while(0)
#endif
四,支持软件定时器
之前移植的版本没有适配软件定时器功能。OS_VERSION >= 281 支持软件定时器。
移植过程中遇到一个困难,就是回调函数,函数指针问题。重点是指针ptmr需要加上xdata修饰。
typedef void (code *OS_TMR_CALLBACK)(void xdata*ptmr, void *parg) large reentrant;
五,解决RET和RETI混用问题
OSIntCtxSw 是在中断中调用的,需要调用RETI返回
OSStartHighRdy 运行第一个任务,应该用RET返回
OSCtxSw 在任务中调用的,应该用RET返回
这三个函数最后部分的代码都一样。之前移植的版本统一用RETI返回,导致混用。
本移植提供2个解决方法,原则是OSIntCtxSw 函数提前退出中断,统一用RET返回
;方法1:直接把 OSIntCtxSw_in函数地址入栈,由RETI中断返回,跳转到OSIntCtxSw_in函数,共6T
MOV DPTR,#OSIntCtxSw_in
PUSH DPL
PUSH DPH
RETI
;方法2:;巧妙的利用函数返回RETI执行了中断完毕,跳转到下一条语句。这样任务可以无忧使用RET作为返回,共9T。
LCALL RE_RETI
LJMP OSIntCtxSw_in
RE_RETI: RETI
显然方法1更优秀
当然还有方法3,3个函数独立,不要共同出口。这里不推荐,浪费代码空间。
六,关于中断嵌套的做法
提供了2个中断例子:
定时器1 中断演示允许中断嵌套
定时器3 中断演示禁止中断嵌套
=== 定时器1 中断服务程序 ============= =
;演示允许中断嵌套
EXTRN CODE (_?Timer1_Handler)
Timer1_ISR:
USING 0 ;工作寄存器0
CLR EA ;调用OSIntEnter之前先关中断
P20
PUSHALL ;现场保护
LCALL _?OSIntEnter ;通知内核进入中断
SETB EA ;允许中断嵌套
LCALL _?Timer1_Handler
LCALL _?OSIntExit ;通知内核退出中断
POPALL ;恢复现场
P20
RETI
=== 定时器3 中断服务程序 ============= =
;演示禁止中断嵌套
EXTRN CODE (_?Timer3_Handler)
Timer3_ISR:
USING 0 ;工作寄存器0
P20;
CLR EA ;禁止中断嵌套
PUSHALL ;现场保护
LCALL _?Timer3_Handler
POPALL ;恢复现场
P20
SETB EA ;中断函数执行完毕,重新允许中断
RETI
对于OSIntEnter 还有更优秀的做法,在中断里面直接OSIntNesting++,我之前另一帖子有讲,需要改动源码。
推荐使用OSIntEnter 函数。
七,堆栈初始化函数,对void *p_arg正确做法
//R3、R2、R1用于传递任务参数p_arg,其中R3代表存储器类型,R2为高字节偏移,R1为低字节位移。
*stk++ = (INT16U)p_arg & 0xFF; // R1
*stk++ = (INT16U)p_arg >> 8; // R2
*stk++ = (INT32U)p_arg >> 16; // R3 存储器类型有code(0xFF),xdata(0x01),data(0x00),idata(0x00),pdata(0xFE)
八,统一用寄存器组0,加快出入栈速度
汇编使用 USING 0
;定义压栈出栈宏
PUSHALL MACRO
PUSH PSW
PUSH ACC
PUSH B
PUSH DPL
PUSH DPH
PUSH AR0
PUSH AR1
PUSH AR2
PUSH AR3
PUSH AR4
PUSH AR5
PUSH AR6
PUSH AR7
ENDM
POPALL MACRO
POP AR7
POP AR6
POP AR5
POP AR4
POP AR3
POP AR2
POP AR1
POP AR0
POP DPH
POP DPL
POP B
POP ACC
POP PSW
ENDM
九,修复一个隐藏很深的bug,任务堆栈总空间需要分配很大系统才能运行。
折腾很长时间,最后跟踪代码发现OSTCBFreeList指针指向xdata 地址0,空指针作怪。
任务堆栈总空间比较少的时候OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS];数组从地址0开始存放。
初始化的时候OSTCBFreeList = &OSTCBTbl[0]; 导致OSTCBFreeList指针指向xdata 地址0,分配到一个空指针。
解决方法。xdata地址0声明一个null变量占用了该地址。
修改后测试,任务堆栈空间分配32字节,也能顺利运行。
源码:
最新版本uC/OS-II源码在本贴的附件中,也可以如下链接下载:https://github.com/weston-embedded/uC-OS2
uCOSII-STC8-V1.00-20230821.zip