文档库 最新最全的文档下载
当前位置:文档库 › 简单例子学习DSP编程和CCS使用

简单例子学习DSP编程和CCS使用

简单例子学习DSP编程和CCS使用
简单例子学习DSP编程和CCS使用

目录

实验一 CCS基本使用和DSP汇编编程基础

一、预习内容

复习汇编语言指令内容,汇编语言使用中的基本概念,CCS的基本概念。

二、实验目的

1.掌握一个DSP软件开发流程。

2.理解DSP的启动过程。

3.掌握汇编的书写规范,学会汇编指令的运用。

4.掌握编译器和连接器的使用,能够合理地分配存储空间。

5.学习CCS的各种调试技巧,如:CUP寄存器、数据和程序存储器的观察,断点的设

置,反汇编窗口的使用。

6.学习定点数的运算方法。

三、实验要求

1.用.set定义四个立即数。

2.在.bss段建立几个存储空间。

3.把立即数相加和相乘,结果放在.bss分配的存储空间。

4.合理分配各个段的存储地址,并使用CCS观察。

四、实验原理

1. 软件开发流程

从大的步骤来讲,一个软件要能够在DSP上面正常运行,需要用户完成以图1-1所示的流程。

第一步:在用户的工程里面,必须包含至少两个文件。一个是程序指令的源文件,这个文件可以是汇编编写的,也可以是C语言编写的。当然,这样的文件在一个工程当中可以用多个,而且,编程的语言可以不统一。工程当中必须包含的第二个文件是连接文件。这个文件的作用是把源文件中定义的各个段放到用户指定的DSP的程序存储区,从而可以在该区域按一定顺序执行。

第二步:两个文件编辑完成以后,可以调用编译、汇编和连接命令。编译功能把C源文件转化为汇编文件,期间的转化有一定的规则,这些规则是在书写C语言语句是应该注意的东西。汇编的功能是把编译形成的汇编文件或者是用户自己编辑的汇编文件转化为COFF格式文件。而最后通过连接命令,结合连接文件,把COFF文件连接成为可执行的COFF文件,即后缀名为.OUT的文件。

第三步:通过CCS的加载功能,把.OUT文件加载到DSP内部,其地址由连接文件规定。如果成功,可以在CCS中运行该文件,并相应进行调试。

图1-1 开发流程图

2.汇编源文件书写

在CCS中,新建一个文件,在这个文件中,如果使用汇编语言,那就命名时使用ASM的后缀名,如果使用C语言,那命名时就使用C的后缀名。同时应该把文件

加载到相应的工程当中去。

对于使用汇编的源文件,他有一定的语法规范。包括语法格式,标号的表示,立即数的表示,段的定义和宏定义。《TMS320C54x Assembly Language Tools User’s Guide》和《TMS320C54x DSP Reference Set---Volume 2: Mnemonic Instruction Set》是掌握汇编语言书写方法的两本手册。

3.启动方式

DSP有多种启动方式。但是,由于我们采用通过CCS从JTAG直接加载的方式,对于其他的加载过程可以不必考虑。但是,MP/MC方式,对于启动却有一定影响。

这里,有两个问题要注意。一是系统复位后,MP/MC寄存器的值究竟是多少。二是

MP、MC方式

(DSP是由MP/MC引脚来决定是访问内部程序存储器还是外部程序存储器,MP/MC=1访问外部程序存储器空间,MP/MC=0访问内部程序存储器

空间)

对应的DSP片内存储器分配关系如何。

通过硬件跳线,可以设置启动方式,通过连接文件,可以把程序代码放到指定的位置。

4.CCS的调试工具

在本次实验中,CCS的调试工具将要使用到一些基本的操作。主要包括如何观察CPU的各个寄存器,如何观察数据区和程序区的数据;单步执行的方法和断点执

行。

五、实验步骤

1.双击桌面的CCS5000图标,进入CCS。如果遇到问题,检查硬件线路是否连接正

确,电源是否打开,CCS SETUP是否合理。当然,也有可能是PC机内的ISA插卡

接触不好。

2.新建一个工程。执行Project菜单下的New,选择一个合适的位置,存放这个新

建的工程,最好是自己建立的目录,便于管理。工程的名称以字母开头,其他没

有要求,不需要加后缀名,如:sy1。

3.新建一个文件作为汇编源文件。执行File菜单下的New/Sourse File命令,接

着就可以在编辑框里,按照汇编语言的规范,编辑相应的指令代码。指令完成以

后,执行File/Save as命令,给汇编文件取一个合适的文件名,保存在同工程

相同的目录下。保存时,不需要加后缀名,但是,要选择文件的类型为Assembly

Source File(.asm)。

4.编辑汇编源文件,完成以后,需要把这个文件加入到相应的工程中去。在左边的

工程管理窗口里,鼠标移到工程文件名上面,单击右键,选择Add File。寻找到

刚才编辑的汇编源文件,选择确认。之后可以在工程管理器中观察到,Source

文件夹中,出现了刚才的汇编文件。

5.汇编文件的代码如下:

.global _c_int00

VAL1 .set 012h ; 18

VAL2 .set 034h ; 52

.bss temp,1 ; address of 0x080

.bss temp1,1 ; address of 0x081

.bss temp2,1 ; address of 0x082

;result register

.bss add_result,1 ; address of 0x088

.bss mpy_i_h,1 ; address of 0x08a

.bss mpy_i_l,1 ; address of 0x08b

.text

_c_int00:

ld #temp,DP ; load DP of temp1

st #VAL1,temp1

st #VAL2,temp2 ; init temp1 & temp2 ,18+52=70(0x46)

;-------- test ADD ---------------------------

ld temp1,a ; load temp1 -> a

add temp2,a ; a+temp2 -> a

stl a,add_result ; save a(low 16 bits) -> add_result

nop ; set breakpoint

st #VAL1,temp1

st #VAL2,temp2 ; init temp1 & temp2,18*52=936(0x3a8)

;-------- test MPY (integer) -----------------

rsbx FRCT ; prepare for integer mpy

ld temp1,T ; temp1 -> T

mpy temp2,a ; temp1*temp2 -> A (result is 32 bit)

sth a,mpy_i_h ; the high 16bit in mpy_i_h

stl a,mpy_i_l ; the low 16bit in mpy_i_l

nop ; set breakpoint

end:

b end

.end

使用.set定义立即数的值;使用.bss分配一定存储空间;在.text段编写指令,主要有:ld、st、add、mpy、stl、sth等等。

6.根据MP/MC的值,理解DSP存储器地址分配,编辑好连接文件。连接文件的编辑

方法同汇编源文件是一样的,也是建立、编辑、存盘、命名、添加入工程。不同在于文件的内容和文件的后缀名不同。连接文件的后缀名文.cmd,选择存盘类型的时候要注意。文件的内容如下:

MEMORY

{

PAGE 0:

IPROG: origin = 0x2000, len = 0x1000

PAGE 1:

IDATA: origin = 0x80, len = 0x1000

}

SECTIONS

{

.text: {} > IPROG PAGE 0

.bss: {} > IDATA PAGE 1

}

7.连接文件本来可以设置许多的参数,但是,这些参数也可以在Project/Options

中设置。在这个窗口中,可以对编译、汇编和连接的过程设置参数,各个参数的

意思查阅相关的手册。通常,使用默认参数就可以了。参数设置完成后,就可以

执行Debug/build命令。这个命令将依次执行编译、汇编、连接的三个过程,任

何一个过程中出现错误,都会在荧幕下方的窗口中显示出来。对于错误,要认真

阅读提示信息,从而知道错误出现的位置,并进行相应的改正。

8.如果Build的过程没有错,将会在工程所在目下出现一个后缀名为.out的文件,

这个文件文件名是在Project/Options中设置产生的。目标板采用MC方式,load

刚生成的.out文件,如果提示错误,检查跳线设置。

9.加载成功,光标出现在程序起始位置,呈黄色。这时,在需要设置断点的地方设

置断点。打开View里面的CPU Register,在新出现的窗口里,可以观察DSP CPU

寄存器的各个数据。同样,可以打开View/Memery,选择.data代码段所在的位

置,开始地址为0X80,位于data页,在新窗口中,可以观察该地址开始的数据

存储区的各个数据。

10.执行Debug/Run命令,程序开始运行。到断点处停止,加法的运算完成。这时,

可以再次观察CPU Register窗口,红色数字代表数据有变换,注意PC指针的

改变。数据窗口中,则注意用于存放运算结果的地址单元的值的变化。

11.继续执行Run命令,观察乘法的运算结果。

12.执行Debug中Reset Dsp和Restart命令,光标从新回到程序入口。执行

Debug/StepInto,单步运行程序,再次观察结果。

六、实验报告要求

给出数据空间的定义数据大小和存储位置,记录各个计算结果变化前后的内容,给出连接文件和汇编程序。

实验二 C 和ASSEMBLY 的混合编程

一、预习内容

C 语言的基本指令和编程方法,DSP 中断矢量表的内容和中断的执行过程的知识。 二、实验目的

1. 掌握C 语言编写源文件的方法。

2. 掌握中断矢量表的使用,理解BOOT LOADER 的过程。

3. 掌握定时器中断的使用方法。

4. 学习C 和ASSEMBLY 的混合编程。

5. 学习CCS 中图形观察动态数据的方式。

6. 学习用定时器实现数字振荡器的算法。 三、实验要求

1. 设计一个数字振荡器的实现算法,产生一个2KHZ 的正弦信号。

2. 定时器设置为25μS 产生一次中断(等效于采样速率40K )。

3. 使用汇编语言编辑一个中断矢量表,而主程序和中断服务程序使用C 语言编写。

4. 使用CCS 图形观察功能,观察输出正弦信号。 四、实验原理

1、数字振荡器原理

设传递函数为正弦序列T k ωsin , 其Z 变换为:

B

Az z Cz

T k --?

2sin ω

其中,其中T C B T A ωωsin ,1,cos 2=-==,即: 1

cos 2sin )sin(2+-?

T z z T

z T k ωωω

设初始条件为0,求出上式的反Z 变换得:

]1[]2[]1[][-+-+-=k Cx k By k Ay k y

这是一个二阶差分方程,其单位冲击相应为 T k ωsin 。利用单位冲击函数性质知道,仅当1=k 的时候,[]1-k x 为1,其他情况都为0。当输入为单位冲击时,输出就是我们需要的正弦序列。

]

2[]1[][]1[]2[]3[]

1[]1[]0[]1[]2[]0[]1[]0[]1[0]1[]2[]1[]0[-+-=+==++==+-+==-+-+-=n By n Ay n y By Ay y Ay Cx By Ay y C Cx By Ay y Cx By Ay y

可见,2>k 后,可以用递推的方法求][k y 。

根据上面的说明,设振荡器频率为2KHz ,采样频频率为40KHz ,定时器每隔25

μS 产生一次中断,得到一个][k y 。递归的差分方程系数为:

951056

.0]1[]2[587785.0]1[587785.036sin sin 1

618034.136cos 2cos 2=====?==-==?==Ay y C y T C B T A ωω

2.定时器使用

图2-1 定时器组成框图

定时器是一个片内递减计数定时器,可以产生周期的CPU 中断,每个CPU 时钟周期,定时器的计数值减1。当定时器计数器的值减到0时,产生一个定时器中断TINT 。上图是定时器的组成框图。5000系列DSP 中,5402和5420有两个定时器,其他型号DSP 只有一个。

用户使用三个寄存器来控制定时器,他们是TIM,PRD,TCR 。他们地址如下:

表2-1 定时器相关寄存器地址

定时器启动时,PRD 的值装入TIM 作为初始值,然后在TIM 中开始减计数。TCR 为16bit 的寄存器,包含了定时器的控制和状态比特。具体如下所示:

图2-2 TCR 各位说明

具体含义参考相应手册。由于EVM 板的CPU 时钟是100MHz ,

)

1()1(1

+?+?=

PRD TDDR t TINT c 频率,(其中tc 代表CPU 时钟),所以,我们

设置PRD 的值为2499h 。

定时器启动的过程主要有四个方面:1)设置PRD ;2)打开定时器中断;3)TSS=1,停止定时器;4)设置TCR ,包括TDDR ,同时TSS=0,TRB=1,启动定时器。

3.DSP 中断的使用

在C54X 中,用户可以通过中断屏蔽寄存器IMR 来决定开放或关闭一个中断请求。图2-3给出C5410的IMR 寄存器各个比特定义。

图2-3 IMR 的结构

其中,HPINT 表示HPI 接口中断,INT3-INT0位外部引脚中断,TXINT 和TRINT 位TDM 串口中断,BXINT0和BRINT0位BSP 串口发送和接收中断,TINT 位定时器0中断。在中断屏蔽寄存器中,比特为1便是允许CPU 响应对应的中断,0表示禁止。当然要响应中断,ST1寄存器中的INTM 还要为0,表示允许所有中断。

当DSP 相应中断时,PC 指针指向中断向量表中对应中断的地址,进入中断服务程序。中断向量表是C54X 存放中断服务程序的一段内存区域,大小为80h 。在中断向量表中,每个中断占4个字的空间,一般情况是将一条跳转或者延时跳转指令放在其中。中断向量表的位置首先在连接文件中加以确定,用户必须知道他在出现空间的起始地址,然后通过修改基地址来通知DSP ,其基地址由PMST 寄存器中的IPTR (15-7bits )决定。 4.C 和汇编语言混合编程

前面提过,在同一个工程里面,可以同时存在C 语言源文件和汇编语言源文件。C 和汇编是编辑源文件的两种基本语言,各自有自己的优点和缺点。对于C 语言,他的优点在于编程方便,缺点则是效率不高;汇编语言的编程效率很高,但是使用起来又很不方便。通常,混合使用,利用各自的优点,避免缺点。主程序我们采用C 语言,而关键代码,特别是一些占用CPU 较多的代码,我们使用汇编语言来编辑。

实验中,我们把中断矢量表作为一个汇编语言代码段,使用单独的一个汇编文件来生成。这个汇编文件有自己的段的定义,通过连接文件,我们把它放在一个有确定开始地址的程序段里,并记下这个地址。通常,中断矢量表有一个基本格式,我们在编辑他的时候,可以直接参照这个格式:他的长度为80h ,每4条字长的指令代表着一个中断产生时需要执行的动作。中断的顺序是系统统一约定的。通常,对于不使用的中断,我们用

b __ret

nop

nop

来处理。上面共三条指令,但是第一条是跳转指令,是双字指令,nop才是单字指令,所以,一共是4个字的指令长度,满足矢量表要求。对于需要使用的中断,我们在这里放置一个跳转指令,转移到中断服务程序。

在这个实验中,中断矢量表使用汇编语言,中断服务程序使用C语言。所以,中断服务程序位于另一个C语言文件中。如何实现汇编语言文件调用C语言程序,是我们要解决的一个问题。首先,我们在C语言文件中,建立一个中断服务子程序,他同一般的C语言子程序有一点不同,就是他需要在定义子程序时,加上一个interrupt 的说明。如:

void interrupt tint()

{

y0 = aa*y1 + bb*y2;

y2 = y1;

y1 = y0;

buf[con_buf]=y0;

con_buf++;

if(con_buf == 128)

con_buf=0; /* set breakpoint to show wave ! */

}

在汇编文件中,我们则使用一个跳转指令,把子程序的程序名作为一个标号,跳转到这个标号,就是调用这个子程序。但是,有几点要注意:一是汇编文件中的标号是子程序名称,他的前面还有加上一个下划线;二是汇编中,这个标号要声明为全局标号;三是如果存在参数传递,而且参数只有一个,那参数将放在A寄存器中,有多个参数时,过程比较复杂,在此不加阐述。

C语言文件中,如果要使用汇编指令,方法比较简单。把要执行的汇编语句加上引号,加上括号,再在整个前面加上asm,如:

asm( " nop" );

在C语言文件编译为汇编语言文件的时候,上面的汇编语句会不加变换地在该位置生成。如果是C语言文件要转移到汇编文件中的某个位置,方法如下。首先在汇编文件的目的指令处加上一个标号,并把标号用下划线开头,同时声明为全局标号。然后在C语言文件中,把这个标号作为一个子函数直接调用就可以了,这时,不需要加下划线。

