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

2015-05-13  EDN电子技术设计
关注我们随时获精品 加关注


目前很多嵌入式系统以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 (华东交通大学) 肖宛昂 曾为民)

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值