嵌入式C语言开发ADSP21XX系列DSP_DSP论文
关键词:dsp visualdsp 嵌入式c语言 汇编语言
引言
长期以来,在dsp系统开发中,一直把汇编语言作为主要的开发工具;但汇编语言与自然语言差距很大,不易常,而且汇编语言是依赖于处理器的,不利于软件的可重复利用和系统的稳定性,程序不易移植,给开发工作带来了很大的困难。随着嵌入式系统复杂程度的不断提高,用汇编语言编写一个巨大的程度将是困难,甚至是不可能的。为此,ad公司推出了针对adsp21xx系列dsp的嵌入式c和c++语言集成开发工具,分别是visualdsp和visualdsp++系列,这些开发工具提供了c语言和c++语音的开发功能。以下就以笔者在实际开发中的一些经验,结合visualdsp6.1版本,介绍用c语言开发visualdsp6.1版本,介绍用c语言开发adsp21xx的方法。visualdsp提供了一个开放源码软件组织gnu的c编译器,和一套成熟稳定的c运行时间库(c run time library)等。gnu的编译器一向以编译效率高著称,在编译后的代码长度和运行速度方面非常优秀;c运行时间库则把很多重复性的工作,如浮点运行、三角函数、fft等作为c语言的库函数,提供给用户,大大提高了用户的开发效率和程序的稳定性,降低了开发难度,另外,由于把这些库函数的源代码提供给了用户,还提高了c语言与汇编语言之间的透明性,使用户开发的程序兼具两者的优点。
1 visual dsp简介
visualdsp是ad公司的dsp开发工具,主要由可执行文件、库文件和各种帮助文档组成。6.1版本还带有一个基于图形界面,针对21xx系列dsp的软件仿真和调试工具。
visualdsp的可执行文件包括汇编、编译、链接工具以及可执行文件重新格式化工具等,见表1。
表1 visualdsp的可执行文件及用途
可执行文件名 | 用 途 |
asmpp.exe* | 汇编预处理程序 |
asm2.exe* | 21xx系列汇编程序的汇编程序 |
asm21.exe | 21xx系列命令行汇编程序 |
ld21.exe | 21xx系列链接工具 |
g21.exe | 基于gnu的21xx系列c编译器 |
bld21.exe | 系统编译工具,产生相应于不同硬件体系的.ach文件 |
lib21.exe | 21xx系列的库管理工具 |
hspl21.exe | 把21xx系列可执行文件变成hip口可加载的格式 |
spl21.exe | 把21xx系列可执行文件变成prom可烧写的格式 |
注:“*”代表该程序一般不单独使用,而昌由g21.exe或asm21.exe调用。
visualdsp套件中的软件仿真调试工具debugapp,采用windows图形界面,使用方便。它的主要特点是:可以仿真调试从adsp2101~2189全系列的dsp;支持断点、单步、全速运行等各种常见调试方法;可以随时查询和修改dsp的程序ram(pm)、数据ram(dm)和各寄存器的内容;可以仿真中断,进行可执行程序性能评估(profile),因此可以进行时序仿真。debugapp是调试程序和验证复杂算法的极好工具。
visualdsp6.1还提供了丰富的帮助文档,包括21xx系列的用户手册、汇编和c语言工具以及仿真调试程序的使用手册;还有c运行库的参考手册,列出了所有可用的c库函数。用c语言开发dsp的典型流程如图1所示。
2 c语言运行库结构
c语言运行时间库是位于lib目录下的*.a文件,是整个c开发工具的核心之一,提供了大量的可以直接调用的库函数。这些库函数的函数原型包含在include目录下的头文件中。这些头文件有的还包含一些宏定义。另外,visualdsp还把这些库函数的汇编语言源代码提供给出了用户,方便了用户从中提取有用的代码,甚至修改源代码,生成新的库,来适应自己的要求。利用lib21程序,还可以把自己的常用汇编子程序做成库,或是将实时性要求较高的代码用汇编语言来写,做成库,供c语言程序调用。
visualdsp的c语言运行库由两部分组成:应用程序框架和预定义的各种库函数。
不同的dsp型号有不同的硬件结构、中断向量表,所以对应的应用程序框架库也不同,相应的文件是21*_hdr.dsp.其中*代表不同的dsp型号。应用程序框架的主体是中断向量处理部分,把中断向量引到合适的地址。其中最重要的是对系统复位(reset_vector)的中断向量的处理:
_reset_vector:call_lib_setup_everything;
call main_;
jump_lib_prog_term;
nop;
第一条指令是调用c库函数中的_ _lib_setup_everything函数作程序启动时的初始化工作。接下来,调用c语言程序中的main_函数,进入c程序的主体,也就是进入用户自己程序,开始正常工作。主程序结束后,再调用_lib_prog_term函数,作程序退出时的结尾工作。由于嵌入式系统的特性,系统绝大多数都在主程序运行时被继电了,所以_lib_prog_term得到执行的机会很小。
其它的中断向量由c运行库来管理,汇编指令如下:
_interrupt2:jump_lib_int2_ctrl;nop;nop;nop;
其中的_lib_int2_crtl就是c语言库中控制int2的函数。如果用户要使用该中断,应先把中断服务程序用一个c库函数interrupt()把服务函数指针设定好,并打开相应的中断允许位,当该中断发生时,_lib_int2_ctr1函数就会控制dsp跳转到相应的指针位置。
visualdsp预定义的c语言库函数包括函数、fft函数、ansi标准内存管理和字符串管理函数的一个子集。所有的函数列表可参考visualdsp的联机文档。这些库函数以二进制代码的形式,打包集合在lib*.a文件中,用户的c语言程序可以像使用自己的子程序一样方便地调用这些库函数。下面是调用库函数的一个例子。
#include<math.h> //包含所需的头文件
……
float a,b,c; //定义所需的变量
……
c=a*sin(b); //运算
编译后产生的汇编源代码中有call sin_指令,就是调用sin库函数的汇编语言指令语句。
从嵌入式开发的角度讲,visualdsp的c语言工具已经提供了一个操作系统雏形的功能。在ad公司的admc系列dsp中,已经把这些库函数和一些电机控制专用的函数,以及程序加载功能,集成在了dsp的片内rom中。
3 c语言与汇编语言混合编程方法
用c语言开发的缺点是不能精确控制程序运行的时间,对于实时性要求较高的应用,必须用汇编语言。visualdsp为用户提供了两种与汇编语言的接口方法:用asm()方法,直接嵌入汇编语言语句;用汇编语言编写子程序,供c语言程序调用。为了支持c语言与汇编程序程序的接口,visualdsp预定义了诸如function_entry、exit、save_reg、restore_reg等13个宏。限于篇幅,不详细介绍其功能。使用这些宏以前,要包含asm_sprt.h头文件。
3.1 使用asm()嵌入行的方法
使用这一方法时,一定要注意各寄存器和堆栈当前的状态,以免破坏程序运行的环境,产生错误的结果。visualdsp保留了一些内部寄存器供用户的汇编代码使用。用户可以自由地修改其内容,而不会对程序造成破坏。这些寄存器包括ar、af、ay1、m5、11、16、mf、mr0等18个。如果不够用,可以用系统定义的宏save_reg和restore_reg保护现场,得到另外11个可用寄存器。另外要注意的是,在汇编语言中操作c语言中定义的变量时,要在变量名后加下划线。下面是一个嵌套汇编语言的例子:
int img228; //定义c语言变量
asm("ax0=0x5c;")
asm("dm(ing228_)=ax0"); //用汇编语言赋值要将img228变成img228_
img228=0x5c; //直接用c语言赋值
编译后的汇编语言代码是
ax0=0x5c;
dm(img228_)=ax0
my1=92;
dm(img228_)=my1;
注意前者可能会破坏程序结构,因为它使用了未经保护的寄存器ax0;而由c语言产生的汇编代码,则会自动选择合适的临时寄存器my1。
3.2 使用汇编子程序的方法
使用汇编子程序是c语言程序与汇编语言接口的另一种方法。用户定义的子程序放在单独的汇编文件中,或是做成二进制的库文件,并将子程序的定义用globel输出,汇编后就可以供c语言程序调用。下面是一个不需要参数的子程序的例子:
.module/ram_delay_;
.external del_cycle; //声明del_cycle是外部变量
.global delay; //声明delay为全局函数
delay_:
runction_entry; //子程序开始标志,必须要的
ar=dm(del_cycle_);
cntr=ar;
do d_loop until ce;
d_loop:nop;
exit; //子程序结束标志,必须要的
.endmod;
如果汇编语言子程序中用到了参数,情况就复杂些。子程序中的入口参数前两个一定要保存在ar、ay1中。如果参数多于两个就要把其余的放在堆栈中。所有子程序的第一个返回值放在ar中。如果返回值不止一个,就要用到变量型参数或者指针来获得取所有的返回值了。下面是一个有5个输入参数、1个返回值的子程序例子。
add5_:
function_entry;
ar=ar+ay1; //前面的两个变量放在ar、ay1中
readsfirst(ay1); //从程序堆栈中读取第三个变量
ar=ar+ay1;
ay1=readsnext; //从程序堆栈中读取第四个变量
ar=ar+ay1;
ay1=readsnext; //从程序堆栈中读取第五个变量
ar=ar+ay1; //返回值放在ar中
exit;
注意其中的readsfirst和readsnext都是汇编语言接口宏。其功能是从堆栈中读取所有的参数。
4 c运行库的汇编源代码
如果只用c语言来开发21xx程序,只要有c运行库的二进制版就够了。幸运的是,ad公司把所有c运行库的汇编源代码随visualdsp提供给了用户,所以对那些用汇编语言开发的工程师来说,这些源代码也提供了很大的帮助。因此这代表很多功能的子程序不需要自己去编码、调试,用到某功能时只要把相应的汇编代码链接进自己的程序就可以。c运行库的源代码是扩展名为dsp的文本文件。基本上一个库函数对应一个文件,文件名就是函数名。比如说sin.dsp是正弦、余弦查找、使用都很方便,但是对于其中的交叉调用要注意。
反过来,用户也可以把自己已经调试、验证过的汇编子程序,做成二进制库文件,供c程序调用,这样可以大大提高软件的可重复利用率。要制作二进制库文件,只要用lib21.exe工具处理就行了。注意,生成的二制库文件的名字必须以.a作为文件扩展名。
笔者在实际的开发中,遇到这样的情况,自制的2181目标板上有一个自己开忍气吞声驻留程序,通过软件模拟的异步串口与pc通信,加载程序。但是这个驻留程序占据了0~0x500的空间,用户开发的程序只能加载到从0x500开始的空间内,而用c语言开发的程序起始地址都是从0开始的。为了解决这个问题,只能自己修改2181_hdr.dsp源文件。首先把第一行的.module/abs=0改成.module/abs=0x500,然后汇编成obj文件,代替原来的文件。另外,在自己的程序中定义一个从0开始0x500大小的pm区域,并初始化成0,就可以防止编译器在该区域内分配别的变量或程序代码,这样编译后的可执行文件的0~0x500空间都是0,加载时把它剔除,而其它有用的指令代码都在0x500之后,解决了这一个问题。
5
从实际开发的经验来看,visualdsp的c语言开发功能十分丰富。虽然提供的库函数只是ansi的一个不完备子集,但是对于一般的工程开发来说已经足够用了,而且visualdsp还提供了c运行库的源代码,这对于解决函数不完备的问题也好处。用c语言开发的好处还包括开发时间大大减少,程序的稳定性大大提高,这对于面对激烈的市场竞争,对于减轻设计工程师的工作量都很有好处。最后,用c语言开发是趋势,必将更加流行。