网站导航: 首页 > 设计参考 > 正文 文章搜索
TMS320F240学习及uCOS II移植心得
 
文章编号:
090105153955
文章分类: 单片机 DSP
点 击:
...
关 键 词: TMS320F240,uCOS
文章来源:
网络
摘 要:

(二)系统调试及总结
    明白了以上的规则就可以大胆的用C编程了,确实要比汇编方便了很多。现在还回到我的移植程序中,弄懂上面的东西后又修改了一些错误,到了六月上旬,整个程序编译通过了。我很兴奋,终于有了进展。接下来就是调试,调试是比编写程序痛苦的事,对那些隐藏的错误要能顺利的找出来实在是困难。系统不能正常工作,首先怀疑的就是移植代码部分,移植代码是按照ucOS提供的步骤写的,写的时候由于借鉴了别的例子并没有深刻理解,调试时就必须有个深刻地认识。

难点一:对任务堆栈初始初始化函数OSTaskStkInit()的作用的理解,方法是模拟TI公司的I$$SAVE库函数对任务堆栈初始化,按照库函数地保存顺序开辟栈空间,得到堆栈指针。这个函数的编写要充分理解"堆栈"的概念。芯片本身的堆栈只有 8 级,无法作为系统堆栈使用,这8级堆栈用来保存函数调用和中断的返回地址。C 编译器将寄存器AR0、AR1作为SP和FP管理系统堆栈和局部帧(上面有说明)。编译器使保存在硬件堆栈里的返回地址弹出保存在系统堆栈里,并保存其他寄存器,即保存了任务运行的现场,这些工作都由I$$SAVE来做。有了任务堆栈初始化函数OSTaskStkInit(),系统在进行初始化时,这个函数将任务地址放在堆栈中,然后用中断返回也就是I$$REST函数将寄存器和TOS初始化,将任务的起始地址弹回到TOS中,这样就能从中断的任务开始运行了。

难点二:时钟节拍。对节拍地作用是在看了操作系统的内核代码后有了深刻认识的,虽然移植是可能不太了解具体的代码,但没有操作系统的概念最好把这个比较易学的操作系统的代码看看,有个结构和原理的认识,我大概花了一周仔细的看了书和代码,这次和以前看不同,了解了代码实现地细节,看完会更加头脑清醒。最初地时钟节拍中断是用实时时钟中断RTI,看完代码知道了应该不可以用它来实现节拍,因为系统时钟地启动是在初始化结束后第一个任务开始前启动的,实时时钟不能这样控制,它在板子上电是就启动了,所以我改用定时器1实现。时钟节拍函数OSTickISR()的实现是定时器的硬件中断,因为在我修改过程中不断地出错,这使我熟练掌握了F240的中断编程方法。我对中断编程做了总结,包括用C编写中断,对初学者应该有帮助:

在UCOS中的中断编程和一般的中断编程稍有不同。共同的是:

1.中断矢量表。中断矢量表一定要定位在程序空间的地址0开始的地方,0000h~003Fh为中断矢量表。第0行跳转到代码开始的地方、第1到第6行是硬件中断跳转指令,除NMI中断其他是软件中断指令(INTk最大为INT31)。发生硬件中断后,处理器自动到前面查表跳转到相应位置。软件中断是在程序中执行了INTk指令才会发生,然后根据x查中断矢量表跳转到相应的ISR(移植系统时用到了软件中断指令,即任务切换函数)。

2.硬件中断系统。其中的可屏蔽中断INTk(k=1~6)对应了多个中断源,有系统中断和EV中断,它们都通过INTk和CPU相关(将INTk称为内核中断)。写系统中断或EV中断必须知道对应哪个内核中断。因为每个内核中断对应了几个中断源(具体对应查阅书籍)。有中断源复用问题,当一个内核中断中有多于一个的外部中断发生时,就要查外部中断矢量表,它是根据中断标识排列的。注意每条跳转指令占两个字节。外部中断矢量表的位置可以在程序空间的任何位置(表中有一个代表表的起始位置的符号)。对它的查表方式为基址+变址。基址为表的起始地址,变址为中断源的标识×2。

3.中断编程。本程序的 INT1有中断复用,如果用汇编编写,需要外部中断矢量表。但在ucOS中编写串口通讯时,发送任务没有采用中断方式,它要和接收任务通讯而有相应的动作(没有操作系统时就是查询方式)。即接收中断发生后要调用发送信号量函数(也可以使用别的通讯机制),在汇编中调用较麻烦,所以采用了C编程,这样在 INT1的ISR中用了switch语句,就不用外部中断矢量表。其他中断源没有复用问题。所以整个程序也不需要外部中断矢量表。

在ucOS中的中断编程要注意的是进入中断调用库函数I$$SAVE(C编写自动调用),而程序结束是调用OSINTExit()(最后调用了I$$REST)。

