📄 ucos51移植心得zt(铁匠).htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0036)http://www.mcu51.com/list.asp?id=904 -->
<HTML><HEAD><TITLE> uCOS51移植心得zt(铁匠)</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312"><LINK
href="uCOS51移植心得zt(铁匠).files/style.css" rel=stylesheet>
<META content="MSHTML 6.00.2800.1106" name=GENERATOR></HEAD>
<BODY>
<DIV align=center>
<CENTER></CENTER></DIV>
<DIV align=center>
<CENTER>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=0 width="97%"
borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#d0d0d0>
<P align=center>文章编号:904----加入日期:2003-12-4 </P></TD></TR>
<TR>
<TD width="100%">
<P align=right><A href="javascript:self.close()">『关闭窗口』</A></P></TD></TR>
<TR>
<TD width="100%">
<P align=center><B> uCOS51移植心得zt(铁匠)</B></P></TD></TR>
<TR>
<TD width="100%">
<BLOCKQUOTE><BR> uCOS51移植心得<BR> 巨龙公司系统集成开发部
杨屹
asdjf@163.com 2002/10/03<BR><BR>引言<BR><BR> 自从发表《uCOS51移植心得》以来,我收到了很多朋友们的来信,大家对公开源码表<BR>示鼓励,谢谢大家的支持!很多人对于编写自己的操作系统很感兴趣,uCOS51是个不错的<BR>选择。它的优点是简单易懂,学习成本低,有利于向32位CPU过渡。目前,嵌入式BBS上的<BR>热点是:嵌入式实时多任务操作系统、单片机上网、32bitCPU(如ARM等)。其实通过<BR>uCOS51学习完全可以掌握这些热门技术的精髓,而且学习成本低廉。为此我会陆续将我在<BR>研发过程中的经验体会写出来与大家交流,共同进步。<BR> 我准备讨论以下内容:uCOS51高效内核、OS人机界面SHELL的编写、51机开发板的硬件<BR>设计、RTL8019AS网卡驱动程序、51TCP/IP协议栈设计、应用协议FTP、PPP、<A
href="http://www.mcu51.com/HTTP"
target=_blank>HTTP</A>、SMTP、<BR>SNMP……在51上的实现技术、51OS任务划分和应用程序实例、由51软件系统向ARM的移植以<BR>及其他想到的题目。欢迎大家积极参与。<BR> <BR> 注:开发板原理图、PCB图、GAL烧录文件、芯片手册、全部源程序可以来信索取,在<BR>整理好后会共享在网上。<BR> <BR> 讨论1----uCOS51高效内核<BR> <BR> 前一段时间,我参与了一个SNMP网管板的项目,我负责硬件设计和单板软件开发。该<BR>板的硬件由MCS51+RTL8019AS组成,有64K
FLASH 和64K
SRAM。软件部分有操作系统和<BR>TCPIP协议栈。硬件比较简单,用了一个月就搞定了,协议栈我参考了老古开发板的部分程<BR>序又上网找了SNMP源代码也很快完成了,但是测试时发现当使用较低时钟频率的CPU时(为<BR>了降低成本),由于ASN.1编解码部分过于庞大,而我的程序又是一个大循环,AGENT的响<BR>应速度受到严重影响,用户界面也反应迟钝。更坏的消息是公司为了适应市场需求,还要<BR>在上面跑PPP和<A
href="http://www.mcu51.com/HTTP"
target=_blank>HTTP</A>。那样的话,我就得用40MHz的AT89C51RD2或者人为的把程序断成几部<BR>分然后用状态机的方法在运行时再把它们连接起来。不过,我不想增加成本,也不想把程<BR>序搞乱,迫不得已,只好使用操作系统。<BR> 说实在的,一开始我也不是很有把握,一来我不清楚51的FLASH是否装得下这么多代<BR>码,二来我只做过OS应用开发,对于它的移植想都不敢想。不过,我在BBS上搜索了一阵儿<BR>后还是有了一些头绪。我找到了几个OS的源代码(我喜欢用现成的),按照代码大小、实<BR>时性、使用人数、众人口碑等标准,最后选定了uCOS2。我感觉它的实时性有保障,延时可<BR>预测,代码据说可小到2K,网上讨论这个话题的人也比较多,而且它的网站上有针对KEIL
<BR>C51的移植实例。<BR> 经过一番查找,我得到了5个版本。其中3个是用KEIL编译的。本来我想直接把OS代码<BR>嵌到应用程序中,但后来发现没有一个可以直接使用。有的无法用KEIL直接编译,有的需<BR>要修改DLL在软件仿真下使用。而我需要的是能在串口输入输出,不需要修改任何无关软<BR>件,能在软件仿真和硬件上运行的实时多任务操作系统。没有办法,我只好硬着头皮去改<BR>编。<BR> 我分析了自己的劣势:1。KEIL刚开始使用,不太熟悉;2。混合编程以前从没有作<BR>过;3。时间紧迫,要在1个月内搞定。而我的优势就是有5个移植实例可供参考,可以上网<BR>查资料。一开始,我用“堆栈”、“混合编程”、“汇编”、“ucos”等关键字在C51BBS<BR>和老古论坛上检索相关信息并逐条阅读,读过之后,头脑中的思路逐渐清晰了。我了解到<BR>在KEIL的HLP目录下有A51.PDF和C51.PDF非常全面的介绍了汇编和C51,是KEIL的权威用户<BR>手册;SP初始化、内存清0等操作在STARTUP.A51文件中实现,用户可以改写它;KEIL的变<BR>量,子程序等的分配信息可以在.M51文件里查到;KEIL自己的论坛里有很多疑难问题的解<BR>答……通过阅读并经过思考,解决了堆栈起点、堆栈空间大小的设定等关键问题。论坛里<BR>的问题有些是我没有想到的,这使我发现了自己的疏漏。<BR> 在网上获得大量信息后,我开始阅读《uCOSII》中文版,一共读了3遍。第一遍是浏<BR>览,了解到uCOSII包括任务调度、时间管理、内存管理、资源管理(信号量、邮箱、消息<BR>队列)四大部分,没有文件系统、网络接口、输入输出界面。它的移植只与4个文件相关:<BR>汇编文件(OS_CPU_A.ASM)、处理器相关C文件(OS_CPU.H、OS_CPU_C.C)和配置文件<BR>(OS_CFG.H)。有64个优先级,系统占用8个,用户可创建56个任务,不支持时间片轮转。<BR>第二遍主要是把整个工作过程在头脑里过了一下,不懂的地方有针对性地查书,重点是思<BR>考工作原理和流程。我发现其实它的思路挺简单的。就是
“近似地每时每刻总是让优先级<BR>最高的就绪任务处于运行状态”
。为了保证这一点,它在调用系统API函数、中断结束、<BR>定时中断结束时总是执行调度算法。原作者通过事先计算好数据,简化了运算量,通过精<BR>心设计就绪表结构,使得延时可预知。任务的切换是通过模拟一次中断实现的。第三遍重<BR>点看了移植部分的内容。对照实例,研究了代码的具体实现方法。<BR> 前期准备用了20几天,真正编写代码只用了1.5天,调试用了2天。具体过程如下:<BR> (1)拷贝书后附赠光盘sourcecode目录下的内容到C:\YY下,删除不必要的文件和<BR>EX1L.C,只剩下p187(《uCOSII》)上列出的文件。<BR> (2)改写最简单的OS_CPU.H<BR> 数据类型的设定见C51.PDF第176页。注意BOOLEAN要定义成unsigned
char
类型,<BR>因为bit类型为C51特有,不能用在结构体里。<BR> EA=0关中断;EA=1开中断。这样定义即减少了程序行数,又避免了退出临界区后关<BR>中断造成的死机。<BR> MCS-51堆栈从下往上增长(1=向下,0=向上),OS_STK_GROWTH定义为0<BR> #define OS_TASK_SW()
OSCtxSw()
因为MCS-51没有软中断指令,所以用程序调用<BR>代替。两者的堆栈格式相同,RETI指令复位中断系统,RET则没有。实践表明,对于MCS-<BR>51,用子程序调用入栈,用中断返回指令RETI出栈是没有问题的,反之中断入栈RET出栈则<BR>不行。总之,对于入栈,子程序调用与中断调用效果是一样的,可以混用。在没有中断发<BR>生的情况下复位中断系统也不会影响系统正常运行。详见《uC/OS-II》第八章193页第12行<BR> (3)改写OS_CPU_C.C<BR> 我设计的堆栈结构如下图所示:<BR><BR><BR><BR> TCB结构体中OSTCBStkPtr总是指向用户堆栈最低地址,该地址空间内存放用户堆栈<BR>长度,其上空间存放系统堆栈映像,即:用户堆栈空间大小=系统堆栈空间大小+1。<BR> SP总是先加1再存数据,因此,SP初始时指向系统堆栈起始地址(OSStack)减1处<BR>(OSStkStart)。很明显系统堆栈存储空间大小=SP-OSStkStart。<BR> 任务切换时,先保存当前任务堆栈内容。方法是:用SP-OSStkStart得出保存字节<BR>数,将其写入用户堆栈最低地址内,以用户堆栈最低地址为起址,以OSStkStart为系统堆<BR>栈起址,由系统栈向用户栈拷贝数据,循环SP-OSStkStart次,每次拷贝前先将各自栈指针<BR>增1。<BR> 其次,恢复最高优先级任务系统堆栈。方法是:获得最高优先级任务用户堆栈最低<BR>地址,从中取出“长度”,以最高优先级任务用户堆栈最低地址为起址,以OSStkStart为<BR>系统堆栈起址,由用户栈向系统栈拷贝数据,循环“长度”数值指示的次数,每次拷贝前<BR>先将各自栈指针增1。<BR> 用户堆栈初始化时从下向上依次保存:用户堆栈长度(15),PCL,PCH,PSW,<BR>ACC,B,DPL,DPH,R0,R1,R2,R3,R4,R5,R6,R7。不保存SP,任务切换时根据用户<BR>堆栈长度计算得出。<BR> OSTaskStkInit函数总是返回用户栈最低地址。<BR> 操作系统tick时钟我使用了51单片机的T0定时器,它的初始化代码用C写在了本文<BR>件中。<BR> 最后还有几点必须注意的事项。本来原则上我们不用修改与处理器无关的代码,但<BR>是由于KEIL编译器的特殊性,这些代码仍要多处改动。因为KEIL缺省情况下编译的代码不<BR>可重入,而多任务系统要求并发操作导致重入,所以要在每个C函数及其声明后标注<BR>reentrant关键字。另外,“pdata”、“data”在uCOS中用做一些函数的形参,但它同时<BR>又是KEIL的关键字,会导致编译错误,我通过把“pdata”改成“ppdata”,“data”改成<BR>“ddata”解决了此问题。OSTCBCur、OSTCBHighRdy、OSRunning、OSPrioCur、<BR>OSPrioHighRdy这几个变量在汇编程序中用到了,为了使用Ri访问而不用DPTR,应该用KEIL<BR>扩展关键字IDATA将它们定义在内部RAM中。<BR> (4)重写OS_CPU_A.ASM<BR> A51宏汇编的大致结构如下:<BR> NAME
模块名 ;与文件名无关<BR> ;定义重定位段 必须按照C51格式定义,汇编遵守C51规范。段名格式为:?PR?函<BR>数名?模块名<BR> ;声明引用全局变量和外部子程序 注意关键字为“EXTRN”没有‘E’<BR> 全局变量名直接引用<BR> 无参数/无寄存器参数函数
FUNC<BR> 带寄存器参数函数 _FUNC<BR> 重入函数 _?FUNC<BR> ;分配堆栈空间<BR> 只关心大小,堆栈起点由keil决定,通过标号可以获得keil分配的SP起点。切<BR>莫自己分配堆栈起点,只要用DS通知KEIL预留堆栈空间即可。<BR> ?STACK段名与STARTUP.A51中的段名相同,这意味着KEIL在LINK时将把两个同<BR>名段拼在一起,我预留了40H个字节,STARTUP.A51预留了1个字节,LINK完成后堆栈段总长<BR>为41H。查看yy.m51知KEIL将堆栈起点定在21H,长度41H,处于内部RAM中。<BR> ;定义宏<BR> 宏名
MACRO 实体 ENDM<BR> ;子程序<BR> OSStartHighRdy<BR> OSCtxSw<BR> OSIntCtxSw<BR> OSTickISR<BR> SerialISR<BR> END ;声明汇编源文件结束<BR> <BR> 一般指针占3字节。+0类型+1高8位数据+2低8位数据
详见C51.PDF第178页<BR> 低位地址存高8位值,高位地址存低8位值。例如0x1234,基址+0:0x12
基址<BR>+1:0x34<BR> <BR> (5)移植串口驱动程序<BR> 在此之前我写过基于中断的串口驱动程序,包括打印字节/字/长字/字符串,读串<BR>口,初始化串口/缓冲区。把它改成重入函数即可直接使用。<BR> 系统提供的显示函数是并发的,它不是直接显示到串口,而是先输出到显存,用户<BR>不必担心IO慢速操作影响程序运行。串口输入也采用了同样的技术,他使得用户在CPU忙于<BR>处理其他任务时照样可以盲打输入命令。<BR> (6)编写测试程序Demo(YY.C)<BR> Demo程序创建了3个任务A、B、C优先级分别为2、3、4,A每秒显示一次,B每3秒显<BR>示一次,C每6秒显示一次。从显示结果看,显示3个A后显示1个B,显示6个A和2个B后显示1<BR>个C,结果显然正确。<BR> 显示结果如下:<BR> AAAAAA111111
is active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> BBBBBB333333 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> BBBBBB333333 is
active<BR> CCCCCC666666 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> BBBBBB333333 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> AAAAAA111111 is
active<BR> BBBBBB333333 is
active<BR> CCCCCC666666 is
active<BR> Demo程序经Keil701编译后,代码量为7-8K,可直接在KeilC51上仿真运行。<BR> 编译时要将OS_CPU_C.C、UCOS_II.C、OS_CPU_A.ASM、YY.C加入项目<BR> <BR> 以上是我这次移植uCOS51的一些心得,写出来只是让准备在51上运行操作系统的同行<BR>们少走弯路并增强使用信心。我强烈推荐大家在自己的51系统中使用uCOS这个简单实用的<BR>自己的操作系统。它的大小应该不是问题,性能上的提高却是显著的。但愿此文能对朋友<BR>们有所帮助,错误在所难免,希望各位大虾指正,诸位高手们见笑了!<BR> <BR>注:全部源码可来信索要(asdjf@163.com),以下仅为关键代码部分。 <BR><BR>文件名
:
OS_CPU_A.ASM<BR><BR>$NOMOD51<BR>EA BIT 0A8H.7<BR>SP DATA 081H<BR>B DATA 0F0H<BR>ACC DATA 0E0H<BR>DPH DATA 083H<BR>DPL DATA 082H<BR>PSW DATA 0D0H<BR>TR0 BIT 088H.4<BR>TH0 DATA 08CH<BR>TL0 DATA 08AH<BR><BR> NAME
OS_CPU_A ;模块名<BR> <BR>;定义重定位段<BR>?PR?OSStartHighRdy?OS_CPU_A SEGMENT
CODE<BR>?PR?OSCtxSw?OS_CPU_A SEGMENT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -