发新帖本帖赏金 1.00元(功能说明)我要提问
12345下一页
返回列表
打印
[应用相关]

基于STM32单片机IIR滤波器设计

[复制链接]
7772|98
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
stm32jy|  楼主 | 2019-6-27 14:31 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
数字信号处理是用数字序列来研究系统的一门技术,不同于模拟信号,数字信号处理起来更加灵活,不仅可以处理一维信号,还可以处理二维、三维以上的信号。这门技术应用非常广泛,如生物医学、声学、雷达、语音通信、数据通信等都采用了数字信号处理技术。在数字信号处理技术中经常用的一种算法叫IIR滤波器,这种滤波器可以设计成带通、低通和高通滤波,在设计原型上有butterworth函数、chebyshev函数、bessel函数、椭圆滤波器函数。本次使用Matlab工具生成IIR滤波器的相关系数将其作为STM32单片机处理的参数,对单片机生成的多种频率的波形进行处理。本课题最大的不同是将matlab语言转为C语言,在单片机上实现了真正的滤波效果。

使用特权

评论回复

打赏榜单

wang29800 打赏了 1.00 元 2022-04-19
理由:对着楼主的代码,写不好,能私聊吗

沙发
stm32jy|  楼主 | 2019-6-27 14:32 | 只看该作者
STM32 单片机具有片上 DAC 外设, 它的分辨率可配置为 8 位或 12 位的数字输入信号。STM32单片机自带ST官方的标准库,新建工程的时候我们将DAC外设添加到工程中。如下图所示,在FWLB分组下添加stm32f10xdac.c文件,该文件为dac的外设,用来输出不同大小的电压。

使用特权

评论回复
板凳
stm32jy|  楼主 | 2019-6-27 14:33 | 只看该作者
利用 STM32 的 DAC 配合 TIM 定时器,可以输出随时间变化的电压,先实现输出一种频率的正弦波电压波形,主要实现过程如下:
PA4和PA5 GPIO引脚和DAC的初始化。
void DAC_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  DAC_InitTypeDef  DAC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);/* 使能GPIOA时钟 */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);        /* 使能DAC时钟 */               
  /* DAC的GPIO配置,模拟输入 */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOA, &GPIO_InitStructure);       
  /* 配置DAC 通道1 */
  //使用TIM2作为触发
  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
  //不使用波形发生器
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
  //不使用DAC输出缓冲
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;       
  DAC_Init(DAC_Channel_1, &DAC_InitStructure); /* 配置DAC 通道1 */
  DAC_Init(DAC_Channel_2, &DAC_InitStructure); /* 配置DAC 通道2 */
  DAC_Cmd(DAC_Channel_1, ENABLE);/使能通道1 由PA4输出*/
  DAC_Cmd(DAC_Channel_2, ENABLE); /* 使能通道2 由PA5输出 */
  DAC_DMACmd(DAC_Channel_2, ENABLE);  /* 使能DAC的DMA请求 */
}

使用特权

评论回复
地板
stm32jy|  楼主 | 2019-6-27 14:34 | 只看该作者
1)        TIM 定时器初始化;
void DAC_TIM_Config(void)
{
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;       
  /* 使能TIM2时钟,TIM2CLK 为72M */       
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);       
  /* TIM2基本定时器配置 */
  TIM_TimeBaseStructure.TIM_Period = (20-1);//定时周期 20
  //预分频,不分频 72M / (0+1) = 72M                                                                                
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
  //时钟分频系数                                                           
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
  //向上计数模式                                                
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  /* 配置TIM2触发源 */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);       
  TIM_Cmd(TIM2, ENABLE);/* 使能TIM2 */
}

使用特权

评论回复
5
stm32jy|  楼主 | 2019-6-27 14:35 | 只看该作者
计算获取正弦波数据表;
由于模拟信号连续而数字信号是离散的, 所以使用 DAC 产生正弦波时, 只能按一定时间间隔输出正弦曲线上的点, 在该时间段内输出相同的电压值, 若缩短时间间隔, 提高单个周期内的输出点数, 可以得到逼近连续正弦波的图形。由于正弦曲线是周期函数,所以只需要得到单个周期内的数据后按周期重复即可, 而单个周期内取样输出的点数又是有限的, 所以为了得到呈 v=sin(t)函数关系电压值的数据通常不会实时计算获取,而是预先计算好函数单个周期内的电压数据表, 并且转化成以 DAC寄存器表示的值。sin函数值的范围为[-1: +1], 而 STM32 的 DAC 输出电压范围为[0~3.3]V, 按 12 位DAC 分辨率表示的方法, 可写入寄存器的最大值为 212 = 4096,即范围为[0:4096]。

