DSP

如何用C语言开发DSP嵌入式系统

2019-07-13 10:28发布

如何用C 语言开发DSP 嵌入式系统
肖宛昂 曾为民
Xiao,Wanang Ceng,Weimin
(华东交通大学) 肖宛昂曾为民
摘要目前很多嵌入式系统以 DSP 为核心构建,但是,采用汇编语言开发DSP 系统存在开发
难度大、开发周期长、维护性差等缺点,应用C 语言开发DSP 系统是广大嵌入式开发者的
迫切要求。有关单片机的C 语言开发有相当多的资料可以参考,而DSP 系统的C 语言开发
却很少见。本文以TI 公司的DSP 器件TMS320F24X 系列为例,讲述怎样用C 语言开发一
个完整的DSP 嵌入式系统。
关键词:嵌入式系统; DSP 系统; C 语言开发; TMS320F24X 系列
引言
大家在开发嵌入式产品时首先会想到用控制器的汇编语言编写监控程序,主要原因是:①汇
编语言生成的程序对应的二进制代码少,程序执行要比高级语言生成的程序快;②控制器刚
问世时,没有相应的高级语言可供使用;③存储器的价格问题和寻址空间的限制。
以上所述问题目前已基本解决,在这就不阐述了。实际情况是:在单片机的应用领域,开发
者已开始使用C 语言进行开发。大家发现用高级语言开发嵌入式产品是如此轻松,并且C
语言程序编译后的二进制代码也非常短小精练。
目前使用最多的数字信号处理器(DSP)是美国TI 公司的TMS320 家族,而工业控制上用
得最多的又是TMS320F2XX 系列。TI 公司为每一个DSP 芯片提供了汇编语言和C 语言供
开发者选用。本人一直使用C 语言进行产品开发,而目前很少见到这方面的介绍、所以特
撰此文,以TMS320F240 为例,向各位同行推荐用C 语言开发DSP 嵌入式系统。
1 DSP 的C 语言的特殊性
大家在使用 51 系列C 语言时已经注意到,控制器的C 语言和PC 机上使用的C 有一个显著
的特点:经常要对硬件操作,程序中有大量针对控制器内部资源进行操作的语句。所以,开
发者要明白怎样用C 语言来操纵控制器的内部资源,即怎样用C 语句操作寄存器和内部存
储器等。
举个例子:在 51 汇编中我们写 MOV A,#20H;汇编程序能够识别A 是指累加器;而在
51 的C 程序中我们写ACC=32;,编译器能够识别ACC 是指累加器而不是一般的变量。即
每一个寄存器都有一个专有名字供开发者使用,它们定义在一个头文件reg51.h 中,程序
员只需在程序的开始部分用#include“reg51.h”语句将该文件包含进来即可。注意:这些
寄存器的名字不能用作变量名。
同样,在TMS320F240 的C 语言中也有一个头文件C240.H 定义各个寄存器的名称,这里
摘录几条语句进行介绍。
比如:#define IMR((PORT)0x0004)
#define XINTI_CR((PORT) 0x07070)
IMR、XINT1_CR 就对应两个寄存器,实际是寄存器的地址,用高级语言的说法是指针。我
们也在程序的开始部分用#include“c240.h”语句将该文件包含进来。这样,在DSP 的C
语言中使用它们只需在前面加一个星号(*),例如,
*IMR=0X1010;/*将十六进制数1010H 赋给IMR 寄存器*/
*XINT1_CR=0X0A0B0;/*将十六进制数A0B0H 赋给XINT1_CR 寄存器*/
开发者最好将c240.h 这个文件打印出来,弄清楚各个寄存器的定义名称。至于不涉及硬件
的语法和ANSI 语法一样。需要注意的是,有些ANSI 标准中的函数在DSP 的编译器中不提
供,读者可以参考DSP 编译器的C 语言手册。搞清楚了这些特殊性,由汇编语言转到C 语
言开发是很容易的事。当然,没有汇编语言编程基础的人同样可以用C 语言开发DSP 应用
系统。
有关嵌入式系统的 C 语言编程可参考《单片机与嵌入式系统应用》2001 年1~6 期《嵌入式
C 编程技术》,本文不作讨论。下面只针对以TMS320F240 芯片为处理器的嵌入式C 语言编
程进行阐述,希望能够指导读者进行具体操作。
2 TMS320F240 芯片的C 语言开发过程
简单地说,整个过程包括以下 5 个步骤:
①编辑 C 语言源程序;
②编译源程序(注意编译参数);
③链接目标文件(注意用 CMD 文件);
④在线仿真;
⑤固化程序。
2.1 源程序的编辑
可以用任何一个编辑器书写源程序,如 EDIT。NOTEPAD 等,最后以.C 为后缀存盘。源
代码可以写在一个C 文件中,也可写在多个C 文件中;有些预定义变量和函数原型声明可
以集中放在一个头文件中。
注意事项:不要忘记在 C 程序的前面用#in-clude “c240.h”将寄存器定义文件包括进
来。
2.2 源程序的编译
源程序编辑好后可以用 DSPCL 编译程序进行编译,生成OBJ 文件。
使用格式:DSPCL 源文件名参数
例如: DSPCL EX1.C-V2XX-GK-MN
常用参数的意义:
V2XX——表示C 编译器选择处理器2XX 系列;
GK——保留编译生成的汇编文件(.ASM 文件);
MN——进行正常优化。
其它参数请参考 DSP 编译器的手册。如果有多个源文件分别编译,每一个源文件经编译后
产生一个OBJ 文件和ASM 文件。
2.3 目标文件的链接
2.3.1 TI 公司的COFF 文件格式
TI 公司新的汇编器和编译器创建的目标文件采用COFF(Common Object File Format)的目
标文件格式。采用COFF 格式有利于模块化编程,为管理代码段和目标系统存储器提供更加
有力和灵活的方法。基于COFF 格式编写汇编程序或C 语言程序时,不必为程序代码和变
量指定目标地址;为程序编写和程序移植提供了极大的方便。
COFF 格式的基本思想是:鼓励程序员在用汇编语言或C 语言编程时运用代码块和数据块的
概念。这种块称为SECTION,是目标文件中的最小单位。
所有的块分为两大类:已初始化块和未初始化块。已初始化块包含程序代码和数据,未初始
化块是为未初始化的数据在存储器中的保留块。C 编译器对C 程序编译后产生已初始化块
和未初始化块,已初始化块如.text 块、.const 块、.cinit 块;未初始化块如.bss 块。
举个例子,当程序员用 C 语句float data[100];定义一个数组时,不需要指定这100 个数
组元素的具体位置,编译器会在数据区预留所需空间。到链接时链接器会具体定位。
2.3.2 链接器对块的处理
链接器对块的处理有两个功能:其一,将 COFF 目标文件中的块用来建立程序块和数据块,
并将这些块组合成可以被DSP 芯片执行的COFF 输出模块;其二,链接器为输出块指定存
储位置。
链接器提供两个命令实现上述功能:MEMORY 和SECTIONS。MEMORY 命令定义目标系
统的存储器,程序员可以定义每一块存储器并指定起始地址和长度;SECTIONS 命令用来定
义输入块的组合和输出块在存储器中的存放位置。若不用MEMORY 和SECTIONS 命令,
链接器采用缺省的分配算法。推荐使用这两个命令,但要注意这两个命令在CMD 文件(链
接器命令文件)中使用。
下面分析一个 TMS320F240 芯片的典型CMD 文件。(假设文件名 EX1.CMD。)
(1)CMD 文件的构成及其详细解释
BOOT.OBJ /*F240 的中断矢量表,参见后面的说明*/
EX1.OBJ /*源程序编译后对应的目标文件*/
/*若程序有多个目标文件,一块写在这里*/
-STACK 0X400 /*设定系统堆栈*/
-C /*ROM初始化*/
-O EX1.OUT /*输出的文件名*/
-M EX1.MAP /*输出映像文件名*/
-L RTS2XX.LIB /*涟接RTS2XX.LIB 库*/
MEMORY /*MEMORY 命令规定系统的存储器配置*/

