【STM32H7的DSP教程】第46章 STM32H7的IIR带通滤波器实现(支持逐个数据的实时滤波)

完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

第46章       STM32H7的IIR带通滤波器实现(支持逐个数据的实时滤波)

本章节讲解IIR带通滤波器实现。

目录

46.1 初学者重要提示

46.2 带通滤波器介绍

46.3 IIR滤波器介绍

46.4 Matlab工具箱filterDesigner生成IIR带通滤波器系数

46.5 IIR带通滤波器设计

46.5.1 函数arm_biquad_cascade_df1_init_f32

46.5.2 函数arm_biquad_cascade_df1_f32

46.5.3 filterDesigner获取带通滤波器系数

46.5.4 带通滤波器实现

46.6 实验例程说明(MDK)

46.7 实验例程说明(IAR)

46.8 总结


46.1 初学者重要提示

  1、本章节提供的带通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:

  •   所有数据是在同一个采样率下依次采集的数据。
  •   每次过滤数据个数一旦固定下来,运行中不可再修改。

  2、FIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。

46.2 带通滤波器介绍

允许一个范围内的频率信号通过,而减弱范围之外频率的信号通过。比如混合信号含有50Hz + 200Hz 信号,我们可通过带通滤波器,仅让200Hz信号通过。

46.3 IIR滤波器介绍

ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。

直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:

y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]

直接I型算法每个阶段需要5个系数和4个状态变量。

这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:

y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]

比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。

高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。

 

如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。

46.4 Matlab工具箱filterDesigner生成IIR带通滤波器系数

前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。

下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:

 

filterDesigner界面打开效果如下:

IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:

点击Design Filter之后,注意左上角生成的滤波器结构:

默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。

转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:

转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):

第一项选择好以后,第二项选择Decimal:

两个选项都选择好以后,点击Export进行导出,导出后保存即可:

保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:

% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
% Generated on: 15-Aug-2021 22:20:26

% Coefficient Format: Decimal

% Discrete-Time IIR Filter (real)                                                                 
% -------------------------------                                                                 
% Filter Structure    : Direct-Form I, Second-Order Sections                                      
% Number of Sections  : 2                                                                         
% Stable              : Yes                                                                       
% Linear Phase        : No                                                                        

                                                                                                 
SOS Matrix:                                                                                       
1  0  -1  1   1.127651872054164616798743736580945551395  0.470013145087532668853214090631809085608
1  0  -1  1  -0.774953058046049081397654845204669982195  0.367077500556684199750634434167295694351
                                                                                                  
Scale Values:                                                                                     
0.558156585760773649163013487850548699498                                                         
0.558156585760773649163013487850548699498     

由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:

SOS Matrix:                                                  
1   2   1   1   1.127651872054164616798743736580945551395  0.470013145087532668853214090631809085608        
b0  b1  b2  a0          a1                                                   a2
1    2   1   1   -0.774953058046049081397654845204669982195  0.367077500556684199750634434167295694351        
b0  b1  b2  a0        a1                                                     a2

注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:

0.558156585760773649163013487850548699498                                                         
0.558156585760773649163013487850548699498    

实际的滤波系数调用方法,看下面的例子即可。

46.5 IIR带通滤波器设计

本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器

46.5.1 函数arm_biquad_cascade_df1_init_f32

函数原型:

void arm_biquad_cascade_df1_init_f32(
        arm_biquad_casd_df1_inst_f32 * S,
        uint8_t numStages,
  const float32_t * pCoeffs,
        float32_t * pState)

函数描述:

这个函数用于IIR初始化。

函数参数:

  •   第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
  •   第2个参数是2阶滤波器的个数。
  •   第3个参数是滤波器系数地址。
  •   第4个参数是缓冲状态地址。

注意事项:

结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):

typedef struct
{
  uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */
 float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */
 const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */
} arm_biquad_casd_df1_inst_f32;
  1. numStages表示二阶滤波器的个数,总阶数是2*numStages。
  2. pState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。
  3. 参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:

{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}

先放第一个二阶Biquad系数,然后放第二个,以此类推。

46.5.2 函数arm_biquad_cascade_df1_f32

函数定义如下:

void arm_biquad_cascade_df1_f32(
      const arm_biquad_casd_df1_inst_f32 * S,
      float32_t * pSrc,
      float32_t * pDst,
      uint32_t blockSize)

函数描述:

这个函数用于IIR滤波。

函数参数:

  •   第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
  •   第2个参数是源数据地址。
  •   第3个参数是滤波后的数据地址。
  •   第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。

46.5.3 filterDesigner获取带通滤波器系数

设计一个如下的例子:

信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器带通滤波器,采用直接I型,截止频率140Hz和400,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:

配置好带通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。

46.5.4 带通滤波器实现

通过工具箱filterDesigner获得带通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。

#define numStages  2                /* 2阶IIR滤波的个数 */
#define TEST_LENGTH_SAMPLES  400    /* 采样点数 */
#define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */


uint32_t blockSize = BLOCK_SIZE;
uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;       /* 需要调用arm_biquad_cascade_df1_f32的次数 */


static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */
static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */
static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
      
