当前位置:文档之家› FIR滤波器及其DSP实现

FIR滤波器及其DSP实现

FIR滤波器及其DSP实现
FIR滤波器及其DSP实现

FIR滤波器及其DSP实现

摘要

DSP技术一般指将DSP 处理器用于完成数字信号处理的方法与技术。目前的DSP芯片以其强大的数据处理功能在通信和其他信号处理领域得到广泛注意并已成为开发应用的热点技术。许多领域对于数字信号处理器的应用都是围绕美国德州仪器所开发的DSP处理器来进行的。DSP芯片是一种特别适合于进行数字信号处理运算的微处理器。主要应用是实时快速的实现各种数字信号处理算法,如卷积及各种变换等。

关键词:DSP;FIR滤波器

1 绪论

1.1引言

随着信息时代和数字世界的到来,数字信号处理已成为如今一门极其重要的学科和技术领域。数字信号处理在通信、语音、图像、自动控制、雷达、军事、航空航天、医疗和家用电器等众多领域得到了广泛的应用。在数字信号处理的应用中,数字滤波器很重要而且得到了广泛的应用。按照数字滤波器的特性,它可以被分为线性与非线性、因果与非因果、无限长冲击响应(IIR)与有限长冲击响应(FIR)等等。目前FIR滤波器的实现方法大致可分为三种:利用单片通用数字滤波器集成电路、DSP器件和可编程逻辑器件实现。单片通用数字滤波器使用方便,但由于字长和阶数的规格较少,不能完全满足实际需要,使用以串行运算为主导的通用DSP芯片实现要简单,是一种实时、快速、特别适合于实现各种数字信号处理运算的微处理器,借助于通用数字计算机按滤波器的设计算法编出程序进行数字滤波计算。由于它具有丰富的硬件资源、改进的哈佛结构、高速数据处理能力和强大的指令系统而在通信、航空、航天、雷达、工业控制、网络及家用电器等各个领域得到广泛应用。

1.2课题背景

数字信号处理就是用数字信号处理器(DSP)来实现各种算法,由于具有精度高、灵活性强等优点,已广泛应用在数字图像处理、数字通信、数字音响、声纳、雷达等领域。数字滤波技术又是进行数字信号处理的最基本手段之一,它是对数字输人信号进行运算,产数字输出信号,以改善信号品质,提取有用信息,或者

把组合在一起的多个信号分量分离开来为目的。

在信号处理领域中,对于信号处理的实时性、快速性的要求越来越高,因此在许多信息处理过程中,如对信号的过滤、检测、预测等,都要广泛地用到滤波器。其中数字滤波器具有稳定性高、精度高、设计灵活、实现方便等许多突出的优点,避免了模拟滤波器所无法克服的电压漂移、温度漂移和噪声等问题,因而随着数字技术的发展,用数字技术实现滤波器的功能越来越受到人们的注意和广泛的应用。而有限冲激响应(FIR)滤波器能在设计任意幅频特性的同时保证严格的线性相位特性,在示否音、数据传输中应用非常广泛。

1.3 研究意义

用可编程DSP 芯片实现数字滤波可通过修改滤波器的参数十分方便地改变滤波器的特性。因此,我们有必要对滤波器的设计方法进行研究,理解其工作原理,优化设计方法,设计开发稳定性好的滤波器系统。我们将通过DSP 设计平台,实现较为重要的FIR 和自适应滤波器系统。从而通过本课题的研究,掌握滤波器的设计技术,为通信、信号处理等领域实用化数字滤波器设计提供技术准备。本科题的研究,将为今后设计以DSP 为核心部件的嵌入式系统集成提供技术准备,这不仅具有重要的理论意义,同时还具有重要的实际意义。

1.4主要研究内容

本课题是基于TI 公司近年推出的高性能定点DSP TMS320C5410设计滤波器系统,如:有限冲击响应滤波器(FIR )。本次课题的主要任务,就是掌握DSP 芯片开发技术,完成如下工作:

1、用窗函数法实现FIR 滤波器,通过调用四种窗口函数,截取不同的带通与低通滤波原型,满足以下性能要求:

带通滤波器:下阻带边缘:πω2.01=s ,dB A s 60=;下通带边缘:πω35.01=p ,dB R p 1=

