新闻  |   论坛  |   博客  |   在线研讨会
手把手教你用matlab生成STM32官方IIR滤波器的系数(三)
powermod | 2014-11-18 09:27:58    阅读:5778   发布文章

本节主要介绍如何在STM32上实现一个IIR高通滤波器。

在介绍如何实现STM32的IIR滤波程序之前,再看一下用matlab仿真得到的结果,以下是输入300Hz时的输入和输出(下)。

将输入频率改为100Hz,再看仿真结果:

可以观察到输入300Hz的时候,输出幅值大约为1800,而输入降到100Hz时,输出信号的幅值只有240。

IIR的实现

主要处理过程如下,STM32通过ADC采集一路正弦波信号,采集完成后立即对数据进行处理,然后将处理之后的数据送到DAC数据寄存器,通过DAC1的引脚将处理后的数据用电平方式体现出来,这样就可以对比输入和滤波之后输出的效果。

在了解了IIR滤波器的二阶直接II型结构以后,接下来我们就可以在STM32的DSP库函数中修改IIR的系数了。首先看一下库文件中基于C的IIR函数,如下所示。

void iir_biquad_stm32(u16 *y, u16 *x, int16_t *IIRCoeff, u16 ny)

{

  u32 i;

  u32 w1_2 = 0, w1_1 = 0, w1;

  for (i=0; i<ny-2; i++)

  {

    w1 = x[2+i] - IIRCoeff[0]*w1_1 - IIRCoeff[1]*w1_2;

    y[2+i] = (IIRCoeff[2]*w1 + IIRCoeff[3]*w1_1 + IIRCoeff[4]*w1_2);

    w1_2 = w1_1;

    w1_1 = w1;

  }

}

其中y参数表示输出数组指针,x参数表示输入数组指针,IIRCoeff表示IIR滤波器系数数组指针。由于官方给出的函数需要使用到数组,在程序中需要用到缓冲区,这里我们先从简单的着手,即STM32通过ADC采集一次数据之后马上对数据进行IIR滤波处理,所以不必用到缓冲区。在处理完一次数据之后,将输出数据y通过DAC1输出到引脚上,便于示波器观察。这里我们只用了一级二阶滤波,因此将后段的程序注释掉了。另外,由于我们采用浮点数来处理,而官方程序给的是定点处理,再看matlab生成的IIR滤波器内部结构图,该结构对参数进行了归一化处理,不能直接用于IIRCoeff数组,因此还需要对程序进行修改,修改后的程序如下。

void iir_biquad_stm32(float *y, float *x, float *IIRCoeff, u16 ny)

{

  static float w1_2 = 0, w1_1 = 0, w1;

  /** Canonic form **/

  /* 1st section */

    w1 = IIRCoeff[0]*x[0] - IIRCoeff[1]*w1_1 - IIRCoeff[2]*w1_2;

    y[0] = (w1 + IIRCoeff[3]*w1_1 + w1_2)*IIRCoeff[4];

    w1_2 = w1_1;

    w1_1 = w1;

}

通过对比程序和滤波器内部结构图,可以将各个参数对应上,如下图所示。其中w1,w1_1,w1_2表示输入x的过去状态,因此在程序中要使用static将其定义为静态变量,防止下一次调用该函数时将x[N-n]清零。

将每个变量的位置搞清楚之后,就可以给IIRCoeff数组赋值了,比如该高通滤波器的系数赋值为

float IIRCoeff[5] = {0.039834401095698677,

-1.7465710178967648,0.79152631529839501,-2,19.790259679335225};

由于STM32的ADC只能输入正电压,因此硬件上必须加一个运放将正弦波信号加上一个直流分量,让ADC的输入限制在0~3.3V。

现在就可以使用STM32来做一个300Hz的高通滤波器了。首先将STM32的ADC配置为TIM1触发,采集通道为ADC1,每隔8KHz采集一次(必须为8KHz,因为前面在使用matlab设计滤波器时填入的采样率为8KHz,你也可以改为其他采样率,但两者必须保持一致),ADC采集完成中断使能,在ADC中断程序中调用IIR滤波器函数,这里滤波器的输入为x,也即ADC的采样值,输出为y,由于是高通滤波,因此采集到的带直流分量的正弦波信号在通过滤波器后会将直流分量削掉,输出y的范围在-2048~+2047,在将y赋值到DAC数据寄存器之前还要加上一个2048。

ADC中断参考程序如下:

u16 adctmp;//ADC临时值

void ADC1_2_IRQHandler(void)

{

  /* Clear ADC1 EOC pending interrupt bit */

if(ADC_GetITStatus(ADC1, ADC_IT_EOC)!=RESET)

{

ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);

adctmp = (ADC_GetConversionValue(ADC1));

x[0] = ((float)adctmp); //采集到的整数变成浮点数

iir_biquad_stm32(y, x, IIRCoeff, 3); //调用IIR滤波程序

DAC_SetChannel1Data(DAC_Align_12b_R,((int16_t)y[0])+2048); //DAC1输出滤波后的波形

DAC_SetChannel2Data(DAC_Align_12b_R,adctmp); //DAC2输出采集到的波形,与DAC1对比

}

将信号发生器连接到运放的输入,幅值调到合适的位置,频率输入50Hz、100Hz、200Hz、300Hz、500Hz、800Hz,观察DAC1 和DAC2的输出,如下图所示。

f = 50Hz

f = 100Hz 

f = 200Hz

f = 300Hz

f = 500Hz 

f = 800Hz

可以直观的看到随着频率从50Hz到800Hz,输出信号的幅度大于300Hz以后基本保持不变,而小于300Hz的信号随频率减小而减小。由于采样率较低,只有8KHz,可以看到输入频率为800Hz时输入量化误差较大,波形出现了变形,解决这一问题的办法就是提高采样率。

以上是做的一个一级二阶IIR高通滤波,当然还可以设计4阶、6阶等以上的滤波器,需要注意的是滤波器的处理时间必须在两次采样间隔内完成,否则将出现第一次采集到的数还没处理完毕,第二次采集就已经开始了,这将导致最终的计算错误。

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
英雄之歌  2020-03-09 16:15:14 

问什么图片看不了

推荐文章
最近访客