看了操作系统的源代码再加上对F240编程方法进一步掌握,感觉对这个系统的整体有很清晰的认识,很高兴自己在无数的错误中摸索出来了。不幸的是我的程序仍然不能成功地运行,但我相信应该问题不太大,应该不是在关键处。因为系统可以单步运行且结果正确。我很幸运的在网上遇到了一位做过在2407移植的人,网上还有完整地源码下载。我对比了程序,仍然不知错在哪里,然后我向他讨教,他给了一个建议是,查看map文件,找到两个常量表OSMapTbl和 OSUnMapTbl的映射地址,看看它们的值是否正确。我查看了一下没错,但偶然发现一个寄存器的映射地址是错的,再看其他寄存器也是错的,因为用C编程寄存器的映射头文件要重新定义,我采用的是赋值语句,定义指针,程序中用指针寻址,这种方法应该是没有问题的,但我知道有它的不方便的地方,不利于我的程序中在每个需要的文件中包含这个头文件,于是就改成宏定义寄存器,这样改过之后地址映射就正确了。我把程序从头到尾的看了很多遍,包括反汇编的代码都仔细去找错,倒是有点小bug被找出来,但都不是致命的那个。在我身心俱惫时,我决定放松一下,这时是七月上旬,非典的阴影快要消失了,算算我也有三个多月没出门了,这中间同学没有联系,没有看一部电影,没有上网聊天,程序完全占据了我的头脑,现在隐约觉得看到了胜利的曙光,我要放松一下。放风了两天,仍然心有不甘,就又着手我的程序。这次我把所有的寄存器配置从操作系统中拿开,在汇编中检验,终于找到了那个bug:SYSCR的配置错误,这个常用的寄存器不用看帮助就知道怎样配置,但我写错了,可能是最初的笔误,直接导致了系统的复位。系统终于可以运行了!从我的第一个程序到这时有两个多月的时间,我学业生涯中最不平坦的两个多月。

我换了一块板子,以前不能显示输出语句结果的问题也解决了,于是我又写了比较复杂的测试程序测试系统运行情况,虽然不是一次就可以写成功的,但这次调试用了很短的时间。还有下面的总结,是C语言几个关键字有关的内容,虽然较为基础,在操作系统中可能用到的,对透彻把握程序很有帮助。

1.volatile类型限定符。用它修饰的对象叫易变对象,用于告诉编译程序它所修饰的对象(可以是变量或常量)的值可能会以程序中未显式指定的方式发生变化,即不是由程序中的赋值、初始化等显式指定的方式发生变化。如,其变化可能式由中断程序或IO端口所施加的。再如,在程序中可能会把某个全局变量的地址传送给操作系统的时钟并用于存放系统的实际时间,尽管程序中没有对这个变量使用赋值语句赋值,但它的内容还是变了。举个例子:

 
  1. volatile int ticks;   
  2. interrupt timer( )   
  3. {   
  4.     ticks++;   
  5. }   
  6. wait (int time)   
  7. {   
  8.     ticks = 0;   
  9.     while (ticks < time ) ;   
  10. }  

 

如果ticks没有声明为易失变量,编译程序可能会把它当作寄存器变量分配,从而wait函数执行永远不会中止。上例中的interrupt 关键字是修饰中断函数的,表示改函数与中断相联系。

2.typedef 定义新的数据类型。例:

typedef unsigned int INT16U;

定义了类型INT16U,这样做可以提高程序的可移植性。例如,有些C编译系统没有提供无符号短整数unsigned short int类型,这样,在其他编译系统整运行的使用了这种类型的编译器就要把程序中出现的unsigned short int都换成另一种合适的类型如unsigned int,这样改动比较大。使用类型定义可以解决这个问题,这时,在允许使用unsigned short int 的编译系统中编写程序时,先使用一个类型定义:

typedef unsigned short int USINT ;

以后在其他编译系统运行时,只要把改类型定义改成:

typedef unsigned int USINT ;

其他地方不动就可以了。

还有一点就是可以避免因不同编译系统实现上的差别而带来的可移植性问题。例在某些编译系统中char、short(有无符号)用8位表示,而在 TMS320C2xx的系统中,都用16位表示,为使在前者系统中运行的程序在C2xx中运行,必须把char、short改成int,这时,使用类型定义可以减少修改。

3.register存储类区分符。用它修饰说明的变量叫寄存器变量,它的用途一是与auto一样,使它所修饰的对象成为自动的,二是建议编译程序在存储分配使尽可能的把它分配到机器存储器中,以便用时能快速存取。使用寄存器变量最常见的情况时把循环控制变量说明成寄存器变量,因为这个变量在每次循环时都要至少访问一次,例:

 
  1. register int I ;   
  2. fun( )   
  3. {   
  4.     for ( I =0 ;I <1000;I ++)   
  5.     {   
  6.         ......    
  7.     }   
  8. }  

 

理论上,一个程序中可说明任意多个寄存器变量,实际上,由于寄存器数目的物理限制,编译系统只把最前面的有限数目的寄存器变量分配在寄存器中,而把其余的当作普通自动变量处理。在F240中,编译系统允许把AR6、AR7用了存储寄存器变量,使用优化选项是还允许使用AR5(见 spru024D4.2.4)。

以上是我学习中的一些总结,有很多是说自己的感受和心情的,可能不太值得一提,最初接触硬件的人应该都有强烈的感受。但我相信有很多像我这样从零开始的后来者,从不知到知,会遇到很多困难,我希望能给他们一点信心和勇气,让他们知道有我这样一个人也经历许多郁闷的日子,最终解决了当时觉得解决不了的问题。如果看到我用了"幸运"这个词,不要羡慕我的运气,不要怪自己得问题为什么不能"幸运"的解决(我就曾这样痛苦的想过),因为可能你更幸运,根本就不会像我这么"倒霉"遇到它。但总会有问题发生,如果解决了,你更幸运的获得了一个宝贵的经验、非常难得的财富。万事开头难,入了门脚下的路就就会逐步平坦了。

 
相关文章:

上一页 12
 
最新开源项目
 
 
  查看更多...  
 
本站相关产品   淘宝网店
 




 
  查看更多...  

 

本站程序由百合电子工作室开发和维护
Copyright @ baihe electric studio
渝ICP备09006681号-4