上通带边缘:πω65.02=p ,dB R p 1=;上阻带边缘:πω8.02=s ,

dB A s 60=

低通滤波器:πω2.0=p ,dB R p 25.0=;πω3.0=s ,dB A s 50=

2、研究DSP 的结构特点,了解TI 公司的TMS3205410 DSP 器件,掌握DSP 系统的构成及软硬件设计方法和CCS 软件的调试方法;并以TI 公司的TMS3205410 DSP 为核心处理器,在DSP 上实现FIR 滤波器系统。

2、DSP 及其开发环境

一个典型的 DSP 系统如图2.1

示。

图2.1 典型的DSP 系统

图2.1是一个用DSP 做信号处理的典型框图。由于DSP 是用来对数字信号进行处理的,所以首先必须将输入的模拟信号变换为数字信号。于是先对输入模拟信号进行调整,输出的模拟信号经过A/D 变换后变成DSP 可以处理的数字信号,DSP 根据实际需要对其进行相应的处理,如FFT 、卷积等;处理得到的结果仍然是数字信号,可以直接通过相应通信接口将它传输出去,或者对它进行D/A 变换将其转换为模拟采样值,最后再经过内插和平滑滤波就得到了连续的模拟波形模拟信号。

一般来说DSP 的设计过程应遵循一定的设计流程,如图2.2示。

图2.2 DSP 基本设计流程

3、FIR 滤波器的DSP 的实现

3.1 FIR 滤波器的实现方法 DSP 应用

定义系统性能指标

硬件调试

选择DSP 芯片

软件编程 硬件设计

系统集成

软件调试 系统测试和调试

在数字信号处理系统中,常常要用到FIR 数字滤波器,这是因为用FIR 滤波器可以逼近任意幅频特性的滤波器,并获得很好的性能,在本论文中介绍FIR 低通滤波器的DSP 实现。

TMS3205410是16位的定点处理器,所以在进行汇编程序设计时,FIR 滤波器系数要采用Q15格式,即必须将上述系数转化为Q15格式,这只要将滤波器各系数乘以215即可。

用DSP 实现Z 1-算法是十分方便的,常用的方法有两种:线性缓冲区法和循环缓冲区法。在本文中采用的是循环缓冲法,循环缓冲区法的特点是:

(1) 对于N 阶的FIR 滤波器,在数据存储器中开辟一个也称为滑窗的N 个单元的缓冲区,滑窗中存放最新的N 个输入样本;

(2) 每次移入新的样本时,以新样本改写滑窗中老的数据,而滑窗中的其他数据不需要移动;

(3) 利用片内BK (循环缓冲区长度)寄存器对滑窗进行间接寻址,循环缓冲区地址首尾相邻。

FIR 滤波器的核心算法是计算输入信号与滤波器系数的卷积。设x(n)为输入信号,h(n)为FIR 滤波器的冲击响应,n=0,…,N-1,则FIR 滤波器的输出y(n)就是x(n)与h(n)的卷积,即:

()()()m h m n x n y N m *1

0∑-=-= 由于卷积是数字信号处理中最常用到的算法,因此几乎所有的DSP 芯片中都

设有专门的指令支持卷积运算。在TMS3205410中可以用macd 指令完成卷积。macd 指令的形式如下:

macd (Smem ,pmad ,src );

在macd 指令中,Smem 是间接寻址参数,它是指令中指向数据存储器的单地址。Pmad 是表示程序存储器地址的16位常数。Src 表示累加器,可以是A 累加器或是B 累加器。这条指令在执行时,先把指令中的pmad 常数送到程序地址寄存器的PAR 中,然后将Smem 地址中的数据用PAR 地址在程序存储器中读取的数据相乘,并将乘积结果累加到由Src 指定的累加器中。在指令执行时,Smem 地址中的内容被同时复制到T 寄存器和Smem 低中之后的下一个地址单元中。若采用repeat 指令重复执行macd 指令,则在执行指令的最后,PAR 寄存器自动加1,这样当macd 再次执行时就直接用PAR 中的地址读取程序存储器中的数据。通常情况下,macd 指令执行时需要三个周期。但是若用repeat 指令执行macd ,进入流水线后只要一

个周期就可以执行一次macd指令。由此可见,该指令同时完成了乘累加和数据延迟(移位)的功能,这正是卷积算法所要求的。

