嵌入式C中一种基于宏的软仿真方法探索
2011-07-12贵阳学院计科系杜隆胤
贵阳学院计科系 杜隆胤
泸州医学院生物医学工程系 曹高飞
当前,嵌入式应用已经深入人们生活,嵌入式开发正如火如荼。相应地,也激起了高校计算机及相关专业学生的嵌入式学习热潮。但是在内地一些嵌入式学习环境较差的地方,由于硬件资源短缺给学习带来了不少困难。本文拟通过一种简单可行的办法以规避嵌入式开发对硬件环境的依赖,使学习者能将精力集中到嵌入式软件开发上来,从而降低学习门槛,提升学习速度。
一、嵌入式开发语言及仿真方式
嵌入式开发语言非常多,包括具有对硬件操作独具优势的低级语言——汇编语言、同时具备低级语言和高级语言特性的C语言以及面向对象编程的C++语言等等。其中、汇编语言因为接近于机器语言,在硬件底层操作方面独具优势,如果编程得当可获得很好的时间和空间效率,但它掌握起来相对困难且编程效率较低,而且程序可读性差,后期调试和维护困难大,所以一般在资源相对较少的应用场合和与硬件联系紧密的程序段使用;面向对象的高级语言编程接近人的思维方式,更贴近人类生活,能有效地提高编程效率为程序的可维护性,但编制的程序时间效率和空间效率都不高,且对底层硬件操作较一般需要通过低级语言程序来实现,因此一般只适合于各种资源都很丰富的大型的嵌入式系统;C同时具备低级语言和高级语言的优势、在讲求时间和空间效率基础上兼备易学、编程效率高等特点,在中小型嵌入式开发中以及大型系统的驱动程序开发中得到了广泛的应用。
Keil是目前最流行的开发80C51系列单片机的嵌入式的集成开发环境,它提供了C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,并通过一个集成开发环境(µVision)将这些功能组合起来。[1]特别地,Keil支持的软件仿真为缺少硬件环境开发调试提供了方便,很多嵌入式爱好者又在Internet网上提供了不少自制的软仿真DLL插件,为嵌入式开发学习者提供了很多便利。
但是,当前的仿真大都停留在观察寄存器值的层次上,很难如实反映外接部件的最终实现效果,即使能找到个别能仿真外围设备的插件又和开发者需要的存在一些差距,开发者自己开发适合自己的仿真环境又难度太大且耗时耗力。这矛盾促使了笔者寻找一种开发者自己能搭建仿真环境方法的动机。
二、基于宏的软仿真
现有的软件仿真方式很难达到即契合开发实际、如实反应设备最终运行效果的要求,那么、有没有一种不费事又能达到以上要求的软件仿真方式呢?本文就Keil环境下的C程序如何在TC下编译并仿真进行讨论。至于其他环境,如VC或BorlandC,GCC等。读者可以参照本文实现通过宏定义的软仿真。
首先,变量长度问题。
在嵌入式开发中,变量长度是非常敏感的,因此在处理变量长度是一定要小心。为使得编写的程序具有较好的编译环境适应性,一般都不会直接使用int、long、short和byte等变量类型进行定义,而是使用型如int8、int16等能直接表示变量类型及长度的定义方式,这要求在使用这些定义语句前先进行相应的定义。如在TC下就可以作如下的宏定义:
在Keil环境下却要把以上定义却为如下形式:
因此一般嵌入式开发都会将此部分内容针对不同的环境编织成不同的头文件,然后include到源文件中。同事可以在一个总的配置头文件中用如下方式为调试和最后编译配置不同的头文件。
这样就只需要在不同环境下编译时改变宏定义DEBUG的值即可。
其次,关键字问题。
Keil中有部分关键字不是ANSI C标准,因此对这些关键字需要进行特别的处理。主要有sfr、sbit和interrupt。
1.sfr与sbit
sfr是Keil为能直接访问80C51中的(特殊功能寄存器(SFR)而提供了一个专用的关键词,其用法是:
sfrt变量名=寄存器地址值。[2]
但是TC中却没有此用法,因此该定义此部分内容的配置文件作一定的修改。可以将sfr定义的特殊功能寄存器名字定义为对应长度的无符号整型变量。如:
sfr p0=0x80;
响应的替换语句为:
unsigned char p0;
这样只能是简单地解决了sfr的问题,而sbit就非常难模拟了。sbit是Keil为访问C51中特殊寄存器的位变量而设置的,但在TC下没为位变量,无法直接替换该关键字。
一般地,在嵌入式开发中,为了提高程序的可移植性,不会直接在程序里对特殊寄存器赋值,而以响应功能函数的方式出现。[3]比如在电路中用p1_1以共阳极方式控制指示灯L1,则可以在配置文件中设置如下宏定义:
#define SetL1(a) p1_1=!a;
那么在程序中需要点亮或熄灭指示灯L1时就可以执行语句:
SetL1(1); //点亮指示灯L1
SetL1(0); //熄灭指示灯L1
这样,在芯片更改或电路变动后只修改宏定义文件就能使程序适应新的硬件环境,从而增强了程序的可移植性。如此,为实现基于宏的软仿真,可以在对应TC仿真环境下的配置文件中按功能进行相应的宏定义,即编写相应的函数实现指示灯控制的模拟。笔者称此仿真为功能级仿真,而相对的只反映变量值变化的仿真方法可称为变量级仿真。
2.interrupt
关键字interrupt是Keil环境中定义中断函数用的,使用该扩展属性的函数定义语法如下:
返回值 函数名 interrupt n
其中n对应中断源的编号,其值从0开始,在80C51单片机中,编号从0到4,个编号对应外中断、定时器中断和串行口中断等。由于中断与芯片联系非常紧密,很难仿真。当然、TC下也可以利用PC键盘产生的中断来模拟,但工作量较大。所以,遇到像“void timer0() interrupt 1”之类的语句,只能先把“()”后面部分注释掉了,不不予仿真。
第三,功能仿真。
常用的嵌入式外接部件主要包括:液晶显示,flash存储器,指示灯,按键,扬声器等,如果以查询方式控制,就比较容易仿真。
1.液晶显示的仿真
液晶显示器可以利用TC的图形化函数进行。因为一般点阵液晶都是以描点为基础的,可以用TC下图形化描点函数(void far putpixel(int x,int y,int color);)代替。在TC下进行图形编程必须进行一系列的初始化,而嵌入式编程一开始也需要对硬件环境进行初始化,因此可以编写一个代替嵌入式环境初始化的函数以实现图形模式的启动。这同样可以在宏定义部分实现。一般嵌入式系统启动后就会进入一个无限循环,而在TC下仿真时不可以能让程序无限运行下去,因此需要设置一个出口,同时调用closegraph()以关闭图形模式。
2.flash存储仿真
对应外存储器的仿真,可以在硬盘上开辟一个文件进行模拟。但是在编写相应的仿真存取函数时,需要考虑flash的页面大小以及页内地址反转问题,即在某页内从起始地址以猝发方式连续存储数据时,一旦存储的数据超过一页容量,页内地址将反转到页面起始处,导致覆盖先前写入数据的现象。
为实现该功能的仿真,也需要在初始化函数中加入打开硬盘文件的语句,系统出口处加入文件关闭函数。在编写存取功能的仿真函数时配合seek()函数进行存取位置的定位,存数据时注意猝发方式的地址及页面大小即可实现基于宏定义的软仿真。
3.指示灯仿真
如果配置文件中关于指示灯的配置为如下形式:
#define SetLx(a) p1_x=!a //x=0,1,2,…,7
那么在仿真配置文件中就可以相应作如下宏定义:
#define SetL0(a) setl1(a)
setl1(a)是为实现软仿真编写的控制指示灯的函数,以其实现TC环境下640*480的图形环境中划分一部分区域专门显示指示灯的明灭状态。
4.按键仿真
一般按键应该在配置头文件中作如下配置:
#define IsKey0Down() (p3_5==1)
或者就直接有一个int IsKey0Down()函数。仿真中可以用键盘上某些键对应按键,此时可以使用boiskey()函数来检测键盘是否有按键以及何键被按下。假如用键盘上的键“A”模拟“K0”键,则可编写如下函数:
在配置头文件中作如下定义即可:
#define IsKey0Down() iskey0down()
以上方式只能模拟系统中只有一个按键的情形,在系统有多个按键的情况需要考虑一次判断多个按键。则将以上函数需稍作修改,以适应读一次键值作多个判断的需要。
5.扬声器仿真
扬声器的仿真类似于指示灯,只是注意扬声器一般打开一会就会立即关闭,在此不再赘述。
三、TC中基于宏的软仿真缺点
本文讨论的基于宏的软仿真方法实质为利用C的空定义,在配置头文件中实现基本功能的宏定义替换以实现嵌入式程序的仿真。
本文所讨论的仿真方法一般只适应于没有中断的嵌入式系统,而嵌入式系统又不可能没有中断,因此,此方法只实用于嵌入式前期学习,不适于系统开发。但是,在完全以循环查询方式编写的嵌入式系统中,几乎全部功能都能以宏定义方式实现TC下的软仿真。
四、结束语
嵌入式开发中因为软件与硬件联系非常紧密,因此初学时难度较大,利用仿真方式进行学习能降低初学者入门难度。目前相对较好的Keil+DLL仿真方式依赖他人提供,很难适合开发者(学习者)的需要。
本文立足于程序初学者相对容易掌握的TC下的宏定义的方式搭建用户自制的软仿真环境,如此也使得开发者(学习者)比较容易根据需要对仿真环境进行增删修改,使仿真更能贴近任务。但因为该方法要求具有一定的TC下的C编程基础,而且本身很难实现中断和数据收发的仿真,所以还有待进一步改善甚至寻找更便捷且更贴近任务的仿真方法。
[1]郭天祥.新概念51单片机C语言教程——入门、提高、开发、拓展[M].电子工业出版社,2009,01.
[2]Keil Elektronik GmbH.and Keil Software,Inc.Keil User’s Guide[J].1988.
[3]凌明.嵌入式系统高级C语言编程[M].北京航空航天大学出版社,2011-1-1.
[4]丁明亮,唐前辉.51单片机应用设计与仿真-基于Keil C与Proteus.北京航空航天大学出版社,2009-2-1.