CCS中C语言应用开发实例

山东大学信息学院DSP课程实验二

【实验目的】   

1. 熟练掌握DSP开发流程

2. 掌握运用C语言基于CCS的项目设计思路及过程

【实验要求】  

1. 利用MATLAB的滤波器设计软件,生成低通滤波器、高通滤波器,分别给出滤波器系数。

2. 利用MATLAB产生由不同的正弦波合成的波形文件,送入CCS。

3. 编写C程序,分别实现低通、高通滤波,用CCS画出波形,给出结果分析

4. 利用编译器的op选项,对实验二的C语言程序按照级别进行优化,给出耗时的变化的分析。

5. 应用CCS的内联函数实现低通、高通滤波,与步骤1中的耗时进行比较

【实验具体内容】

1. 利用MATLAB的滤波器设计软件,生成低通滤波器、高通滤波器,分别给出滤波器系数。

采用开发环境为:Win 10 Matlab2020a版本

  1. 在Matlab命令窗口输入:filterDesigner命令,打开FilterDesigner设计工具

uploading.4e448015.gif

正在上传…重新上传取消

图1.1  打开FilterDesigner

或直接选择Matlab中APP选择中的:FilterDesigner

图1.2  FilterDesigner设计工具

  1. 调整FIR低通滤波器的参数

选择FIR等波纹型、滤波器阶数200阶,采样频率4096、=200Hz/=300Hz

图1.3  FIR低通滤波器设计

设置好参数后点击“设计滤波器”,可通过观察“幅值响应”波形图直观的看出滤波效果。接下来需要将生成的滤波系数导出以便后续加载到CCS中。

图1.4  点击目标生成头文件

图1.5 生成C头文件界面

生成C头文件如下:

图1.6  生成低通滤波器系数头文件

  1. 调整FIR高通滤波器的参数

选择FIR等波纹型、滤波器阶数200阶,采样频率4096、=800Hz/=100Hz

图1.7  高通滤波器设计

同上述步骤生成C头文件:

图1.8  生成高通滤波器系数头文件

2. 利用MATLAB产生由不同的正弦波合成的波形文件,送入CCS。

在进行DSP算法设计时,我们通常会在Matlab上验证一下算法是否正确,然后再把算法移植到DSP当中,然而在移植的过程中会不可避免的出现各种问题,这时就需要将DSP内存中的数据导出来传到Matlab中进行分析处理,同时我们也需要从Matlab传递一些数据到DSP当中,比如我们想生成某种比较复杂的数据(例如各种类型的噪声),如果用C语言来写的话将会非常的麻烦,而这些用matlab能够亲而易举的完成,这时我们只需要将matlab生成的数据导入到DSP中就可以了

首先需要利用Matlab生成若干个数据,然后存储为dat格式的文件,然后通过CCS将dat格式的文件导入到DSP中。

dat文件时CCS能够识别的文件之一、CCS支持的dat文件格式为:

文件头为:

定数   数据格式 起始地址 页类型   数据块大小

1651         1        80000000         0           10

固定标识   数据格式     基地址       页类型       长度

固定标识:它的值固定为1651

数据格式:1-十六进制  2-十进制  3-十进制长整型  4-十进制浮点型

基地址:  就是要存入到DSP一段内存空间的首地址

页类型:  0-数据   1-程序  

长度:    装入数据的长度

1)首先给出Matlab源代码:

f1=100;

f2=1500;

fs=4096;

N=1024;

T=1/fs;

n=0:N;

y1=1024*sin(2*pi*f1*n*T);

y2=1024*sin(2*pi*f2*n*T);

y=y1+y2;

xto_ccs=round(y);

fid=fopen('input1.dat','w');%将文件头写入文件,将生成的y信号写入到文件,格式四位小数

fprintf(fid,'%d\n',xto_ccs);

fclose(fid);

采用=100Hz和两种频率(高频、低频)组成噪声信号波形

图2.1  源代码截图

  1. 运行一下程序得到一个名为input1的dat文件,其保存在当前Matlab活动的目录。

图2.2  生成.dat文件

用记事本的方式打开此文件里面的内容如下:

图2.3  生成.dat文件中内容

  1. 将生成的的input1.dat文件拷贝到在CCS中新创建的工程项目工作文件夹中

  1. 在CCS中编写程序实现波形导入

源程序如下:

#include "stdio.h"

#include "LPF1.h"

#include <math.h>

#include <stdlib.h>

#define Length 1024

#define pi 3.1415926

  

long yn; //保存滤波后结果,32位长整型

long input[Length]; //

long output[Length];

int i;

void main() {

int m,n;

for(n=0;n<Length+BL;n++) //卷积计算

{

yn=0;

for(m=0;(m<BL)&&(m<n);m++)

yn+=B[m]*input[n-m];

output[n]=yn;

}

while(1);

}

  1. 在while(1)处设置断点

图2.4  设置断点

接下来打开断点管理器Breakpoints,进行断点设置

图2.5  打开断点管理器

图2.6  断点管理器内容

图2.7  设置断点Action

进行如下选择:

File:选择所产生的.dat文件作为数据导入

Wrap Around:True

Start Address:input

Page:DATA

Length:1024

图2.8  Read Data from File 设置

点击OK后即可完成Matlab合成波形文件并送入CCS

3. 编写C程序,分别实现低通、高通滤波,用CCS画出波形,给出结果分析

建立工程添加文件。建立工程,工程结构如下图所示。将在matlab中生成的c语言头文件添加至CCS工程文件夹下面。在matlab生成的CCS头文件中,需要调用matlab的头文件。将头文件拷贝到工程路径下即可。

图3.1  工程结构框架

只拷贝生成的头文件后,运行可能会出错,提示缺失某个文件“tmwtypes.h”。该文件在matlab安装路径下。可以直接在安装路径下搜索该文件,直接拷贝到工程路径下即可。

输入信号生成,代码如下:

      for(i=0;i<Length;i++)

                   input[i]=1024*sin(w1*i)+1024*sin(w2*i);  

因为计算时,其他数据类型都为整数,因此需要将-1~+1的小数格式转换为整数,不然在-1~+1的小数数据绝对值不超过1,那么在内存中都将被视作0,会导致波形严重失真。所以在此处需要将小数数据转换为整数型,又因为最后输出数据类型设置为long型,为了保证在计算过程中不溢出,Q值不能设置过大。

卷积计算公式为:

在计算机中要处理的信号和处理完成后的信号都可以用数组来保存,并且信号都为因果信号,即下表没有负数项,下标可以从0开始计算

已知h(n)的长度为BL,x(n)的长度为Length,利用外层循环控制n,内层循环控制m,将卷积计算的公式可表示为如下代码。

for(n=0;n<Length+BL;n++)              

    {undefined

        yn=0;

        for(m=0;(m<BL)&&(m<n);m++)

            yn+=B[m]*input[n-m];

        output[n]=yn;

    }

CCS仿真测试情况  

  1. 源代码如下:

#include "stdio.h"

#include "LPF1.h"

#include <math.h>

#include <stdlib.h>

#define Length 1024

#define pi 3.1415926

int fs=4096; //采样频率

int f1=100; //信号频率

int f2=1500; //噪声频率

#define w1 2*pi*f1/fs //信号角频率

#define w2 2*pi*f2/fs //噪声角频率

long yn; //保存滤波后结果,32位长整型

long input[Length];

long output[Length];

int i;

void main() {

int m,n;

for(i=0;i<Length;i++)

input[i]=1024*sin(w1*i)+1024*sin(w2*i); //待滤波信号,乘以1024转换为Q10格式(sin范围为-1~1,将其转换为整数格式)

for(n=0;n<Length+BL;n++) //卷积计算

{

yn=0;

for(m=0;(m<BL)&&(m<n);m++)

yn+=B[m]*input[n-m];

output[n]=yn;

}

while(1);

}

  1. 点击调试按钮,出现如下界面

图3.2  调试结果

  1. 设置断点。在while(1)处设置断点。如下图所示:

图3.3  设置断点

  1. 装载项目

图3.4  装载项目

选择volume2.out文件,并点击打开

图3.5  选择volume2.out文件

  1. 运行程序。将光标置于断点处后,点击调试栏中的“Run to cursor”

图3.6  点击Run to cursor执行程序

  1. 波形生成。利用CCS软件自带的graph工具测试输入输出波形,并且可以对信号进行频谱分析。操作过程如下图所示:

图3.7  创建Graph图标窗口

  1. Graph显示参数设置

①时域显示参数设置,如下图,设置完成后点击OK即可显示曲线

图3.8  时域显示参数设置

参数说明:

Acquisition Buffer Size:表示通过仿真器从DSP的内存中读取的数据的大小。由于程序里用于存储信号的数组大小是1024,这里我们设置成1024,也可以设置成比他更小的值,这样就只能看到信号的部分图像。

Dsp Data Type:数据类型设置,因为在程序中使用的是long型数据,在这里需要设置成32位整形数据。

Index Increment:索引增量,表示每个数据点序号的相差值,这里是逐点显示的,因此默认设置为1,如果想要每隔一个点显示一个那么就应该设置成2。

Q_Value:Q值,一般这不常用,默认。

Sampling Rate Hz:以Hz为单位的采样率,在这里我们的采样率是4096,因此设置成4096。

Start Address:开始地址,表示要显示的数组的首地址,假设我们现在想要看的是数组input因此,设置这里设置成input。

Auto Scale:自动调整显示比例使其适应整个显示窗口,默认即可。

Axis Display :显示坐标轴,默认即可。

Data Plot Style:数据绘制类型,有Line和Bar两种,这里选择Line。

Display Data Size:显示数据大小,表示要将多少个点的数据进行显示,注意这个值一定要小于等于Acquisition Buffer Size,这里设置成1024。

Grid Style:网格类型,有 NoGrid, Minor Grid, Major Grid三种选项,默认即可。

Magnitude Display Scale:幅值显示类型,有Linear(线性的)Logarithmic(对数的),默认即可。

Time Display Unit:显示时间的单位有sample,s, ms, us几种可选,其中sample表示显示的点的序号,默认即可。

Use Dc Value For Graph:是否使用Dc值,一般不常用,默认即可。

②频谱显示参数设置,如下图

图3.9  FFT显示参数设置

参数说明:部分参数和时域显示时的参数相同,有部分不同如下:

Signal Type :信号类型,有Real,Complex这两种,此处信号数据均为实数据,因此我们选择Real,如果数据类型是Complex,选择Complex。

Frequency Display Unit :频率显示单位,有Hz,KHz,MHz三种,这里选择Hz。

FFT Order :FFT的阶数,由它来决定FFT Frame Size,例如 FFT Order 设置为5 那么FFT Frame Size 就为32 = 2^5。这里我们设置成12,那么FFT Frame Size就是4096。

FFT Frame Size :FFT做变换的点数,FFT Order决定,此处选择4096点FFT。

FFT Window Function :FFT窗函数,选择做FFT变换时采用什么窗函数,有Rectangular(矩形窗),Bartlett,Blackman,Hamming,Hanning,选择什么窗函数根据实际的需求来决定到底采用哪一个窗函数,默认即可。

  1. 波形显示

①按照上述操作实现的低通滤波

图3.10  低通显示图形

②将头文件中的LPF1.h文件更换成为HPF.h后,重复上述操作实现高通滤波

图3.11  高通显示图形

  1. 利用编译器的op选项,对实验二的C语言程序按照级别进行优化,给出耗时的变化的分析。

每个文件都可以设置编译器优化,右键.c文件->File Specific options>optimization level 设置成空的,即可在debug的时候避免出现异常。开编译器优化可能导致跟踪函数的变量时值是错的。编译优化的好处是加快代码运行速度,但缺点就是只能把函数当做黑盒,函数内部的bebug结果是不可靠的。所以一般将算法和流程编到不同的文件中去,因为算法文件一般都是验证完了的,不怎么需要调试,所以算法文件开适当的优化,流程文件不开优化。这样既能提高运行效率又可以不影响流程的调试

图4.1  选择File Specific options

图4.2  Opt Level选择

进行代码优化,先要找出程序的瓶颈,即占用CPU时间较多的代码,然后对其进行有针对性的优化。使用CCS提供的代码剖析工具Profile可以统计显示出程序中各个重要段和函数的运行时间,找出运算量较大的程序段,优化这些程序段,对于提高算法的性能有巨大影响。
  ①联合使用-pm和-03编译选项,对代码进行项目级的优化:CCS提供了强大功能的编译选项,从-O0到-O3共四级优化。-O3编译选项使能软件流水和其他优化方法,-pm选项从程序代码角度,把整个项目的所有源程序联合起来,作为一个模块来处理。-pm和-03两个选项联合使用,能进行一系列的优化,并且代码尺寸变小很多。 

②使用const、restrict 关键字修饰指针:const指示编译器其修饰的指针所指向的内容不能修改;restrict指示编译器其修饰的指针与其他指针指向的内容不会覆盖,这些信息使两个指针不会访问同一存储器地址,可以消除存储器之间的相关性,这样可以并行执行多个数据的读取和运算,使代码运行达到昀大效率。
③对短字长数据使用宽长度的存储器访问(数据打包处理):即当CPU执行一连串短型数据(如16bit数据)操作时,可将数据类型设置为32bit长度的int型,这样可以一次性访问2个短型数据,然后使用C6000指令,同时进行两个数据的操作,减少了对内存的访问,这比采用16bit长度short型节约一半的时间。
  ④循环展开,把C语言中的循环打开,把多循环变为少循环,减少循环嵌套,使得可能并行的指令增加,从而改进软件流水编排,改善代码性能。
⑤减少C函数的调用,尽量使用系统提供的内联函数(intrinsics函数)代替C函数,C6000编译器提供了许多intrinsics,是直接与C6000汇编指令映射的在线函数,可以快速优化C代码,这样减少许多不必要的操作,提高代码运算速度。
 ⑥使用软件流水技术,软件流水是一种对循环中的指令进行调度优化的技术,利用软件流水可生成非常紧凑的循环代码。当编译时采用-O2或-O3级别的优化选项时,编译器将对程序中的循环进行软件流水。通过软件流水的优化,可以大大提高循环代码的效率,极大地实现指令的并行性。

  1. 打开耗时计数时钟

图4.3  打开Clock视图

  1. 观察不同优化级别耗时情况

图4.4  优化级别为NONE

图4.5  优化级别为o0

图4.5  优化级别为o1

图4.5  优化级别为o2

图4.5  优化级别为o3

  1. 应用CCS的内联函数实现低通、高通滤波,与步骤1中的耗时进行比较

    在使用fir函数之前首先要把相关的头文件和一些库文件加入到工程中,在如图的安装路径下找到并打开此头文件和一个库文件,复制到工程所在文件夹中

图5.1  复制相关头文件库文件

接着在工程右键打开build option选项里把头文件的路径加进去即可,最后还要把54xdsp.lib这个库文件导入工程的library中,不然会有报错

         

图5.2  添加头文件

接着编写C文件的程序和cmd文件,核心程序如下:

#include <stdio.h>

#include <math.h>

#include "dsplib.h"

#define pi 3.1415926

//采样频率,单位Hz

int fs=4096;

//产生正弦波频率,单位为Hz

int f1=100,f2=1500;

//定义信号角频率

#define w1 2*pi*f1/fs

#define w2 2*pi*f2/fs

//输入波形数组大小

DATA In[1024];

/*------------等波纹设计法(Equiripple),阶数64,通带幅值衰减 Wpass=1dB,阻带幅值衰减 Wstop1=80dB---------------*/

/*通带频率 Fpass=100Hz,截止频率 Fstop=150Hz,采样频率 Fs=4096Hz*/

#pragma DATA_SECTION(lowpass,".lowpass")//定义一个数据段,需要在cmd文件中有相应的段定义

DATA lowpass[64] = {

     -129,      4,     11,     24,     43,     67,     98,    136,    180,

      232,    291,    357,    430,    510,    596,    686,    782,    880,

      981,   1083,   1185,   1285,   1381,   1474,   1560,   1638,   1708,

     1769,   1818,   1856,   1882,   1895,   1895,   1882,   1856,   1818,

     1769,   1708,   1638,   1560,   1474,   1381,   1285,   1185,   1083,

      981,    880,    782,    686,    596,    510,    430,    357,    291,

      232,    180,    136,     98,     67,     43,     24,     11,      4,

     -129

};

/*------------------------------------------------------------------------------------------------------*/

/*低通滤波数据缓存*/

#pragma DATA_SECTION(buf1,".buffer1")//定义一个数据段,需要在cmd文件中有相应的段定义

DATA buf1[64] ;

DATA  *buf1ptr = &buf1[0];

DATA ff1[1024];//低通滤波输出数组

void main()

{

    int i;

  printf("HelloWorld!\n");

for(i=0;i<1024;i++) //产生波形

In[i]=1024*sin(w1*i)+1024*sin(w2*i)

/*fir(DATA *x, DATA *h, DATA *r,DATA **d, ushort nh, ushort nx);

    *x:待滤波信号输入         

    *h:滤波系数,即一个数组

    *r:滤波输出

    *d:滤波输出缓存

    *nh:滤波器的阶数

    *nx:待滤波信号输入的大小,即数组的大小

*/

fir(In, lowpass, ff1, &buf1ptr, 64, 1024); //  调用函数,低通滤波

while(1);

}

MEMORY {

   PAGE 0:   VECT:       origin = 0x80,         len = 0x80

   PAGE 0:   PROG:       origin = 0x100,        len = 0x3f00  

   PAGE 1:   DATA:       origin = 0x4000,       len = 0x3c00   

   PAGE 1:   f1aDATA:     origin = 0x8000,       len = 0x80

   PAGE 1:   f1bDATA:     origin = 0x8080,       len = 0x80

} /* MEMORY */

SECTIONS {

   .text    > PROG PAGE 0               /* code                     */

   .switch  > PROG PAGE 0               /* switch table info        */

   .cinit   > PROG PAGE 0

   .vectors > VECT PAGE 0                /* interrupt vectors         */

   

   .cio     > DATA PAGE 1               /* C I/O                     */  

   .data    > DATA PAGE 1       /* initialized data          */

   .bss     > DATA PAGE 1       /* global & static variables */

   .const   > DATA PAGE 1               /* constant data             */

   .sysmem  > DATA PAGE 1               /* heap                      */

   .stack   > DATA PAGE 1               /* stack                     */

   .buffer1:  {} align =64,  load = f1aDATA PAGE 1   

   .lowpass:  {} align =64,  load = f1bDATA PAGE 1      

}   /*SECTIONS */

由于在程序中定义了数据段,所以要在cmd文件中定义使用的数据段大小

图5.3  定义cmd文字内容

再次编译一下,如果没有问题的话应该是没有错误和警告的,然后就可以查看频谱了,操作跟上面看输入信号频谱一样,需要修改的是开始的地址,改为滤波输出的数组的名称即可

图5.4  修改滤波输出数组名

图 5.5  运用内联函数实现低通滤波

图5.6  运用内联函数实现高通滤波

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值