请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的IIR高通滤波器实现(支持逐个数据的实时滤波)

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示
( S: N( P5 a5 K/ }0 j  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:  ?) U- M/ D, y" {
+ a7 F$ E( `# V" m0 X, g  F
  所有数据是在同一个采样率下依次采集的数据。
7 a4 p1 V4 \/ G& r0 J: d2 E& a8 ~  每次过滤数据个数一旦固定下来,运行中不可再修改。
! p/ q! Q8 d6 D; e0 j" z  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。
" {; s' g7 ?' b9 H5 o- H# t  v  J# m3 P& v7 _' R  v
45.2 高通滤波器介绍
# T- q2 Y, z4 j: w2 o& Q4 n! K# O允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。# A6 R4 m7 {8 K& S

* i" ~2 _) F# a7 h
c1e7dbbd579e8c1e535c9ce0678c3532.png

) H6 O4 E; u" K
- z# w# H' |& |: ?! z; o" @45.3 IIR滤波器介绍
- v' K  m" z! X" f; ]6 `ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。
% c! f2 L$ c! E' B% d2 o, k* Q$ Z3 g4 C9 Y5 J
直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:
7 \% S( o% |. P% b$ y4 a0 N
" t4 B  G8 j+ x5 w1 b" Q9 y: y5 Ny[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
# ]+ [1 ^7 A1 E/ U" @
! d' `+ D! `4 I  a直接I型算法每个阶段需要5个系数和4个状态变量。' K! F; R$ q& I# E$ H& s4 R

5 B0 r- k$ G$ a/ Z. w
7afa71129081e82218f8d501da73448e.png

/ |; }3 T% L# r: o0 Z
9 r. m2 _$ s! e1 e  h7 |这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:/ d2 n; o# j6 \7 h
: S) C; j6 j  D( C
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]  W) w1 U0 k+ f! N; z6 f

; N# T# B) V/ T. k, X比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。5 z! k9 p" r! d3 v7 j5 U: {

6 Q, z$ f/ C4 o( y- m高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。9 u1 G% v1 D- T$ M. w7 o  {

$ R: {  I/ ~$ B9 z
75ec77cc1b971aa24bf16e169c67d8aa.png
" S1 E5 p( [7 W& d* F" u" r9 S' Y
$ q7 X. A) Z5 Y
如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。+ M/ |( F' e- v) J0 P( i- u
* a& [  |; j4 S4 T0 ]/ r4 v4 v# [
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数3 q* |+ `5 i+ S+ Q
前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
7 a/ U+ |9 N6 P5 K; T* r; P% B/ J) U& \$ h; }' N: \& T
下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:+ s6 v; V5 @2 L2 a$ u$ f8 A# K
  x5 I! E% ], S
4e2868a42a0de4b63d0a13d64952e470.png
- A: S. C# [" r( L; M' C

0 f, p& M% a2 b) ZfilterDesigner界面打开效果如下:2 A  n# H7 M9 e# [7 l
1 g! U0 {+ }" t! S6 j$ Y4 ~# Q& [+ Y
b9bee9bb0a4ada395e69793b38c566fa.png

! O0 W" }% m, P- J: Q  G, [! p4 e: w/ _! [0 w* P
IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:" X8 {0 n8 {% j0 g* G

0 w4 H' N  F* A, p  Z" P
84c4d0373d7c29786a6620c1050ee40f.png
8 d6 p& j8 w+ j9 i1 R! H

" {  F* N' \  X9 |点击Design Filter之后,注意左上角生成的滤波器结构:
0 T/ G% g0 P# r" M
% y# o" ~6 b8 o( T: f1 {
e51daea41c797cdfd4a5c1857cc35eff.png
  Z4 j8 Z4 ~9 U$ G

) M* j+ A1 H0 s& n默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。
# k0 h# Z( S' R3 @. Y% h- @& W7 C+ D
转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:1 s2 c" o* t- z# ~" i
" M7 i7 @8 g8 e
0328d6421fbe59b9299acd1c889e3dff.png
; O9 L# ], _. V7 e0 O
# R/ J* E% o3 r, o5 i
转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):& r; \' i2 k5 d% R' Z; d- H( S

4 Z3 {, U$ \; [: @5 a* @& @/ r8 V
232d594c793c269063eb16a62ce71fff.png
. h- l3 f) Z7 y- [" e  H2 o

! I' \/ `0 n  I) w9 `! L第一项选择好以后,第二项选择Decimal:
1 h, ]5 z- V3 a4 c. b( z$ D  u' v6 A
( F  q0 J" c- W8 E- G% A
31265a3570a3c4ff16c53050672e0e99.png
2 _7 ]: ^/ A9 K6 ^
/ @" {# S, ^/ ?4 \/ t; X" f
两个选项都选择好以后,点击Export进行导出,导出后保存即可:
; B" a% F2 H6 `- `9 o& g4 S! `  @: c! V; G2 o& L
a5e27f83f7223e9a95c29a47d1c79238.png

5 ~5 Z2 E3 I' J; y$ q, g  t! e" m* M: c' P
保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:
& `/ u& f) q& M, u
9 T! ]1 }) Z( p# n# O0 {% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
( [" }& w% Z8 T0 G/ T/ x  C; x% Generated on: 15-Aug-2021 20:38:33
! `  W3 M% ?/ Z" g6 k* b# c6 t3 b3 X
% Coefficient Format: Decimal0 k: M" ~2 V) A

' L) x" D0 |8 p% Discrete-Time IIR Filter (real)                                                                 , s8 [2 J6 f8 Y1 d' l* f
% -------------------------------                                                                   C; h2 }; ~1 y4 e3 q/ u
% Filter Structure    : Direct-Form I, Second-Order Sections                                      
- l* d* p0 K9 M6 f; J: m4 N( |$ l% Number of Sections  : 2                                                                         3 e8 D& @, |, h. c
% Stable              : Yes                                                                       
# ~, L5 e3 i. X: _# C$ H$ f3 t) |% Linear Phase        : No                                                                        ! I" ]8 W# _- U" ^( J
6 C' Q' U4 r1 d1 U; a9 R# |
; N& K" o& L8 P- o& K
SOS Matrix:                                                                                       : P% R; I1 [+ n! r* _
1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
* ?) M: a, N3 e* u, [& }1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
4 k  u! ?7 G  V& r9 |
8 ?; U1 E- G! j9 D8 `Scale Values:                                                                                     9 e; I" x. ~# g2 ^2 m; A9 V+ E
0.632277093897992026327870007662568241358                                                         # O! d' R) T7 U1 y* g
0.478258337927073562401147910350118763745                                                                                                               
, o( \  O. g4 ~由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:
6 A) p5 }+ N# S- m. j4 F5 k
2 M/ _  U  }9 `7 j) t7 i4 l' B1 ^SOS Matrix:                                                  
" |# m; P) t4 |1 G* k4 K& n1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        1 [* K8 ]* m9 Z/ \3 K& ~
b0  b1  b2  a0          a1                                                   a2/ t# b- a9 O8 P4 g
1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        
3 X' L1 g* z% l: S; d% tb0  b1  b2  a0        a1                                                     a21 x* H1 W9 w# m9 M9 L  j
注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:
3 _" a7 ]% w& Q* X9 Q+ v6 C  h$ L9 O7 V: _8 m! N; q
0.632277093897992026327870007662568241358                                                         / x& F9 b7 u+ o8 E
0.478258337927073562401147910350118763745  
* ?0 F9 _6 t& Z# v实际的滤波系数调用方法,看下面的例子即可。
4 _, ~5 T7 |. q7 r8 ~6 Y
2 n9 X4 |3 z; y45.5 IIR高通滤波器设计

' f* h: F% O: u$ g( z% t本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器
' W) @1 E, H8 y( ]( ?
) s. ^3 V, k3 T. g% v+ R) h; U5 F45.5.1 函数arm_biquad_cascade_df1_init_f32
5 E( T7 ^& m0 M( e; l函数原型:
+ G+ a* R' X6 b1 K% j2 p/ C: H2 n0 E2 k' O3 d, q
  1. void arm_biquad_cascade_df1_init_f32() ^6 Z% l/ X- d" m
  2.         arm_biquad_casd_df1_inst_f32 * S,( ]2 }" Q# L, X
  3.         uint8_t numStages,
    ; H: L  M* Q) ?6 O1 m
  4.   const float32_t * pCoeffs,' }3 G; b1 T1 H, f
  5.         float32_t * pState)