https://www.wendangku.net/doc/0717180181.html,S图形观察窗口

图形观察窗口的作用是把数据区和程序区的某一块连续存储区域数据,用笛卡儿坐标系的纵坐标表示,横坐标则是地址的归一化值。我们在使这些数据变换的程序代码处设置断点,运行程序使用动画执行,那么,程序运行到断点处不会停止,只会更新显示区域。这样,我们就可以观察到一个动态的显示效果。这个工具位于

View/Graph/ Time/Frequency。使用图形观察工具之前需要对他的一些属性进行设

置,他的具体使用方法我们在实验步骤中讲解。

五、实验步骤

1.首先建立好一个工程,在这个工程中我们要建立三个相关文件。一个是C语言源文件,

一个是汇编语言源文件,还有一个是连接文件。建立的方法同以前的实验是一样的。

建立好这些文件后,需要把他们统统加入进建立的工程当中。文件在命名的时候,一定要注意文件名的后缀,也就是文件的类型要正确。

2.我们首先编辑中断矢量表。中断矢量表编辑的时候,他的长度要正确,每个中断的位

图2-4 VC5410中断顺序图

置要清楚,在定时器中断位置,加上一个跳转指令,指向C源程序的中断服务程序的地方。

中断矢量表的汇编语言文件代码如下

.mmregs

.ref _c_int00

.ref _tint

.global vector

.sect ".int_table"

;--------------------------------------------------------------------

; interrupte vector table !

;--------------------------------------------------------------------

vector:

rs b _c_int00

nop

nop

nmi b __ret

nop

nop

sint17 b __ret

nop

nop

sint18 b __ret

nop

nop

sint19 b __ret

nop

nop

sint20 b __ret

.word 0,0

sint21 b __ret

.word 0,0

sint22 .word 01000h

.word 0,0,0

sint23 .word 0ff80h

.word 0,0,0

sint24 .word 01000h

.word 0,0,0

sint25 .word 0ff80h

.word 0,0,0

sint26 .word 01000h

.word 0,0,0

sint27 .word 0ff80h

.word 0,0,0

sint28 .word 01000h .word 0,0,0 sint29 .word 0ff80h .word 0,0,0 sint30 .word 01000h .word 0,0,0 int0 b __ret

nop

nop

int1 b __ret

nop

nop

int2 b __ret

nop

nop

tint b _tint

nop

nop

brint0 b __ret

nop

nop

bxint0 b __ret

nop

nop

trint b __ret

nop

nop

dmac1 b __ret

nop

nop

int3 b __ret

nop

nop

hpint b __ret

nop

nop

q26 .word 0ff80h .word 0,0,0 q27 .word 01000h .word 0,0,0 dmac4 b __ret

nop

nop

dmac5 b __ret

nop nop

q30 .word 0ff80h

.word 0,0,0

q31 .word 01000h .word 0,0,0

;-------------------------------------------------------------------------- ; end of interrupte vector table !

;-------------------------------------------------------------------------

__ret rete

3. 对于C 语言文件,主要分为两个部分。

首先是对系统参数的设置,包括DP 指针,矢量表的起始地址,定时器的参数设置。这些设置过程,实际上就是对每个相关的寄存器的值的设定。为了正确的设置这些值,一是要理解每个寄存器的每个比特代表什么意思,而是要通过分析设计的要求,计算出这些值的大小。如果对寄存器的比特位的意思不是很理解,可以使用CCS 的Help 菜单。CCS 的Help 功能非常强大,几乎包含了编程时候需要的关于DSP 的一切信息。对于这些寄存器赋值的方法有两种,一种是使用C 语言中嵌套汇编语句的方法,因为这样可以利用各个寄存器的助记符,直接赋值给这些助记符;二是可以使用C 语言的指针功能。我们知道,DSP 的所谓每一个寄存器,实际上都是存储器映射寄存器,他们本质上都是DSP 位于数据区开始的一些存储空间。用指针的方法对这些地址赋值,实际上就是对寄存器赋值。同学们可以使用两种方法分别尝试,在数据空间观察两种方法结果的异同。