PAGEO:ROM0:origin=0000h,length=003fh
/*FLASH ROM*/
PAGE0:ROM1:origin=0040h,length=0200h
/*FLASH ROM*/
PAGEO:ROM2:origin=0240h,length=3000h
/*FLASH ROM*/
PAGE1:RAM_B2:origin=0060h,length=0020h
/*内部RAMB2*/
PAGE1:RAM_B1:origin=0300h,length=0100h
/*内部RAM B1*/
PAGE1:RAM_B0:origin=0100h,length=0100h
/*内部RAM B0*/
PAGE1:RAM_EX:origin=0d000h,length=2800h
/*外部扩展RAM*/
}
SECTIONS /*SECTIONS 命令规定了程序中块的具体分配方法*/
{
.vectors:load=ROM0 /*规定矢量表的存放位置*/
.cinit:load=rom1 /*C 初始化表的存放位置*/
.text: load=ROM2 /*系统程序的存放位置*/
.bSS load=RAM_B0 /*未初始化数据的存放位置*/
.const load=RAM_B1 *已初始化数据的存放位置*/

(2)TMS320F240 链接时所需的中断矢量表文件
TMS320F240 的目标文件在链接时要用到中断矢量表。中断矢量表用汇编语言编写,和具体
的DSP 芯片有关。假设TMS320F240 的中断矢量表对应的汇编程序为BOOT.ASM,汇编
后的文件名为BOOT.OBJ。
下面是一个典型的矢量表文件。(假设程序名为BOOT.ASM。)
.port /*定义中断函数的名字*/
.globl_c_int0 /*中断0 对应的函数名*/
.globl_c_int1 /*中断1 对应的函数名,以下语句的意义相同*/
.globl_c_int2 /*可以将中断函数名看作中断入口地址*/
.globl_c_int3 /*矢量表的存放不需程序员干预*/
.globl_c_int4
.globl_c_int5
.globl_c_int6
.globl_c_int7
.globl_c_int8
·sect“.vectors”/*用.sect 命令自定义一个块,用于存放中断矢量表*/
RSVECT B _c_int0 /*中断0 发生后,程序的跳转目的地址*/
INT1 B _c_int1 /*中断1 发生后,则跳到c_int1()函数处*/
INT2 B _c_int2 /*意义同上,下同*/
INT3 B _c_int3
INT4 B _c_int4
INT5 B _c_int5
INT6 B _c_int6
用汇编器汇编该程序,命令形式:DSPABOOT.ASM-V2XX 生成BOOT.OBJ 文件供链
接器使用。这样,就可以按如下形式在C 源程序中编写中断函数:
voidc_inx() /*x 为1~8 中之一*/

中断程序的 C 语句系列;

注意事项:c_int0()是系统入口函数,用户不能编写。
经过上面对命令文件(CMD 文件)和中断矢量表的介绍,接下来可以链接命令文件来生成
所需要的OUT 文件供DSP 芯片执行或进行软仿真。
命令形式:DSPLNK CMD 文件名
例如:DSPLNK EX1.CMD
另一种情况是,不使用CMD 文件,使用缺省配置,简单介绍如下:
命令形式:DSPLNK OBJ 文件名参数
例如:DSPLNK EX1.OBJ BOOT.OBJ-O XX1.OUT-M XX1.MAP
以上三步可以用图1 描述。
2.4 程序的仿真
用 EMURST 仿真器复位命令
EMU2XXW EX1.OUT
载入COFF 格式的二进制代码仿真运行。有关调试器的使用略。
2.5 程序的固化
程序仿真运行正确后,需要固化到Flash ROM 中。TMS320F240 内部有16K 字的Flash ROM
可以用来固化程序,而不需要外扩EPROM(程序不大于16K 字的情况下)。
TI 公司提供有固化程序的软件,可以通过仿真器经JTAG 口将程序写入芯片内、目前发展
了一种新的固化技术,可以通过串口写入DSP 芯片,特别适合于现场调试。下面介绍通过
JTAG 口的固化方法。
首先用 EMURST 命令复位调试器,然后执行下面三个批处理文件。
第一步,执行 BCO.BAT 批处理文件,将FlashROM 清除(CLEAR),使全为0。
第二步,执行 BE0.BAT 批处理文件,将FlashROM 擦除(ERASE),使全为1。
(以上两步不需要修改软件包中自带的这两个 BAT 文件。)
第三步,执行BP16K.BAT 批处理文件,将自己的OUT 文件写入到DSP 内部的Flash ROM
中。执行这一步之前,要先修改BP16K,BAT,将待写入的OUT 文件替换成自己的OUT
文件。下面看一下这个批处理文件。假设软件包的安装目录为C:DSP,该目录下有一个子
目录SRC。
prg2xx-p240-m0x0006-w6srcc2xx_bpx.out 要写入的OUT 文件
如果要将 EX1.OUT 写入到DSP 的Flash 中,则执行下面的命令:
prg2xx-p240-m0x0006-w6srcc2xx_bpX.out c:dspEX1.out
经过以上步骤即完成了程序固化,可以将系统放到现场实验了。
注意:固化程序时,CPU 一定要工作在20MHz 的频率下。在SRC 子目录下有一个配置文
件C240_CFG.I,读者可以根据程序说明并结合自己系统的外部晶振频率将CPU 的工作
频率设为20MHz(写入时的频率)。
本文以 TMS320F240 的开发为例,介绍了怎样用C 语言开发DSP 系统的全过程。希望对读
者会有所启发和帮助。
参考文献
[1]Texas Instruments . TMS320F/C24XDSP Con-trollers Peripheral Library And Specific
Devive.1999
[2]Texas Instrument.TMS320C2X/C5X OptimizingC Compiler User’s Guide.1994
[3]张雄伟.DSP 芯片的原理与开发应用.北京:电子工业出版社, 2001
[4]章云.DSP 控制器及其应用.北京:机械工业出版社, 2001
(华东交通大学) 肖宛昂曾为民