使用特权

评论回复
6
stm32jy|  楼主 | 2019-6-27 14:36 | 只看该作者
实际输出时,会进行如下处理:
01.将sin函数的输出为正值:v = sin(t)+1 ,此时v 的输出范围为[0:2];
02.扩展输出至 DAC 的全电压范围: v = 3.3*(sin(t)+1)/2 ,此时v 的输出范围为[0:3.3],正是 DAC 的电压输出范围, 扩展至全电压范围可以充分利用 DAC 的分辨率;
03.把电压值以 DAC 寄存器的形式表示: Reg_val = 2^12/3.3 * v = 2^11*(sin(t)+1), 此时, 存储到 DAC 寄存器的值范围为[0:4096];
04.实践证明, 在 sin(t)的单个周期内, 取 32 个点进行电压输出已经能较好地还原正弦波形,所以在 t∈[0:2π]区间内等间距根据上述 Reg_val 公式运算得到 32 个寄存器值,即可得到正弦波表;
05.控制 DAC 输出时,每隔一段相同的时间从上述正弦波表中取出一个新数据进行输出,即可输出正弦波。改变间隔时间的单位长度,可以改变正弦波曲线的周期。

使用特权

评论回复
7
stm32jy|  楼主 | 2019-6-27 14:36 | 只看该作者
使用matlab工具脚本制作正弦波表,脚本代码和绘制的正弦波如下图
%用于产生正弦数据表,输出到文件dac_sinWave.c 文件中,复制到c语言数组即可
n = 2*pi/32 : 2*pi/32 : 2*pi;      %分成32等份
a = sin(n)+1;                     %求取sin函数值并向上平移一个单位,消除负数值
a = a * 3.3/2;                    %调整幅值,使范围限制为0~3.3   
r = a* (2.^12) /3.3;              %求取dac数值,12位dac LSB = 3.3/2.^12
r = uint16(r);                    %把double型数据转化成16位整型数据
for i = 1:32                        
if r(i) > 4095                      %限制数据最大不超过4095
    r(i) = 4095;
end
end
dlmwrite('dac_sinWave.c',r);      %把数据写入到文件,方便添加到stm32工程中
plot(n,r,'.')                     %把这些点画出来

使用特权

评论回复
8
stm32jy|  楼主 | 2019-6-27 14:46 | 只看该作者
32点正弦波形

使用特权

评论回复
9
stm32jy|  楼主 | 2019-6-27 14:47 | 只看该作者
通过matlab输出32个点的数据作为DA一个周期输出的正弦波的值,将该数组加入STM32单片机工程中,编译成功后烧写到开发板,将对应引脚连接到示波器的探头,可以看到如图所示的波形。
/*波形数据 ---------------------------------------------------------*/
const uint16_t Sine12bit[POINT_NUM] = {
        2048        , 2460        , 2856        , 3218        , 3532  , 3786  , 3969  , 4072  ,
        4093        , 4031        , 3887        , 3668        , 3382  , 3042  ,2661  , 2255  ,
        1841        , 1435        , 1054        , 714        , 428   , 209   , 65           , 3    ,
        24        , 127        , 310        , 564        , 878        , 1240             , 1636  , 2048
};

使用特权

评论回复
10
stm32jy|  楼主 | 2019-6-27 14:47 | 只看该作者
示波器探头连接开发板

使用特权

评论回复
11
stm32jy|  楼主 | 2019-6-27 14:50 | 只看该作者
示波器2.8KHZ正弦波

使用特权

评论回复
12
stm32jy|  楼主 | 2019-6-27 14:51 | 只看该作者
通过按键触发产生两种频率的正弦波电压波形。
为了方便后面进行IIR滤波测试,选择使用按键产生1.3KHZ 和2.8KHZ的正弦波信号,每个周期点数为32个点。下图中黄色波形为2.8KHZ频率,绿色波形为1.3KHZ频率。

使用特权

评论回复
13
stm32jy|  楼主 | 2019-6-27 14:52 | 只看该作者
将两个不同频率的正弦波叠加在一起,即将PA4和PA5短接一起输出,得到的波形如下图所示

使用特权

