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版本
- 在Matlab命令窗口输入:filterDesigner命令,打开FilterDesigner设计工具
图1.1 打开FilterDesigner
或直接选择Matlab中APP选择中的:FilterDesigner
图1.2 FilterDesigner设计工具
- 调整FIR低通滤波器的参数
选择FIR等波纹型、滤波器阶数200阶,采样频率4096、=200Hz/=300Hz
图1.3 FIR低通滤波器设计
设置好参数后点击“设计滤波器”,可通过观察“幅值响应”波形图直观的看出滤波效果。接下来需要将生成的滤波系数导出以便后续加载到CCS中。
图1.4 点击目标生成头文件
图1.5 生成C头文件界面
生成C头文件如下:
图1.6 生成低通滤波器系数头文件
- 调整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 源代码截图
- 运行一下程序得到一个名为input1的dat文件,其保存在当前Matlab活动的目录。
图2.2 生成.dat文件
用记事本的方式打开此文件里面的内容如下:
图2.3 生成.dat文件中内容
- 将生成的的input1.dat文件拷贝到在CCS中新创建的工程项目工作文件夹中
- 在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);
}
- 在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仿真测试情况
- 源代码如下:
#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);
}
- 点击调试按钮,出现如下界面
图3.2 调试结果
- 设置断点。在while(1)处设置断点。如下图所示:
图3.3 设置断点
- 装载项目
图3.4 装载项目
选择volume2.out文件,并点击打开
图3.5 选择volume2.out文件
- 运行程序。将光标置于断点处后,点击调试栏中的“Run to cursor”
图3.6 点击Run to cursor执行程序
- 波形生成。利用CCS软件自带的graph工具测试输入输出波形,并且可以对信号进行频谱分析。操作过程如下图所示:
图3.7 创建Graph图标窗口
- 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,选择什么窗函数根据实际的需求来决定到底采用哪一个窗函数,默认即可。
- 波形显示
①按照上述操作实现的低通滤波
图3.10 低通显示图形
②将头文件中的LPF1.h文件更换成为HPF.h后,重复上述操作实现高通滤波
图3.11 高通显示图形
- 利用编译器的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级别的优化选项时,编译器将对程序中的循环进行软件流水。通过软件流水的优化,可以大大提高循环代码的效率,极大地实现指令的并行性。
- 打开耗时计数时钟
图4.3 打开Clock视图
- 观察不同优化级别耗时情况
图4.4 优化级别为NONE
图4.5 优化级别为o0
图4.5 优化级别为o1
图4.5 优化级别为o2
图4.5 优化级别为o3
- 应用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 运用内联函数实现高通滤波
标签:文件,头文件,1024,CCS,C语言,实例,DATA,PAGE 来源: https://blog.csdn.net/Alex_Belmont/article/details/121503674