/* 巴特沃斯带通滤波器系数140Hz 400Hz*/                                                                                                                                         
const float32_t IIRCoeffs32BP[5*numStages] = {
    1.0f,  0.0f,  -1.0f,     -1.127651872054164616798743736580945551395f,  
-0.470013145087532668853214090631809085608f,      
    1.0f,  0.0f,  -1.0f,     0.774953058046049081397654845204669982195f,  
-0.367077500556684199750634434167295694351f                               
};                                              

/*
*********************************************************************************************************
*    函 数 名: arm_iir_f32_bp
*    功能说明: 调用函数arm_iir_f32_bp实现带通滤波器
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void arm_iir_f32_bp(void)
{
    uint32_t i;
    arm_biquad_casd_df1_inst_f32 S;
    float32_t ScaleValue;
    float32_t  *inputF32, *outputF32;
    
    /* 初始化输入输出缓存指针 */
    inputF32 = &testInput_f32_50Hz_200Hz[0];
    outputF32 = &testOutput[0];
    
    
    /* 初始化 */
    arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32BP[0], 
(float32_t *)&IIRStateF32[0]);
    
    
    /* 实现IIR滤波,这里每次处理1个点 */
    for(i=0; i < numBlocks; i++)
    {
        arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize), 
 blockSize);
    }
            
    /*放缩系数 */
    ScaleValue = 0.558156585760773649163013487850548699498f * 0.558156585760773649163013487850548699498f; 
    
    /* 打印滤波后结果 */
    for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    {
        printf("%f, %f\r\n", testInput_f32_50Hz_200Hz[i], testOutput[i]*ScaleValue);
    }
}

运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。

对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:

fs=1000;            %设置采样频率 1K
N=400;               %采样点数      
n=0:N-1;
t=n/fs;                %时间序列
f=n*fs/N;              %频率序列

x1=sin(2*pi*50*t);
x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波
subplot(211);
plot(t, x1);
title('滤波后的理想波形');
grid on;

subplot(212);
plot(t, sampledata);
title('ARM官方库滤波后的波形');
grid on;

Matlab计算结果如下:

从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:

fs=1000;                %设置采样频率 1K
N=400;                 %采样点数      
n=0:N-1;
t=n/fs;                  %时间序列
f=n*fs/N;                %频率序列

x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成
  
subplot(211);
y=fft(x, N);                %对信号x做FFT   
plot(f,abs(y));
xlabel('频率/Hz');
ylabel('振幅');
title('原始信号FFT');
grid on;

y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT
subplot(212);                               
plot(f,abs(y3));
xlabel('频率/Hz');
ylabel('振幅');
title('IIR滤波后信号FFT');
grid on;

Matlab计算结果如下:

上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。

46.6 实验例程说明(MDK)

配套例子:

V7-231_IIR带通滤波器(支持逐点实时滤波)

实验目的:

  1. 学习IIR带通滤波器的实现,支持实时滤波

实验内容:

  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  2. 按下按键K1,打印原始波形数据和滤波后的波形数据。

使用AC6注意事项

特别注意附件章节C的问题

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

RTT方式打印信息:

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIC优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
}

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

  主功能:

主程序实现如下操作:

  •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
  •   按下按键K1,打印原始波形数据和滤波后的波形数据。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按键代码 */
    uint16_t i;

    
    bsp_Init();        /* 硬件初始化 */
    PrintfLogo();    /* 打印例程信息到串口1 */

    PrintfHelp();    /* 打印操作提示信息 */
    
    for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    {
        /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
        testInput_f32_50Hz_200Hz[i] = arm_sin_f32(2*3.1415926f*50*i/1000) + 
arm_sin_f32(2*3.1415926f*200*i/1000);
    }
    

    bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */

    /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
        

        if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */
        {
            /* 每隔100ms 进来一次 */
            bsp_LedToggle(2);    /* 翻转LED的状态 */
        }
        
        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1键按下 */
                    arm_iir_f32_bp();
                    break;
                
    
                default:
                    /* 其它的键值不处理 */
                    break;
            }
        }

    }
}

46.7 实验例程说明(IAR)

配套例子:

V7-231_IIR带通滤波器(支持逐点实时滤波)

实验目的:

  1. 学习IIR带通滤波器的实现,支持实时滤波

实验内容:

  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  2. 按下按键K1,打印原始波形数据和滤波后的波形数据。

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

RTT方式打印信息:

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIC优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
}

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

  主功能:

主程序实现如下操作:

  •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
  •   按下按键K1,打印原始波形数据和滤波后的波形数据。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按键代码 */
    uint16_t i;

    
    bsp_Init();        /* 硬件初始化 */
    PrintfLogo();    /* 打印例程信息到串口1 */

    PrintfHelp();    /* 打印操作提示信息 */
    
    for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    {
        /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
        testInput_f32_50Hz_200Hz[i] = arm_sin_f32(2*3.1415926f*50*i/1000) + 
arm_sin_f32(2*3.1415926f*200*i/1000);
    }
    

    bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */

    /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
        

        if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */
        {
            /* 每隔100ms 进来一次 */
            bsp_LedToggle(2);    /* 翻转LED的状态 */
        }
        
        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1键按下 */
                    arm_iir_f32_bp();
                    break;
                
    
                default:
                    /* 其它的键值不处理 */
                    break;
            }
        }

    }
}

46.8 总结

本章节主要讲解了IIR滤波器的带通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。

电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码 电子设计竞赛设计资料 设计程序 参赛备赛参考 源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值