在开始试验之前要确保你的硬件设备已更改开关是

当前位置: >>
dsp技术实验指导书2007版
目录实验一, Code Composer Studio 的使用 .........2 实验二, 常用指令实验 ......................14 实验三, 混合语言编程 ......................17 实验四, 基本运算 ..........................21 实验五, 小数运算 ..........................28 实验六, 信号发生实验 ......................32 实验七, A/D,D/A 实验一 ....................41 实验八, A/D,D/A 实验二 ....................47 实验九, FIR 滤波器实验 .....................56 实验十, FFT 实验 ...........................63 附录 CCS5000 的使用 ........................841 实验一, Code Composer Studio 的使用【实验目的】1, 熟悉 Code Composer Studio 开发环境 2, 熟悉 DSP 软件开发流程 3, 熟悉几种主要的调试方法 4, 熟悉在调试环境下观察指令执行结果的方法【实验内容与步骤】本实验相关程序参见安装光盘的 dsplab\lab1. 在实验之前先确定你的微机 中安装了 CCS5000(关于 CCS5000 的安装与设置详见附件) .1.在 CCS 中创建和测试一个工程项目在这个实验中,你将创建,测试一个简单的程序 hello,运行该程序可以在 CCS 的输出窗口显示&hello world! & 1. 1 创建一个新的工程 1) 在 d:\dsplab\下用你的学号新建一个子目录,并在其中再建立一个&hello& 的子目录. 2) 把 d:\dsplab\lab1\hello 目录下的文件(不包括 debug 目录下的文件) ,都 copy 到这个新建的文件夹中. 3) 把 PC 机的 USB 电缆接到 5410TDK 上,打开 5410TDK 电源开关. 4) 根据提示安装新硬件驱动和用户安装光盘. (若已安装则直接进入下一 步)2 5) 从菜单或者快捷方式中打开 CCS. 6) 选择 Project→New menu 7) 在弹出的窗口中,在最上端第一行填&myhello& ,下一行选择你新建的文 件夹&hello& ,文件名为&myhello& ,然后点击&完成& .这样 CCS 就新 建了一个名为&myhello.pjt&的工程文件.这个文件能保存你的工程的设 置并且包括了你的工程用到的各种文件. 1.2 向工程中添加文件 1) 选择 Project→Add Files to Project,将 hello.c 添加至工程. 2) 选择 Project→Add Files to Project,选择汇编源文件(*.asm)类型的文件,打 开&vector.asm& .这个文件包含了一些汇编指令,这些指令是用来设置 RESET 中断进入程序中的 C 的入口地址 c_int00. 3) 选择 Project→Add Files to Project,添加&hello.cmd&文件. 4) 选择 Project→Add Files to Project,进入 c:\ti\c5400\cgtools\lib,文件类型 选择目标文件和库文件(*.o*,*.lib) ,打开&rts.lib& .这个库对运行中的 DSP 板提供支持. 5) 在 Project View 中展开文件夹&myhello.pjt& . 6) 你可以看见在你的&myhello.pjt&中,include 下没有文件,你不必自己向 你的工程中添加 Include 文件, 因为 CCS 在编译过程中寻找附件文件的时 候可以自动地找到它们. 在编译完你的工程以后, 你就可以在你的 Project View 中找到 Include 文件. 如果你要从你的工程中删除一个文件,在 Project View 中右击文件,在 弹出的菜单中选择&Remove from Project& . 在编译一个程序时,CCS 按照下列顺序在工程文件中查找文件:3 ?包含源文件的文件夹 ?在 Include 搜索路径中列出的文件夹 ?在 C54X_C_DIR(编译器)和 C54X_A_DIR(汇编器)环境变量的定 义中列出的文件夹. 1.3 查看源代码 双击 hello.c,观察源代码. 1.4 编译和运行程序 按以下步骤编译运行程序: 1) 选择 Project→Rebuild All, CCS 将再一次编译, 汇编, 连接工程中的文件. 这个工程的消息将在窗口底部的一个窗口显示. 2) 选择 File→Load Program,在\hello\myhello\debug 目录下打开刚刚生成的 myhello.out 文件. CCS 把程序载入到 DSP 板上, 并且打开一个反汇编 (Dis -Assembly)窗口显示组成程序的反汇编过的指令. 3) 在反汇编窗口中点击一个汇编指令. (在指令上点击,而不是在指令对应 的地址)按下 F1,CCS 可以提供关于这条指令的帮助.这是一个得到一 条你不熟悉的汇编指令的帮助信息的好办法. 4) 选择 Debug→Run 运行完这个程序后,你可以在 Stdout 窗口中看到一条&hello world! &的信息. 1.5 改变程序选项和修改语法错误 在上面的实验中,hello.c 中的预处理程序命令(在#ifdef 和#endif 之间的程 序)并没有运行,这是因为 FILEIO 还没有定义.在本节中,将用 CCS 设置一个 预处理选项,同时,你也将发现并改正一个语法错误. 1) 选择 Project→Build Options4 2) 弹 出 的 小 窗 口 中 Compiler 的 标 签 下 , 在 左 边 的 & category & 中 点 &Preprocessor& ,在&Preprocessor&列表中选择 Define Symbols,在后面 的文本框中输入&FILEIO& .注意到现在在窗口上部编译的命令中包括了 -d 的选项.在你重新编译以后,程序中在#ifdef FILEIO 声明后的代码 将被包含进去. 3) 按下&确定&保存你的设置. 4) 重新编译你的工程,对工程选项作出任何修改都需要重新编译工程. 5) 这时会出现一个消息说程序里包含编译错误.在编译消息框内你可以看 到一个语法错误的信息. 6) 双击描述语法错误的位置的红色文本,注意到 hello.c 的源文件打开了, 你的指针将在: fileStr[I] = 0; 7) 改正这个语法错误,即在 scanStr[I]语句后加一个分号&;,注意在编辑 & 窗口标题栏里,在改动过的文件名前出现了一个*号.文件保存过后,星 号消失. 8) 保存改动后的文件. 9) 重新编译所有的文件. 1.6 使用断点和观察窗 当你开发并测试一个程序的时候,你经常需要查看在程序执行过程中一个变 量的值的变化.本节中,你将使用断点和观察窗来查看这些值,你也可以在到达 断点以后使用单步调试命令. 1) 选择 File→Reload Program. 2) 双击打开 hello.c 程序5 3) 把你的光标指在 fprintf(fptr,&%s&,scanStr); 4) 单击 Toggle Breakpoint 工具栏按钮,或者按下 F9,5) 选择 View→Watch Window,将出现一个 CCS 的对话框,在运行期间, 这个区域将显示被观察的变量的值. 6) 在观察窗上点击 Watch 1,再点击 Name 栏下的小图标. 7) 在小图标右边的空白栏内键入&*scanStr& ,任点击一下大空白处 8) 注意到*scanStr 出现在观察窗中,但是没有定义,这是因为程序现在没有 运行. 9) 选择 Debug→Run 或者按下 F5. 10) 在出现的&standard Input Dialog Box&对话框中,键入&goodbye& ,点 击&OK& ,现在你会发现在观察窗的&value&下出现了*scanStr 的值. 在你键入一个输入字符串后,程序运行并且在断点处停下,下一句将被 执行的命令被黄颜色高亮度显示. 11)单击 或者 F10 来逐步跳过对 fprintf()的引用.12)重新运行程序. 13)清除断点,右击断点所在行,选择&Toggle breakpoint& .2.测试算法和数据在这个实验中,你将建立一个程序来实现一个基本的信号处理过程,并学习 使用探点,画图,动画功能. 2.1 打开并检查工程 在 d:\dsplab\ 你 的 学 号 子 目 录 下 , 再 新 建 一 个 volume1 的 文 件 夹 , 把6 d:\dsplab\lab1\volume1 下的所有文件 copy 到新建的文件夹下 ,打开 CCS,点击 project→open→打开 volume.pjt. 这时,CCS 会出现一个对话框说库文件没有找到.这是因为原来的工程被移 位了.为了找到这个文件,单击 Browse,找到 c:\ti\c5400\cgtools\lib,选择 rts.lib. 在 Project View 中查看所有的文件. 这些文件有: ?volume.c 这是主程序的源代码. ?volume.h 这是 volume.c 包含的头文件,用来定义不同的变量和结构 ?load.asm 这个文件包含了模拟对 DSP 形成运算负载的程序. ? vectors.asm 这是前面曾用到的一个文件, DSP 的中断向量表中定义了系 在 统的入口. ?volume.cmd 这个配置命令文件把段映射到内存相应的位置. ?rts.lib 这个库向 DSP 板运行提供支持. 2.2 查看源代码 打开 volume.c 文件,注意以下几点: 1) 在显示一条消息以后,主函数进入了一个无限循环.在这个循环中,调 用了 dataIO 和 processing 两个函数. 2) processing 函数利用 gain 把每一个值复制到输入缓冲区内, 把结果放到输 出缓冲区.它也调用了 load.asm 程序.这个程序消耗了指令循环次数的 值,而这个值是建立在传递给载入程序的 processingLoad 值的基础上的. 3) dataIO 函数除了返回并没有做任何事情.你可以用 C 语言来实现 I/O,但 这里你将使用探点(Probe Point)从主机上的一个文件向 inp_buffer 读取 数据.7 2.3 为文件的 I/O 添加探点 在本节中,你将添加一个探点, 这个探点将从你的电脑上的文件中读取数据, 探点是在算法开发中十分有用的工具.你可以通过以下几条途径使用他们: ?通过一定的算法,把主机电脑文件中的输入数据向目标板上的缓冲区传输 ?把目标板上缓冲区内的输出数据向主机传输 ?更新窗口,比如一个图表等 探点和断点有些相似,因为他们都可以中止目标板来完成它们的行为. 但是, 他们也有所不同,具体如下: ?探点即刻停止目标板,完成一个单独的行为,然后继续目标板的执行过程. ?断点停止 CPU 的工作,直到它的执行过程重新被手动继续,并且使所有开 着的窗口被更新. ?探点允许输入输出文件自动地被执行,而断点不行. 本节显示了如何使用探点向目标板传送数据.并且使用了一个断点在运行到 探点的时候来更新所有打开的窗口.窗口中包括输入和输出的窗口. 1) 选择 Project→Rebuild All 或者单击 2) 选择 File→Load Program,打开 volume.out 3) 在 Project View 中打开 volume.c,把光标移向主函数中的 dataIO(); 4) 单击 点. 5) 选择 File→FileI/O.出现了文件 I/O 对话框如图 3 所示,这样你就可以选 择输入输出的文件. ,在这一行前面出现一个蓝色菱形标志,表示在这一行插入探8 图 3 File I/O 对话框 6) 选择 AddFile,选择 volume1 目录下的 sine.dat 文件,添加到 FileI/O 的对 话框中.这时出现了一个 sine.dat 的窗口,当你运行程序时,你可以用这 个窗口开始,停止,回放数据文件. 7) 在 File I/O 对话框中, Address 改为 把 &inp_buffer& 把 Length 改为 , &100& , 在 WrapAround 选项前打钩 ?Address 栏指出了从文件中的数据将置于何处 ?Length 栏指出了每一次程序运行到探点时从数据文件中读取了多少个 采样. ?Wrap Around 选项使 CCS 循环从文件中读取数据.这就使数据文件被 视为连续的数据流,尽管每次到达探点的时候,它只包含 1000 个值并且 只有 100 值被读取. 8) 单击 Add Probe Point,出现 Break/Probe/Profile 对话框.9 9) 选中在上面步骤中建立的探点,在 Connect To Field 窗口中,选中√,再 在 Connect 列表中选取 sine.dat 文件. 10)单击 Replace,这时候探点和 sine.dat 文件就建立了连接. 11)单击确认完成设置. 2. 4 显示图表 CCS 提供了许多种用图表显示数据的方法,在这个例子中,你将看到一个根 据时间画出的信号. 1) 选择 View→Graph→Time/Frequency. 2) 在图表属性对话框里,改变如下设置:见步骤 6 下的图. 3) 单击确认,一个 Input buffer 图表窗口弹出. 4) 在图表上单击右键,在弹出的菜单选项中选择&Clear Display& . 5) 选择 View→Graph→Time/Frequency,把 GraphTitle 改为 Output Buffer, 把 Start Address 改为 out_buffer,其它设置不变. 6) 单击确认,在图表单击右键,同样清除显示.2. 5 Animating 程序和图表10 在本节中,你将设置一个断点来更新所有的窗口,并且使用 Animate 功能在 到达断点之后能够自动的继续程序的执行. 1) 在 volume.c 窗口中,把光标置于调用 dataIO()的那一行 2) 单击 3) 单击 设置断点 运行程序.Animate 和 Run 命令有些类型,它使目标板一直运行直到遇到一个断点, 这时目标板被停止, 所有的窗口被更新. 但和 run 命令不同的是, Animate 继续运行程序直到下一个断点.这样的过程直到手动停止目标板的工作. 4) 注意到每个图表都有一个正弦信号,不同的是两者反相了.每次遇到一 个探点,CCS 从 dine.dat 文件中得到 100 个值,并把他们写入 inp_buffer 的地址中.信号反向是因为输入缓冲区包含的从 sine.dat 中读到的数据, 而输出缓冲区包含的 processing 函数产生的数据.11 2.6 更改 Gain 的值 从 volume.c 中,我们可以看出,processing 函数利用 gain 和输入缓冲区的每 一个值相乘,并且把结果置于输出缓冲区.在循环中,执行下列语句: *output++ = *input++*gain gain 初始被置为 MINGAIN,这在 volume.h 中有定义.为了修改输出值,你 需要修改 gain 的值,一种方法是使用一个观察变量(watch window) . 1) 选择 View→Watch Window 2) 在 Watch Window 内点击 Watch 1,点击 Name 下的小图标 3) 在小图标右边空白栏中键入&gain& ,任点击一下空白处 4) 如果你已经停止了目标板,单击 重新运行程序5) 在 Watch Window 区域内,点击 Value 下的值 1 6) 在空白栏内把 1 改为 10,再任点击一下大空白处,即把 gain 的值改成 10 注意以下图表的变化.12 在完成上面两个实验内容后,你还可以结合 CCS 的帮助文件,自行实验和 了解 CCS 的一些其它功能.13 实验二, 常用指令实验【实验目的】1, 了解常用的寻址方式 2, 了解常用的指令 3, 熟悉汇编程序的编写 4, 熟悉 CMD 文件的编写【实验内容及步骤】本实验相关程序参见安装光盘的 dsplab\lab2. 1.在 CCS 下新建立一个项目 lab2. (建立项目和添加文件方法参见实验一) 2.新建立一个汇编文件 lab2.asm,并将其添加至项目中. ; ; ; ; lab2.asm this program looks at several instructions and is used to learn about the operation of the debugger.initialized data to follow offset: .int 4 data: .int 0 .define label for ext. ref. .defines start of program start: RSBX CPL ;CPL=0 LD #0,A ;load acc A. LD #offset,DP ;load data page register NOP NOP loop: ADD offset,A ;add (offset) to accumulator contents ROL A ;left shift acc. contents STL A,*(data) ;store acc low word14 Bbranch to loop 输入完后,点 file→save→存在 lab2 文件夹下(以下同) . 3.新建立一个中断向量表文件 vectors.asm,并将其添加至项目中. ; vectors. this program branches to t location in program memory. .ref .sect b .end start &.vectors& startentry4.新建立一个 CMD 文件 lab2.cmd,并将其添加至项目中.lab2.obj vectors.obj -o Debug\lab2.out -m Debug\lab2.map MEMORY /* these settings correspond to OVLY=1 */ /* MP/MC=0, and DROM=1 and PMST = 7fac*/ { PAGE 0: /* Program */ P_DARAM47: origin = 0x1000, VECT: origin = 0xff80, PAGE 1: /* Data */ USERREGS: origin = 0x60, D_DARAM03: origin = 0x80, D_DARAM47: origin = 0x8000, } SECTIONS { .data: .text: .vectors: }len = 0x2000 len = 0x80len = 0x1c len = 0x7f00 len = 0x8000&& D_DARAM03 & P_DARAM47 VECTPAGE 1 PAGE 0 PAGE 05.编译项目文件,并观察生成的 lab2.map(在 lab2 下的 Debug 中)文件.15 6.加载生成的可执行文件(file→load programe→lab2.out) ,观察此时的 PC 指针值.选择&View→Registers→CPU Registers& . 7.请思考如何将 PC 指针值移动至 lab2.asm 中标号为 start 的程序处. 请提供两 种方法,双击 PC 的值,在弹出的对话框中输入 Value 的值&start&,确认. 8. 开始单步运行程序, &F8& 按 ,观察并记录程序的运行情况. &View→Memory& 选择 在弹出的对话框中,Address 栏中输入 data,在 Format 下拉框中选择 16-Bit Signed Int. 9.按照 VC5410 DSK 的实际内存情况,重新生成一个 CMD 文件,重复前面 1-8 的步骤. 10.仿照前面 1-8 的步骤,用汇编程序实现以下一段操作.x[5,10]={ {0,1,2,3,4,5,6,7,8,9}, {10,11,12,13,14,15,16,17,18,19}, {20,21,22,23,24,25,26,27,28,29}, {30,31,32,33,34,35,36,37,38,39}, {40,41,42,43,44,45,46,47,48,49} }; y[5,10]={ {50,51,52,53,54,55,56,57,58,59}, {60,61,62,63,64,65,66,67,68,69}, {70,71,72,73,74,75,76,77,78,79}, {80,81,82,83,84,85,86,87,88,89}, {90,91,92,93,94,95,96,97,98,99} }; z[5]= {0,0,0,0,0}; for (i=0; i&5; i++) { temp=0; for (j=0; j&10; j++) temp+=x[i,j]* y[4-i,9-j]; if (temp&18500) z[i]=1; }16 实验三, 混合语言编程【实验目的】1,熟悉混合语言编程【实验注意事项】1. 函数调用在 C 语言中必须对被调用的函数用 extern 声明为外部引用,如 C 程序中的语 句 extern BOOL mySub(int a,int b,int c);而在汇编语言程序中要使用标号_mySub:注意 mySub 之前必须有下划线&_& .2. 参数传递C/C++编译器对函数调用有一组严格的原则.参数传递时,第一个参数传递到 累加器 A,其它的参数以相反的顺序压入堆栈,即如果有 N 个参数,先压入第 N 个参数,再压第 N-1 个参数,最后压入第 2 个参数.如例中将参数 i 存入累加器 A,依次将参数 k,j 压入堆栈.而在汇编程序中通过将 SP 加 1,使 SP 指向第 2 个 参数, 并依次取出参数, 然后再使 SP 指向函数的返回地址, frame 如 #-3.函数的返回值存放在累加器 A 的低 16 位. #1 和 frame3. 使用全局变量在 C 语言中定义全局变量, 必须用 extern 说明为全局变量, extern 如语句,而在汇编语言中访问此变量必须使用变量名_k,且用.global 声明为全局 变量,如.bss _k,1 和.global _k.注意 k 前的下划线&_&必不可少.对于全局变量可用.bss 伪指令将其放在.bss 段中,如本实验;或用.usect 伪指令放在17 自己命名的段中.【实验内容及步骤】本实验相关程序参见安装光盘的 dsplab\lab3. 本实验要求使用 C 语言调用一个汇编函数,此汇编函数完成一个减法运算, 存储运算结果,并返回一个布尔量,根据这个布尔量,C 语言判断被减数与减数 的大小.被减数和减数均由 C 语言输入,结果由 C 语言输出. 1. 在 CCS 下新建立一个项目 lab3. (方法见实验一) 2. 新建立一个 C 语言程序 lab.c,并将其添加至项目中.【程序】C 语言程序#include &stdio.h& typedef enum { FALSE=0, TRUE=1 } BOOL; extern BOOL mySub(int a,int b,int c);void main(void) { BOOL fGreater=FALSE; k=0; printf(&Please input i,j\n&); scanf(&%d,%d&,&i,&j); fGreater=mySub(i,j,k); /*C calls assembly function*/18 printf(&i=%d j=%d\n&,i,j); printf(&the result is:\n k=i-j= %d.\n&,k); if(fGreater) printf(&i is greater than j\n&); else printf(&i is little than j\n&); } 3. 新建立一个汇编语言程序 mysub.asm,并将其添加至项目中.汇编语言程序 .mmregs .def _mySub .bss x,1 .bss _k,1 .global _k .text _mySub: ssbx frame stm popd popd frame sub stl bc ld b ld nop ret sxm #1 #x,ar4 *ar4+ *ar4#-3 *ar4+,a a,*ar4 litt,alt #1,a over #0,计算时用有符号数 ;SP 指向传递的参数 ;AR4 指向取参数取参数SP 指向返回地址 ;第一个参数 i 在 a 里面;k 前的_不可少;a 存放返回值litt: over:4.新建立一个 CMD 文件 lab3.cmd,并将其添加至项目中.CMD 文件19 -c -l rts.lib MEMORY { PAGE 0: INT_PM_DRAM: origin = 0080h, length = 1380h EXT_PM_RAM: origin = 1400h, length = 0ec00h PAGE 1: INT_DM_SCRATCH_PAD_DRAM: origin = 060h, length = 20h INT_DM_1: origin = 0080h, length = 380h INT_DM_2: origin = 0400h, length = 0cc00h} SECTIONS { .text : .cinit : .stack : .bss : .const :{} & EXT_PM_RAM {} & EXT_PM_RAM {} & INT_DM_2 {} & INT_DM_2 {} & INT_DM_2PAGE 0 PAGE 0 PAGE 1 PAGE 1 PAGE 1}5. 点击 编译项目文件.6. 加载生成的可执行文件(File→Load programe→lab3.out) . 7. 选择 Debug→Run,运行完这个程序后,在弹出的对话框里输入相应的 i,j 的 值,点击 OK,你可以在 Stdout 窗口中看到结果.20 实验四, 基本运算【实验目的】1, 了解 DSP54xx 的数据格式 2, 熟悉 P 的浮点运算机制 3, 熟悉 P 的汇编语言实现除法算 4, 熟悉 P 的 C 语言编程机制 5, 熟悉混合编程【实验内容】DSP 中数据的格式有多种,包括 16 位有符号数,16 位无符号数,32 位有符 号数,32 位无符号数,32 位浮点数,32 位 IEEE 浮点数等各种形式.由于数据所 设置的小数点位置的不同,即使同一类型的数据也会出现不同的数值.这一点是 程序设计中尤其需要注意的地方.如果采用 C 语言编写程序,CCS 会自动去管理 各种数据;但如果采用汇编语言编写程序, 对于各种数据的管理将显得尤其麻烦, 然而在许多 DSP 的应用场合,采用纯汇编编写程序是必须的,所以对于 DSP 中 数据各种格式的情况最好还是需要了解. DSP 中定点数据表示方法: DSP 表示整数时,有两种格式,有符号数和无符号数,如果作为有符号数表 示时,其最高位表示符号,最高位为 0 表示其为正数,为 1 表示其为负数,最低 位表示 1,次低位表示 2 的 1 次方,次高位表示 2 的 14 次方.无符号数表示时, 最高位仍然作为数值位计算. 所以, 有符号数所能够表示的最大的正数为 07FFFH, 等于 32767(10 进制) ,而 0FFFFH 表示最大的负数-1;无符号数不能表示负数, 其所能够表示的最大的数为 0FFFFH,等于十进制数的 65535.21 DSP 表示小数时,其符号和上面整数的表示一样,但是必须注意如何安排小 数点的位置,原则上小数点的位置根据程序员的爱好可以安排在任何位置,为了 便于数据处理一般安排在最高位后(以下我们仅以小数点在最高位的形式进行讨 论) ,则最高位表示符号位,这样次高位表示 0.5,然后是 0.25,依次减少一半. 例如: 4000H 表示小数 0.5, 1000H 表示小数 0.125, 0001H 表示 16 位定点 DSP 而 表示的最小的小数(有符号)0.125.在下一个实验中将具体介绍小 数的表示法和运算. DSP 中表示 32 位的数据时,使用两个内部存储器保存一个数据.这样,每个 数据需要相邻地址的两个空间, 其中较高地址的空间保存数据的低 16 位, 较高地 址永远为无符号数; 较低地址空间保存数据的高 15 位和符号位 (如果是无符号数, 则保存高 16 位) .例如需要保存 16 进制 0FFFFFH 这个数据,10 进制中等于 1048575,DSP 中使用连续的地址,低地址空间为 0FH(有符号数) ,高地址空间 为 0FFFFH(无符号数) .DSP 中还可以实现更多位数的数据,采用三个甚至四个 地址空间去保存一个数据,这样表示数据的数值将更大(整数)或者更精确(小 数) . 本实验包括两个例子,例 1 用 C 语言完成浮点运算,例 2 用汇编语言完成整 数的除法运算,下面分别介绍. (本实验相关程序参见安装光盘的 dsplab\lab4. )例1浮点数运算本例用 C 语言完成浮点数的乘法和除法运算, 并对第一个数的绝对值进行开方运算.程序运行后要求输入两个浮点数,而运算结果在 Stdout 窗口输出,便于 观察和验证.用汇编语言也可以实现浮点数的运算,但比较复杂,这里不作具体 介绍,对于如何用汇编语言表示浮点数及完成浮点数的运算,读者在对汇编语言 编程熟悉到一定程度时,参阅相关资料,可自己完成.注意:要将 C 语言运行支22 持库函数\ti\c5400\cgtools\lib\rts.lib 加入到 project 中.【程序】#include &math.h& #include &stdio.h& #pragma DATA_SECTION(data3,&data_buf1&) float data3; #pragma DATA_SECTION(data4,&data_buf1&) float data4; #pragma DATA_SECTION(data5,&data_buf1&) float data5; void main(void) { float data1,data2; printf(&Please input data1 & data2: data1,data2\n&); scanf(&%f,%f&,&data1,&data2); data3=data1*data2; data4=data1/data2; if(data1&0) data1=-data1; data5=sqrt(data1); printf(&data1=%f\n&,data1); printf(&data2=%f\n&,data2); printf(&data3=data1*data2=%f\n&,data3); printf(&data4=data1/data2=%f\n&,data4); printf(&data5=sqrt(|data1|)=%f\n&,data5); } CMD 文件如下: MEMORY { PAGE 0: PARAM: org=1000h PAGE 1: IDATA: } SECTIONS{23len=0efd0h len = 0x1380origin = 0x80, .text data_buf1 }: &PARAMPAGE 0 PAGE 1:& IDATA例2整数除法本例使用汇编语言实现有符号整数的除法运算.C54x 中没有专门的硬件除法器,所以没有专门的除法指令完成 16 位数的除法运算, 但可以利用减法完成除 法运算.用 RPT 指令和 SUBC 指令相结合可以完成除法运算,本实验为了使算法 清晰明了,没使用这两条指令,读者可以自己使用这两条指令完成除法运算.通 过本例读者可以进一步熟悉常用的汇编指令,掌握一些编程的技巧,进一步熟悉 混合编程. 程序运行后要求输入被除数和除数,商和余数均在 Stdout 窗口输出,便于观 察和验证,商和除数均是有符号数,余数同被除数的符号一致,如果被除数的绝 对值小于除数的绝对值,商为 0,余数等于被除数.注意:除数不能为 0.【程序】C 语言程序如下: #include &stdio.h& extern int mydivide(int,int); void main(void) { int divident,divisor, printf(&Please input divident and divisor: divident,divisor\n&); scanf(&%d,%d&,&divident,&divisor); remainder=mydivide(divident,divisor); printf(&divident=%d\n&,divident); printf(&divisor=%d\n&,divisor);24 printf(&quotient=%d\n&,quotient); printf(&remainder=%d\n&,remainder); } 汇编语言程序如下,完成除法运算.bss divisor,1 .bss _quotient,1 .global _quotient .def _mydivide .mmregs _mydivide: stm #divisor,ar2 frame #1 popd *ar2 frame #-2 stm stm stm ld #80H,ar0 #0h,ar4 #ar4,ar3 *ar2+,b;取参数 ;SP 指向返回地址;如 ar4 为 1 表示两数异号 ;使 ar3 指向 ar4rsbx tc bc jump0,agt ssbx tc abs a xorm #1h,*ar3 jump0: bc jump1,bgt xorm #1h,*ar3 abs b jump1: stl b,*ar0 ld #-1,b sub *ar0,a bc jump5,alt jump2: add #1,b;判断被除数及除数正负 ;如 tc 为 1 代表被除数负;*ar4!=0,b&0;|divident| & |divisor|25 sub *ar0,a bc jump2,ageq add #1,b add *ar0,a bc jump4,ntc;a 存余数 ;tc=0,a&0,余数为正 ;被除数小于零,余为负banz jump3,*ar4 neg a b jump6 jump3: neg a neg b b jump6 jump4: xorm #1h,*ar3 nop nop banz jump6,*ar4 neg b b jump6 jump5: add *ar0,a bc posit,ntc neg a posit: ld #0h,b jump6: stl b,*ar2 nop ret CMD 文件如下: MEMORY { PAGE 0: PARAM: org=1000h PAGE 1:26;divident&0 & divisor&0;两个 nop 必不可少 ;divisor&0 & divident&0 ;divisor&0 & divident&0 ;|divident| & |divisor|;divident & 0;store the quotientlen=0ef80h USERREGS: BIOSREGS: IDATA: EDATA: EDATA1: } SECTIONS{ .text : &origin origin origin origin origin= = = = =0x60 0x7c 0x100 0x0len = 0x1c len = 0x4 len = 0x1300 len = 0x8000 len = 0x4c00PARAMPAGE 0.bss }:&IDATAPAGE127 实验五, 小数运算【实验目的】1, 掌握定点 DSP 中定点数的 Q 表示法 2, 掌握定点数据格式的转换3, 熟悉定点 DSP 防止数据溢出的方法【实验内容】在定点 DSP 中,数据主要有两种表示法:整数表示法和小数表示法.在数字 和各种信号处理算法中主要用小数表示法,也就是定点数的 Q 表示法,表示成 Qm.n 的形式.整数部分用 m 位表示,小数部分用 n 位表示,最高位为符号位. 下面以 16 位定点数为例说明 Qm.n 的三种常用表示格式:Q15.0,Q3.12,Q0.15. 定点数的位数为 15~0 位,15 位为最高位,也就是符号位 S.Q15.0 也就是 整数表示法,Q3.12 格式中,15 位为符号位 S,14,13,12 位表示数的整数部分 二进制补码,其余位表示数的小数部分二进制补码, 此格式表示数的范围 (-8,8) , Q0.15 格式中,15 位为符号位 S,其余都是二进制补码的 15 位小数位,表示数的 范围(-1,1) ,这是应用最广泛的格式,如下表所示:位数 权值15 -114 2-113 2-212 2-311 2-4… …0 2-15例如 E000H 代表 1*(-1)+1*(0.5)+1*(0.25)= -0.25 给定一个十进制的小数,在汇编语言中要表示成二进制数.对于 Qm.n 格式, 先使小数乘以 2n ,再将所得结果表示乘 2 的补码格式即可.在汇编语言中,要28 定 义 一 个 十 进 制 小 数 的 系 数 , 可 以 写 成 ( 以 Q0.15 为 例 ) : x -375*,不能写成-0.375*32768..word当两个小数相乘时,会出现两个符号位,即结果的前两位均为符号位,产生 错误.解决的办法是在程序中设定 ST1 中的 FRCT 位为 1,这样,乘法器将结果 送往累加器时自动将结果左移 1 位.两个 16 位小数相乘,结果为 31 位,应取高 16 位作为乘积结果,舍去的低位权值较小,对结果影响不大.这也就是为什么使 用小数运算的原因. 程序中实现下式运算 y=∑hixi 程序中已固定 6 个小数,可通过改变另外两个小数的值改变结果的大小,注 意输入必须是纯小数程序才能正确运行.对于上式防止溢出的最简单的方法是使 ∑|hi|&1 该程序用 C 语言和汇编语言混合编程,用 C 程序实现数据的输入和输出, 使结果容易观察和验证,用汇编程序实现算法. (本实验相关程序参见安装光盘 的 dsplab\lab5. )【程序】C 语言程序如下: #include &stdio.h& extern void corre(); void main(void) { extern int x3,x4; float x33,x44; float z=1; printf(&Please input decimal fraction x3,x4\n&); input: scanf(&%f,%f&,&x33,&x44);29 if(x33&=1||x33&=-1||x44&=1||x44&=-1) { printf(&Input is error\n&); } x3=(int)(x33*32768); x4=(int)(x44*32768); x33=(float)x3/32768; x44=(float)x4/32768; corre(); /*调汇编函数*/printf(&the result is:\n&); printf(&16 进制 y= 0%x\n&,y); z=y; z=z/32768; /*将 y 转化为小数*/ printf(&y=0.2*0.8+(-0.1)*0.5+\n&); printf(& +(-0.4)*(%f)+0.2*(%f)\n&,x33,x44); printf(& =%f\n&,z); } 汇编语言程序如下: .mmregs .bss h,4 .bss x,2 .bss _x3,1 .global _x3 .bss _x4,1 .global _x4 .bss _y,1 .global _y .def _corre .data table: .word 2**32768/10 .word -4**32768/10 .word 8**32768/10 .text _corre:使用小数运算30 stm rpt mvpd stm stm rptz mac rsbx sth ret CMD 文件如下: MEMORY { PAGE 0:#h,ar2 #5 table,*ar2+ #x,ar3 #h,ar4 a,#3 *ar4+,*ar3+,a frct a,*(_y)PARAM: org=1000h PAGE 1: USERREGS: BIOSREGS: IDATA: EDATA: EDATA1: } SECTIONS{ .text .data .bss } origin origin origin origin origin = = = = =len=0efd0h 0x60, 0x7c, 0x80, 0x0, len len len len len = = = = = 0x1c 0x4 0x0 0x4c00: & PARAM PAGE 0 :& PARAM PAGE 0 :& IDATA PAGE 131 实验六, 信号发生实验【实验目的】1, 了解产生信号的两种方法及各自的优缺点 2, 熟悉使用 C 语言编写较复杂的程序 3, 熟悉使用汇编语言编写较复杂的程序【实验原理】产生连续的波形的方法主要有两种,第一种方法为查表法,即事先将需要输 出的数据计算好,存储在 DSP 中,然后依次输出就可以了.查表法的优点是速度 快,可以产生频率较高的波形,而且不占用 DSP 的计算时间;查表法的缺点是在 于需要占用 DSP 的内部的存储空间,尤其对采样频率比较大的输出波形,这样, 需要占用的内部的空间将更大,而 DSP 内部的存储空间毕竟有所限制.这样使得 查表法的应用场合十分有限. 第二种方法为计算法,即采用计算的方法依次计算数据而后输出,然后再计 算而后输出.计算法的优缺点正好和查表法相反.其优点是不占用 DSP 的存储空 间,其缺点是占用 DSP 的计算时间,使得执行程序的开销变大. 由于查表法实现较为方便, 本实验仅介绍计算法产生一个余弦波信号, DA 从 输出.由余弦信号的递推公式:cos(nx) = 2 cos x cos[(n
2) x]从上式得知,如果需要产生连续的余弦信号,必须知道首先两个余弦值的大 小,然后就可以利用上式计算出后面的数据.【实验内容】32 本实验要求产生连续的信号,这些连续的信号可以是方波,锯齿波,三角波, 余弦波等其他各种波形,各种波形产生的原理基本相同,本实验以产生连续的余 弦波为例,如果需要产生其他波形,请参考本实验的设计. 本实验有两个例子,分别介绍用 C 和汇编语言的设计方法,下面分别说明.(本实验相关程序参见安装光盘的 dsplab\lab6. ) 例1 C 语言产生余弦波本例产生两个单周期的余弦波,采用两种数值表示法,二个余弦波含有一个 直流分量.读者可在此基础上产生多周期的余弦波.按下述步骤可以查看结果. 在程序运行后,选择 View→Graph→Time/Frequency,在弹出的对话框中输 入如下内容:单击 OK,即可看到如下波形33 选择 View→Graph→Time/Frequency,在弹出的对话框中输入如下内容:单击 OK,即可看到如下波形34 【程序】#define _COSX 0. #pragma DATA_SECTION(_cosx,&data_buf1&) float _cosx[180]; #pragma DATA_SECTION(dacdata,&data_buf2&) int dacdata[180]; void main(void) { _cosx[0]=1.000; dacdata[0]=4095; /*dacdata[0]=1, Q3.12 表示法*/ _cosx[1]=_COSX; dacdata[1]=_COSX*; for(i=2;i&180;i++) { _cosx[i]=2*_COSX*_cosx[i-1]-_cosx[i-2]; dacdata[i]=_cosx[i]*; /*dactada=0.5*_cos+0.5*/ } }35 CMD 文件如下: MEMORY { PAGE 0: PARAM: org = 2000h PAGE 1: DARAM: org = 080h } SECTIONS{ .text : data_buf1 data_buf2 .bss : len = 01780h len = 01780h& :& :& &PARAM DARAM DARAM DARAMPAGE PAGE PAGE PAGE0 1 1 1}例2汇编语言产生余弦波下面对本例作几点说明:(1)程序利用了以下公式:cos(na) = 2*cos(a)*cos{(n-1)a} - cos{(n-2)a} 其中,a 的大小决定了信号的分辨率, 程序中数值 a 为 5, 那么一个信号周期采样 72 个点. 在 开始的时候使用公式 cos(3a) = 2*cos(a)*cos(2a) - cos(a) 之后利用第一个公式进行递推运算就可以了.所以在开始的时候需要 cos(a)和 cos(2a)的值作为常数输 入. (2)为防止溢出,运算的值不可大于 1 或小于-1,所以起始值乘了系数 0.8.这 样,输出值会在 -0.8~0.8 之间.如果需要扩大数据的值,只需要在最后输出时乘 上相应的系数就可以了.其中,10 进制纯小数(绝对值小于 1)转换成十六进制 的方法是将该数乘以 32768,然后转换成十六进制.例如:0.13×.8436 (10A3) ,0.13 的十六进制为 10A3. (3)为了保证是小数乘法,必须将标志寄存器 FRCT 设为 1 (SSBX FRCT). (4)递推时,由于实际只需要 cos(na)和 cos((n-1)a),故只分配了 2 个空间,用 BK 来循环计数.相应的语句有: STM STM MPY #k_2,BK #1,AR0 *AR2,*AR3+0%,A下面查看输出结果: 程序运行后,选择 View→Graph→Time/Frequency,在弹出的对话框中输入 如下内容:单击 OK,即可看到输出波形37 【程序】.mmregs .def cos_generate,end_of_cose,main .def d_cos_delay1,d_cos_delay2,d_theta,d_cosx .usect &cos_vars&,1 .usect &cos_vars&,1 .usect &cos_vars&,1 .usect &cos_vars&,1 .set .set .set .set .set .set .usect .text main: RSBX ld SSBX SSBX STM CPL #0,dp SXM FRCT #COSDATA,AR5 8*.8 8*/1000000 ;cos(5)*0.8 8*/100000 ;cos(10)*0.8 /100000 ;cos(5) 2循环缓冲区大小 216 ;指针 &cos_data&,216 ; 3 cyclesd_cos_delay1 d_cos_delay2 d_theta d_cosx k_cos_delay_0 k_cos_delay_1 k_cos_delay_2 k_cos_theta k_2 k_216 COSDATA38 NOP LD NOP#d_cos_delay1,DPSTM STM RPTZ STL;初始化缓冲区 #d_cos_delay1,AR3 #d_theta,AR4 A, #3h A,*AR3+ ;设置循环缓冲区大小 #1,AR0 #d_cosx,AR2 #k_2,BK #k_216-1,BRC #d_cos_delay1,AR3 #k_cos_delay_1,*AR3+ #k_cos_delay_2,*AR3 #k_cos_theta,d_cosx #k_cos_delay_0,*ar5+ #k_cos_delay_1,*ar5+ #k_cos_delay_2,*ar5+ end_of_cose-1 ;cos(theta)*cos((n-1)theta) *AR3,15,A ;1/2*cos((n-2)theta) A,1,A A,*AR3 ;保存 *AR3,*AR5+STM STM STM STM NOP STM ST ST ST ST ST ST cos_generate: RPTB MPY *AR2,*AR3+0%,A SUB SFTA STH MVDD NOP NOP B .end CMD 文件如下: -e mainend_of_cose: cosend:cosend39 MEMORY { PAGE 0: PAGE 1: } SECTIONS { .textPARAM: org = 0800h DARAM: org = 080hlen = 1780h len = 1780h: & PARAMPAGE 0 PAGE 1cos_vars: & DARAM }40 实验七, A/D,D/A 实验一【实验目的】1, 了解 A/D,D/A 的转换原理 2, 熟悉 DSP 对数据的保存方法 3, 熟悉 C54x DSP McBSP 的使用方法 4, 熟悉使用查询方式实现 A/D 和 D/A 转换【实验原理】DSP 的应用系统一般包括模数转换器(AD) ,信号处理芯片(DSP) ,数模转 换器(DA)等主要器件,一个典型的数字信号处理系统如图 2.1 所示.该系统首 先将模拟信号(实验时可以由信号发生器产生) ,经过一个或多个硬件滤波器,或 其他的信号预处理,到达 AD,经过 AD 转换成数字信号,传输到 DSP,DSP 对 这个信号进行采集,处理,分析,如果有必要再经过 DA,转换成模拟信号输出, 实验中可以由示波器查看输出的信号波形.图 2.1 典型数字信号处理系统图在 DSP 系统中,首要的就是数据采集.实验中,我们选择对一个确定的信号41 进行采样,然后将采样后的数据从 DA 输出,从 DA 的输出使用示波器查看输出 后的波形.也可以使用 CCS 的画图功能查看采集到的数据. AD 转换器的采样频率必须大于信号频率的 2 倍,采样频率的确定方法将在 下一个实验中介绍. 本实验从 AD 采集数据和硬件系统联系较大,不同的硬件系统有不同的设置 方法,这些设置方法都会在硬件系统的说明书中有详细的说明.例如,我们公司 的 DSP 教学开发系统采用的 AD 型号为 AD50,关于 AD50 的设置和使用可以参 考 AD50 的用户手册. AD50 使用过采样(over sampling)的∑-Δ技术提供从数字至模拟(D/A)和 模拟至数字(A/D)的高分辨率低速信号转换.该器件包括两个串行的同步转换 通道 (用于各自的数据方向) 在 DAC 之前有一个插入滤波器 ; (interpolation filter) 和 ADC 之后有一个抽取滤波器(decimation filter) .其它的高级功能有片内时序 和控制.∑-Δ结构在低系统速度和低价格下产生高分辨率的模数和数模转换. 该器件的选项和电路结构可通过串行接口进行编程.其选项包括:复位,掉 电,通信协议,串行时钟率,信号采样率,增益控制及测试方式等.TLC320AD50 的工作温度范围从 0℃~70℃.对 AD50 提供 5V 电源,利用运放将单端输入转为 差分输入到 AD50.该板就是利用此原理工作.【实验内容】本实验要求使用 AD 将模拟信号变换成数字信号,使用 DSP 对转换后的数 字信号读取,保存,并将数据送往 D/A,在示波器上观察 D/A 的输出波形,也可 以使用 CCS 查看采集的数据. 注意:A/D 输入的模拟信号幅值应小于 1V,而 D/A 输出信号的幅值是 A/D42 输入幅值的 2 倍.A/D 与 D/A 的精度为 15 位,D/A 的最低位必须为 0.采集的数 据存储在 indata 段中,长度为 1000,可以用 CCS 查看数据,画图时显示 50 个数 据比较清楚. 如果在程序中不设置断点,则 D/A 输出连续的波形;如果在程序结束前的 nop 处设置断点,如程序中说明所示,然后选择 Debug→Animate 运行程序,则示 波器间断地显示波形,此时,用 CCS 画出的图形(例如,起始地址为 indata,长 度为 50)也在不断地刷新. 数据的存储长度为 1000,如果存满则重新开始存储.用 TMS320VC5410 的 McBSP0 完成数据的传送.A/D 的采样频率为 8KHz,由于 A/D 前有一个低通滤 波器,如果输入信号频率高于 3.3KHz,输出迅速衰减到 0.A/D 的采样频率可以 改变,这将在下一个实验中介绍. 本实验相关程序参见安装光盘的 ( dsplab\lab7. )【实验步骤】1. 打开实验箱电源开关,打开信号发生器开关,根据实验内容中对输入波 形的要求,通过示波器的观察调整合适的波形. 2. 3. 4. 5. 6. 用音频线连接信号发生器的输出口到 5410TDK 模拟输入接口(JP8). 将 USB 电缆接到 5410TDK 上,打开 5410TDK 电源开关. 进入 CCS,选择 File→Load Program→打开\lab7\Debug\lab7.out. 选择 Debug→Run. 用示波器在输出端 JP7 能看到输出的波形,与输入波形相同.【程序】.mmregs43 .def main indata .usect spsa0 .set 38h spcd0 .set 39h drr20 .set 20h drr10 .set 21h dxr20 .set 22h dxr10 .set 23h spcr10 spcr20 rcr10 rcr20 xcr10 xcr20 srgr10 srgr20 mcr10 mcr20 rcera0 rcerb0 xcera0 xcerb0 pcr0 main: stm rsbx nop ld stm stm ssbx ssbx McBSP0: stm stm nop stm stm nop .set 00h .set 01h .set 02h .set 03h .set 04h .set 05h .set 06h .set 07h .set 08h .set 09h .set 0ah .set 0bh .set 0ch .set 0dh .set 0eh .text&indata&,1000 ;mcbsp0 subbamcbsp0 sudata receive register 1(mcbsp0) ;data receive register 2(mcbsp0) ;data transmit register 1(mcbsp0) ;data transmit register 2(mcbsp0)#0ffe0h,pmst cpl #0h,dp #indata,ar2 #999,ar3初始化 McBSP spcr10,spsa0 4000h,spcd0 spcr20,spsa0 200h,spcd044 stm stm nop stm stm nop stm stm nop stm stm nop stm stm nop rpt nop stm nop stm stm nop stm stm nop stm nop loop1: ldm nop nop nop and bc ldm nop stl and nop noppcr0,spsa0 0ch,spcd0 rcr10,spsa0 40h,spcd0 rcr20,spsa0 00h,spcd0 xcr10,spsa0 40h,spcd0 xcr20,spsa0 0h,spcd0 #100 0h,dxr10 spcr10,spsa0 4001h,spcd0 spcr20,spsa0 201h,spcd0spcr20,spsa0 ;查询发送是否准备好 spcd0,a#2h,a loop1,aeq drr10,a a,*ar2+ #0fffeh,a;读数据 ;保存数据45 stl nop banz nop nop stm stm nop nop nop ba,dxr10 loop1,*ar3-;送往 D/A#999,ar3 #indata,ar2;重新读数 ;可在此设置断点loop1CMD 文件如下: -e main MEMORY { PAGE 0: VEC PROG PAGE 1: STACKS : origin=0x0200, length=0x100 DATA : origin=0x0300, length=0x2000 } SECTIONS { vectors : .text : indata : & .bss :& .data :& } : origin=0xff80, length=0x0080 : origin=0x100, length=0x1000{} & VEC {} & PROG DATA PAGE 1 DATA DATAPAGE 0 PAGE 0PAGE 1 PAGE 146 实验八, A/D,D/A 实验二【实验目的】1, 熟悉 C54x DSP McBSP 的使用方法 2, 熟悉使用中断方式实现 A/D 和 D/A 转换 3, 熟悉中断的使用 4, 熟悉 DSP 对 A/D 采样率的控制方法【实验原理】香农采样定理指出:如果 AD 转换器的输入信号具有有限带宽,并且有直到ω k 的频率分量,则只需要 AD 转换器的采样周期 T 满足如下条件:T ≤ π / ω K ,信号就可以完全从采样信号中恢复出来.反之,如果采样频率低于信号频率的 2 倍,基本上不能恢复原始信号. 根据采样定理,对于一个单正弦的模拟信号,假设其频率为 f 0 ,当采样率f s ≥ 2 f 0 时就可保证采样后的信号无失真地保持原模拟信号的信息,即可重现原模拟信号;如果采样率低于 2 f 0 就会发生频域的混叠失真.在实际的情况中,一 般的情况下首先要使模拟信号通过一个截止频率不高于 0.5 f 0 的低通滤波器,使 其成为一个限带信号.然后,对其采样就可以保证信号无混叠失真.该低通滤波 器又叫抗混叠滤波器. 实验中,我们选择对一个确定的信号进行采样,然后将采样后的数据从 DA 输出,从 DA 的输出使用示波器查看输出后的波形.如果满足采样定理,可以从47 示波器看到和原始信号一样的波形;反之,如果不满足采样定理,就不能从示波 器看到和原始信号一样的波形.实验中,我们调整 AD 转换器的采样频率,将以 上两种情况分别进行,以验证采样定理. AD50 的最大采样频率为 22K, 我们通过 DSP 缓冲串口设置 AD50 的采样率. AD50 的寄存器 4 包含采样率相关信息,寄存器 4 的第 4~7 位是采样率设置位, 第 7 位使能内部 PLL,4~6 位具体设置采样率,计算公式 Fs=MCLK/(128*N)(第 7 位为 0) 或 MCLK/(512*N)(第 7 位为 1) .MCLK 是缓冲串口的时钟,闻亭公 司的实验开发系统中 MCLK≈8MHz.该实验从 A/D 端口采样模拟信号,紧接着 从 D/A 端口输出.用户可以设置不同的采样率,运行程序后,改变 A/D 输入信号 的频率,可以看到,当输入信号频率接近采样率的一半时,D/A 输出会有明显的 衰减直至消失,从而验证香农采样定理.关于 AD50 更详细的设置,请参考 AD50 用户手册.【实验内容】本实验与实验七可作比较,实验七中 A/D 的采样率约为 8KHz,本实验中的 采样率约为 16KHz,故输入信号的频率可以是实验七中的 2 倍.A/D 与 D/A 的精 度为 15 位,D/A 的最低位必须为 0.当 D/A 的最低位为 1 时,向 A/D 请求一次 二次通信,以修改 A/D 的寄存器值. 注意:A/D 输入的模拟信号幅值应小于 1V,而 D/A 输出信号的幅值是 A/D 输入幅值的 2 倍. 本实验采用中断的方式从 A/D 读取数据并送往 A/D,中断向量表存放在由 PMST 寄存器中 IPTR 位指定的位置. 中断向量表有一定的书写格式, 请参阅本例 并阅读有关资料. 本实验仍然采用多通道缓冲串口 McBSP0 进行数据的接受与发送, McBSP 对48 的初始化与实验七一样. 在示波器上观察 D/A 的输出信号, 改变输入信号的频率, 输出信号的频率也随之改变.当输入信号频率大于 7KHz 时,输出信号幅度迅速 衰减,这是因为 A/D 前的低通滤波器的通带与 A/D 的采样频率成线性关系,与实 验七一起验证了采样定理. (本实验相关程序参见安装光盘的 dsplab\lab8. )【实验步骤】1. 打开实验箱电源开关,打开信号发生器开关,根据实验内容中对输入 波形的要求,通过示波器的观察调整合适的波形. 2. 用音频线连接信号发生器的输出口到 5410TDK 模拟输入接口(JP8). 3. 把 USB 电缆接到 5410TDK 上,打开 5410TDK 电源开关. 4. 进入 CCS,选择 File→Load Program→打开\lab8\Debug\lab8.out. 5. 选择 Debug→Run. 6. 用示波器在输出端 JP7 能看到输出的波形,与输入波形相同.【程序】.mmregs .def main .def receive spsa0 .set 38h spcd0 .set 39h drr20 .set 20h drr10 .set 21h dxr20 .set 22h dxr10 .set 23h spcr10 spcr20 rcr10 rcr20 .set .set .set .set 00h 01h 02h 03h;mcbsp0 subbamcbsp0 sudata receive register 1(mcbsp0) ;data receive register 2(mcbsp0) ;data transmit register 1(mcbsp0) ;data transmit register 2(mcbsp0)49 xcr10 xcr20 srgr10 srgr20 mcr10 mcr20 rcera0 rcerb0 xcera0 xcerb0 pcr0 main: stm rsbx nop ld ssbx ssbx McBSP0: stm stm nop stm stm nop stm stm nop stm stm nop stm stm nop stm stm nop stm stm nop.set .set .set .set .set .set .set .set .set .set .set04h 05h 06h 07h 08h 09h 0ah 0bh 0ch 0dh 0eh#0ffe0h,pmst cpl #0h,dp intm sxm spcr10,spsa0 4000h,spcd0 spcr20,spsa0 200h,spcd0 pcr0,spsa0 0ch,spcd0 rcr10,spsa0 40h,spcd0 rcr20,spsa0 00h,spcd0 xcr10,spsa0 40h,spcd0 xcr20,spsa0 0h,spcd0;定义中断向量的位置;禁止所有中断 ;初始化 McBSP050 rpt nop stm nop stm stm nop stm stm nop stm nop wait1: ldm nop and nop nop bc ldm stm stm nop wait2: ldm nop nop nop and nop nop nop nop bc ldm stm nop wait3: stm#100 0h,dxr10 spcr10,spsa0 4001h,spcd0 spcr20,spsa0 201h,spcd0spcr20,spsa0spcd0,a #2h,await1,aeq drr10,a #1h,dxr10 spcr20,spsa0;请求写 A/D 的寄存器spcd0,a#2h,await2,aeq drr10,a #0440h,dxr10;写寄存器 4,设置采样频率spcr20,spsa051 nop ldm nop and nop nop bc ldm stm nop stm orm stm stm rsbx nop wait: nop nop nop b receive: ldm nop and nop stl nop stm nop retespcd0,a #2h,await3,aeq drr10,a #00h,dxr10;确保写入寄存器 4#0h,imr #10h,imr #0ffffh,ifr #2h,dxr10 intm;允许接受中断;允许所有中断 ;等待接受中断wait drr10,a #0fffeh,a a,dxr10 #0ffffh,送往 D/A ;中断服务程序 ;读 A/D中断向量程序如下: .sect &vectors& .mmregs .ref main .ref receive rs: bd main stm 4000h,sp52 nmi:software interrupts sint17 .space sint18 .space sint19 .space sint20 .space sint21 .space sint22 .space sint23 .space sint24 .space sint25 .space sint26 .space sint27 .space sint28 .space sint29 .space sint30 .space int0: rete nop nop nop rete nop nop nop rete nop nop nop4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16 4*16int1:int2:tint0: rete nop nop nop53 brint0: b receive nop nop bxint0: rete nop nop nop dmac0: rete nop nop nop dmac1: rete nop nop nop int3: rete nop nop nophpint: rete nop nop nop dmac2: rete nop nop nop bxint1: rete nop nop nop dmac4: rete nop nop54 nop dmac5: rete nop nop nop rsvd1: rete nop nop nop rsvd2: rete nop nop nop .end CMD 文件如下: -e main MEMORY { PAGE 0: VEC PROG PAGE 1: STACKS : origin=0x0200, length=0x100 DATA : origin=0x0300, length=0x2000 } SECTIONS { vectors : .text : .bss .data } :& :& : origin=0xff80, length=0x0080 : origin=0x100, length=0x1000{} & VEC {} & PROG DATA DATAPAGE 0 PAGE 0 PAGE 1 PAGE 155 实验九, FIR 滤波器实验【实验目的】1, 了解 FIR 滤波器的原理及使用方法 2, 了解 FIR 滤波器的特点 3, 熟悉循环缓冲区的使用方法 4, 熟悉 DSP 对 FIR 滤波器的设计及编程方法【实验原理】在数字信号处理中,FIR 滤波器占有很重要的的低位,这主要是由 FIR 滤波 器的特点决定的.FIR 滤波器最主要的特点是它没有反馈回路,因此它不存在不 稳定现象,它的单位脉冲响应 h(n)是一个有限长序列,如果 h(n)是实数,且满足 偶对称或奇对称的条件,即 h(n)=h(N-1-n)或 h(n)=-h(N-1-n),则滤波器具有线性 相位特性. 假设 FIR 滤波器的冲击响应为 h(0),h(1),……,h(N-1),x(n)为滤波器的输 入信号,则对应的滤波器输出由下面关系式决定:由此可见 FIR 滤波算法实际上是一种乘法累加运算,它不断地输入样本 x(n) 经延迟,做乘法累加,再输出滤波结果 y(n).C54x 有乘累加指令 MAC,我们可 以利用循环缓冲区有效地实现上面的运算,以完成滤波.从上面关系式我们可以 看出,首先必须知道 FIR 滤波器的冲击响应系数 h(0),h(1),……,h(N-1),我们 使用 Matlab 语言设计 FIR 滤波器,这很简单,确定 FIR 滤波器的参数,只需一条 语句就可以求出系数 h(n), 本实验不再具体介绍 MATLAB 的使用方法, 直接给出56 FIR 滤波器的系数.将得到的冲击响应系数应用到 DSP 汇编语言程序中,实现上 面的计算公式,就可以方便的实现 FIR 滤波器,完成实验的要求,达到滤波效果. 下图是 N 阶均衡 FIR 滤波器的方框图.图中表明了滤波器实现的整个过程.N 阶均衡 FIR 滤波器的方框图【实验内容】1,滤波器系数 本实验设计一个 FIR 低通数字滤波器, 技术指标为: 通带截至频率为 1500 Hz, 阻带截至频率为 2000 Hz, 通带波纹为 0.01, 阻带波纹为 0.1, 采样频率为 8000Hz. 根据技术指标, MATLAB 设计该 FIR 数字低通滤波器, 用 下面给出用 FIR1 函数, 采用 Kaiser 窗设计的滤波器系数,该滤波器有 37 个系数. h(0)=h(36)=-0.0010 h(3)=h(33)=0.0065 h(6)=h(30)=-0.0093 h(9)=h(27)=-0.0047 h(12)=h(24)=0.0417 h(1)=h(35)=-0.0036 h(4)=h(32)=0.0032 h(7)=h(29)=0.0090 h(10)=h(26)=-0.0297 h(13)=h(23)=0.031657h(2)=h(34)=0.0000 h(5)=h(31)=-0.0088 h(8)=h(28)=0.0184 h(11)=h(25)=-0.0071 h(14)=h(22)=-0.0524 h(15)=h(21)=-0.0848 h(18)=0.4375 2,输入数据h(16)=h(20)=0.0598h(17)=h(19)=0.3108FIR 滤波器的输入数据存储在 indata.dat 文件中, 300 个点, 共 由频率为 1000Hz 和 2500Hz 的两个余弦波叠加而成.在将程序 load 进仿真系统后,要将输入数据 装入 data 存储器,起始地址为 indata,长度为 300. (本实验相关程序参见安装光 盘的 dsplab\lab9. ) 操作步骤: 1) 把 USB 电缆接到 5410TDK 上,打开 5410TDK 电源开关. 2) 进入 CCS. 3) 选择 File→Load Progrome→打开\lab9\Debug\fir.out. 4) 选择 File→Data→Load,在弹出的对话框中选择本实验文件夹下的 indata.dat 文件并打开,在弹出的对话框中输入起始地址和长度,如下图所 示:5) 输入完后点击 OK,然后选择 Debug →Run .3,输出数据FIR 滤波器的输出数据存储在 data 存储空间中,起始地址在 outdata,长度为 256.58 4,查看输入和输出 选择 View→Graph→Time/Frequency,可以查看输入与输出的时域与频域波 形.下面是输出的时域与频域波形, 在弹出的对话框中按下图输入即可得到输出波形.59 只需把 Start Address 改为 indata, Graph Title 改变, 把 画时域波形时把 Maximum Y-value 改为 2.5,即可得到输入波形,如下图:60 【程序】.title &FIR.ASM& .mmregs .def start .bss yn,1 xn hn indata outdata .usect .usect .usect .usect &xn&,37 &hn&,37 &indata&,300 &outdata&,256.data fir_coff: .word -10*,-36* .word 0*,65* .word 32*,-88* .word -93*,90* .word 184*,-47* .word -297*,-71* .word 417*,316* .word -524*,-848* .word 598*,/10000 .word /10000 .word /768/10000 .word -848*,-524* .word 316*,417* .word -71*,-297* .word -47*,184* .word 90*,-93* .word -88*,32* .word 65*,0* .word -36*,-10* .text start: ssbx frct stm #hn,ar1 rpt #36 mvpd fir_coff,*ar1+ stm #-1,ar0;使用小数运算 ;系数首地址 ;将系数移入循 ;环缓冲区61 stm stm stm rpt mvdd stm stm stm stm stm rptb rptz mac sth mvkd mvdd loop: nop b .end#outdata,ar5 #indata,ar4 #xn+36,ar2 #36 *ar4+,*ar2+0% #xn+36,ar2 #hn+36,ar3 #37,bk #255,brc #-1,ar0 loop-1 a,#36 *ar2+0%,*ar3+0%,a a,*(yn) *(yn),*ar5+ *ar4+,*ar2+0% loop;将输入数据移入 ;循环缓冲区 ;指向第一个输入 ;循环缓冲区大小 37 ;块重复 256 次 ;块程序重复大小 ;计算一个输出, 滤波 ;保存输出 ;读进一个输入CMD 文件如下: -e start MEMORY { PAGE 0: PARAM: PAGE 1: SPRAM DARAM: } SECTIONS { .text :& .data :& .bss :& xn : align hn : align indata :& outdata :& }62org=1000h org=0060h org=0080hlen=4000h len=0020h len=1380hPARAM PAGE 0 PARAM PAGE 0 SPRAM PAGE 1 (64) {} & DARAM (64) {} & DARAM DARAM PAGE 1 DARAM PAGE 1PAGE 1 PAGE 1 实验十, FFT 实验【实验目的】1, 了解 FFT 的原理; 2, 了解使用 Matlab 语言实现 FFT 的方法; 3, 了解在 DSP 中 FFT 的设计及编程方法; 4, 熟悉对 FFT 的调试方法;【实验内容】本实验要求使用 FFT 变换求一个时域信号的频域特性,并从这个频域特性求 出该信号的频率值.使用 Matlab 语言实现对 FFT 算法的仿真,然后使用 DSP 汇 编 语 言 实 现 对 FFT 的 DSP 编 程 . 本 实 验 相 关 程 序 参 见 安 装 光 盘 的 ( dsplab\lab10. )【实验原理】对于有限长离散数字信号{x[n]},0≤x≤N-1,它的频谱离散数学值{X(K)} 可由离散傅氏变换(DFT)求得.DFT 定义为:X (K ) =N =0∑ x[n]eN 1 j ( 2π / N ) nkk=0,1,…,N-1也可以方便的把它改写成如下形式:X (K ) =N =0∑ x[n]WN 1nk N式中 WN(有时简写为 W)代表 e 周期为 N,即 j ( 2π / N ).不难看出, Wnk是周期性的,且63 ( nk W N n + mN )( k +lN ) = W Nm, l=0, ±1, ±2, ……Wnk 的周期性是 DFT 的关键之一. 为了强调起见, 常用表达式 WN 取代 W 以 nk 便明确地给出 W 的周期为 N. 由 DFT 的定义可以看出,在 x[n]为复数序列的情况下,完全可以直接运算 N 点 DFT 需要 ( N
1) 次复数乘法和 N(N-1)次复数加法.因此,对于一些相当2大的 N 值(如 1024 点)来说,直接计算它的 DFT 所需要的计算量是很大的.一 个优化的实数 FFT 算法是一个组合以后的算法.原始的 2N 个点的实输入序列组 合成一个 N 点的复序列,然后对复序列进行 N 点的 FFT 运算,最后再由 N 点复 数输出拆散成 2N 点的复数序列,这 2N 点的复数序列与原始的 2N 点的实数输入 序列的 DFT 输出一致. FFT 的基本思想在于:将原来的 N 点序列分成两个较短的序列,这些序列的 DFT 可很简单地组合起来得到原序列的 DFT.例如,若 N 为偶数,将原有的 N 点序列分成两个(N/2)点序列,那么计算 N 点 DFT 将只需要约[(N/2)2*2]=N2/2 次复数乘法.即比直接计算少作一半乘法.因子(N/2)2 表示直接计算(N/2)点 DFT 所需要的乘法次数,而乘数 2 代表必须完成两个 DFT.上述处理方法可以反 复使用,即(N/2)点的 DFT 计算也可以化成两个(N/4)点 DFT(假定 N/2 为偶 数) 从而又少作一半乘法. , 使用这种方法, 在组合输入和拆散输出的操作中, FFT 的运算量减半.这样,利用实数 FFT 算法来计算实输入序列的 DFT 的速度几乎 是一般复 FFT 算法的两倍. 假定序列{x[n]}的点数 N 是 2 的幂,按上述处理方法,定义两个分别为 x[n] 的偶数项和基数项的(N/2)点序列 x1[n]和 x2[n],即: x1[n]=x[2n] n=0,1,…, (N/2)-1 x2[n]=x[2n+1] n=0,1,…, (N/2)-1 {x[n]}的 N 点 DFT 可写成:X (K ) =N =0∑ x[n]WN / 2 1N 1nk N+N =0∑ x[n]WN / 2 1N 1nk N(n 为偶数) =(n 为奇数) +N =0 k∑ x[2n]WN2nkN =0∑ x[2n + 1]W( 2 n +1) k N因考虑到 W N 可写成:64 k WN=[e
j ( 2π ) / N ] 2=e
j ( 2π ) /( N / 2 ) = W N / 2故 X(k)可写为:X (K ) =N / 2 1 N =0∑ x1[n]WNnk/ 2kN / 2 1+k WNN =0∑ x [n]W2nk N /2= X 1 ( K ) + W N X(K) 2 式中 X 1 ( K ) 和 X 2 ( K ) 是 x1 ( n) 和 x 2 ( n) 的(N/2)点 DFT.上式表明,N 点 DFT X(k) 可分解为按上式的规则加以组合的两个(N/2)点 DFT.【FFT 的高级编程】FFT 算法的 Matlab 语言编程十分简单,直接使用 Matlab 下的 FFT 函数就可 以了,可以参考 FFT 函数的帮助文件,使得 Matlab 编程简单方便,这里不再做详 细的描述,下面介绍 C 语言对 FFT 的编程,了解 FFT 实现的方法. 在本程序中采用地址表查找,其中的数据是用 C 语言计算出来,然后输出到 文件中拷上去的. 其中 FFT 的算法验证的输入是用 C 语言生成正弦波和方波的数 据放在 FFT_INPUT 中的,其中的C语言程序如下: #include &stdio.h& #include &stdlib.h& #include &file.h& #include &math.h& int signal(int x)//(方波子程序) { int t=x%8; if(t&=3) return 1; else return -1; }65 void main() { int i,k; FILE* int mm[256]; for(i=0;i&=255;i++) mm[i]=(sin(3.*i/4+3.1414/16)*(-32768)/2);//正弦波 程序//mm[i]=signal(i)*(-32768)/2; k=0; for(i=0;i&=255;i++) { fp=fopen(&sinsin.txt&, &a&); if(k%8==0) fprintf(fp, &\n .word&); fprintf(fp, &0%xh&,mm[i]); fclose(fp); k++; } } 在程序中由于要用到位倒序地址计算,如果用汇编则必须使得所分的首地址 为整数,为避免这种情况,采用查表的方法进行位地址的计算,而该表也是由 C 语言生成的其程序如下: #include&stdio.h& #include&stdlib.h& #include&math.h& #include &file.h& void main() { int num[256],i,m=0,n,k,q; FILE* for(i=0;i&=255;i++) {num[i]=i; printf(&%d,&,num[i]); } for(i=0;i&=255;i++) { q=num[i]; for(k=0;k&=7;k++) {66 n=q%2; q=q/2; m=m+n*pow(2,(7-k)); } num[i]=m; m=0; } k=0; for(i=0;i&=255;i++) { fp=fopen(&fft_real.txt&, &a&); if(k%8==0) fprintf(fp, &\n .word&); fprintf(fp, &000%xh,&,num[i]); fclose(fp); k++; } } 在 FFT 的主程序中,是按照每一趟的蝶形来进行运算的,因此,在每一趟的 蝶形中要用到 SIN,COS 的值,这些值也是用 C 语言生成的 #include &stdio.h& #include &stdlib.h& #include &math.h& #include &file.h& #define pi 3. main() { int i,j=0,m; FILE* static int w[512][2]; for(i=0;i&8;i++) { for(m=0;m&pow(2,i);m++) { w[m][0]=cos(2*pi*m/pow(2,i+1))*32767; w[m][1]=sin(2*pi*m/pow(2,i+1))*(-32767); fp=fopen(&xossin.txt&, &a&); if(j%8==0) fprintf(fp, &\n .word &);67 j=j+2; fprintf(fp, &0%xh,0%xh,&,w[m][0], w[m][1]); fclose(fp); } } } 其中得到值按照用到的顺序排列,使用时只要顺序取出就可以了. FFT 的主体部分又分为 5 个小部分,其中的顺序如下:1.除掉波形总的直流 部分 2.将要进行 FFT 变换的数据按照位到序的顺序装入到蝶形运算区 3.进行蝶形 运算 4.进行功率谱计算 5.求波形的频率 其程序的解释见程序注释.【FFT 的 DSP 编程】从上面FFT实现的过程可以看出,其实现步骤主要有以下四步: (1) 将输入序列压缩和位倒序. (2) N 点的复数 FFT. (3) 奇数号部分和偶数号部分分离. (4) 产生最后的输出数据. 最初,将原始输入的 2N 点的实序列 a(n),存储在 4N 大小的数据处理缓冲区 的下半部分. .asg AR1,FFT_TWID_P LD #FFT_DP,DP STM #SYSTEM_STACK,SP rfft_task: STM #FFT_ORIGIN,AR3 ; to data STM #data_input,AR4 RPT #K_FFT_SIZE*2-1 ;K_FFT_SIZE=128 MVDD *AR4+,*AR3+ 0C00h 0C01h …… 0CFEh68 0CFFh 0D00h 0D01h …… 0DFFEh 0DFFFh a(0) a(1) …… a(254) a(255)然后通过子程序调用,有以下几步计算: ①去除原始数据的直流分量. 由于采样的需要,信号发生器输出的为叠加了直流分量的正弦信号,所以有 此一步. .asg AR3,FFT_INPUT .def data_ave data_ave: STM #FFT_ORIGIN,FFT_INPUT LD #0,A RPT #K_FFT_SIZE*2-1 ADD *FFT_INPUT+,A NOP LD A,#-1-K_LOGN STM #K_FFT_SIZE*2-1,BRC STM #FFT_ORIGIN,FFT_INPUT RPTB ave_end-1 SUB *FFT_INPUT,A,B NEG B STL B,-1,*FFT_INPUT+ ave_end: NOP RET; AR31 st original input;累加原始数据到 A ;A右移8位,即A/256,输入数据取平均; AR31 st original input;B=A-(*FFT_INPUT) ;B=(*FFT_INPUT)-A,即将原始数据去除 直流分量 ;除以2以防止溢出后存入原址②将输入序列位倒序存储,以使算法结束后的输出序列为自然顺序. 首先, 2N 点的实输入序列拷贝到标记为 FFT_ORIGIN 的连续内存空间中, 将 理解为一个 N 点的复数序列 d(n).时序列中索引为偶数的作为 d(n)的实部,索引69 为奇数的作为 d(n)的虚部.这一部就是压缩.然后,将得到的复数序列位倒序存 储到数据处理缓冲区 fft_data. 0C00h 0C01h 0C02h 0C03h …… 0CFCh 0CFDh 0CFEh 0CFFh R(0)=a(0) I(0)=a(1) R(64)=a(128) I(64)= a(129) …… R(63)=a(126) I(63)=a(127) R(127)=a(254) I(127)=a(255) 0D00h 0D01h 0D02h 0D03h …… 0DFCh 0DFDh 0DFEh 0DFFh a(0) a(1) a(2) a(3) …… a(252) a(253) a(254) a(255);Bit Reversal Routine .asg AR2,REORDERED_DATA .asg AR3,ORIGINAL_INPUT .asg AR7,DATA_PROC_BUF .def bit_rev bit_rev: SSBX FRCT ;FRCT=1;准备小数乘法 STM #FFT_ORIGIN,ORIGINAL_INPUT ; AR3 -& 1 st original input STM #fft_data,DATA_PROC_BUF ; AR7 -& data processing buffer MVMM DATA_PROC_BUF,REORDERED_DATA ; AR2 -& 1st bit-DATA_PROC_BUF,REORDERED_DATA 同指向 fft_ORIGINAL_INPUT 指向 FFT_ORIGIN STM #K_FFT_SIZE-1,BRC RPTBD bit_rev_end-1 STM #K_FFT_SIZE,AR0 ; AR0 = 1/2 size of circ buffer MVDD *ORIGINAL_INPUT+,*REORDERED_DATA+ MVDD *ORIGINAL_INPUT-,*REORDERED_DATA+ MAR *ORIGINAL_INPUT+0B ;位倒序 bit_rev_end: RET ; return to Real FFT main module ③一个 N 点的复数序列存储在数据处理缓冲区. 对d(n)作 fft 变换,结果得到 D(k)=f{d(n)}=R(k)+jI(k).70 0C00h 0C01h 0C02h 0C03h …… 0CFCh 0CFDh 0CFEh 0CFFhR(0) I(0) R(1) I(1) …… R(126) I(126) R(127) I(127)0D00h 0D01h 0D02h 0D03h …… 0DFCh 0DFDh 0DFEh 0DFFha(0) a(1) a(2) a(3) …… a(252) a(253) a(254) a(255);256-Point Real FFT Routine .asg AR1,GROUP_COUNTER .asg AR2,PX .asg AR3,QX .asg AR4,WR .asg AR5,WI .asg AR6,BUTTERFLY_COUNTER .asg AR7,DATA_PROC_BUF ; for Stages 1 & 2 .asg AR7,STAGE_COUNTER ; for the remaining stages .def fft fft: ;Stage1 ---------------------------------------------------------------------STM #K_ZERO_BK,BK ;BK=0 so that *ARn+0% = *ARn+0 ;循环缓冲器大小为0 LD #-1,ASM ; outputs div by 2 at each stage MVMM DATA_PROC_BUF,PX ; PX -& PR LD *PX,A ; A := PR STM #fft_data+K_DATA_IDX_1,QX ; QX -& QR STM #K_FFT_SIZE/2-1,BRC RPTBD stage1end-1 STM #K_DATA_IDX_1+1,AR0 SUB *QX,16,A,B ; B := PR-QR ADD *QX,16,A ; A := PR+QR STH A,ASM,*PX+ ; PR':= (PR+QR)/2 ST B,*QX+ ; QR':= (PR-QR)/2 ||LD *PX,A ; A := PI SUB *QX,16,A,B ; B := PI-QI ADD *QX,16,A ; A := PI+QI STH A,ASM,*PX+0 ; PI':= (PI+QI)/2 ST B,*QX+0% ; QI':= (PI-QI)/2 ||LD *PX,A ; A := next PR stage1end: ;Stage2 ---------------------------------------------------------------------MVMM DATA_PROC_BUF,PX ; PX -& PR STM #fft_data+K_DATA_IDX_2,QX ; QX -& QR STM #K_FFT_SIZE/4-1,BRC LD *PX,A ; A := PR RPTBD stage2end-1 STM #K_DATA_IDX_2+1,AR0 ; 1st butterfly SUB *QX,16,A,B ; B := PR-QR ADD *QX,16,A ; A := PR+QR STH A,ASM,*PX+ ; PR':= (PR+QR)/2 ST B,*QX+ ; QR':= (PR-QR)/2 ||LD *PX,A ; A := PI SUB *QX,16,A,B ; B := PI-QI ADD *QX,16,A ; A := PI+QI STH A,ASM,*PX+ ; PI':= (PI+QI)/2 STH B,ASM,*QX+ ; QI':= (PI-QI)/2 ; 2nd butterfly MAR *QX+ ADD *PX,*QX,A ; A := PR+QI SUB *PX,*QX-,B ; B := PR-QI STH A,ASM,*PX+ ; PR':= (PR+QI)/2 SUB *PX,*QX,A ; A := PI-QR ST B,*QX ; QR':= (PR-QI)/2 ||LD *QX+,B ; B := QR ST A, *PX ; PI':= (PI-QR)/2 ||ADD *PX+0%,A ; A := PI+QR ST A,*QX+0% ; QI':= (PI+QR)/2 ||LD *PX,A ; A := PR stage2end: ; Stage 3 thru Stage logN-1 STM #K_TWID_TBL_SIZE,BK ST #K_TWID_IDX_3,d_twid_idx STM #K_TWID_IDX_3,AR0 STM #cosine,WR; ; ; ;BK = twiddle table size always init index of twiddle table AR0 = index of twiddle table init WR pointer72 STM #sine,WI ; init WI pointer STM #K_LOGN-2-1,STAGE_COUNTER ; init stage counter ST #K_FFT_SIZE/8-1,d_grps_ init group counter STM #K_FLY_COUNT_3-1,BUTTERFLY_COUNTER ; init butterfly counter ST #K_DATA_IDX_3,d_data_ init index for input data stage: STM #fft_data,PX ; PX -& PR LD d_data_idx, A ADD *(PX),A STLM A,QX ; QX -& QR MVDK d_grps_cnt,GROUP_COUNTER ; AR1 conA circular buffer of size R must start on a N-bit boundary (that is, the N LSBof the base address of the circular buffer must be 0), where Ninteger that satisfies 2 N & R. The value R must be loaded into BK. group: MVMD BUTTERFLY_COUNTER,BRC ; # of butterflies in each grp RPTBD butterflyend-1 LD *WR,T ; T := WR MPY *QX+,A ; A := QR*WR QX-&QI MACR *WI+0%,*QX-,A ; A := QR*WR+QI*WI QX-&QR ADD *PX,16,A,B ; B := (QR*WR+QI*WI)+PR ST B,*PX || ; PR':=((QR*WR+QI*WI)+PR)/2 SUB *PX+,B ; B := PR-(QR*WR+QI*WI) PX-&PI ST B,*QX || ; QR':=(PR-(QR*WR+QI*WI))/2 MPY *QX+,A ; A := QR*WI [T=WI] QX-&QI MASR *QX,*WR+0%,A ; A := QR*WI-QI*WR ADD *PX,16,A,B ; B := (QR*WI-QI*WR)+PI ST B,*QX+ || ; QI':=((QR*WI-QI*WR)+PI)/2 QX-&QR SUB *PX,B ; B := PI-(QR*WI-QI*WR) LD *WR,T ; T := WR ST B,*PX+ || ; PI':=(PI-(QR*WI-QI*WR))/2 PX-&PR MPY *QX+,A ; A := QR*WR || QX-&QIbutterflyend: ; Update pointers for next group PSHM AR0 MVDK d_data_idx,AR0 MAR *PX+0; preserve AR0 ; increment PX for next group73 MAR *QX+0 BANZD group,*GROUP_COUNTERPOPM AR0 MAR *QXLD d_data_idx,A SUB #1,A,B STLM B,BUTTERFLY_COUNTER STL A,1,d_data_idx LD d_grps_cnt,A STL A,ASM,d_grps_cnt LD d_twid_idx,A STL A,ASM,d_twid_idx BANZD stage,*STAGE_COUNTERMVDK d_twid_idx,AR0 fft_end: RET; increment QX restore AR0 ; Update counters and ind B = A-1 ; BUTTERFLY_COUNTER = #flies-1 ; doubl 1/2 the o 1/2 the ind AR0 = ind return to Real FFT main module④将 fft 的计算结果分离为 RP(偶实部) ,RM(奇实部) ,IP(偶虚部) ,IM (奇虚部) . RP(k)=RP(N-k)=0.5*(R(k)+R(N-k)) RM(k)=-RM(N-k)=0.5*(R(k)-R(N-k)) IP(k)=IP(N-k)=0.5*(I(k)+I(N-k)) IM(k)=-IM(N-k)=0.5*(I(k)-I(N-k)) RP(0)=R(0) IP(0)=I(0) RM(0)=IM(0)=RM(N/2)=IM(N/2)=0 RP(N/2)=R(N/2) IP(N/2)=I(N/2) 0C00h 0C01h 0C02h 0C03h …… 0CFCh 0CFDh 0CFEh 0CFFh RP(0)=R(0) IP(0)=I(0) RP(1) IP(1) …… RP(126) IP(126) RP(127) IP(127)740D00h 0D01h 0D02h 0D03h …… 0DFCh 0DFDh 0DFEh 0DFFha(0) a(1) IM(127) RM(127) …… IM(2) RM(2) IM(1) RM(1) ;========================================================= ;Unpack 256-Point Real FFT Output .def unpack unpack: ; Compute intermediate values RP, RM, IP, IM .asg AR2,XP_k .asg AR3,XP_Nminusk .asg AR6,XM_k .asg AR7,XM_Nminusk STM #fft_data+2,XP_k STM #fft_data+2*K_FFT_SIZE-2,XP_N AR2 -& R[k] (temp RP[k]) ; AR3 -& R[N-k] (tempRP[N-k]) ; AR7 -& temp RM[N-k] ; AR6 -& temp RM[k]STM #fft_data+2*K_FFT_SIZE+3,XM_Nminusk STM #fft_data+4*K_FFT_SIZE-1,XM_k STM #-2+K_FFT_SIZE/2,BRC RPTBD phase3end-1 STM #3,AR0 ADD *XP_k,*XP_Nminusk,A ; A := R[k]+R[N-k] =2*RP[k] SUB *XP_k,*XP_Nminusk,B ; B := R[k]-R[N-k] =2*RM[k] STH A,ASM,*XP_k+ ; store RP[k] at AR[k] STH A,ASM,*XP_Nminusk+ ; store RP[N-k]=RP[k] at AR[N-k] STH B,ASM,*XM_k; store RM[k] at AI[2N-k] NEG B ; B := R[N-k]-R[k] =2*RM[N-k] STH B,ASM,*XM_N store RM[N-k] at AI[N+k] ADD *XP_k,*XP_Nminusk,A ; A := I[k]+I[N-k] =2*IP[k] SUB *XP_k,*XP_Nminusk,B ; B := I[k]-I[N-k] =2*IM[k] STH A,ASM,*XP_k+ ; store IP[k] at AI[k] STH A,ASM,*XP_Nminusk-0 ; store IP[N-k]=IP[k] at AI[N-k] STH B,ASM,*XM_k; store IM[k] at AR[2N-k] NEG B ; B := I[N-k]-I[k] =2*IM[N-k] STH B,ASM,*XM_Nminusk+0 ; store IM[N-k] at AR[N+k] phase3end: ST #0,*XM_k; RM[N/2]=0 ST #0,*XM_ IM[N/2]=0 进而得到原始序列的傅利叶变换: AR(k)=AR(2NCk)=RP(k)+cos(k /N)*IP(k)Csin(k /N)*RM(k) AI(k)=CAI(2NCk)=IM(k)Ccos(k /N)*RM(k)Csin(k /N)*IP(k) AR(0)=RP(0)+IP(0) AI(0)=IM(0)CRM(0) AR(N)=R(0)CI(0) AI(N)=0 这里: A(k)=A(2NCk)=AR(k)+j AI(k)=F{a(n)} 0C00h 0C01h 0C02h 0C03h …… 0CFEh 0CFFh 0D00h 0D01h …… 0DFCh 0DFDh 0DFEh 0DFFh AR(0) AI(0) AR(1) AI(1) …… AR(127) AI(127) AR(128) AI(128) …… AR(254) AI(254) AR(255) AI(255); Compute AR[0],AI[0], AR[N], AI[N] .asg AR2,AX_k .asg AR4,IP_0 .asg AR5,AX_N STM #fft_data,AX_ AR2 -& AR[0] (tempRP[0]) STM #fft_data+1,IP_0 ; AR4 -& AI[0] (tempIP[0]) STM #fft_data+2*K_FFT_SIZE+1,AX_N ; AR5 -& AI[N] ADD *AX_k,*IP_0,A ; A := RP[0]+IP[0] SUB *AX_k,*IP_0,B ; B := RP[0]-IP[0] STH A,ASM,*AX_k+ ; AR[0] = (RP[0]+IP[0])/2 ST #0,*AX_ AI[0] = 0 MVDD *AX_k+,*AX_N; AI[N] = 0 STH B,ASM,*AX_N ; AR[N] = (RP[0]-IP[0])/2 ; Compute final output values AR[k], AI[k] .asg AR3,AX_2Nminusk76 .asg AR4,COS .asg AR5,SIN STM #fft_data+4*K_FFT_SIZE-1,AX_2NminuskSTM #cosine+K_TWID_TBL_SIZE/K_FFT_SIZE,COS STM #sine+K_TWID_TBL_SIZE/K_FFT_SIZE,SIN STM #K_FFT_SIZE-2,BRC RPTBD phase4end-1 STM #K_TWID_TBL_SIZE/K_FFT_SIZE,AR0 ; index of twiddle tables LD *AX_k+,16,A ; A := RP[k] ||AR2-&IP[k] MACR *COS,*AX_k,A ; A :=A+cos(k*pi/N)*IP[k] MASR *SIN,*AX_2Nminusk-,A ; A := A-sin(k*pi/N)*RM[k] || AR3-&IM[k] LD *AX_2Nminusk+,16,B ; B := IM[k] ||AR3-&RM[k] MASR *SIN+0%,*AX_k-,B ; B := B-sin(k*pi/N)*IP[k] || AR2-&RP[k] MASR *COS+0%,*AX_2Nminusk,B ; B := B-cos(k*pi/N)*RM[k] STH A,ASM,*AX_k+ ; AR[k] = A/2 STH B,ASM,*AX_k+ ; AI[k] = B/2 NEG B ; B := -B STH B,ASM,*AX_2N AI[2N-k] = -AI[k]= B/2 STH A,ASM,*AX_2N AR[2N-k] = AR[k] = A/2 phase4end: RET ; returntoRealFFTmain module ⑤计算功率谱密度 P[k]=AR(k)2+ AI(k)2 ;===================================================== ;Compute the Power Spectrum of the Complex Output of the 256-Point Real FFT .asg AR2,AX .asg AR3,OUTPUT_BUF .def power power: STM #data_output,OUTPUT_BUF STM #K_FFT_SIZE*2-1,BRC RPTBD power_end-1 STM #fft_data,AX SQUR *AX+,A SQURA *AX+,A;AR3-& || AI[2N-1](temp RM[1]) ; AR4 -& cos(k*pi/N) ; AR5 -& sin(k*pi/N); AR3 points to output buffer; AR2 points to AR[0] ; A := AR^2 ; A := AR^2 + AI^277 STH A,*OUTPUT_BUF+ power_end: RET; return to main program⑥计算输入信号主频 f 输入= Kfs/2N 其中 P ( K ) 为功率谱的最大值,fs 为采样频率,2N 为样本点数 .def find .asg AR2,POWER .asg AR3,STORE .asg AR4,INDEX .asg AR5,MAXFREQ find: STM #data_output,POWER STM #data_find,STORE STM #data_index,INDEX STM #data_freq,MAXFREQ LD #0,A LD #0,B STL A,*STORE STM #K_FFT_SIZE-1,BRC RPTB find_end-1 SUB *POWER,*STORE,A BC next,ALEQ MVDD *POWER,*STORE STL B,*INDEX MAR *POWER+ ADD #1,B find_end: RSBX FRCT MPY *INDEX,#K_SAMPLE_RATE,A ;A *Sample rate LD A,#-1-K_LOGN ;A/256 STL A,*MAXFREQ RET78 next: MAR *POWER+ ADD #1,B find_end: RET .end 本程序的配置文件如下: -E MAIN MEMORY { PAGE 0: PARAM: org = 3000h PAGE 1: STACK: SINE: COSINE: FFT: DATAIO: DARAM: } SECTIONS{ .text stack sin_tbl cos_tbl fft_bffr data_in data_out tsk_vars fft_vars org org org org org org = = = = = = h h h len=200h len=400h len=400h len=800h len=800h len=4000h len = 4000h: : : : : : : : :& & & & & & & & &PARAM STACK SINE COSINE FFT DATAIO DATAIO DARAM DARAMPAGE PAGE PAGE PAGE PAGE PAGE PAGE PAGE PAGE0 1 1 1 1 1 1 1 179 fft_result .bss .data }: & : & : &DARAM DARAM DARAMPAGE 1 PAGE 1 PAGE 1对程序的说明: 1. 程序运行起始地址为 3000H,输入的数据在数据空间地址为 1400H,长 度为 400H,输出的功率谱在数据空间地址 1800H,长度为 400H.程序 的输入数据请参考附带在光盘中的程序. 2. 程序运行前的输入数据的时域图和频域图和运行后的输出数据图如下:图 10.1 输入数据的时域图80 图 10.2 输入数据的频域图(用 CCS 画出)图 10.3 输出数据图(计算得到的输入信号的频域图) 图 10.1 中的横坐标表示点数; 10.2 中的横坐标表示频率, 图 频率可以在 CCS 中设置,没有实际意义,图中设置为 1024Hz.图 10.1 和图 10.2 在调入 程序到 CCS 中,在程序运行之前就可以得到. 运行程序,可以得到图 10.3,图 10.3 为程序运行后计算出输入信号的功 率谱图,直接在 CCS 中描点画图就可以得到图 10.3.81 从图 10.2 中可以看到,输入信号的频率成分为 341Hz,图 10.3 中频率成 分为 170Hz, 这是由于 CCS 内部的 FFT 变换已经将频率的高一半去除. 此外 图 10.3 的右边还出现一个频率成分,这正是频率的高一半的功率谱.它是和 低一半的频率成中心对称,这是由于采样引起的.实际上,对于一定的采样 频率,高一半的频率成分没有任何意义,因为不满足采样定理.【实验步骤】1, 把 USB 电缆接到 5410TDK 上,打开 5410TDK 电源开关. 2, 进入 CCS 后,选择 File→Load Programe→打开 lab10\Debug\fft.out. 3, 选择 View→Graph→Time/Frequency. 4, 在弹出的对话框中修改如下项目: Display Type: Single Time Start Address: 0x1400 Acquisition Buffer Size: 400 Display Data Size: 400 DSP Data Type: 16-bit signed integer Autoscale: On 5, 设置完成后点击 OK,即可出现图 10.1 图形. 6, 然后选择 View→Graph→Time/Frequency. 7, 在弹出的对话框中修改如下项目: Display Type: 8, 设置完成后点击 OK,即可出现图 10.2 图形. 9, 然后选择 Debug→Run.82 第一部分 基础性实验实验一,CCS 操作一,实验目的1. 2. 3. 掌握 TMS320C5400 系列汇编语言程序的基本格式, 掌握程序编译,连接,运行和调试的基本过程 熟悉 Code Composer Studio 的使用二,实验设备1. 2. 集成开发环境 Code Composer Studio(以下简称 CCS) 实验代码 ccs_basic.s54,ccs_basic.cmd 和 ccs_basic.gel三,实验内容1. 基本操作 a) 运行 CCS setup,选择 C54xx Simulator b) 建立一个新的项目 ccs_basic.pjt,并加入文件 ccs_basic.s54 和 ccs_basic.cmd c) 载入 ccs_basic.gel d) 在 Project--&Option 中加入适当的编译和连接的选项(如下图) e) Compiler --& files --& Asm File Ext --& 写入&s54&汇编语言扩展名 f) Build 整个项目,产生可执行文件 ccs_basic.outg) File-&Load Program,装载可执行文件 ccs_basic.out,并运行和调试. 2. 练习 a) b) 观察存储器映象文件 ccs_basic.Map,理解存储器的配置情况 观察和修改存储器单元的内容四,实验结果和提示1. 建立项目,并加入文件 a) 运行 C54xx simulator,选择 Project→New 菜单项,建立一个新的项目 ccs_basic.pjt,并 选 择 Project → add files to new project 菜 单 项 , 加 入 文 件 ccs_basic.s54 和 ccs_basic.cmd. b) 在工程视图中选中 GEL files 文件夹,单击鼠标右键,在弹出的菜单中选择 Load GEL 选项,载入 ccs_basic.gel 2. 编译和连接 a) 编译:选择 Project--&Build Option,在 Compile 表单的 Category 列表中,选择 basic 选 项,并设置 generate debug info 为 full symbolic debug 选项, 如图 1-1 所示. b) 连接:选择 Project--&Build Option,在 linker 表单中的 autoinit mode 选项中选择 no autointialization 模式,Output Filename 中输入\debug\ ccs_basic.out,Code Entry Point1 中输入 main,Map Filename 中输入 ccs_basic.map,然后保存选项设置,如图 1-2 所示.图 1-1 编译选项的选择图 1-2 连接选项的选择2 c) d) 3.选择 Project→Build 构建整个项目,产生可执行文件 ccs_basic.out 选择 File-&Load Program,装载可执行文件 ccs_basic.out观察存储器映象 Map 文件 a) b) c) d) e) f) g) 存储器的配置情况:页面 0 为程序空间,页面 1 为数据空间 程序文本段:起始地址 0x1000,存储区长度为 0x0021 数据段:其中需初始化数据段.data 起始地址 0x2005,存储区长度为 4 不需初始化数据段.bss 起始地址 0x2000,存储区长度为 5 堆栈段:起始地址 0x0100,存储区长度为 0x0400 向量段:起始地址 0x0080,存储区长度为 0x0080 全局符号在存储器中的位置:共 14 个符号,如符号.bss 在存储器中的位置为 0x2000选择 File→Open 打开 ccs_basic.Map 文件,可观察将上述信息与 cmd 文件中的设置比较,理解 cmd 文件和连接器的关系. 4. 输出文件 ccs_basic.out 的运行和调试 a) 运行程序:将可执行文件装载到 simulator 中,选择 Debug→Run 或按 F5 键运行程序, 按 shift F5 键可中止程序的运行;选择 Debug→Step into 或按 F8 键可实现程序的 单步执行. b) 5. 设置断点:把光标移到某一行,按 F9 键,这一行将会以红色高亮显示,表示在该行加 了一个断点. 观察和修改存储器单元的内容 a) 点击 CCS 操作界面左侧调试工具栏 的地址 b) 选择 View→memory 或点击 CCS 操作界面左侧调试工具栏 图标,在弹出的菜单中 输入存储器地址,可观察存储器的内容.如存储器地址为 0x2005 中内容为 0x000A. c) 修改源汇编程序中数据段内容,可查看存储器单元内容的变化.在数据表格复制到数 组 a[]后,如果把 a[0]的值由 10 改为 1,那么求和的结果将变为 0x001A. 图标,出现寄存器窗口,在该窗口查看存储器图 1-3自定义文件扩展名3 注: CCS 使用说明请参阅文献 spru327c.pdf,spru328b,pdf 及 CCS 在线帮助.程序代码及详细说 明见目录 D:\exp2004\chap3\lab1.实验二,DSP CPU 基础一,实验目的1.了解 TMS320C5400 系列汇编语言程序的基本格式,以及编译,连接的基本过程 2.进一步熟悉 Code Composer Studio 的使用 3.了解 C5400 中标志位对计算的影响,以及计算对标志位的影响二,实验设备1.集成开发环境 CCS 2.实验代码 cpu_basic.s54,cpu_basic.cmd 和 cpu_basic.gel三,实验内容1.基本操作 运行 CCS,选择 C54xx Simulator 建立一个新的项目,并加入文件 cpu_basic.s54 和 cpu_basic.cmd 载入 cpu_basic.gel 在 Project--&Option 中加入适当的编译和连接的选项 Build 整个项目,产生可执行代码(.out) File-&Load Program,装载可执行代码,并运行. 2. 基本调试 设置断点 单步执行 3. 练习 观察并理解程序和数据空间安排 测试 SXM,OVM,C16,FRCT 对计算结果的影响 测试计算和逻辑运算对 TC,C,OVA,OVB 的影响 4. DSK 仿真练习 a) b) 测试 SMUL 标志对计算的影响 测试 MP/MC,OVLY,DROM 对计算结果的影响MP/MC=0,切换 DROM 的数值,观测 0xfc00 地址存放的 A,U 律数据是否存在 MP/MC=0,OVLY=1, 观测 0x1000 地址存放程序是否存在(改变该处为只读) 四,实验结果和提示 1. 与运算相关的标志位 SXM 当 SXM 置 1 时,数据读写按照符号扩展的方式,因此 A=0xff ffff ff804 当 SXM 置 0 时,数据读写为无符号扩展的方式,因此 A=0x00 0000 ff80 OVM 当 OVM 置 1 时,数据运算的结果将按照 32 位饱和,因此 A=0xff
当 OVM 置 0 时,数据运算不饱和,因此 B=0x00 fffe 0000 C16 双 16 位计算比较 FRCT 乘法移位比较 TC 比特测试比较 C 进位,借位和大小比较 OVA, OVB 溢出标志比较 SMUL(软件仿真器无法模拟该标志位,需用 DSK 开发板观察该位的影响) 乘法饱和 2. 与存储器配置相关的标志位 DROM ROM 映射到数据空间 从 View 菜单的 Graph 中选择 Time/Frequency 即可生成一个时/频显示窗口,首先弹出的是&Graph Property&对话框,将其中 Start Address 改为 0xfe00.正弦表的长度为 0x0100 即 256,Acquisition Buffer Size 和 Display Data size 设为 256, (1)若 MC 为 0,DROM 为 1,page 设为 data 或 program,则有下图:图 2-1 ROM 中正弦表5 说明片内 ROM 可访问,片内 ROM 映射到数据空间 (2)若 MC 为 0,DROM 为 0,page 设为 data,则无数据显示.说明片内 ROM 不映射到数据空间 (3)若 MC 为 1,page 设为 program,无数据显示,说明片内 ROM 无法访问 OVLY 存储空间重叠 OVLY 为 1,片内 RAM 同时映射到程序和数据空间 注:程序代码说明见目录 D:\exp2004\chap3\lab2.实验三,数据寻址一,实验目的1. 2. 熟悉 C5400 的寻址方式 比较利用不同寻址方式的指令执行时间二,实验设备1. 集成开发环境 CCS 2. 实验程序 addressing.s54,addressing.cmd 和 addressing.gel三,实验内容h) 单步执行程序,观察地址寄存器的修改和目标数据 i) 利用 Profile 功能,观察不同寻址方式的指令的执行时间 使用 Profile 时,首先从 Profile 菜单中选择 Enable Clock 来打开测试时钟.测试时钟对 应着 CCS 中的一个全局变量 CLK, 可以通过在 Profile 菜单中选择 View Clock 来弹出 Profile Clock 窗口,并且从该窗口中观察变

我要回帖

更多关于 怎么查看你的硬件 的文章

 

随机推荐