对于输入序列,它在两个循环缓冲器里的存储情况如下,要建立缓冲区首先将循环缓冲区大小寄存器的值设为N/2辅助寄存器AR4指到缓冲区1(Bufferl)的顶部AR5指到缓冲区2(Buffer2)的底部,新来一个样本存储到缓冲区1中时,应先将缓冲区1顶部的数据移到缓冲区2底部,处理器然后进行乘加运算,滤波程序每步运算后AR4 指向数据移到的下一个窗口,而AR5则指向下一个输入数据,对于下一步运算AR4指向地址1,AR5指向地址N/2。如图3.1示。

图3.1 FIR系数存储格式示意图

考虑到在执行macd指令时是将低地址的数据复制到高地址来完成延迟功能,所以在用macd指令计算卷积时,先计算x(n-N+1)与h(N-1)的乘积,最后才计算x(n)与h(0)的乘积。因此在程序中,FIR滤波器的系数在程序存储器中按倒序存储。

3.2FIR滤波器的软件设计及其调试

本设计中采用C语言和汇编语言混合编程的方式进行的,程序主体由C语言编写,而核心的FIR滤波算法则由汇编语言编写。用C语言开发DSP程序不仅使DSP开发的速度大大加快,而且开发出来的DSP程序的可读性和可移植性也大大增加,程序修改也很方便。采用C编译器的优化功能可以增加C代码的效率,在某些情况下,C代码的效率甚至接近手工代码的效率。用C语言开发DSP程序,在DSP芯片的运算能力不是十分紧张时是非常适合的。虽然C编译器的优化功能可以使C代码的效率大大增加,但是在某些情况下,C代码的效率还是无法与手工编写的汇编代码的效率相比,比如FIR滤波器程序。这是因为,即使最佳的C 编译器,也无法在所有的情况下都能够最佳地利用DSP芯片所提供的各种资源。

用C语言编写的中断程序虽然可读性很好,但由于进入中断程序以后,有时不管程序中是否用到,中断程序都将寄存器进行保护,从而大大降低中断程序的效率。如果中断程序频繁被调用,那么即使一条指令也是至关重要的。此外,用C语言实现DSP芯片的某些硬件控制也不如汇编程序方便,有些甚至无法用c语言实现。因此,在很多情况下,DSP应用程序往往需要用c语言和汇编语言的混合编程方法来实现,以达到最佳利用DSP芯片软硬件资源的目的。用C语言和汇编语言混合编程的方法主要有以下三种:

(1)独立编写C程序和汇编程序,分开编译和汇编,形成各自的目标代码模块,然后用链接器将c模块和汇编模块链接起来。例如,FIR滤波程序用汇编语言编写,对FIR程序用汇编器进行汇编,形成目标代码模块,与C模块链接就可以在C程序中调用FIR程序。

(2) 直接在C语言程序的相应位置嵌入汇编语句。

(3) 对c程序进行编译.生成相应的汇编程序,然后对汇编程序进行手工优化和修改。上面的例子采用的是第一种方法,这是一种常用的C语言和汇编语言接口方法,采用这种方法最重要的是必须遵守c编译器所定义的函数调用规则和寄存器使用规则。遵循这两个规则就可以保证所编写的汇编模块不破坏C语言的运行环境。C模块和汇编模块可以相互访问各自定义的函数或变量。在编写独立的汇编程序时,必须注意以下几点:

(1) 不论是用c语言编写的函数还是用汇编编写的函数,都必须遵循寄存器使用规则。

(2) 必须保护函数要用到的几个特定寄存器,在TMS320C54X的的c编译中,这些特定的寄存器包括:ARD (FP), ARI (SP), AR6和AR70其中,如果SP正常使用,则不必明确加以保护。换句话说,只要汇编函数在函数返回时弹出压入的对象,实际上就已经保护了SP,因而ARl可以自由地使用。其它寄存器是可以自由使用的。函数返回时ARP必须为to(3 ),中断程序必须保护所有用到的寄存器。

(4 )从汇编程序调用C函数时,以逆序方式压入堆栈,调用时再将参数弹出。

(5) 调用C函数时,C函数只保护几个特定的寄存器,而对于其它寄存器C函数是自由使用的。