复制代码

: H! u, U, r  `: C& u函数描述:
$ A+ f/ u: m0 U2 P  J& N! b& L& j$ ~: s
这个函数用于IIR初始化。
5 S9 g5 |  v4 l/ k0 T) i9 P. K2 `6 F# ^! D8 ]/ L/ K
函数参数:
8 \& p8 [2 o* Z" ~% Q( d  D. M( W3 O' l
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。  |2 {0 q) A0 @1 U
  第2个参数是2阶滤波器的个数。
% Q# [& r) i1 ?! i1 d( H5 `; D  第3个参数是滤波器系数地址。: E: W% b# \4 }* u
  第4个参数是缓冲状态地址。% {! W8 r; h! h
注意事项:# a( @* f1 X- o& K

4 Q- W) y3 p, y. k* V结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):, p! L0 F8 w& y. [  M

' D% ~' \) _$ P$ z# M3 V/ j& N
  1. typedef struct; N6 I/ {+ n$ T  D0 z) |5 W
  2. {
    7 N, d+ p: C- i% P5 L6 k+ o' }
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */
    ( p) ]2 h3 m, x  y
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */! T  w7 @6 Z1 F5 z& E5 |1 G
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */
    4 X& w, S! a1 b) r
  6. } arm_biquad_casd_df1_inst_f32;
