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
。本文将采用单速率的方式,此处不需要进行设置。
好,我们来看看产生的代码例子,#define
TAPS 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,//
链式指针寄存器
512 TAPS,//
系数buffer
长度寄存器,我们修改为宏定义TAPS
1 -1, //
系数修改寄存器,根据加速器
FIR_CF_buff2,
FIR_OP_buff2,
512 WINDOWSIZE, //
输出BUFFER
长度寄存器
1,
FIR_OP_buff2+0,
FIR_IP_buff2,
512 TAPS+ WINDOWSIZE-1,//
输入BUFFER
长度寄存器
1,
FIR_IP_buff2+0,
511|(511<<14) TAPS -1|((WINDOW_SIZE-1)<<14)
};
//
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
*pFIRCTL1=FIR_EN|FIR_DMAEN|FIR_CH2|FIR_RND0;
}
上面呢,将加速器的使能语句注释掉了,是什么原因呢。在音频系统里面,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
opt为FIR输出buffer
*handle->inptr = in_ptr[i];
//音频采样过来的数据,填给FIR输入buffer
handle->inptr = circptr(handle->inptr,1,iptr, handle->taps + WINDOW_SIZE - 1);//移动指针
}
这样明了吗,有什么不明白的地方请在公众号ddongcloud发信息给我!
作为一名技术工程师,我希望将我的知识分享给大家,帮助到大家一点点。如有写得有误的地方请指教,欢迎探讨!原创不易,尤其一个一个文字敲打出来,这都是实践中总结出来的步骤和方法。大家喜欢,请关注我的公众号:ddongcloud ,更多的原创文章和您分享。