评论回复
14
stm32jy|  楼主 | 2019-6-27 14:52 | 只看该作者
接下来设计一个IIR滤波器来将其滤波,滤除一种频率的波形,工程代码中需要加入ADC功能,对自身输出的DAC的信号进行采样,之后送入IIR滤波算法中,将输出的结果以串口的形式打印出来,最终在Matlab画出图像来。

使用特权

评论回复
15
stm32jy|  楼主 | 2019-6-27 14:54 | 只看该作者
通过matlab的fdatool工具设计一个IIR的低通滤波器,将大于1KHZ的波形滤掉。

使用特权

评论回复
16
stm32jy|  楼主 | 2019-6-27 14:54 | 只看该作者
得到相关B  A系数:
B:   1,2,1
A:   1,  -0.753537,  0.406307
Gain : 0.163192
在C语言中,首先声明数组,存放这些参数:
float B[3] = {1,2,1};
float A[3] = {1,-0.753537,0.406307};
float Gain = 0.163192;
再声明另两个数组:
float w_x[3] = {0,0,0};
float w_y[3] = {0,0,0};

使用特权

评论回复
17
stm32jy|  楼主 | 2019-6-27 14:56 | 只看该作者
IIR函数具体实现如下:
void IIR_Filter(float x[],int len)
{
        unsigned char i;
        w_x[0]=w_x[1]=w_x[2]=0;
        w_y[0]=w_y[1]=w_y[2]=0;
        for(i=0;i<len;i++)
        {
                w_x[0]=x[i];
                w_y[0]=(B[0]*w_x[0]+B[1]*w_x[1]+B[2]*w_x[2])*Gain-w_y[1]*A[1]-w_y[2]*A[2];
                y[i]=w_y[0]/A[0];
                w_x[2]=w_x[1];w_x[1]=w_x[0];
                w_y[2]=w_y[1];w_y[1]=w_y[0];
        }
} 

使用特权

评论回复
18
stm32jy|  楼主 | 2019-6-27 14:57 | 只看该作者
工程中iir.c文件:
#include "iir.h"
float B[3] = {1,2,1};
float A[3] = {1,-0.753537,0.406307};
float Gain = 0.163192;
float w_x[3] = {0,0,0};
float w_y[3] = {0,0,0};
float y[32] = {0};
// IIR滤波函数
// x[i]:输入信号
// y[i]:滤波信号
// len:输入数据长度
void IIR_Filter(float x[],int len)
{
        unsigned char i;
        w_x[0]=w_x[1]=w_x[2]=0;
        w_y[0]=w_y[1]=w_y[2]=0;
        for(i=0;i<len;i++)
        {
                w_x[0]=x[i];
                w_y[0]=(B[0]*w_x[0]+B[1]*w_x[1]+B[2]*w_x[2])*Gain-w_y[1]*A[1]-w_y[2]*A[2];
                y[i]=w_y[0]/A[0];
                w_x[2]=w_x[1];w_x[1]=w_x[0];
                w_y[2]=w_y[1];w_y[1]=w_y[0];
        }
}

使用特权

评论回复
19
stm32jy|  楼主 | 2019-6-27 14:59 | 只看该作者
工程中main.c文件:
#include "stm32f10x.h"
#include "./dac/bsp_dac.h"
#include "iir.h"
#include "bsp_adc.h"
#include "usart.h"       
#include "timer.h"



float ax[32]={0};
unsigned char j;
int main(void)
{
                /*初始化DAC,开始DAC转换*/
                DAC_Mode_Init();
            ADCx_Init();
            uart_init(9600);
            TIM3_Int_Init(499,7199);
            printf("hello adc!\r\n");
            while(1)
      {       
                                if(timeflag == 1)
                                {
                                        timeflag = 0;
                                        printf("%d\r\n",ADC_ConvertedValue);       
                                        for(j=0;j<32;j++)
                                        {
                                          ax[j] = ADC_ConvertedValue;
                                        }
                                        IIR_Filter(ax,32);
                                 for(j=0;j<32;j++)
                            {
                                   printf("%f\r\n",y[j]);       
                            }               
                                }                                               
                        }                               
}

使用特权

评论回复
20
stm32jy|  楼主 | 2019-6-27 15:00 | 只看该作者
将IIR滤波低通滤波后的数据通过串口的形式打印出来,下图是串口打印的一些列的数据。

使用特权

评论回复
发新帖 本帖赏金 1.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

1118

帖子

4

粉丝