|
本帖最后由 max-men 于 2013-4-17 19:50 编辑
这段时间被IIR摧残了千百遍,足足两个多星期一点进展都没有。心情很是不爽像大姨父来了般。其间几度要放弃了,但问题是存在的,有问题还是得去解决,无奈在此求助坛友。如有坛友帮忙解决愿以充话费形式答谢(注:本人学生党一枚,所涉问题无商业利益,少量话费以表谢意)。
如果问题得到解决我会将源代码完整奉上,以供坛友学习研究之用。如果你不明白IIR数字滤波器,如果你想学习了解数字滤波器,麻烦将帖子顶上去让更多的人看到。
背景:平台是STM32F3DISCOVERY, 开发环境MDK。
目的:AD采集数据(正弦信号20<F<20K),用IIR在F3实现数字滤波,DA输出滤波后的结果。(AD、DA均为片内)
遇到的问题:我要实现的是一个带通滤波器,为验证程序的正确我先做了一个简单的低通滤波器,滤波器系数是在MATLAB fadtool中生成的(系数生成步骤如下图)。现在的问题是假如我要实现一个通带为200Hz的低通,实际出来的是在通带内就不断衰减,不到200Hz基本就滤完了的一个“低通”。滤波器代码如下。
- /* 这是一个四阶IIR滤波函数
- *Src:是AD采样回的数据指针
- *Dst:运算完要送给DA的数据指针
- *coffs:滤波器系数指针
- **State:运算中需要的状态量指针
- Number:一次处理的数据量
- */
- void IIR_Filter(float32_t *Src,float32_t *Dst,float32_t *coffs,float32_t *State,uint8_t Number)
- {
- float32_t b0,b1,b2,b3,b4,a1,a2,a3,a4;
- float32_t x,y,x1,x2,x3,x4,y1,y2,y3,y4;
- float32_t acc;
-
- b0 = *coffs++;
- b1 = *coffs++;
- b2 = *coffs++;
- b3 = *coffs++;
- b4 = *coffs++;
- a1 = *coffs++;
- a2 = *coffs++;
- a3 = *coffs++;
- a4 = *coffs;
-
- x1 = State[0];
- x2 = State[1];
- x3 = State[2];
- x4 = State[3];
- y1 = State[4];
- y2 = State[5];
- y3 = State[6];
- y4 = State[7];
-
- while(Number--)
- {
- x = *Src++;
-
- acc = b0*x + b1*x1 + b2*x2 + b3*x3 + b4*x4;
- y = acc-a1*y1 - a2*y2 - a3*y3 - a4*y4;
- x1 = x;
- x2 = x1;
- x3 = x2;
- x4 = x3;
- y1 = y;
- y2 = y1;
- y4 = y3;
- y3 = y2;
- *Dst++ = y;
- }
-
- *State++ = x1;
- *State++ = x2;
- *State++ = x3;
- *State++ = x4;
- *State++ = y1;
- *State++ = y2;
- *State++ = y3;
- *State = y4;
- }
复制代码
- /*中断处理函数**/
- #include "stm32f30x_it.h"
- #include "Mine_iir.h"
- #define BlockSize 100 /*每次处理数据块的大小*/
- #define NumberOfConvertedValue 200 /*ADC,DAC数组大小*/
- extern __IO uint16_t ADC1ConvertedValue[NumberOfConvertedValue];
- float32_t Float32_ADC1ConvertedValue[NumberOfConvertedValue];
- float32_t Float32_DAC1ConvertedValue[NumberOfConvertedValue];
- __IO uint16_t DAC1ConvertedValue[NumberOfConvertedValue];
- /* 系数表*/
- float32_t CoeffTable[9] = {0.0004, 0.0017, 0.0025, 0.0017,0.0004,
- -3.1806,3.8612, -2.1122,0.4383};
- /*状态变量*/
- float32_t State[8]={0};
- void DMA1_Channel1_IRQHandler(void)
- {
- uint8_t j;
-
- if(DMA_GetITStatus(DMA1_IT_HT1))
- {
- DMA1->IFCR = DMA1_IT_GL1|DMA1_IT_HT1; /*Clears the DMAy Channelx's interrupt pending bits*/
-
- for(j=0;j<BlockSize;j++) //将无符号数转换为浮点数
- Float32_ADC1ConvertedValue[j]= ((float32_t)ADC1ConvertedValue[j])-683; //减直流分量
-
- IIR_Filter(Float32_ADC1ConvertedValue,Float32_DAC1ConvertedValue,CoeffTable,State,BlockSize);
-
-
- for(j=0;j<BlockSize;j++) //将浮点数转换为无符号数
- DAC1ConvertedValue[j]=(uint16_t)(Float32_DAC1ConvertedValue[j]+683);//加直流分量
- }
- else
- {
- DMA1->IFCR = DMA1_IT_GL1|DMA1_IT_TC1; /*Clears the DMAy Channelx's interrupt pending bits*/
-
- for(j=BlockSize;j<NumberOfConvertedValue;j++) //将无符号数转换为浮点数
- Float32_ADC1ConvertedValue[j]=((float32_t)ADC1ConvertedValue[j])-683;//减直流分量
-
- IIR_Filter(&Float32_ADC1ConvertedValue[BlockSize],&Float32_DAC1ConvertedValu[BlockSize],CoeffTable,State,BlockSize);
-
- for(j=BlockSize;j<NumberOfConvertedValue;j++) //将浮点数转换为无符号数
- DAC1ConvertedValue[j]=(uint16_t)(Float32_DAC1ConvertedValue[j]+683);//加直流分量
- }
- }
复制代码
我的滤波函数一次处理一个数据块,而不是一次只处理一个数据。这样做的目的是让内核少进中断提高效率。由于ADC、DAC速率要一致,而运算一个数据的时间要小于采样间隔,这就带来速度不一致的问题,解决的办法是为ADC到处理、处理到DAC之间构造缓冲区。我的方法是用DMA构造双缓冲区。实现如下
1、先定义两个长度相同数组,一个数组内存放ADC转换所得到的数,另一个数组存放滤波完要送给DAC的数。
开启ADC的DMA传输一半中断、传输完中断。
2、用定时器去触发ADC单次采样以实现采样率的控制。一次采样转换完后触发DMA将数据传到上面所定义的采样数组内。当传输完采样数组一半的数据后进入半传输完中断,在中断中处理前一半的数据,处理完的数据保存在1)中定义的DAC数组前半段内。在处理前一半数据的同时,ADC、DMA并没有停止,而是同时进行采样传送,因为DMA传送并不会打断内核的运行。当采样数组被填满后进入DMA传输完中断,同样在中断中处理后一半的采样数据,并把数据送到滤波完数组的后半段。因为前半段的采样数据已经处理完,在处理采后半段数据的同时新的数据可以覆盖旧的数据。
3、把DAC同样设置成定时器触发,并且和ADC用同一定时器。开启DAC的DMA,将滤波完的数据输出。只要处理一半数据的时间小于采样一半数据的时间就可以实现ADC连续采样的同时DAC以相同速率连续输出。
我不知道我的问题是不是出在滤波函数上,这里我也把我的工程传上来,一来可以给各位一点点参考,二来是让高人帮我找到问题的所在。(DMA构造双缓冲区,这个方法应该是没有错的),在此先行谢过!
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|