复制代码
" p5 r  @0 x+ F- O# g6 B9 P5 z  U
numStages表示二阶滤波器的个数,总阶数是2*numStages。+ {: I( `2 }# Q( h5 [$ E3 B  b
pState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。
9 I' Z! D1 z4 _参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:2 i9 F2 \: ?* E9 L, H
{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}
& }# }1 F* V6 j  o( Q9 R1 @7 T# n/ ^1 K, G" v
先放第一个二阶Biquad系数,然后放第二个,以此类推。* X; K" H6 o1 e4 K

5 j7 g: J& L  V+ f9 Z5 v3 F/ A! T" b; d45.5.2 函数arm_biquad_cascade_df1_f32
* H& x7 ]2 f9 d) H$ w函数定义如下:" _: Z5 l; b5 r$ I  L# m* F
6 P2 J- [- ~  d( a7 T
  1. void arm_biquad_cascade_df1_f32(8 V- S' Y6 H3 ?9 u; [# G7 o0 c( P
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    6 Y; f5 b' ]& Z
  3.       float32_t * pSrc,
    & x+ J) T6 E+ [
  4.       float32_t * pDst,
    , Y9 {0 e1 ]% J# i1 K2 E5 a1 r* R
  5.       uint32_t blockSize)
复制代码

! Z6 s/ k4 [: Z$ M9 h2 W" O6 m函数描述:
$ I8 [# y. X* ^) t$ g* b0 Z1 i3 O/ e6 @" @9 ]2 ?# L
这个函数用于IIR滤波。! \& }6 f1 u7 E8 _% a9 T
% L0 ~: ~6 K9 ]7 \9 {" _  `4 }
函数参数:
7 k$ H# Y3 ?1 w. [$ o# b" k% e; f6 [. M
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。# E0 F4 B. m# p: u/ U# m1 t& L
  第2个参数是源数据地址。
. q% Q; @" ?$ y* I, a  第3个参数是滤波后的数据地址。: z8 j& |1 O8 h& r  g
  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。
, P3 `6 ]/ i9 r& I( e* w45.5.3 filterDesigner获取高通滤波器系数
7 }3 `) [6 a/ \4 f设计一个如下的例子:
0 s7 z" l( e+ O# B0 Z9 i' w/ p; x* p4 `+ I) F7 X
信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:) T4 U2 ]; z$ E+ V( X+ G+ P* D

$ C& x! B4 a1 I3 y3 W9 ^0 V
7f8be339ea66c602e1b7ea4836e58544.png
4 h" W; G) {% k: ]* P

0 K2 U5 j/ Y' Q/ ^+ |4 |/ ?4 }配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。* R3 V/ }: h1 z. e
3 I6 Q* V3 D! N" ]5 V1 L
45.5.4 高通滤波器实现) `2 T( R1 m! K5 ^& P, U1 d
通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。
4 i+ ^/ Y4 ?  S, j0 ^& H7 P6 o3 ~. U
& W9 {( q4 G; Q* G# I
  1. #define numStages  2                /* 2阶IIR滤波的个数 */7 N8 d4 e, q# N
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */
      X0 Q8 S% [( G  p7 r. H
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */* V7 M7 v2 K' g

  4. 8 S9 n* j1 y. i4 m' \
  5. , s: f, p- f$ h+ j& Z8 P% P: p
  6. uint32_t blockSize = BLOCK_SIZE;
    % p" c7 w8 h& w$ K1 m
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */  \2 g% S/ ^& Q) [& q

  8. ( [; Q8 B4 ?! w  T

  9. ! K# T- F& R) k5 {1 Y3 ]. O
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */  v4 X2 @0 B( L: E( a
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */' d' A- ?" q# \: U  V6 S6 @
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
      N2 N0 _4 r7 U5 G& T

  13.   j) H" u" W4 W4 K) `5 e7 x
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         9 q* x, [8 {% j6 O0 s
  15. const float32_t IIRCoeffs32HP[5*numStages] = {
    ; o9 c2 ]5 Q  j8 l6 L( T) b
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   
    7 K- H' m7 X' [. ]7 j/ v
  17. -0.544565360850816415627662081533344462514f,     
    , T/ k6 F, H/ o8 @7 M9 [
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  $ g7 F( N3 n- v( {! s  }3 A
  19. -0.168318873843973093595849377379636280239                              - I6 V( ^7 @7 }. ?
  20. };                                               
      {6 g4 n/ X/ c" w6 \% S

  21. 2 V3 s* _6 Q* d$ C) ^' f5 p
  22. /*
    9 n- B) R! }5 R& \
  23. *********************************************************************************************************
      R! J" i+ M; d) a' G
  24. *    函 数 名: arm_iir_f32_hp. k9 |* v% X. [# w: y
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器! x2 s: H; j6 t9 A2 S8 O4 |- @: T
  26. *    形    参:无
    9 @9 D: V* V! E5 V, d
  27. *    返 回 值: 无: r$ A6 W) z+ |' v
  28. *********************************************************************************************************# F9 f, \' ]+ f0 M2 ~( F2 F, X" b# g% h
  29. */0 R, D4 ^! @3 o% ]* v+ t$ `1 ]4 ^+ _
  30. static void arm_iir_f32_hp(void)5 W( c! n+ x, \" X
  31. {/ P: {" F" S% F* P" n5 d
  32.     uint32_t i;
    8 N. E5 d* e6 f" b9 _+ U8 }6 K
  33.     arm_biquad_casd_df1_inst_f32 S;
    . B/ B3 Z' Y) u; S: Q# N4 E& y9 D; ~
  34.     float32_t ScaleValue;! @# D3 m1 i) Z- A! G4 k
  35.     float32_t  *inputF32, *outputF32;) M2 e0 v' ^( B' ?
  36. 8 Q- S: z! ^" X" _6 V6 _& A
  37.     /* 初始化输入输出缓存指针 */' g' D- D9 n1 P9 Z4 C8 F+ r& o" X
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];
    ( K% {- i( y8 G; V9 n8 |6 A: i
  39.     outputF32 = &testOutput[0];- p. Q. Z! a- r9 F- `0 q

  40. * i5 W; T& m7 }/ `
  41. 0 u' b3 i, j3 R: i: N
  42.     /* 初始化 */
    - M  h$ S; \4 T2 g5 ^2 ~
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0], 5 o  I( V0 g8 k6 I% J7 w8 r# p
  44. (float32_t *)&IIRStateF32[0]);
    0 \# M! k5 n  ~8 i. \& O
  45. 4 r" f- `, U; s- v9 \4 g

  46. 1 [& J4 q0 o( e: O8 \
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    7 E1 V0 h# P& l5 [2 m( l* n5 L
  48.     for(i=0; i < numBlocks; i++)
    9 a0 J* q, j1 |7 Y6 k( K( N+ I: `
  49.     {4 }- s9 L" w0 Q$ T- l+ E( v4 {
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),- i. U5 z& u  V" s& h* c
  51.   blockSize);
    ' f4 n, P& j9 e' Y! W! E  k$ N# C
  52.     }! A3 M: ]( N* T, Y6 l3 [5 [/ |5 H

  53. 0 h" @! G  S1 N- f4 q5 d  F
  54.     /*放缩系数 */% y# b0 a% e) N( a' h7 V& I) J* L! m
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f;
    ! V% I) M" \1 Z+ h

  56. # o1 M& E4 A; D) p% C) W
  57.     /* 打印滤波后结果 */
    ; [+ s. t- Q. _6 o- @8 H0 ~! [
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)# }7 n0 n) y4 y* r" M1 j" E
  59.     {
    6 V6 O; t2 Y4 p
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);
    + f0 i, F: _! Z8 T, J4 {; n! m
  61.     }
    2 V5 A: e% @2 s( U
  62. }
复制代码

3 H* Z# p! O' d2 k6 X运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。
7 c. r! i+ Q' [( u5 E- z" x- X
! Q! M8 f0 U, E# n! A+ d4 I1 h对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:
: Y! p8 e/ |% O  v/ |& f0 m- Q
; H( D( w2 c% d6 R1 b! G
  1. fs=1000;             %设置采样频率 1K# ~3 x- \5 N: Y  a! N3 Z: w
  2. N=400;               %采样点数      
    8 V) t0 M/ F2 G$ L2 K
  3. n=0:N-1;- A! S9 Q5 n1 q- ~( h* }- N, Y
  4. t=n/fs;                %时间序列! V; q) P* R" Z( E" j, Z
  5. f=n*fs/N;              %频率序列
    & i; ]$ `1 O, W) T8 M

  6. ' S$ u  L1 `% r/ K
  7. x1=sin(2*pi*50*t);
    0 D5 B2 B& f8 K: x) U
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波
    , D) X- L( W( m- o7 T# D5 R: j
  9. subplot(211);
    % {5 K) N* y& k, f0 y' u
  10. plot(t, x2);2 [: T/ a3 A5 H) T
  11. title('滤波后的理想波形');
    + l8 U) [" V  D& U
  12. grid on;
    + ]4 _! q: M, x9 p- ~8 I
  13. 3 ^; ~/ L! v9 m; y( U
  14. subplot(212);/ _! r# a# e( l5 h, h
  15. plot(t, sampledata);- `8 i! E+ ^% C/ n/ U
  16. title('ARM官方库滤波后的波形');+ X8 N$ D1 _9 Q" I
  17. grid on;
复制代码
" G4 \; y3 [% F) \5 q, f
Matlab计算结果如下:
6 }. G8 ?0 |# A: H; g/ c" ?" F0 i2 a0 ?: G5 ]) E. p
91111ef8530bb9e40d9736ccc2c12424.png

9 \) P, Q3 I5 ^5 x* J
0 m  J0 f) n6 R& n& a+ ~: l, B从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:- E' a! \& q1 b1 z

) ^0 f( L- R! S8 X0 y' L
  1. fs=1000;               %设置采样频率 1K8 ?2 K8 F+ p: Y* q# A) v
  2. N=400;                 %采样点数      
    1 ~! f6 J2 g" J, ~1 B
  3. n=0:N-1;0 M( t6 T( ^0 X8 K4 r$ P+ e
  4. t=n/fs;                 %时间序列
    4 e: P/ {0 D8 m6 [% l
  5. f=n*fs/N;               %频率序列5 u- X8 V* B$ r" k! ?2 e

  6. ; R* ~3 A$ K! Y0 k6 B$ `
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成/ J( H% S0 {- `$ [) K0 z8 o

  8. + U% D( O) ~4 l) V* p
  9. subplot(211);
    & ~8 |" U9 T$ P
  10. y=fft(x, N);                %对信号x做FFT   4 `7 C% r% y# V
  11. plot(f,abs(y));
    + a- `  O. w9 ?) V5 o
  12. xlabel('频率/Hz');
    ; \4 c1 f, L# [* c
  13. ylabel('振幅');( J; t- ]% u$ |
  14. title('原始信号FFT');( V7 @. J! [' u
  15. grid on;+ \& [! V/ O  _2 q/ |

  16. & @6 R) b7 B' ^- e  ]0 b1 C. o
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT1 d# G9 D  l* f3 Q- c0 E
  18. subplot(212);                              
    3 D) _1 |/ t( w3 p. {9 @$ k5 C$ A1 h3 x
  19. plot(f,abs(y3));
    % H% F2 c( r5 D# [0 K
  20. xlabel('频率/Hz');
    + [5 T* [! R3 c% a! k  @
  21. ylabel('振幅');
    % n9 x, _1 U* G& o7 b
  22. title('IIR滤波后信号FFT');: F! y6 u: t' B. r6 M. v) q) Z
  23. grid on;
复制代码
- P; C$ E7 H6 |6 z  P
Matlab计算结果如下:4 R1 F( W+ |; h" ?$ A; u( z2 o
7 o( y. @7 C/ g0 n3 z, z  V
d13d47932e1b37bd5046b7ad31415509.png
& a! F5 O2 e! ^0 Z
6 g4 l) P/ A8 Q0 J' n2 X+ b5 p6 H
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。
+ w) }( y$ V( t+ D% s5 B. p
5 m( }) W' c* D! z9 d: j  ?# U5 x0 c45.6 实验例程说明(MDK)- V' ?$ p; J4 M
配套例子:
4 u3 L' s) \$ u& z7 Z( r+ SV7-230_IIR高通滤波器(支持逐点实时滤波)% [* z$ O1 ?$ o& U, l5 G
3 X5 R; `7 v; i& m4 S
实验目的:
0 r: T( j9 b" Y& F& r9 M学习IIR高通滤波器的实现,支持实时滤波# y% V# b; R' Q0 g) F
' i$ k% v" B8 ?
实验内容:* P- o$ Z3 M/ \8 |
启动一个自动重装软件定时器,每100ms翻转一次LED2。
. x+ h: {" D4 X: X7 a, S按下按键K1,打印原始波形数据和滤波后的波形数据。$ V) w/ W& t% z0 o& d6 m: X. @

3 ^; V. O5 N6 m% i8 [* z  S使用AC6注意事项% f# b! Y) _( P1 r
特别注意附件章节C的问题! P2 I. I" V. {2 F

- P& T' O* ?: h0 m上电后串口打印的信息:
( d1 Y6 U, K+ G; ^8 f
1 L/ D; h; d; E$ z# J波特率 115200,数据位 8,奇偶校验位无,停止位 1。
' [: l4 q# k3 [! I  I8 R* q2 o* H4 G# h; r% n4 e. W
262f2df9123b012595369835f600f93d.png
: x1 t& G0 i) b: E0 P
- Q; T; Z+ E! B9 }9 N
RTT方式打印信息:' ?! b: o$ v0 D/ V

( `" z' I) x$ _3 _' f; O
75dd602d2d2eb24a5dea611cc45ce934.png
1 k5 `% z8 M6 s" G' I
+ @+ I4 p, q3 W
程序设计:$ T) |2 x& j3 L$ `! {4 M; c# U
1 r- ^1 p/ R& h$ L. d% e# b
  系统栈大小分配:" P8 W1 g) i" A; U# d# }$ j6 [
& R! i# J: v8 K6 J- U
640570198f69835d8ac9eb982ccf39f6.png
/ [9 Z. Y- B$ u9 O! g* E

: b/ D2 d  y9 p/ S  RAM空间用的DTCM:
3 I( R* e6 |! k- `5 w5 [  U* z7 Z5 i' c* }
6efb3697225eacc369e069ab80d69ed2.png
0 Y  H6 F4 q% g
! Q& m; s9 j: j4 `, \. y
  硬件外设初始化
) O: g, s) Z3 T* C
. e0 t1 A! |6 P硬件外设的初始化是在 bsp.c 文件实现:
9 o6 N. W0 ?& m, G8 @; a" ^2 a" Q8 P% Y- G# J7 p: X
  1. /*) ~* C: ?& h! m. R' z
  2. *********************************************************************************************************+ P7 W) b) }8 d% z* \& u1 {
  3. *    函 数 名: bsp_Init- A) ~7 P: g7 f  L
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    $ U1 X2 ^3 n; H+ h. l( Z# U8 U) L
  5. *    形    参:无
    3 m9 }8 U" i4 {6 R& u* L. Y
  6. *    返 回 值: 无
    " r, `) ~! ^: C6 a* ]; A
  7. *********************************************************************************************************
    + l/ F5 Q+ Y: [5 H  x
  8. */
    1 a- V9 E" S2 R# f
  9. void bsp_Init(void)% a  z8 a' L, V! G
  10. {4 v, A4 Z: U# D0 ]4 z, i4 p
  11.     /* 配置MPU */
    8 y* A" ?4 Y( }' a: |% l
  12.     MPU_Config();
    1 N7 m2 S5 M& o" a: t2 y

  13. 0 h% c3 _* [% L4 E, Y0 o' F5 Y
  14.     /* 使能L1 Cache */
    " e: U! ?6 K) Z( u4 ?0 s7 Q
  15.     CPU_CACHE_Enable();
    # a8 R0 B8 Z8 T

  16. & _% S) [. }/ I. x; ^5 C$ o) i4 P
  17.     /* " q1 M6 \4 \2 T- m
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:, V9 w8 F0 T2 }" O# e2 i& |
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    * `$ }" o+ U2 I6 h; k! ^
  20.        - 设置NVIC优先级分组为4。& i' }* B' q, O  T2 K; M& i
  21.      */! N# ~0 c5 V' N# d. F2 l& F! j2 P
  22.     HAL_Init();7 f* V# ]- D/ \9 P% F, [/ L
  23. 2 t' t$ ]. ]  L
  24.     /* 6 B  J0 d- b3 i% r
  25.        配置系统时钟到400MHz
    ! ]% X6 f" Q, m1 ]# h6 a3 O
  26.        - 切换使用HSE。0 w& G4 k$ r- ~7 C
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    / c4 W6 d% \) H4 J2 `7 l
  28.     */0 H: V) b' {% t" F1 y
  29.     SystemClock_Config();
    8 P# }5 A# N+ }6 U* y4 p0 j+ j* H

  30. . S% ~1 x2 X7 L, }9 V, V
  31.     /*
    # m5 s4 n5 y7 R: _1 h6 o
  32.        Event Recorder:4 f, d+ V. g8 K( ~
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    : |4 U$ u, b0 |8 h5 G% T
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    & j5 ]3 S* Z; Y: w
  35.     */    ; E* I& L# O# h8 @
  36. #if Enable_EventRecorder == 1  1 @$ E9 u; U7 P. Y: _
  37.     /* 初始化EventRecorder并开启 */
    + y. J# z2 H( D8 H! }
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    3 v7 y0 ]5 p" a
  39.     EventRecorderStart();
    $ q3 L0 d0 O: I% U& x
  40. #endif# Z: R6 W$ Z, G/ _" h+ P* ^9 _2 R

  41. 1 Q0 `! }8 l1 H; Y% q; H- I
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    0 z8 a8 B. ?1 }' G9 b+ @8 P1 L
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */3 t7 l6 ?) }8 d$ z* _
  44.     bsp_InitUart();    /* 初始化串口 */7 r; x) M) I6 r8 B
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    0 N3 Q  N1 c; ]
  46.     bsp_InitLed();        /* 初始化LED */    & y( ^! j, E% D+ E& k
  47. }
复制代码

2 u  A) @$ M: R, }" ^  MPU配置和Cache配置:
: s; C# f8 X  t8 v& l
2 N+ z: t8 G" h  m& v8 ?$ |7 |数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
5 t6 `- w2 w4 F6 ?9 d) Q. W2 Y2 E6 u2 v! ^
  1. /*
    : \. h* O7 r0 `0 T. f8 F6 j
  2. *********************************************************************************************************3 r! j. p( e- j7 X4 L
  3. *    函 数 名: MPU_Config
    4 \  c/ h! E, Z
  4. *    功能说明: 配置MPU
    0 M' C( ]0 Y& d( k. Q
  5. *    形    参: 无) p( H/ z  R+ j! f
  6. *    返 回 值: 无, s0 R8 W# f3 ?) ?- C: b0 \
  7. *********************************************************************************************************3 D3 _* a3 u  n( C
  8. */* W' P; B) E& S! l
  9. static void MPU_Config( void )# A$ s- `& {. g6 @, K, b
  10. {
    1 O% Z8 l/ Y, t
  11.     MPU_Region_InitTypeDef MPU_InitStruct;% i" ?: ]2 m) X( e- w0 e
  12.   C" _& n! a5 X: d8 |5 P& ?, a
  13.     /* 禁止 MPU */# O7 H9 t* v2 `: S
  14.     HAL_MPU_Disable();
    ! P$ k( r# P& W$ {# M  k3 v/ n& l
  15. $ _6 I. T0 I* \- @3 E
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    + {$ A3 ]9 C8 V: y8 P1 e' ]
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    % C; u# G( J; n& {' R
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    + ^& l2 ~8 o6 z: \! D
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;$ i6 G. U7 o& Y( W, R
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;" F4 j6 _( e" H+ [
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;
    * ]+ b+ Z2 c  Y, U- v
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;' m( A" i8 W# Q
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;$ f- j! f1 K( L/ N
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    4 Y- W5 ^" h- T3 ^) g  ]7 e
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;9 ], K. a9 a1 X# h  @9 A
  26.     MPU_InitStruct.SubRegionDisable = 0x00;; {2 X& C. W; j. }. o$ i5 b
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;% Y. Q, \6 S$ ]! S& m$ F5 v1 Y
  28. + R* p" w: d" @9 ~
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    * f; R2 g% E$ _! h* k; b# s
  30. , M6 v% d5 _- I: V! |9 K# `! Y
  31. ! `% w9 `( I1 P8 C, k
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */" t) W7 Y' _7 i2 ?$ }
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    7 W  t- Q; i9 K: Y
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    3 C! B$ e' o( h
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    + k: T1 C$ u) T1 E2 ^: v
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    $ K; a& o) a& x( d, _
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;0 d' m6 W% l* ]! ^
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    " H: v  u8 H2 q3 H; ?; z
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    0 u; V2 H- Y' U; l
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    , u. s* \5 R- ^1 l* s
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    % s; m/ W2 V4 D+ Y( `7 y3 p6 T$ p
  42.     MPU_InitStruct.SubRegionDisable = 0x00;" [# G/ \7 z+ h: i! O' x
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    3 K5 Y8 r( f8 q& s! s

  44. * o% V! y. {3 J
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);$ i/ v0 d7 N8 f

  46. # e% S6 p! A& n+ `4 _. r* }# H
  47.     /*使能 MPU */
    2 i' |3 s9 H0 l; l" H/ h9 s
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    / i9 c2 n) }6 u: {; o" I2 |
  49. }# D0 \5 J& R9 C
  50. # l% F8 a. t. K* p
  51. /*; f# i3 z/ |2 H9 U. o
  52. *********************************************************************************************************2 L9 O% G* b1 W3 W- x. q
  53. *    函 数 名: CPU_CACHE_Enable' j) J! F/ X: A% s
  54. *    功能说明: 使能L1 Cache
    . }: U: g; x" F) e
  55. *    形    参: 无
    0 \$ M- u* N9 q4 m: x& y7 I
  56. *    返 回 值: 无7 K& [: @9 R( `! q
  57. *********************************************************************************************************
    5 e2 S: s7 G% z0 e: I+ I
  58. */
    ) P0 I- |% M% |' d, r
  59. static void CPU_CACHE_Enable(void); V  {; V7 S/ [1 M$ I) p# K/ D
  60. {
    - c8 j# i8 N) g2 A7 Y
  61.     /* 使能 I-Cache */
    4 e: \* q  ]8 H
  62.     SCB_EnableICache();
    0 N$ p4 Y; y+ U; |2 u; Y7 T# Y0 U
  63. 8 W1 K6 C, Y$ ^2 R
  64.     /* 使能 D-Cache */
      n9 l6 Y4 z3 N$ w8 o; i  E/ H/ |1 h
  65.     SCB_EnableDCache();! {4 g8 L% K8 {0 s1 H6 z  p( F
  66. }
复制代码
" a/ }9 r0 ?4 E8 R% l* g6 ?4 K' [0 [
  主功能:
$ [. z* m7 ~3 ]! R/ T+ l# o: F3 M2 f" m6 k' C$ }: x5 Q
主程序实现如下操作:3 [& x# ~4 N5 G! d8 s/ K
. g/ Q. _$ a! t8 ]& D4 |
  启动一个自动重装软件定时器,每100ms翻转一次LED2。. J9 P7 X5 \( f- j: d1 |+ h
  按下按键K1,打印原始波形数据和滤波后的波形数据。, K9 n3 i, g! Q) ]
  1. /*
    0 j( c5 E% H1 _# X: Q
  2. *********************************************************************************************************
    # L" U) [, w' @5 `4 N6 n
  3. *    函 数 名: main  U1 @4 P4 W. ~- e0 L
  4. *    功能说明: c程序入口
    3 q7 A5 j  a" `
  5. *    形    参: 无
    6 K7 k: ~0 X" f9 P% _" e, ^& x
  6. *    返 回 值: 错误代码(无需处理)
    6 l8 }( a8 J  @' D+ |/ y
  7. *********************************************************************************************************
    1 C+ [8 \& b9 h) u
  8. */9 F4 Z: ]# }0 k9 T
  9. int main(void)
    3 f  [" u- `* M* n- f+ ]
  10. {
    1 F9 V! q4 U1 j& Z" y! q( ~
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    9 Q) ^8 ^" C* O+ M
  12.     uint16_t i;
    ; f* T& |/ X5 W+ |
  13. ) `1 y+ d- S( `# s& ?, {+ R& Q" N

  14. ) h! V+ [+ w1 y) u
  15.     bsp_Init();        /* 硬件初始化 */% C! C# B* ]4 ~4 p
  16.     PrintfLogo();    /* 打印例程信息到串口1 */9 g' ?; h. z5 A1 ]! h7 d

  17. , h% n9 |$ d- t" u' w* I$ c: J+ E! H
  18.     PrintfHelp();    /* 打印操作提示信息 */
    $ `! M/ Y0 Q9 J( Q$ n

  19. " N4 w! }1 K% e
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    6 \' D' @: V; t
  21.     {
    , i. V" x1 |, b/ g& t# e4 w5 x/ N: B
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
    * e) b, I1 Z# ~5 J
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    * I/ ^6 u2 L5 X) s0 i( Q
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    8 K) O, Z8 d. K5 j- D, a
  25.     }8 S3 H7 F# U1 |$ i$ w! H7 U# m# h

  26. " r/ \+ J# N4 M6 l0 h
  27. : a* W0 [) B( ^. D& {( s3 W) |
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    * R' ^" ?! E" v. g. T& R( X

  29. " P  y2 g8 e% j0 [0 K1 [. g6 x8 `; L
  30.     /* 进入主程序循环体 */
    1 S9 v; p# j/ A! A: C
  31.     while (1)* }) ^8 v9 S+ H! h3 L5 k
  32.     {- N1 H% S" Q( {$ l& E1 O
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */+ D' y+ [  A4 ]' D$ \: B, F

  34. 2 t( `3 N' E! S5 j: G/ q
  35. * W9 Q5 C) f3 b7 s$ }
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */; ]/ {6 q9 X7 j, ?! z
  37.         {  X1 [! z8 A4 R7 j) O2 o6 Z2 h3 ]
  38.             /* 每隔100ms 进来一次 */; m5 z/ u( @2 O: p+ h
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    * ]3 q, t1 C5 T! \
  40.         }
    & {0 y1 E$ I; V; B% ^) V% u2 f1 }. p: z
  41. ( F* p* G" B+ ?
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */# Z# ]6 S/ z0 c% Q
  43.         if (ucKeyCode != KEY_NONE)0 H1 l3 r; @& q. g
  44.         {
    & D! _% N$ q9 P
  45.             switch (ucKeyCode)6 R+ y7 m9 V1 M
  46.             {
    , r9 R' O, S4 [5 ?( i+ u3 j
  47.                 case KEY_DOWN_K1:            /* K1键按下 */9 z; A2 E7 v5 n1 T; ?
  48.                     arm_iir_f32_hp();% @3 M3 t4 p* z2 F1 y
  49.                     break;3 z6 t, v, |6 A

  50. * {0 S0 V7 W- \8 m
  51. ' z* q6 R* x  \5 R; l' m
  52.                 default:1 f" x% V, c4 w; G* |$ g
  53.                     /* 其它的键值不处理 */
    1 }2 @# \- J8 E' C) Q- ~
  54.                     break;
    " P: y. q# }7 F6 m" m6 S
  55.             }
    0 Z* w0 @8 a; {' K
  56.         }( X. J" i7 v: p; a8 j4 a
  57. + N- Q5 p( V% U1 k
  58.     }
    3 |! B$ F# A8 Z) M0 V
  59. }</span></span>
复制代码

& }6 `' Q6 K9 p) C  A- |45.7 实验例程说明(IAR)- c2 A+ s6 Y) T$ O; c
配套例子:
. `, J& v6 o* L8 q5 J* E" E+ p9 zV7-230_IIR高通滤波器(支持逐点实时滤波)
. [' |# p$ J( A! l% P* s
$ [  D6 y' n$ Y! L; k* m实验目的:! w( R" E3 e1 y% {# a9 V* t
学习IIR高通滤波器的实现,支持实时滤波

+ w$ C# m  y+ f$ q4 ^% a
) H7 Q! k' V. }6 {, ?% Q实验内容:
1 W* \# }7 B% u+ z5 p4 A$ ?启动一个自动重装软件定时器,每100ms翻转一次LED2。- B; F6 D5 s, ~
按下按键K1,打印原始波形数据和滤波后的波形数据。
$ K( e& r$ C  p! L6 s6 E
4 T2 |; w# P, M8 J/ C) q$ U8 i3 r
上电后串口打印的信息:
  O1 K- k, B3 g: y9 k- r
' ^* C3 V9 L1 r! @/ ?% _( U8 t9 T  s波特率 115200,数据位 8,奇偶校验位无,停止位 1。
+ N6 l/ K; Q' D* s
' ~1 r+ \! w3 c1 m) Z
a66c11d15f6f8dd3c49ad9b587ab3a90.png

' N3 b) O% L; A! {" F' a( ^% g$ @* I3 i  [5 G2 F+ P+ U
RTT方式打印信息:
5 V% g3 G: N  h7 o4 B' i
5 a1 a2 j5 P+ f& h) X
5b42e93b5c59803fdc45c18e564e61f1.png
* V$ a- u& [( f% _, i5 }
# K2 r# I+ `* i, T- C7 N
程序设计:
3 K2 n2 E: J1 h2 k; F
# d, `; @3 [/ }9 S/ S  系统栈大小分配:
: ~' Q5 s7 H0 `0 g4 q3 r- a8 \+ K7 J" t1 i, u
a48a46f1265dd3824bc690f874e90613.png

% r% \$ s( b  e  n+ l) d& _7 P6 G1 X
  RAM空间用的DTCM:
5 m9 a! y0 |! M0 ^. ^7 X# I, R; \* ?8 w
025eeb1d94bcb86c7bec22ed1e822f36.png

* J: R5 A' P0 S3 B( O. o
4 A2 G3 A! [6 c: {& j" I  硬件外设初始化- R( A7 O2 s. Y) B. L0 \8 Z1 j
" N3 v6 L3 L  Q- r) p5 r8 A" S* D
硬件外设的初始化是在 bsp.c 文件实现:; U4 Y% i4 y8 `8 Q6 I% z/ G- ~* \' P
4 B% Z1 V1 t+ t9 e
  1. /*
    / y% Q' f( @' M. D* B- l! |
  2. *********************************************************************************************************
    " a) ~/ t- m/ \) W  G1 H! d! [
  3. *    函 数 名: bsp_Init
    5 I- A4 U. b: T6 H
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次5 o+ {& `7 D, x, F
  5. *    形    参:无/ _% z4 i! \7 h
  6. *    返 回 值: 无
    , C( r: {( D( o2 t
  7. *********************************************************************************************************  Y. N1 V1 F8 j, F( n- V
  8. */3 q- C, `4 l* y8 P$ w' K4 v
  9. void bsp_Init(void): _: G1 x% b1 [% Q
  10. {
    1 ]  y% U1 W1 F. Q2 t2 |
  11.     /* 配置MPU */
    3 b+ L; B' ?& H& q5 q% {" B4 n
  12.     MPU_Config();
    7 p% v* e5 u+ x

  13. : o1 N4 J( G' A: @/ _5 s0 a9 N
  14.     /* 使能L1 Cache */  ^: g) V. T) x4 R. r
  15.     CPU_CACHE_Enable();
    8 l  U. M) _8 D4 J) V
  16. + s6 Y: R: e. m/ }/ B8 e4 e, B
  17.     /*
    : x, w! J5 j! X
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:; z( W+ i1 K3 |1 ~5 W3 X
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    $ [& E8 U4 u/ [
  20.        - 设置NVIC优先级分组为4。8 k2 b0 H. z/ K
  21.      */
    $ ~# i. S$ W$ B: I2 H
  22.     HAL_Init();) t6 B; \$ d0 W% G! V) t
  23. 0 ?% c! ^( J3 U% ?, F! k3 v
  24.     /*
    1 S1 }( J1 w) S1 p, S
  25.        配置系统时钟到400MHz
    $ y; j& P; A! h, I7 E  t
  26.        - 切换使用HSE。
    % {9 {9 I" u) U; L. e5 S
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    5 [4 H1 |; G/ D( n
  28.     */
    2 f- G) P& |) |" A, _. @5 P- {# q
  29.     SystemClock_Config();
    % A& @! |" m$ O4 K* }8 y+ M
  30. / g/ G7 n' t3 M1 W5 F1 _- l
  31.     /*
    * W+ k" n, q  k3 p
  32.        Event Recorder:
    : u0 Q% V* b# O" O5 ?& ?) `. G
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。/ _7 [3 J. E! @3 f% \7 W- x, M
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章6 Q0 J9 V( R7 @
  35.     */   
    # L- |  W1 C) V* A0 q  |' @6 c( m
  36. #if Enable_EventRecorder == 1  7 [+ P$ I1 h" h3 o
  37.     /* 初始化EventRecorder并开启 */# M9 v$ k  b3 t9 P) b
  38.     EventRecorderInitialize(EventRecordAll, 1U);5 ~3 Y- k4 V+ I/ V- P& A* K4 Z1 o0 e/ t
  39.     EventRecorderStart();+ O- ]$ S( H& z  K; h/ ~& X
  40. #endif) P. U+ L' y1 z# L
  41. - W  b: s/ o+ w
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    9 b# _2 S: e2 j1 u' b8 E/ q  ~8 @
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */3 P5 o( r. ?# t) Q9 c
  44.     bsp_InitUart();    /* 初始化串口 */1 t9 D2 f! V* ?8 h3 [; `% j! D7 O" [
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    % _# l( [1 y7 j" c* m/ X
  46.     bsp_InitLed();        /* 初始化LED */   
    6 ?: Y- C1 L$ q6 l4 a
  47. }
复制代码
; f, ^) p5 a& X' D
  MPU配置和Cache配置:
* ]' I2 N, w% D  s5 l/ _1 ^' p( C! c$ i
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。( c1 c3 n0 b  i( Y  C
, n& h& ~+ W8 z! y
  1. /*$ F' o3 H  k& f# B/ K
  2. *********************************************************************************************************, s5 T. N' p# ^: X
  3. *    函 数 名: MPU_Config
    ) e( X, a  ?4 E. \" D
  4. *    功能说明: 配置MPU
    6 u- {" E4 v! Z# w* ~- K
  5. *    形    参: 无* B* k8 ~/ f# y  q0 q6 ~/ `8 G
  6. *    返 回 值: 无! g! U9 _# m; l/ Y0 K% q' F9 D* O
  7. *********************************************************************************************************8 \$ G; o8 I: [" E- B* k2 |
  8. */" S! l; v5 G8 E& S0 W8 N9 c' s7 \2 w
  9. static void MPU_Config( void )  \  ^: i/ y5 l. e0 J; M
  10. {+ d6 b! u& P7 v" R. x2 |3 x
  11.     MPU_Region_InitTypeDef MPU_InitStruct;% u/ ~" d& @6 U, X

  12. 3 p. W1 l8 K$ e
  13.     /* 禁止 MPU */
    8 \, `) v! O0 h' K9 }% A
  14.     HAL_MPU_Disable();
    " M- L6 C/ D# N: a2 K

  15. / l8 B% m% G7 q
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */6 V- w/ m0 K5 s
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;+ D6 F. Y4 l6 S! v7 e
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;% t  S8 Q! q2 T! [; j
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;2 Z) w& |6 k* x* L# U
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;$ }. Q, ~4 L0 \" t  F; B
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    , S! f, s& p, d* Q7 q& c
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    / l$ B0 _$ R0 }/ v' _) V( M
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ! E" a4 j  I( c5 f- F$ Q2 P* |* Q
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    ) J* g; n' r& y3 }) _
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;' m3 o9 c- y' `; L+ S5 N6 i
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    3 e- i9 r. N& M, n1 A' Q2 w. E
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;8 ^+ Z1 H7 V3 ]+ ^
  28. ; Z7 v1 B( u3 w- D+ v1 w
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);8 u5 w4 m* ]/ ~7 C5 m

  30. " [! K/ ~7 M; m$ _
  31. 8 i& |* T0 v5 Y! }- d
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    , J6 N# _0 ~: D1 `0 r
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;, p$ b. [0 E# m+ U" a
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;* }' C: F7 b. N
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    % H( c$ M% Z6 s) I
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;+ {: f4 v+ f- M& Z9 |
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;+ |$ \* }. S( M5 {/ k1 _3 m* W
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    ! S4 }; b$ {/ C
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    , u7 \; E1 i/ T6 H
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;$ n% j* d7 e) B7 a3 a
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;& s9 t, q0 E) X9 [7 p3 r9 ?; o
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    : d8 R9 h! H2 q. a( P
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    . E" p/ a- d1 w9 k; O% @. D5 H6 q1 d
  44. . I& P2 |$ L: \8 V+ A  G
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);6 ^$ w; O! k, x
  46. ' q8 I  }$ g' ?4 n# i
  47.     /*使能 MPU */
    2 w! {" A1 N. M; B$ T3 S/ W# S
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    $ P6 n9 T' Q7 H& f
  49. }. y7 l& G# E6 X
  50. 0 K* s8 T, Z* S% Z
  51. /*
    9 {9 U8 _5 m6 w. n
  52. *********************************************************************************************************
    . Q+ k( [' s% |# R. K) t6 o
  53. *    函 数 名: CPU_CACHE_Enable& B& ~- f/ U3 R- P3 w2 B
  54. *    功能说明: 使能L1 Cache
    " ]( e0 b  O! L/ y: P$ y
  55. *    形    参: 无* u( j& v0 Q! P' p
  56. *    返 回 值: 无
    $ I2 a1 `" I( h2 y8 u
  57. *********************************************************************************************************3 R+ D2 Q4 b1 Q0 W5 X
  58. *// s, h* l8 t3 g
  59. static void CPU_CACHE_Enable(void)0 j* J/ E$ u# ~) a
  60. {" b# N& h$ R8 ~
  61.     /* 使能 I-Cache */
    ( G2 S, W4 s  @; A' l6 ^0 k! g" |
  62.     SCB_EnableICache();' ?6 |. j' d2 X; Q
  63. . M  }4 i% f3 I5 t
  64.     /* 使能 D-Cache */
      y# T+ x% m0 j3 c2 ~
  65.     SCB_EnableDCache();0 a; D$ U* b6 n' |' r2 s+ `
  66. }
复制代码
+ x) y, D8 I# `& d6 a
  主功能:; `% G9 b& {( E

) e2 h, t/ K! g1 s/ v: b主程序实现如下操作:2 V' F: M9 `* C: z( D$ `+ w
( S5 t- a$ Y5 \
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
9 }& b3 x  m" y) I  按下按键K1,打印原始波形数据和滤波后的波形数据。
) R. \5 p. B9 `+ o$ ^8 V
  1. /*' E* l; Z% S) {! m# z
  2. *********************************************************************************************************: z5 R$ a( d. M% w9 S" N2 X
  3. *    函 数 名: main) e' b( w& x( L: q
  4. *    功能说明: c程序入口6 K3 Q- m$ Q3 |/ N( `8 t4 f% R
  5. *    形    参: 无6 k7 v( D; e" A, M: ~, d5 H
  6. *    返 回 值: 错误代码(无需处理)
    8 j( K% H$ c0 ^, B+ h, O/ ^6 E
  7. *********************************************************************************************************" X7 _+ X. O/ O: \1 O
  8. */
    * E8 f  D( v" s( C: e7 b
  9. int main(void)8 E, B/ L# s; P$ G2 m
  10. {) B: H- o8 J! h( R" v3 c3 j: i, g
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    : _. F! i6 p; t: T! P" t; v0 g
  12.     uint16_t i;& T- r+ m: ]/ B* y0 @8 e
  13.   w# l/ T! J' ]3 W

  14. 5 T7 n( [0 O" v
  15.     bsp_Init();        /* 硬件初始化 */
    & X2 o& Y6 M4 c( t4 ?7 Q- F
  16.     PrintfLogo();    /* 打印例程信息到串口1 */* B. \2 Q7 X. `' R

  17. ( r+ ?+ C! r, C* F
  18.     PrintfHelp();    /* 打印操作提示信息 */* ?5 U7 I4 u) R5 X( E

  19. 5 A9 \9 ]9 D; i! s  M0 l7 @+ O8 y
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)# j4 a, Z- h) g, P" [
  21.     {
    3 K6 ~% W% I" D9 V! |' `( E& r
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */$ \- k; z% ^/ b( d# t
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    ' S1 @6 f( S5 j4 v' @
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    5 M7 U# A/ \0 `) ^; a  |; f: ]
  25.     }
    / Z$ H* r7 t8 F# v

  26. ' M3 ~  e" g# s+ L# x+ r1 `

  27. 8 L6 j: D, {1 I+ i
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    1 p  q) `& g  M6 T1 h, C
  29. 0 h$ d2 W1 y* r3 u
  30.     /* 进入主程序循环体 */
    7 z1 B. L; ^. N
  31.     while (1)
    ' D. U3 b' h* q
  32.     {+ @. s. e+ P# a- d; Q
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */- s" ]& [0 l2 M# s1 c" n
  34. 7 b1 b+ H8 f2 p7 Q0 f: N" D! _

  35. $ y8 }! H+ w5 _* C( ^7 J/ x
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */& o1 Z. f; K# u- o
  37.         {9 a2 r$ R* x5 R9 U2 o0 i+ J
  38.             /* 每隔100ms 进来一次 */# Q7 {' k5 [2 Q/ {5 p0 C  ~+ @
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    2 _* v/ h. [$ F  L
  40.         }" k& [4 ]. T# g, \# ~" u
  41. 5 |( x' G! D& j; ?, T
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */* N) c5 n* x  k7 a, s; D
  43.         if (ucKeyCode != KEY_NONE); I. G0 L% K  x
  44.         {
    " i: S$ _& V/ R  J3 ]+ Y3 [) m. N
  45.             switch (ucKeyCode)* W- ^! Z& m9 ]6 e9 q" V1 F4 i/ x$ h
  46.             {
    ' `5 n6 g- q1 s& |$ w  n5 [
  47.                 case KEY_DOWN_K1:            /* K1键按下 */5 D/ E/ j6 g3 ^, G
  48.                     arm_iir_f32_hp();
    ; x* m2 X' I1 {" V# l
  49.                     break;
    0 L7 H: Y( n$ O: G0 n2 w

  50. % ?2 Q" \8 c: F3 Y# J/ a8 w

  51. # \8 B: N. y( i$ o2 m+ A# L
  52.                 default:
    9 b2 Y. L5 j* W: ~& {. N
  53.                     /* 其它的键值不处理 */
    7 ^1 s" n; v& R7 |. x" I
  54.                     break;
    ! F* o& ]' R4 D* Z* y4 l% n
  55.             }
    1 Q: h( e) `& H4 n4 g- M" Y
  56.         }
    0 o7 A" P3 n2 P  z
  57. ) d6 G1 c4 b1 F$ w5 d
  58.     }" K6 E6 {) l& s* M, a
  59. }</span></span>
复制代码
45.8 总结
! j9 V& l* {3 I7 o( S" U本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
+ c9 j4 l: F6 S# q  \) y$ l" ~9 |" j( h0 X  H

" r! G( b6 r, S/ n  b

& l0 U+ a) Q. x8 _/ V' @2 \. ^2 p# k( {9 R
收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版