目前很多嵌入式系统以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-w6src\c2xx_bpx.out 要写入的 OUT 文件 如果要将 EX1.OUT 写入到 DSP 的 Flash 中,则执行下面的命令: prg2xx-p240-m0x0006-w6src\c2xx_bpX.out c:\dsp\EX1.out
经过以上步骤即完成了程序固化,可以将系统放到现场实验了。 注意:固化程序时,CPU 一定要工作在 20MHz 的频率下。在 SRC 子目录下有一个配置文 件 C240_CFG.I,读者可以根据程序说明并结合自己系统的外部晶振频率将 CPU 的工作 频率设为 20MHz(写入时的频率)。
本文以 TMS320F240 的开发为例,介绍了怎样用 C 语言开发 DSP 系统的全过程。希望对读者会有所启发和帮助。
(Xiao,Wanang Ceng,Weimin (华东交通大学) 肖宛昂 曾为民)