(6) 长整型和浮点数在存储器中存放的顺序是低位字在低地址,高位字在高地址。

(7 )如果函数有返回值,则返回值存放在累加器ACC中。

(8)汇编模块不能改变由C程序产生的.cinit块,如果改变其内容则会引起不可预测的后果。

(9 )编译器在所有的标识符(函数名、变量名)前要加一下划杠“_”。因此,编写汇编语言程序时,必须在C程序可以访问的所有对象前加“_”。例如,在C程序中定义了变量x,如果要在汇编程序使用,既标记为“x",如果仅在汇编中使用,则只要不加下划杠,即使与C程序中定义的对象名相同,也不会造成冲突。

(10 ) 任何在汇编中定义的对象或函数,如果需要在C程序中访问或调用,则必须用汇编指令.global定义。同样,如果在C程序中定义的对象或函数,需要在汇编程序中访问或调用,在汇编程序中也必须用.global指令定义。另外一种C 语言和汇编混合编程的方法就是直接在C程序中嵌入汇编语句。采用这种方法一方面可以在C程序中实现用C语言无法实现的一些硬件控制功能,如修改中断控制寄存器、中断使能或无效、读取状态寄存器和中断标志寄存器等。另一方面,也可以用这种方法在C程序中的关键部分用汇编语句代替C语句以优化程序。采用这种方法的缺点是比较容易破坏C环境,因为C编译器在编译嵌入了汇编语句的C程序时,并不检查或分析所嵌入的汇编语句。但是如果对C编译器和C环境比较熟悉,采用这种方法也可以对C变量进行自由地操作。与独立编写汇编程序实现混合编程相比,这种方法具有以下优点:

(1) 程序的入口和出口由C语言自动管理,不必手工编写汇编程序实现。

(2) 程序结构清晰。这种方法保留了C程序的结构,如变量的定义等,因此程序结构清晰,可读性强。

(3) 程序调试方便。由于C程序中的变量全部由C语言来定义,因此采用C源码调试器可以方便地观察C语言变量。

汇编程序调试首先在CCS中建立新的工程fir.mak, 然后将汇编语言源文件(.asm或. c)文件以及中断向量文件(. asm)、位文件(. cmd)添加到工程中。如果是C 语言程序则还要添加“stdio. h”、“math. h”“rts. lib”编译通过后生成. out文件, 可以直接下载到实验板上。这时,要注意硬件状况, 特别是要注意硬件指示灯是否各自处于正常状况, 也要注意硬件仿真器是否正常工作。如果硬件没有问题, 程序就可以正常下载, 这时打开Momery窗口, 可以看到数据存储器已经被划分coff_ fir, d_ data_ buffer, in_ buffer,out_ buffer等区。编译并将整个FIR项目成功地下载到目标系统板后, FIR程序就可以在EVM 板上运行。程序见附录。

在主程序中,在flag = 0处设置断点;单击“Run”运行程序,程序将运行

至断点处停止;用View / Graph / Time/Frequency打开一个图形观察窗口如图3.2所示。

图3.2 图形参数设置窗口

设置观察图形窗口变量及参数为:采用双踪观察启始地址分别为x和y,长度为256的单元中数值的变化,数值类型为32位浮点型变量,这两个数组中分别存放的是经A/D转换后的输入混叠信号(输入信号)和对该信号进行FIR滤波的结果;单击“Animate”运行程序,调整观察窗口并观察滤波结果,如图3.3示。

图3.3 测试结果

通过测试波形可以看到,该DSP5410实际滤波表现达到了算法仿真的要求。考虑到DSP5410是定点DSP,所以将输入信号以及滤波器系数都转换成了定点数,为了防止溢出,将输入信号的幅值进行缩小,将其控制在-1到1之间。

附录 FIR的DSP实现程序

C1:FIR.c

/****************************************************************************** The programme of the FIR filter.

Using INT2 to get the input signal.

Array x is the input signal from A/D, the length is 256, 32-bit floating point.

Array y is the signal out of filter, the length is 256, 32-bit floating point.

Array h is the coefficient of the FIR filter, the length is 101, 101 order filter.

******************************************************************************* #pragma CODE_SECTION(vect,"vect")

#include "stdio.h"

#include "math.h"

#define pi 3.1415927

#define IMR *(pmem+0x0000)

#define IFR *(pmem+0x0001)

#define PMST *(pmem+0x001D)

#define SWCR *(pmem+0x002B)

#define SWWSR *(pmem+0x0028)

#define AL *(pmem+0x0008)

#define CLKMD 0x0058 /* clock mode reg*/

