手把手教你用matlab生成STM32官方IIR滤波器的系数

本文采用的matlab版本为R2008a,芯片采用stm32f103v。

先回顾一下数字信号处理和IIR滤波器的相关知识。

数字信号处理是用数字序列来研究系统的一门技术,不同于模拟信号,数字信号处理起来更加灵活,不仅可以处理一维信号,还可以处理二维、三维以上的信号。这门技术应用非常广泛,如生物医学、声学、雷达、语音通信、数据通信等都采用了数字信号处理技术。

在数字信号处理技术中经常用的一种算法叫IIR滤波器,这种滤波器可以设计成带通、低通和高通滤波,在设计原型上有butterworth函数、chebyshev函数、bessel函数、椭圆滤波器函数。IIR数字滤波器是一种递归型线形时不变因果系统,其差分方程可以写成

式中x表示输入系列,y表示输出序列。通过z变换,可以得到传递函数

由差分方程可以直接得到下面的信号流图

以上这种形式叫做直接形式I,对应matlab中IIR滤波器结构选项的Direct-Form I,SOS。注:matlab中在命令行输入FDAtool可以打开滤波器设计GUI界面,在Edit菜单栏选项下有“Convert Structure...”选项,点击后可以选择滤波器结构。我们假设设计一个直接I型的1阶滤波,首先选择滤波器结构第一项”Direct-Form I,SOS”,然后将模型实现,点击”Relize Model“,大家又没有发现这个结构和上图的流程图很相似,只要把流程图改为1阶,那么和下面这个图就是一样的了。

在线形非移变系统情况下,级联系统的总输入-输出关系和子系统级联的次序无关,根据这一特性,将y(n)部分与x(n)部分交换可以得到滤波器的第二种直接形式。

由于此时z-1支路的输入相等,因此可以将这两路合并,这样简化流图后得到滤波器的直接形式II。

更一般的,采用级联的二阶形式(这也是matlab和STM32官方库中采用的形式)信号流图如下(2级级联)

以上这种结构就是以后在设计和实现时采用的流图,我们将按照图中的a11、a12...b22来确定STM32官方IIR滤波器的滤波系数。

(未完待续,转载自www.mcukey.com)

本文引用通告地址: http://powermod.spaces.eepw.com.cn/articles/trackback/item/106436


本节主要介绍如何使用matlab2008生成IIR数字滤波器系数。

一、在matlab上设计一个高通滤波

打开matlab程序之后,在命令行窗口输入“fdatool”,弹出滤波器设计对话框如图6

图6.matlab中fdatool滤波器设计

为了便于分析,我们先从设计一个简单的一级2阶高通滤波器开始,按照下图中的步骤进行。

1.Design Method用于选择IIR滤波器还是FIR滤波器,这里我们选择IIR滤波器,类型选择Chebyshev Type I,当然你也可以选择其他类型,不同类型的频率响应不同,选择后默认的滤波器结构是直接II型,如图7中的蓝色框所示。

2.ResponseType用于选择低通、高通、带通以及陷波,选择高通滤波“Highpass”,这里我们选择高通滤波用于滤出300HZ以下的工频频率。

3.Fiter Order 选择阶数,为了使用STM32官方库给的程序,这里需要填入偶数,为了简单起见,先选择一个2阶滤波器做实验。

4.Frequency Specifications用于设置采样频率以及截止频率,这里填入8000以及300,也就是采样率是8KHz,300Hz以下的频率都不能通过。

5.点击“Design Filter”,matlab将生成一个按照以上参数设计的滤波器,点击图中数字6的图标,可以在坐标框中看到该滤波器的幅频响应,正如图中所示,频率在300Hz以下的信号将被滤除。

图7.设计高通IIR滤波器

 

二、导出系数

设计完毕一个高通滤波器之后,这个模型就已经在matlab内部保存起来了。接下来就需要将模型的参数导出来以供stm32f1使用,在导出数据之前,先看一下用matlab将该数据模型实例化之后是什么样子。

按照图中数字标号进行,第一步点击左边小图标,第二步“Build model using basic elements”这一项打钩,右边四个灰色的项将自动打钩,默认不要修改,最后点击“Realize Model”,matlab将自动把滤波器的数学模型通过仿真的形式展现出来,在弹出的窗口中双击模型,双击之后将在新窗口中展示该模型的内部结构,为了便于观察分析,这里我将模型的各个模块进行了位置调整,并将各放大器的增益都用数字表示出来了。

图8.将滤波器实例化

图9.生成的仿真模型

 

图10.模型内部结构

由该图可以看出,这是一个典型的一级二阶直接II型结构的滤波器,若是我们在前面的fdatool中选择4阶以上的滤波器,那么这个模型内部将会有更多的二阶结构级联。

导出C语言的滤波器系数

接下来我们看一下滤波器数据模型的系数到底是如何对应到图10中所示结构的系数的。

在fdatool中点击”Targets”菜单下的”Generate C Header”,弹出下面的对话框,图中”Data type use in export”选项用于选择导出的数据类型,比如双精度浮点、单精度浮点、16位无符号定点、32位有符号定点等等,这里我们先导出为双精度浮点,定点计算比较麻烦,在以后的教程中再举例说明。

图11.基于C系数的导出

 

选好之后直接点击”Gererate”,弹出一个对话框让你填写保存数据文件的文件名,这里默认为”fdacoefs.txt”。在我们保存的目录下打开fdacoefs.txt文件,打开后出现以下代码

#define MWSPT_NSEC 3 //NL、DL数组中元素的个数

const int NL[MWSPT_NSEC] = { 1,3,1 }; //定义分子数组NUM[MWSPT_NSEC][3]有效数据的个数,比如{ 1,3,1 }就表示数组NUM[][]中第一行第一个数据有效,第二行三个数据全都有效,第三行第一个数据有效,以此类推。

const real64_T NUM[MWSPT_NSEC][3] = {

  { //第一个数有效,对应图12中输入的增益系数s(1),图中用浅绿色圈框住。

     0.0398344010957,  0, 0 

  },

  { //三个数都有效,对应图12中右侧系数b,图中用蓝色圈框住

    1, -2, 1 

  },

  { //第一个数有效,对应图12中输出的增益系数,图中用金色表示

      19.79025967934, 0, 0 

  }

};

const int DL[MWSPT_NSEC] = { 1,3,1 };//定义分母数组DEN[MWSPT_NSEC][3]有效数据的个数,比如{ 1,3,1 }就表示数组DEN[][]中第一行第一个数据有效,第二行三个数据全都有效,第三行第一个数据有效,以此类推。

const real64_T DEN[MWSPT_NSEC][3] = {

  {

    1,  0,  0 

  },

  { //三个数都有效,对应图12中左侧系数a,图中用红色圈框住

     1, -1.746571017897, 0.7915263152984 

  },

  {

     1, 0,  0 

  }

};

图12.滤波器系数


本文引用通告地址: http://powermod.spaces.eepw.com.cn/articles/trackback/item/106438


本节主要介绍如何在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

  {

    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阶等以上的滤波器,需要注意的是滤波器的处理时间必须在两次采样间隔内完成,否则将出现第一次采集到的数还没处理完毕,第二次采集就已经开始了,这将导致最终的计算错误。


本文引用通告地址: http://powermod.spaces.eepw.com.cn/articles/trackback/item/106439

你可能感兴趣的:(matlab,stm32,算法)