C 语言文件的第二部分是中断服务程序。服务程序的子程序名称要和中断矢量表中跳转指令的标号一样,只是没有了下划线。指令的内容就是根据前面的推导,使用递推

的方法,每次执行一次中断函数就产生一个][k y 数据。程序的原理前面已经讲解了。C 程序代码如下:

#include "stdio.h"

#define IMR 0 #define ST0 6 #define ST1 7 #define TIM 0x24 #define PRD 0x25 #define TCR 0x26 #define PMST 29

int con_buf=0;

float buf[128]; /* save out wave buffer ! */ float y0;

float y1=0.30901699;

float y2=0.58778525; /* y2= y1*A */ const float aa=1.90211304; const float bb=-1.;

main()

{

int *dest;

dest = (int *)IMR;

*dest = 0;

dest = (int *)PMST;

*dest = 0x1020; /* interrupt vector at 0x1000 */

dest = (int *)TCR;

*dest = 0x0010;

dest = (int *)PRD;

*dest = 2499;

dest = (int *)IMR;

*dest = 0x0008; /* enable timer0 TINT0 */

dest = (int *)TCR;

*dest = 0x0030;

*dest = 0x0000;

asm (" rsbx intm");

}

void interrupt tint()

{

y0 = aa*y1 + bb*y2;

y2 = y1;

y1 = y0;

buf[con_buf]=y0;

con_buf++;

if(con_buf == 128)

con_buf=0; /* set breakpoint to show wave ! */ }

4.对于连接文件的编辑,由于出现了C语言文件,系统默认的段增加了。如果是单独的汇编文件我们知道,只有.text和.data、.bss是三个默认段,但是,有了C文件,增加了其他一些段,主要有.cinit、.switch、.const、.systmem、.stack等。这些段是在由C编译为汇编的时候产生的,不同的段运用于存放不同的C的程序内容,具体参考首册。在编辑连接文件时,一个值的注意的问题时中断矢量表现在是一个单独定义的初始化段,要给他分配一个存储区域。这个区域的首地址要在C程序对PMST 赋值时使用。IPTR的值代表中断矢量位于程序存储区的第几页上,而且大小正好为一页,即80h。参考代码如下:

MEMORY

{

PAGE 0:

VEC: origin = 1000h, length = 0ffh

PROG: origin = 1100h, length = 1000h

PAGE 1:

DATA: origin = 080h, length = 0807h

}

SECTIONS

{

.text > PROG PAGE 0

.cinit > PROG PAGE 0

.switch > PROG PAGE 0

.int_table > VEC PAGE 0

.data > DATA PAGE 1

.bss > DATA PAGE 1

.const > DATA PAGE 1

.sysmem > DATA PAGE 1

.stack > DATA PAGE 1

}

5.在Progect/Options下设置好编译、汇编、连接的各个属性,就可以执行Progect下

Build命令。如果没有错误,把刚刚生成的.out文件load进工程里面。同时,在提示的地方设置好断点。之所以在这里设置断点,是因为这个判断语句是判断图形显示的区域的128个存储地址的内容是不是全部更新完成。如果完成,就执行中断,即更新图形显示区。

6.运行图形观察窗口。执行View/Graph/Time/Frequency,在出现的界面下,按如图设置。

图2-5 图形观察窗口属性设置

Start Address为需要观察的数据区的首地址,Page是观察数据是在数据存储区还是程序存储区,Acquisition Buffer Size表示一次从存储区取多大的数据量,Display Data

Size表示一次显示所取数据的数据量,他必须小于或者等于Acquisition Buffer Size,DSP Data Type表示数据类型。其他参数不必修改,而其含义用户可以通过帮助获取,也可以自己尝试修改来观察。

7.设置完成,执行Debug/Animate,程序开始运行。在断点处,不会停止,但是,图形窗

口可以更新。对于产生的数据,如果我们通过一个D/A,就可以通过示波器观察到一个满足要求的正弦波。

六、实验报告要求

给出数字振荡器设计过程,中断矢量表内容,源程序,图形观察的输出结果。

相关文档