📄 在51系列单片机上移植ucos.htm
字号:
;指针占3字节。+0类型+1高8位数据+2低8位数据<BR>MOVX A,@DPTR ;.OSTCBStkPtr是void指针<BR>MOV
R0,A<BR>INC DPTR<BR>MOVX A,@DPTR<BR>MOV R1,A<BR>MOV DPH,R0<BR>MOV
DPL,R1<BR><BR>;*UserStkPtr ===> R5 用户堆栈起始地址内容(即用户堆栈长度放在此处) 详见文档说明
指针用法详见C51.PDF第178页 <BR>MOVX A,@DPTR ;用户堆栈中是unsigned char类型数据<BR>MOV R5,A
;R5=用户堆栈长度<BR><BR>;恢复现场堆栈内容<BR>MOV
R0,#OSStkStart<BR><BR>restore_stack:<BR><BR>INC DPTR<BR>INC R0<BR>MOVX
A,@DPTR<BR>MOV @R0,A<BR>DJNZ R5,restore_stack<BR><BR>;恢复堆栈指针SP<BR>MOV
SP,R0<BR><BR>;OSRunning=TRUE<BR>MOV R0,#LOW (OSRunning)<BR>MOV
@R0,#01<BR><BR>POPALL<BR>SETB EA
;开中断<BR>RETI<BR>;-------------------------------------------------------------------------<BR>RSEG
?PR?OSCtxSw?OS_CPU_A<BR>OSCtxSw:
<BR>PUSHALL<BR><BR>OSIntCtxSw_in:<BR><BR>;获得堆栈长度和起址<BR>MOV A,SP<BR>CLR
C<BR>SUBB A,#OSStkStart<BR>MOV R5,A ;获得堆栈长度 <BR><BR>;OSTCBCur ===> DPTR
获得当前TCB指针,详见C51.PDF第178页<BR>MOV R0,#LOW (OSTCBCur)
;获得OSTCBCur指针低地址,指针占3字节。+0类型+1高8位数据+2低8位数据<BR>INC R0<BR>MOV DPH,@R0
;全局变量OSTCBCur在IDATA中<BR>INC R0<BR>MOV
DPL,@R0<BR><BR>;OSTCBCur->OSTCBStkPtr ===> DPTR 获得用户堆栈指针<BR>INC DPTR
;指针占3字节。+0类型+1高8位数据+2低8位数据<BR>MOVX A,@DPTR ;.OSTCBStkPtr是void指针<BR>MOV
R0,A<BR>INC DPTR<BR>MOVX A,@DPTR<BR>MOV R1,A<BR>MOV DPH,R0<BR>MOV
DPL,R1<BR><BR>;保存堆栈长度<BR>MOV A,R5<BR>MOVX @DPTR,A<BR><BR>MOV
R0,#OSStkStart ;获得堆栈起址<BR>save_stack:<BR><BR>INC DPTR<BR>INC R0<BR>MOV
A,@R0<BR>MOVX @DPTR,A<BR>DJNZ R5,save_stack<BR><BR>;调用用户程序<BR>LCALL
_?OSTaskSwHook<BR><BR>;OSTCBCur = OSTCBHighRdy<BR>MOV R0,#OSTCBCur<BR>MOV
R1,#OSTCBHighRdy<BR>MOV A,@R1<BR>MOV @R0,A<BR>INC R0<BR>INC R1<BR>MOV
A,@R1<BR>MOV @R0,A<BR>INC R0<BR>INC R1<BR>MOV A,@R1<BR>MOV
@R0,A<BR><BR>;OSPrioCur = OSPrioHighRdy
使用这两个变量主要目的是为了使指针比较变为字节比较,以便节省时间。<BR>MOV R0,#OSPrioCur<BR>MOV
R1,#OSPrioHighRdy<BR>MOV A,@R1<BR>MOV @R0,A<BR><BR>LJMP
OSCtxSw_in<BR>;-------------------------------------------------------------------------<BR>RSEG
?PR?OSIntCtxSw?OS_CPU_A<BR><BR>OSIntCtxSw:</FONT></P>
<P><FONT
color=#000080>;调整SP指针去掉在调用OSIntExit(),OSIntCtxSw()过程中压入堆栈的多余内容<BR>;SP=SP-4</FONT></P>
<P><FONT color=#000080>MOV A,SP<BR>CLR C<BR>SUBB A,#4<BR>MOV
SP,A<BR><BR>LJMP
OSIntCtxSw_in<BR>;-------------------------------------------------------------------------<BR>CSEG
AT 000BH ;OSTickISR<BR>LJMP OSTickISR ;使用定时器0<BR>RSEG
?PR?OSTickISR?OS_CPU_A</FONT></P>
<P><FONT color=#000080>OSTickISR: <BR><BR>USING 0 <BR>PUSHALL<BR><BR>CLR
TR0<BR>MOV TH0,#70H ;定义Tick=50次/秒(即0.02秒/次)<BR>MOV TL0,#00H ;OS_CPU_C.C 和
OS_TICKS_PER_SEC<BR>SETB TR0<BR><BR>LCALL _?OSIntEnter<BR>LCALL
_?OSTimeTick<BR>LCALL _?OSIntExit<BR>POPALL
<BR>RETI<BR>;-------------------------------------------------------------------------<BR>CSEG
AT 0023H ;串口中断<BR>LJMP SerialISR ;工作于系统态,无任务切换。<BR>RSEG
?PR?_?serial?OS_CPU_A<BR><BR>SerialISR:<BR><BR>USING 0 <BR>PUSHALL<BR>CLR
EA<BR>LCALL _?serial <BR>SETB EA<BR>POPALL
<BR>RETI<BR>;-------------------------------------------------------------------------<BR>END<BR>;-------------------------------------------------------------------------</FONT></P>
<P><FONT color=#000080>文件名 : OS_CPU_C.C</FONT></P>
<P><FONT color=#000080>void *OSTaskStkInit (void (*task)(void *pd), void
*ppdata, void *ptos, INT16U opt) reentrant<BR>{ <BR>OS_STK
*stk;</FONT></P>
<P><FONT color=#000080>ppdata = ppdata;<BR>opt = opt;
//opt没被用到,保留此语句防止告警产生 <BR>stk = (OS_STK *)ptos; //用户堆栈最低有效地址<BR>*stk++ =
15; //用户堆栈长度<BR>*stk++ = (INT16U)task & 0xFF; //任务地址低8位<BR>*stk++ =
(INT16U)task >> 8; //任务地址高8位 <BR>*stk++ = 0x00; //PSW<BR>*stk++ =
0x0A; //ACC<BR>*stk++ = 0x0B; //B<BR>*stk++ = 0x00; //DPL<BR>*stk++ =
0x00; //DPH<BR>*stk++ = 0x00; //R0<BR>*stk++ = 0x01; //R1<BR>*stk++ =
0x02; //R2<BR>*stk++ = 0x03; //R3<BR>*stk++ = 0x04; //R4<BR>*stk++ = 0x05;
//R5<BR>*stk++ = 0x06; //R6<BR>*stk++ = 0x07;
//R7<BR>//不用保存SP,任务切换时根据用户堆栈长度计算得出。 <BR>return ((void
*)ptos);<BR>}</FONT></P>
<P><FONT color=#000080>#if OS_CPU_HOOKS_EN<BR>void OSTaskCreateHook
(OS_TCB *ptcb) reentrant<BR>{<BR>ptcb = ptcb; /* Prevent compiler warning
*/<BR>}</FONT></P>
<P><FONT color=#000080>void OSTaskDelHook (OS_TCB *ptcb)
reentrant<BR>{<BR>ptcb = ptcb; /* Prevent compiler warning
*/<BR>}</FONT></P>
<P><FONT color=#000080>void OSTimeTickHook (void)
reentrant<BR>{<BR>}<BR>#endif</FONT></P>
<P><FONT color=#000080>//初始化定时器0<BR>void InitTimer0(void)
reentrant<BR>{<BR>TMOD=TMOD&0xF0;<BR>TMOD=TMOD|0x01;
//模式1(16位定时器),仅受TR0控制<BR>TH0=0x70; //定义Tick=50次/秒(即0.02秒/次)<BR>TL0=0x00;
//OS_CPU_A.ASM 和 OS_TICKS_PER_SEC<BR>ET0=1; //允许T0中断<BR>TR0=1;
<BR>}</FONT></P>
<P><FONT color=#000080>文件名 : YY.C</FONT></P>
<P><FONT color=#000080>#include <INCLUDES.H></FONT></P>
<P><FONT color=#000080>#define MAX_STK_SIZE 64</FONT></P>
<P><FONT color=#000080>void TaskStartyya(void *yydata) reentrant;<BR>void
TaskStartyyb(void *yydata) reentrant;<BR>void TaskStartyyc(void *yydata)
reentrant;</FONT></P>
<P><FONT color=#000080>OS_STK
TaskStartStkyya[MAX_STK_SIZE+1];//注意:我在ASM文件中设置?STACK空间为40H即64,不要超出范围。<BR>OS_STK
TaskStartStkyyb[MAX_STK_SIZE+1];//用户栈多一个字节存长度<BR>OS_STK
TaskStartStkyyc[MAX_STK_SIZE+1];</FONT></P>
<P><FONT color=#000080>void
main(void)<BR>{<BR>OSInit();<BR><BR>InitTimer0();<BR>InitSerial();<BR>InitSerialBuffer();<BR><BR>OSTaskCreate(TaskStartyya,
(void *)0, &TaskStartStkyya[0],2);<BR>OSTaskCreate(TaskStartyyb, (void
*)0, &TaskStartStkyyb[0],3);<BR>OSTaskCreate(TaskStartyyc, (void *)0,
&TaskStartStkyyc[0],4);<BR><BR>OSStart();<BR>}</FONT></P>
<P><FONT color=#000080><BR>void TaskStartyya(void *yydata)
reentrant<BR>{<BR>yydata=yydata;<BR>clrscr();<BR>PrintStr("\n\t\t*******************************\n");<BR>PrintStr("\t\t*
Hello! The world.
*\n");<BR>PrintStr("\t\t*******************************\n\n\n");<BR><BR>for(;;){<BR>PrintStr("\tAAAAAA111111
is active.\n");<BR>OSTimeDly(OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT color=#000080>void TaskStartyyb(void *yydata)
reentrant<BR>{<BR>yydata=yydata;
<BR><BR>for(;;){<BR>PrintStr("\tBBBBBB333333 is
active.\n");<BR>OSTimeDly(3*OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT color=#000080>void TaskStartyyc(void *yydata)
reentrant<BR>{<BR>yydata=yydata;
<BR><BR>for(;;){<BR>PrintStr("\tCCCCCC666666 is
active.\n");<BR>OSTimeDly(6*OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT
color=#000080>重入问题的解决:<BR>任务函数中带有形参和局部变量时若使用reentrant关键字会引起重入,从C51.PDF
129-131页的内容知:为了函数重入,形参和局部变量必须保存在堆栈里,由于51硬件堆栈太小,KEIL将根据内存模式在相应内存空间仿真堆栈(生长方向由上向下,与硬件栈相反)。对于大模式编译,函数返回地址保存在硬件堆栈里,形参和局部变量放在仿真堆栈中,栈指针为?C_XBP,XBPSTACK=1时,起始值在startup.a51中初始化为FFFFH+1。仿真堆栈效率低下,KEIL建议尽量不用,但为了重入操作必须使用。KEIL可以混合使用3种仿真堆栈(大、中、小模式),为了提高效率,针对51推荐统一使用大模式编译。<BR>为了支持重入,重新设计了堆栈结构(如下图)。增加了保存仿真堆栈指针?C_XBP和堆栈内容的数据结构。相应改变的文件有:OS_CPU_A.ASM、OS_CPU_C.C、OS_CPU.H、YY.C。由图可知,用户栈中保存的仿真栈与硬件栈相向生长,中间为空闲间隔,显然uCOSII的堆栈检测函数失效。硬件栈的保存恢复详见上节,仿真堆栈的保存与8086移植中的一样,OS只提供堆栈空间和只操作堆栈指针,不进行内存拷贝,效率相对很高。<BR>建议使用统一的固定大小的堆栈空间,尽管uCOSII原作者把不同任务使用不同空间看成是优点,但为了在51上有效实现任务重入,针对51笔者还是坚持不使用这个优点。<BR>用户堆栈空间的大小是可以精确计算出来的。用户堆栈空间=硬件堆栈空间+仿真堆栈空间。硬件栈占用内部RAM,内部RAM执行效率高,如果堆栈空间过大,会影响KEIL编译的程序性能。如果堆栈空间小,在中断嵌套和程序调用时会造成系统崩溃。综合考虑,我把硬件堆栈空间大小定成了64字节,用户根据实际情况可以自行设定。仿真堆栈大小取决于形参和局部变量的类型及数量,可以精确算出。因为所有用户栈使用相同空间大小,所以取占用空间最大的任务函数的空间大小为仿真堆栈空间大小。这样用户堆栈空间大小就唯一确定了。我将用户堆栈空间大小用宏定义在OS_CFG.H文件中,宏名为MaxStkSize。<BR>51的SP只有8位,无法在64K空间中自由移动,只好采用拷贝全部硬件堆栈内容的笨办法。51
本来就弱,这么一来缺点更明显了。其实,引入OS必然要付出代价,一般OS要占用CPU10%-20%的负荷能力,请权衡利弊决定。切换频率决定了CPU的耗费,频率越高耗费越大,大到一定程度就该换更强的CPU了。我选了50Hz的切换频率,不高也不低,用户可以根据需要自行定夺。在耗费无法避免的情况下,我采取了几个措施来提高效率:1。ret和reti混用减少代码;2。IE、SP不入出栈,通过另外方式解决;3。用IDATA关键字声明在汇编中用到的全局变量,变DPTR操作为Ri操作;4。设计堆栈结构,简化算法;5。让串口输入输出工作在系统态,不占用任务TCB和优先级,增加弹性缓冲区,减少等待。</FONT></P>
<P><FONT
color=#000080>在51单片机上硬件仿真uCOS51的说明:<BR>zyware网友2002/11/22来信询问uCOS51在单片机上的硬件仿真问题,具体情况是“在51上用uCOS51核,以及一些构件,keilc上仿真通过,用wave接硬件仿真程序乱飞,wave仿真以前的程序没有问题,不知是何缘故”。<BR>由于我的OS程序已经在KEIL软件仿真和硬件上实际测试过,所以不可能是程序错。可能的原因只能是硬件仿真软件设置问题。本人用的是Medwin软件,在Insight上调试,使用uCOS51编译测试程序一样跑飞。即使添加修改后的startup.a51(详见《在51单片机上固化uCOS51的说明》)也不正常。我发现Medwin似乎没有编译startup.a51,因为它把该文件加在了other
Files目录下而不是source Files目录,于是我猜测只有放在source
Files目录下的文件才被编译。由观察知,以.c和.asm做后缀的文件均被放在此目录下且被编译。于是我立即将startup.a51改成startup.asm并加入项目编译,结果测试正常。不必担心startup改名造成冲突,KEIL在链接目标文件时会自动处理重名段,本目录的文件优先级高(我是这么理解的,具体原理不清楚,这只是根据实践得到的结论,希望了解此处理过程的朋友能告之,不胜感激。)。<BR><BR>具体做法如下:<BR>1。按《在51单片机上固化uCOS51的说明》一文修改startup.a51,并将其更名为startup.asm。<BR>2。将startup.asm、yy1.c、os_cpu_c.c、ucos_ii.c、os_cpu_a.asm五个文件加入项目编译。<BR>3。运行</FONT></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -