FIR滤波器的作用:它可以对音响进行纠正,使用任何的测量工具测得房间的脉冲响应,我们知道FIR系数的傅里叶变换即为频率响应,那么就可以得到房间最适合音响的一种音质,这对于音响的保护和产生最佳音质是有及其重要的作用,越来越多的数字音频设备中将FIR加入它们的设备中,是一个极大的卖点。
我们来看看Sysmtrix(思美)的FIR滤波器是怎么样的,如下图。这是一个1024 Taps的FIR滤波器,它直接点击Load Table选择,选择滤波器系数文件(*.csv)文件就可实现系数的导入,滤波器图形显示这是一个2k的低通滤波器。
好,接下来我们看看如何实现这个功能呢,大部分的DSP内部函数都带有了FIR函数,直接调用即可。但是FIR滤波器是很占资源的,使用CPU去做,很难达到理想的一种应用,要在时间和处理之间进行平衡。ADI的SHARC DSP呢,它自带有FIR硬件加速器,使用加速器不占用CPU时间,是非常棒的。
那么FIR加速器如何使用呢,用过VDSP的朋友应该知道,VDSP5.0有一个Accelerators插件,使用它我们可以通过参数配置的方式轻松得到一份代码例子(没有的朋友请到官网进行下载,按照文档安装)。
我们看左边的FIR Accelerator,它有很多参数,下面逐一解释:
GlobalSettings for all channels:
-No ofChannels : 需要做FIR滤波器的通道数,FIR采用TDM方式将外部数据搬移到加速器。
-DataFormat: 数据格式指定定点还是浮点,既然是SHARC DSP,它肯定是浮点了。
-AutoIterate?: 自动迭代,一帧数据处理完后是否自动从TCB加载数据处理。设置为否,需要CPU启动。
-RoundingMode: 舍入模式,此处默认即可,既然是浮点,影响不大。
-DMAInterrupt: 指定DMA中断发生在CPU哪个中断脚。
-Statusinterrupt: 状态中断,正常状态下无需设置。
-InterruptWhen: 何时中断,是所有通道都处理完了中断还是单个通道处理完了就中断CPU。
ChannelSpecific Setting: 这里的设置我们先不管吧,主要对BUFFER进行设置,使用这个软件进行设置还不如直接进行代码修改来得快。注意到里面有一个Sample Rate Convertion,也就是说它还有采样率转换,比如输入48K,FIR滤波之后得到8k的数据,降采样,这个时候我们将Ratio设置为6。本文将采用单速率的方式,此处不需要进行设置。
好,我们来看看产生的代码例子,#defineTAPS 512,#define WINDOWSIZE 512,具体根据应用情况进行修改。
/* This file includes the initialization codes for FIR, IIR and FFT Accelerators */
#include "def21489.h" #include "Cdef21489.h"
void Init_FIR();
/* Declaring the external buffers needed for FIR Accelerator*/ extern int FIR_IP_buff1[]; //输入数据BUFFER,大小为TAPS+WINDOWSIZE-1 extern int FIR_OP_buff1[]; //输出BUFFER,大小为WINDOWSIZE extern int FIR_CF_buff1[]; //系数BUFFER,大小为TAPS
extern int FIR_IP_buff2[]; extern int FIR_OP_buff2[]; extern int FIR_CF_buff2[];
//这个很重要,DMA TCB配置 /*Adding the TCB for FIR channels*/ int FIR_TCB_CH2[13]={ 0,//链式指针寄存器 FIR_CF_buff2, FIR_OP_buff2, 1, FIR_OP_buff2+0, FIR_IP_buff2, 1, FIR_IP_buff2+0, };
//TCB1亦如是配置 int FIR_TCB_CH1[13]={ FIR_TCB_CH2+12, 512, 1, FIR_CF_buff1, FIR_OP_buff1, 512, 1, FIR_OP_buff1+0, FIR_IP_buff1, 512, 1, FIR_IP_buff1+0, 511|(511<<14) };
/* Adding the Initialization Code for FIR Accelerator Now */
void Init_FIR() {
int temp; //链式指针寄存器 FIR_TCB_CH2[0]=FIR_TCB_CH1+12; //这里映射DMA中断到PI0 //Mapping the FIR DMA interrupt temp=*pPICR0; temp&=~(P0I0|P0I1|P0I2|P0I3|P0I4); temp|=P0I0|P0I1|P0I3|P0I4; *pPICR0=temp; //选择FIR加速器,FIR,IIR,FFT只能使用一个,能不能同时使用三个,这是不行的 //Selecting the FIR Accelerator temp=*pPMCTL1; temp&=~(BIT_17|BIT_18); temp|=FIRACCSEL; *pPMCTL1=temp;
//PMCTL1 effect latency asm("nop;nop;nop;nop;");
//Initializing the chain pointer register *pCPFIR=FIR_TCB_CH1+12-0x80000; //使能加速器,一旦配置好,加速器就会运行,这里将它注释掉 //Now Enabling the FIR Accelerator
}
|
上面呢,将加速器的使能语句注释掉了,是什么原因呢。在音频系统里面,DSP是按音频的采样率取帧进行处理,也就是采集到了一个WINDOWSIZE即产生中断,开始音频处理任务。如果你将FIR加速器马上使能或者让它自动迭代处理,无法配合我们的应用程序完成数据同步的问题。最好的办法是音频处理完一帧,使能FIR加速器,在音频的下一帧处理中,我们就可以引用加速器处理之后的数据了,再将需要处理的数据扔回给加速器。
最后,还有一个问题,细心的朋友可能会发现,输入BUFFER长度是TAPS+WINDOWSIZE -1,那么输入数据该如何填充呢?OK,这是一个循环buffer,初始化的时候定义一个指针指向TAPS-1位置。在填充数据时,使用circptr内联函数将指针重定义位置,直接看代码吧。
float* inptr = fir_ip_bufffer(0)+handle->taps-1; //初始化为taps-1位置 ......... //填充数据代码段 for(i=0;i<WINDOWSIZE;i++){ out_ptr[i] = optr[i]; //opt为FIR输出buffer *handle->inptr = in_ptr[i]; //音频采样过来的数据,填给FIR输入buffer handle->inptr = circptr(handle->inptr,1,iptr, handle->taps + WINDOW_SIZE - 1);//移动指针 } |
如果想要了解更多知识,可以关注公众号【DSP-Tech】.你也可以加我微信,和我一起共同成长学习吧。