#define Len 256

#define FLen 101

double npass,h[FLen], x[Len], y[Len], xmid[FLen];

void firdes (double npass);

unsigned int *pmem=0;

ioport unsigned char port8001;

int in_x[Len];

int m = 0;

int intnum = 0;

double xmean=0;

int i=0;

int flag = 0;

double fs,fstop,r,rm;

int i,j,p,k=0;

void cpu_init()

{ //asm(" nop");

//asm(" STM #0, CLKMD");

//asm(" STM #0, CLKMD");

//asm(" rpt #0ffffh");

//asm(" nop");

//asm(" STM #0x97ff, CLKMD");

*(unsigned int*)CLKMD=0x0; //switch to DIV mode clkout= 1/2 clkin while(((*(unsigned int*)CLKMD)&01)!=0);

*(unsigned int*)CLKMD=0x27ff; //switch to PLL X 10 mode

PMST=0x3FA0;

SWWSR=0x7fff;

SWCR=0x0000;

IMR=0;

IFR=IFR;}

interrupt void int2()

{ in_x[m] = port8001;

in_x[m] &= 0x00FF;

m++;

intnum = m;

if (intnum == Len)

{

intnum = 0;

xmean = 0.0;

for (i=0; i

{

xmean = in_x[i] + xmean;

}

xmean = 1.0*xmean/Len;

for (i=0; i

{

x[i] = (double)(in_x[i] - xmean);

}

for (i=0; i

{

for (p=0; p

{

xmid[FLen-p-1] = xmid[FLen-p-2];

}

xmid[0] = x[i];

r = 0;

rm= 0;

for (j=0; j

{

r = xmid[j] * h[j];

rm = rm + r;

}

y[i] = rm;

}

m=0;

flag = 1;

}

}

void firdes(double npass)

{

int t;

for (t=0; t

{

h[t] = sin(pi*(N-(N-1)/2.0))*(0.54-0.46(cos(2*pi/(N-1)))/(pi* (N-(N-1)/2.0));

}

if (t = ((FLen-1)/2)) h[t]=npass;

}

void set_int()

{ asm(" ssbx intm");

IMR=IMR|0x0004;

asm(" rsbx intm");

}

void main(void)

{ cpu_init();

fs =250000 ; /*sampling frequency*/

fstop = 31250; /*cut-off frequency*/

npass = fstop/fs;

for (i=0; i

{

xmid[i]=0;

}

firdes(npass);

set_int();

for(;;)

{

if (flag == 1)

{

flag = 0; /* set breakpoint here */ }

}}

void vect()

{ asm(" .ref _c_int00"); /*pseudoinstruction*/ asm(" .ref _int2");

asm(" b _c_int00"); /* reset */

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* int0 */ asm(" nop");

asm(" nop");

asm(" rete"); /* int1 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" b _int2"); /* int2 */ asm(" nop");

asm(" nop");

asm(" rete"); /* tint0 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* brint0 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* bxint0 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* dmac0 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* tint1 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* int3 */ asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* hpint */ asm(" nop");

asm(" nop");

asm(" rete"); /* brint1 */ asm(" nop");

asm(" nop");

asm(" rete"); /* bxint1 */

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* dmac4 */

asm(" nop");

asm(" nop");

asm(" nop");

asm(" rete"); /* dmac5 */

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");

asm(" nop");}

C3:FIR.cmd 控制命令文件

-stack 0x100

MEMORY

{ PAGE 0: PROG: origin = 2b00h, length = 1500h

PAGE 1: DATA: origin = 0200h, length = 1d00h} SECTIONS

{ .text > PROG PAGE 0

.cinit > PROG PAGE 0

.switch > PROG PAGE 0

.vect > 3f80h PAGE 0

.data > DATA PAGE 1

.bss > DATA PAGE 1

.const > DATA PAGE 1

.sysmem > DATA PAGE 1

.stack > DATA PAGE 1}

相关主题
文本预览
相关文档 最新文档