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

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

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示
6 A# Z) a4 r% V* K8 @  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:2 }" g8 X) z# Q; i9 _6 x, A# q
" u0 R& y% X( V9 j( L
  所有数据是在同一个采样率下依次采集的数据。
. Y5 w+ ~5 Q  A5 @# L  每次过滤数据个数一旦固定下来,运行中不可再修改。
9 @4 p! R; q1 i; Z& \  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。* h8 o& m, y6 a$ D1 y
) v5 W" y5 Y) Z! L
45.2 高通滤波器介绍* S7 P4 P, G, C
允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。, S/ a5 ~+ v% _" e

% r; H. S* y& R2 Y& o5 a( h
c1e7dbbd579e8c1e535c9ce0678c3532.png

- ~7 b/ z. |8 G* p
8 q' {5 t' X3 {" t45.3 IIR滤波器介绍' t, R+ w6 _3 {5 }; U  W/ t
ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。
* T* I4 q: e1 E5 ]
0 p6 k( [! a3 f2 f直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:' d3 _! _5 Y& C. }2 H6 m; D% j

. I; [- p- O5 {y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]6 A( I3 U* M8 j5 f/ w0 ^, J
0 I9 d6 j7 \) b1 ^; ^: e* ?
直接I型算法每个阶段需要5个系数和4个状态变量。
, e# w  }% q5 K5 |  s! ~. K& i
! V' z0 C& a9 z8 k7 X
7afa71129081e82218f8d501da73448e.png

/ N  w" r  \6 {1 i2 y
+ V1 l3 |4 t5 _$ [' _6 L这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:
* h( ?3 L5 B9 z) R- B; ^: |! B. s% ^) C# d. \
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]
* Q& E' V9 w' \6 V$ H
5 h0 V/ q: p5 t- e% w/ n比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。; M6 c: s% N, L0 t$ V1 w/ K

% M& Y! Q4 m1 h高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。
$ z, a5 ^9 e0 z3 y: a) ?: q& F1 T: c% k9 U/ e% w# T2 V
75ec77cc1b971aa24bf16e169c67d8aa.png

4 i: U$ Y/ M& j8 d* [' |2 ^- L- H5 N; T- n
如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。* ^6 g- ^- P/ `7 ~7 C: b
9 J! y: N( c" f0 _% b
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数9 {; X; }' n$ H) n0 G
前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
* A7 d! O( `: A3 ]5 j) e
8 Z- f' D7 P8 e6 ^9 k- y: f下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:
8 Y0 `3 m$ [: o! a/ s
5 C( q" V& L  j, W; O
4e2868a42a0de4b63d0a13d64952e470.png
: W$ D; A7 T( A+ ~( t1 z2 z9 p

* s. |2 x/ T! rfilterDesigner界面打开效果如下:
- g# p: S, S4 Z1 W' L9 J  h7 I4 |/ \- a; p& f/ b0 E8 L+ v* H( L8 W
b9bee9bb0a4ada395e69793b38c566fa.png

2 @" v1 U! _( j) \/ X) p
1 a% n  m, N7 q* S: [IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:
: ^  D1 Y; F7 o1 j6 J2 Z% r& r2 u# G. I$ D& k& w: t" [! Z
84c4d0373d7c29786a6620c1050ee40f.png

) c( D+ |3 T, r1 U4 n; |# E5 X6 L& t" J* t# M
点击Design Filter之后,注意左上角生成的滤波器结构:
' a. p6 B1 @! I7 v5 G/ |
* `" C4 }* ^& r
e51daea41c797cdfd4a5c1857cc35eff.png
& T+ @  h3 R& U9 I- m

0 w% o1 i  y" r( `7 G: T默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。) X4 v# w1 K7 E+ v3 f

6 B. Y' k$ T( i" H. i) E" W2 O转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:, O, c4 I' p: S( G2 K! [$ m
$ x+ ]0 u- e: q$ y) n  Y$ R; |
0328d6421fbe59b9299acd1c889e3dff.png

9 V' i8 U, Q2 G, G$ C' A# C& |5 U, X/ e6 }& [. j
转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):3 n- H. X" {% ~

, T) ?8 ?" E. ^2 G( Y3 w* X$ B
232d594c793c269063eb16a62ce71fff.png
0 z( ]9 A# a: r7 l$ D

) S( x6 C6 o! C. F/ ?0 X0 y第一项选择好以后,第二项选择Decimal:
2 I5 u# [/ ?+ _$ n2 Y4 O+ f1 L% F* ?  c1 D5 P
31265a3570a3c4ff16c53050672e0e99.png

, n" U* i% p; Z5 {; c7 n
# p5 }4 u2 v, `3 L: ~两个选项都选择好以后,点击Export进行导出,导出后保存即可:
+ v2 [* l, h" h2 P+ l3 o: `' C+ r; J9 f* R& B# T/ j! i
a5e27f83f7223e9a95c29a47d1c79238.png
: Y$ @4 K2 Z) c5 q' W7 [

* B. g; \0 X4 j& V, \0 i保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:
3 v1 @7 z4 t& w1 b& [. s+ t" ?1 [& X, \" D
% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
, h2 I. c( a. ^& c& G5 I' Y% Generated on: 15-Aug-2021 20:38:33
9 ^* ?, Y) g7 l! r0 r9 a. v+ S/ @1 P2 l7 \% @/ f* M# W% m
% Coefficient Format: Decimal* c) M% J$ S9 J1 N
5 E  g8 L6 b$ T+ u8 T
% Discrete-Time IIR Filter (real)                                                                 1 P8 A$ O* s  Q( _8 E7 n; G
% -------------------------------                                                                 0 S& l, n0 L! a' W! |2 p+ F
% Filter Structure    : Direct-Form I, Second-Order Sections                                      $ ]3 q) y; i( s% l. H
% Number of Sections  : 2                                                                         " J3 H% A" s9 X* O7 E# N4 ?
% Stable              : Yes                                                                       , D3 d; m! {9 r4 U: n
% Linear Phase        : No                                                                        
: D9 ]/ S7 U: A$ ?0 ^# o
* w+ S( z) \# ^3 g( n6 [! {! v% i- z# t/ ?( H, X0 P2 l
SOS Matrix:                                                                                       
8 l( I9 M" D; M5 z/ D/ M4 y1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
5 r" T3 D0 `7 }1 M5 Z) E1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
2 \, J- S2 z% `3 p# q3 C2 L: L4 f* N5 P4 F
Scale Values:                                                                                     2 v$ u" q6 i) |1 n  w! o3 t
0.632277093897992026327870007662568241358                                                         * H+ Z1 q+ _" D- r$ Z6 u# {
0.478258337927073562401147910350118763745                                                                                                               
; Q; b  G# A8 h5 }) `! n由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:
% h+ X3 j2 N7 B7 F# F% a; x
3 }; R! J$ O: p# O( z2 A- DSOS Matrix:                                                  - h8 D$ }6 n% Q+ c, S+ f
1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        ( O% m& n$ L; @" s5 H! G
b0  b1  b2  a0          a1                                                   a2  E# K/ |5 ~, H3 F
1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        $ J% f8 L7 ?" L4 T  G5 H0 z
b0  b1  b2  a0        a1                                                     a2
, f( A: j" |0 D' v9 b- {注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:
  S/ F0 Q2 H5 i; D( g' B9 q% G6 W, O3 b4 n* R/ C- ~5 W
0.632277093897992026327870007662568241358                                                         
' E4 V& S  w8 u% e0.478258337927073562401147910350118763745  
1 m) K& }/ n5 O实际的滤波系数调用方法,看下面的例子即可。- X, c" X% L9 C' J
, }& |$ l; v. v
45.5 IIR高通滤波器设计
/ ^+ H. p/ M; j+ ]: {' k* O$ G
本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器' i0 J4 t+ t/ P+ [9 |8 ~! f2 L

5 e* M- ]6 h1 e5 @& L; t45.5.1 函数arm_biquad_cascade_df1_init_f32
* E) k( J" _' |& R函数原型:, r; d6 p" n+ S) u8 _
* s- I7 i3 Y' T) D! L' w
  1. void arm_biquad_cascade_df1_init_f32(
    : @1 O. N( ?/ Y7 F3 L2 a5 Y6 `3 q
  2.         arm_biquad_casd_df1_inst_f32 * S,
    # O# N5 N  x! p" ], z( D
  3.         uint8_t numStages,
    4 o* D% G* ^3 t
  4.   const float32_t * pCoeffs,
    * L' \  h* M; @/ D2 M
  5.         float32_t * pState)
复制代码

' z, W5 ^' K6 B: ]. p$ x1 O函数描述:  ]3 m! Q; ]* G4 @6 j' _1 u
5 {6 F( }9 A* d
这个函数用于IIR初始化。
( ?6 N6 B! ?- N; I8 a" K( i' ?2 [: u. o* C0 j, ]% L9 p$ Q
函数参数:3 C; Z1 b2 r2 j6 z! g
2 P/ u7 w. _1 m
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。* d0 b* a  h! H! U* y& `: \
  第2个参数是2阶滤波器的个数。* q$ j7 Q8 v5 ]
  第3个参数是滤波器系数地址。
/ N* G' G4 F) ?! D  第4个参数是缓冲状态地址。2 c2 l* S' z# [! q; k  o+ s
注意事项:  F0 e) F6 C+ [5 ~2 I! [6 @

, j- _+ v8 A) i7 i' F* y2 F结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):
8 O* r; S( j0 H& @) Z, |' S+ j6 N) ~+ I4 [: @2 X( b" B
  1. typedef struct
    * D8 p4 B/ u: k
  2. {$ [# K  m6 a8 }9 h
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */
    ' K- z1 T6 g0 j  ^
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */$ z! m9 I0 p- X& a% p, X1 h( x
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */9 J" F! I! e6 Q, G- A* u, G
  6. } arm_biquad_casd_df1_inst_f32;
复制代码

/ u# I6 w$ r$ ynumStages表示二阶滤波器的个数,总阶数是2*numStages。
* m' @% x1 G3 ?3 F$ apState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。
0 T4 ?7 ?2 [5 e: y参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:
8 J) }; d& O4 v  p+ {) L7 c; M{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}( ~  W( N) Y8 ~, W) j

, B5 O0 D2 _9 J' @7 M8 N先放第一个二阶Biquad系数,然后放第二个,以此类推。
0 @! X. p7 E1 K/ N7 k
# ?6 s# H+ L$ C- g45.5.2 函数arm_biquad_cascade_df1_f32
3 e( q9 w% u  a- i函数定义如下:
" U3 X5 s! d! |" }8 b9 M# H7 {* Z0 r  m+ E
  1. void arm_biquad_cascade_df1_f32(5 C+ E8 B9 }2 a& M5 E& f+ \/ R
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    / {6 {% |. Q" d: N7 t1 p, I
  3.       float32_t * pSrc,
    # l/ s% S& y+ ~* G) z8 y3 ?
  4.       float32_t * pDst,) J2 F9 l' L2 O% R8 w6 O- B
  5.       uint32_t blockSize)
复制代码

/ {, I: ^- v. a; `函数描述:: h6 l# m" p. r

9 L% l7 f( \  F; Y+ y8 i2 a  k这个函数用于IIR滤波。3 m  {& ]8 T# w4 x3 {# x2 C3 e* m5 }
" I4 k4 ~8 M0 n, A8 m' j2 s  Q) m; C
函数参数:. x3 T1 X/ {4 w  f

5 K+ x5 M3 V. e" q8 X: E5 v4 x  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
6 t& @5 u+ H: c" r* h: f! {) Q1 B- u  第2个参数是源数据地址。
& h' j" B2 H0 d2 v  第3个参数是滤波后的数据地址。
4 b+ ?4 H) B: x! @  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。5 m& E+ b+ Y& q3 W% Q
45.5.3 filterDesigner获取高通滤波器系数
. `/ e- l1 U- H* L( |设计一个如下的例子:3 t3 w6 ~3 k3 P% L& K0 w

, q7 J% o6 X+ p' D; h信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:
2 E% F( i6 ]- }7 O' t
) }. _2 \+ X9 s, _
7f8be339ea66c602e1b7ea4836e58544.png

7 ^- _" C  f& |" U! h
8 @9 r# {1 O! @. o: [: O6 p配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。
4 [7 l4 {, A1 W/ ]: J' `! ]( z9 \, x+ o+ z$ f
45.5.4 高通滤波器实现+ Y2 k$ ^/ R  y8 D
通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。
$ Y7 Y3 E3 C$ [9 h1 b0 u( `+ h, v" w: N8 r- P
  1. #define numStages  2                /* 2阶IIR滤波的个数 */# @' i3 o6 }1 o& _. j
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */
    ! y" B' t% f2 p3 x5 J4 @$ T' @9 F5 n
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */% p7 X1 x# o7 P  r7 C+ H+ b* Z, m

  4. 8 N1 w9 o. @5 v, c) L5 j- Y

  5. 9 k: l- w, @" I7 a6 l& @! B
  6. uint32_t blockSize = BLOCK_SIZE;
    " Q; p$ Q+ n$ v+ X: G
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */! q# y# H3 j7 Y1 u: V$ L
  8. 1 _6 C! P0 r$ Z# h/ G5 C

  9. 5 k' C/ |2 i# q) U! H
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */
    ; D. ~/ ^! Y! G, h
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */
    7 M, g  {) X5 S3 M1 a  B; ]0 \
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
    8 o% \) l( R3 k
  13. $ z6 W! h$ `3 [) h( P
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         
    $ e8 l# {) J! Q2 X5 B* R" v* \
  15. const float32_t IIRCoeffs32HP[5*numStages] = {, ]; F" j( v) M( U0 }, X; I
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   ; y" S% G" H8 ^' v" b- O5 W3 b5 U" p
  17. -0.544565360850816415627662081533344462514f,     
    " B5 r' k/ N3 U9 _  g# a& B
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  
    1 w. E6 P# g$ k3 |4 p4 @
  19. -0.168318873843973093595849377379636280239                              " J# d: ?! c* L7 n+ V+ b, }' k
  20. };                                               
    7 ]* D7 ]# d; {) _: A9 q# p
  21. ) h  A6 V4 ~& S8 @2 g; O
  22. /*
    " E1 Z: `4 i" ^# M" y
  23. *********************************************************************************************************& O0 e+ Z: v8 x, P4 d0 T
  24. *    函 数 名: arm_iir_f32_hp
    : [$ d( t' Z6 P/ _" o5 i7 P
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器
    . Q  n/ S3 l0 y/ g/ J5 D
  26. *    形    参:无' S" e9 Q: E) n
  27. *    返 回 值: 无
    7 Z. p( t3 T, q7 \3 P
  28. *********************************************************************************************************$ ^7 v8 S' M" x0 i
  29. */, o/ Y( W1 N% U, c
  30. static void arm_iir_f32_hp(void)8 @2 t2 R. ?7 ?9 |. W1 p3 ^. Y, d
  31. {
    6 d5 X0 G0 e& y
  32.     uint32_t i;- D4 n) |. ^, J& p6 ]5 P
  33.     arm_biquad_casd_df1_inst_f32 S;
    " _& h! F  Z* X# a, W% F
  34.     float32_t ScaleValue;1 h- t9 d5 Q& m+ v6 W" \0 j
  35.     float32_t  *inputF32, *outputF32;9 S  c* ], C  `2 t  t$ h) z( K
  36. & G1 L/ |) M# ?! H) s
  37.     /* 初始化输入输出缓存指针 */3 X7 e+ L8 ~+ \$ Q$ b
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];  n7 Y, X3 _7 y3 E5 s
  39.     outputF32 = &testOutput[0];
    8 p; b. k. Y- O' l& ]) v& a, ^
  40. 8 H$ S1 Y4 w" Z2 b" F, c9 D
  41. ! ~  c' Z3 S" v) m
  42.     /* 初始化 */4 u$ {8 R3 D. U% Q/ x0 Z9 B7 D+ G
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0],
    % t! t7 w) i! W( F" U# D* I
  44. (float32_t *)&IIRStateF32[0]);8 W0 B* \! f8 r4 [/ z  h* _

  45. ( c: C, ]6 |) g) O2 g

  46. # W" a% y1 Q6 R+ t# \
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    3 x( a  s7 y" \$ b$ v% j1 ]$ v
  48.     for(i=0; i < numBlocks; i++)* x2 {9 c5 m' N- s6 Y( V
  49.     {
    ! ]7 @- P' K7 C
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),
    , w5 I1 c8 n& }$ [. o3 g
  51.   blockSize);) q: \) @3 Q# n3 T# _* ^* W
  52.     }7 B7 d; D' U% G  @  z! H

  53. - ~8 @+ M9 w, u0 d
  54.     /*放缩系数 */0 w% ^+ F$ t* ?2 ]6 Z+ x* F1 k8 \# L
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f;
    ' q$ i: V( L1 I' W8 m$ f

  56. & ?4 K% \, t7 ]0 D: O! D
  57.     /* 打印滤波后结果 */
    $ f2 }: P& I2 h5 \, K+ d" N' Y* ^5 t
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    1 f8 l7 Q/ T& R9 m
  59.     {
    8 _* V- v# I3 Z( A. o: U2 j% R3 B" E/ Y- I
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);( i7 y; L' R8 E; y, @9 K" J
  61.     }
    2 n' n. T6 j" |& ]
  62. }
复制代码

. P0 b) e2 S( p" D) i+ _* D, b" w5 G运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。, ^1 j3 I, d' F% v

) |4 E6 f/ l' W0 v  p8 y对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:$ u5 h6 O- _, Z( u& `" Y
& M5 P+ L1 N7 q
  1. fs=1000;             %设置采样频率 1K+ M- h# C( t8 J% U4 l
  2. N=400;               %采样点数      
    % i1 Z/ D- l- g# t# h4 |
  3. n=0:N-1;4 X' O7 B. w4 l7 x% T2 Q& @, {
  4. t=n/fs;                %时间序列
    6 |/ b% Z7 ^5 ?' D
  5. f=n*fs/N;              %频率序列- u/ H6 M2 K: a; o5 t: q

  6. ) W4 O. G. j& |) N1 o4 }
  7. x1=sin(2*pi*50*t);! i/ z8 U, O' }1 k
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波+ m. D  Z9 G/ [  J0 A
  9. subplot(211);* G9 R! Q; I$ g8 g
  10. plot(t, x2);
    * A& z% P1 Q' _1 f# D5 N* K8 R1 G/ H
  11. title('滤波后的理想波形');
    * ^  }5 x! J1 k# D4 A# n
  12. grid on;( C  {- m1 J1 F3 P# ~  y( F: f

  13. 1 P$ Z+ M* j9 t
  14. subplot(212);  ?) j* Z8 p" N( u9 {' J0 b
  15. plot(t, sampledata);
    % A. _9 i$ H: z  q; \/ u
  16. title('ARM官方库滤波后的波形');
    / O* O5 h% i) O- p
  17. grid on;
复制代码

, s* G- q; @$ M% fMatlab计算结果如下:
' ^1 }5 M0 X( a  r! h# T# r; O
( R8 S- Q  x  R
91111ef8530bb9e40d9736ccc2c12424.png
0 X1 G+ ~7 r5 U7 N/ G- \

6 F1 _9 b% T2 a从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:7 D7 z5 M& A, d1 L& E2 U7 ~
" Z. r( Y' p- Q1 D1 U; q
  1. fs=1000;               %设置采样频率 1K
    / G) a0 c6 P& e* @, x1 j
  2. N=400;                 %采样点数      
    ) I9 \' C$ M- o
  3. n=0:N-1;+ i% V& E% b6 c
  4. t=n/fs;                 %时间序列
    % m& b: f4 @1 X% }
  5. f=n*fs/N;               %频率序列
    8 V2 R5 r! N! f

  6. 3 `1 h' ^, [+ Z, t; O$ p3 o) n, K- t
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成, L0 E  ~% c5 Y( `  o5 P' u/ @
  8. 4 e& P2 j1 U8 E( f. G( t! ?
  9. subplot(211);
    : x; i( `4 P0 i! _( @) G* Z
  10. y=fft(x, N);                %对信号x做FFT   
    ! U, W( c( _  n( Y) }
  11. plot(f,abs(y));* {0 p9 Y( w* g5 w
  12. xlabel('频率/Hz');) I  m) ?/ ?$ \0 ^" b
  13. ylabel('振幅');
    & u6 v1 N/ ]3 Z3 D( R. Y# b
  14. title('原始信号FFT');9 R2 W6 J* y4 h4 k3 ~. |. W
  15. grid on;8 A$ i* b; e% i/ M

  16. & w5 O# b* Q  Q# D
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT/ l1 p9 U1 |* H% Z
  18. subplot(212);                               , _/ i0 b% k: D
  19. plot(f,abs(y3));; [4 ~2 g$ E+ W. @  c
  20. xlabel('频率/Hz');
    7 {+ D- B' n+ G4 x: ~+ z6 e* n
  21. ylabel('振幅');8 \1 k/ s; F" F8 M# ?
  22. title('IIR滤波后信号FFT');6 H. U$ C* {  u# [1 u. U$ `$ S1 Z# l
  23. grid on;
复制代码

- F5 {) ~. l) q% q. f6 nMatlab计算结果如下:% i/ y2 y& `/ {, H& f" R# |( o

/ F) y9 J+ `2 d( f
d13d47932e1b37bd5046b7ad31415509.png

( v: o  N& O' f0 ^+ I6 \2 o; ^+ L
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。' _* z8 u9 n: F  u& V8 P

1 x+ a" [1 t. J" h1 X' n45.6 实验例程说明(MDK)/ C. C7 b9 d1 W4 x% g
配套例子:
. x+ ~" K7 p0 G2 d9 JV7-230_IIR高通滤波器(支持逐点实时滤波), @# x% c2 f8 y. q

+ U/ T9 D: U" y* o, X6 J实验目的:
6 E6 j- I/ R6 ]) c" O) |学习IIR高通滤波器的实现,支持实时滤波
0 v7 I: }) r# I$ W" t' \4 t
7 e* I% k5 T! r0 z6 p+ G实验内容:# a1 q( {7 |1 Q* P! C( m
启动一个自动重装软件定时器,每100ms翻转一次LED2。
! U, c+ U# z1 Q3 J& J0 d按下按键K1,打印原始波形数据和滤波后的波形数据。
8 ~) R+ n: o0 T& ~5 T6 ~; [
$ n; ~: i3 s) J( d9 b+ V5 @8 T) u1 D使用AC6注意事项
3 U" z4 ~% q8 e; E/ Q7 [  r: e特别注意附件章节C的问题" ^# F0 V3 a" u# M; V
# q' v4 T- A. B9 w; p
上电后串口打印的信息:
( R4 U8 v2 E, M2 p* J- L& k
. n. C: Z# X- u3 c$ H波特率 115200,数据位 8,奇偶校验位无,停止位 1。
6 `) f+ N& U4 D- v# m: \  y0 n* y0 |. R! L9 G
262f2df9123b012595369835f600f93d.png

! {3 I' i2 F, I. j" ]
, |, Y2 o- I* B! ^& IRTT方式打印信息:
" L! k2 |# J% g! Q8 W" J
0 M" V5 t  W% [3 G9 E0 u: \) x4 B6 Y
75dd602d2d2eb24a5dea611cc45ce934.png
7 w( b' s. j; A6 w

' e" O8 U4 r/ M0 N, [8 {0 r4 @( {程序设计:
  s6 j0 F% }! I; s+ a% c. i( f  f8 D6 j4 f! c
  系统栈大小分配:- z, [7 u/ ^$ r1 u: E# `# U/ c
1 b& y  z; a4 I. b7 S- V% u1 J0 d
640570198f69835d8ac9eb982ccf39f6.png

- J2 E' L) o+ e. N6 l) T/ |6 y" n/ n& Z8 v( P7 K( x& m
  RAM空间用的DTCM:
! z' ~& w7 U0 |/ u" ]3 ]! \" s$ ?& g4 T, y4 K' D% T. V
6efb3697225eacc369e069ab80d69ed2.png

4 b( f! S' u0 W, i
: [8 O1 m$ T4 ^. `  硬件外设初始化
2 J; D+ S0 W, O  n3 [1 w
& y5 N" A; o; l% m8 G8 A硬件外设的初始化是在 bsp.c 文件实现:0 f8 W3 t) D4 O* z

& v  ~, e0 }2 d# l6 |. m
  1. /*5 a8 j4 S9 r, G7 ^( g4 |- {9 `
  2. *********************************************************************************************************
    ) ?  [: y% f% A4 L9 _) a- Q/ e
  3. *    函 数 名: bsp_Init
    8 l0 d  C' X- c. \) O! a+ E
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    $ I' S8 ^5 m, M  U. Q( m1 Y% y5 @
  5. *    形    参:无$ g- r  k" R; j
  6. *    返 回 值: 无
    ; ]0 K3 E: t$ s" T
  7. *********************************************************************************************************
    ) z* O3 Q; a- Y& @% j
  8. */
    5 ?. s, G6 f( n6 s
  9. void bsp_Init(void): L( L- z. B/ u: K
  10. {
    5 {5 j- a3 L7 r
  11.     /* 配置MPU */
    + ^# W/ ^* P  g% j9 ^
  12.     MPU_Config();
    + l) B1 @& h8 Q% N' t' S
  13. 3 y( B5 f2 v8 F/ Q4 a8 e- C
  14.     /* 使能L1 Cache */
    , C- m( t/ O( p2 A7 p) W
  15.     CPU_CACHE_Enable();1 k0 I2 \/ {! b  e) v$ k6 o
  16. % \) H5 y. l5 H& d6 D, j( G% h! a
  17.     /* 2 u" i% M$ J5 s7 g$ N& b+ S$ r" C7 @
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    9 c- B* Z0 O, L1 R9 _9 p
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。7 ^1 m4 e& O) O$ r5 H1 l
  20.        - 设置NVIC优先级分组为4。5 w# }" S+ t6 j
  21.      */- S7 t* Y! m7 _
  22.     HAL_Init();* j5 B* O6 Z( b% N* [9 H

  23. , z. _5 ?5 a/ z* p
  24.     /* $ x; ?* O" `* i
  25.        配置系统时钟到400MHz
    3 r, V( Y# c, `3 e0 K2 l8 G7 q
  26.        - 切换使用HSE。
    5 j( Y- C3 d$ q
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    # L; `7 I8 p% ~0 {2 |
  28.     */
    5 b2 C) ^' h, \" P2 y
  29.     SystemClock_Config();: X8 j+ k+ @9 \( \+ K+ h4 L

  30. ; Y. Q% _' g4 s3 [7 W
  31.     /* 1 n6 d& v# w& A- ~7 h. w1 ]& V
  32.        Event Recorder:4 O' `' B" f2 q$ Q8 i5 l
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    # T4 K% D9 n9 l% ]' Y  c& s4 k
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章. W8 R/ W; |2 b/ P$ }- C2 B% d
  35.     */   
    & Z8 ?1 P1 i* Y& Y
  36. #if Enable_EventRecorder == 1  : _, X3 ?# ?0 \" [0 o
  37.     /* 初始化EventRecorder并开启 */* t, V& Y6 O6 d
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    1 [! k) n5 J5 }# B. I0 u
  39.     EventRecorderStart();
    5 s+ V" U3 I& F  A/ f3 _; _0 Q4 f; m) R
  40. #endif
    ( @% H' b) h# G" O
  41. $ X; {0 D7 Q7 i, L6 n
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    8 F0 V7 N: H3 i( s& R, m7 t
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */5 ~: x- q6 l0 `
  44.     bsp_InitUart();    /* 初始化串口 */
    * U2 V! Z9 ]2 A5 Y: G) L
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    : u' l  R% o& g# b* B) w
  46.     bsp_InitLed();        /* 初始化LED */   
    " i4 `: M1 w  g! T9 f8 `
  47. }
复制代码
9 @0 O0 V8 c3 ~) |& T+ f
  MPU配置和Cache配置:
3 ~* n, s. ^' o$ _
4 W1 J* S! \; T+ S- E数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。% v! g% g. H/ U" t% j7 _$ B" P+ G% Y

' G- q( I  H- t  q
  1. /*0 M; f# J* p% v' [9 J
  2. *********************************************************************************************************
    ( W' q+ P: c4 D: \  x
  3. *    函 数 名: MPU_Config0 ]2 _4 O) u9 w( G+ Y- A2 v9 r" b4 p
  4. *    功能说明: 配置MPU
      [% o0 @; j! D: l
  5. *    形    参: 无
    5 p1 R% ^; x' {9 T" }
  6. *    返 回 值: 无; h; k7 I) D3 _. L
  7. *********************************************************************************************************/ F/ f4 p3 v- R2 G+ H' I8 }$ `7 z
  8. */5 d7 B: L$ A* {, K, Z' E% n
  9. static void MPU_Config( void )
    3 s" M" V0 b$ W. t' |$ N
  10. {" |' M; x8 }* r4 E5 B
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    ( ~$ ?0 x8 K8 }$ f7 _6 g
  12. 8 E! j8 m  u2 M; ]
  13.     /* 禁止 MPU */
    # E; ]/ j2 g/ T# [& {% V9 P
  14.     HAL_MPU_Disable();
    : p, Y4 n- I6 ]. P4 e+ z

  15. 3 j: m' R( m' E  A: m' x0 o
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    " l8 o& B$ {( z
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;% ~1 [: E1 |' x  z% B4 a: M
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    * @9 P& m) x) y) f& c
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;& i8 ~! ?# u! ]
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;9 v7 e3 ?4 z) V7 h/ ^7 \9 y
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;1 m2 _9 i* P) d3 M
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;: G" U% @( J7 B+ Q5 Q9 r+ C1 Q' H
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    $ p# d5 Z* Y7 A& E9 a* C
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    : R. r& x* M3 G& ]( A+ H5 j8 D
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;! f6 e/ M. b4 v
  26.     MPU_InitStruct.SubRegionDisable = 0x00;# i# R) u8 ^& ]5 t8 P9 ]0 k
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;3 x  Q1 {+ M: b( S- C( t( R
  28. 3 f( a+ o6 h& B% x- p
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);3 L# o9 x+ w0 ]6 D  H3 H" K! E- T

  30. 4 D6 j6 x! P8 V/ L* `' O
  31. / L& T5 |* ?- @6 Q; M
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    . ~, m4 `4 R; Y7 w; m2 T2 `
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    ; J/ }6 t* t9 Y* Q& m; Z
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    $ p1 \  F* `. [  S) ?/ S0 ]7 p
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    + m8 G; v6 T; ]
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;* A# K) }( u, W2 W5 s6 S% l
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;& M6 {3 l+ x6 \% Z5 _5 @
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    ( b" u4 V# J2 e7 R1 v) N; _
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    . c" [' ^9 P" Z6 l7 Z) ~
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    / _  t1 Y- }( \  G' n7 S
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    ; l6 o' F/ b( R. W& H# G
  42.     MPU_InitStruct.SubRegionDisable = 0x00;+ O: ~& `/ U, q
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;9 p9 n9 ?; `% c
  44. $ P8 i1 u4 y5 [6 x1 N
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    # ^% y9 K- D/ c5 B
  46. ' w. D+ z% _2 ?% A4 c- [
  47.     /*使能 MPU */
    ' W* ?; @, S1 ~# e# A
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    , n" T& G  i+ v( t7 E
  49. }
    ( Z& L5 }" j7 V8 }1 R$ v& x
  50. $ x7 _  Q0 Y6 `# {, }
  51. /*
    . B! V9 \5 e$ Z: m! m
  52. *********************************************************************************************************
    1 o6 {. z* _& m1 z
  53. *    函 数 名: CPU_CACHE_Enable
    & i0 E( R( B1 v  X( L5 |, q! [4 u. K
  54. *    功能说明: 使能L1 Cache
    ) i( |5 k( R& j
  55. *    形    参: 无* f( ~. A( k; ?" j$ c0 Q* W
  56. *    返 回 值: 无. U. F+ h3 \) X
  57. *********************************************************************************************************
    & K" ?3 t5 }1 y& @
  58. *// l. F: F& U6 Z8 y# V" g
  59. static void CPU_CACHE_Enable(void)
    + M: c$ |3 f' B( ]' r8 ^
  60. {3 E- a8 P7 I: P) N9 x
  61.     /* 使能 I-Cache */
    4 W+ h+ c2 q1 f: w3 H
  62.     SCB_EnableICache();& P, X5 _  H6 s; |

  63. / ?( G% i* o) X+ I
  64.     /* 使能 D-Cache */9 n5 ^: p) B5 ~7 J7 c) `9 j
  65.     SCB_EnableDCache();
    5 m' b/ q/ {! P3 k1 ^
  66. }
复制代码

  C. l7 Z! U  E& A+ A/ F  主功能:7 y3 A8 h: X4 Z+ N8 l
* T4 [3 e& Z6 N
主程序实现如下操作:
5 ~7 ]. L/ A6 ~0 Y6 A6 M: j2 G# K% z4 N0 [/ o0 q
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
. v, j: r( {; T7 i8 e  按下按键K1,打印原始波形数据和滤波后的波形数据。
3 Q4 @  s& m: W( Z
  1. /*
    ; z* ?/ w' u$ s  \( A; g5 V
  2. *********************************************************************************************************
    3 W+ }9 b$ q* `/ |  h
  3. *    函 数 名: main9 {, N" I4 _& f+ l6 u
  4. *    功能说明: c程序入口" u4 k( N- |7 D3 l" V4 e% d5 ?8 W  T
  5. *    形    参: 无
    4 y4 u( a1 T4 J7 V) S
  6. *    返 回 值: 错误代码(无需处理)6 V) e$ h: h3 b  D" M- B- }! R. u
  7. *********************************************************************************************************. O7 b  J& o2 r" m* l  c
  8. */7 h$ m8 Z) G) U/ ]# h( g
  9. int main(void)
    + C5 m4 s" j+ S8 ^0 q9 Y2 D; W
  10. {, u( P. B( X& J
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    3 O: O+ W$ b% C/ ]4 p
  12.     uint16_t i;
    $ n) H7 c" K+ j$ n7 ?% I" l2 s
  13. 8 w' |, Z2 h8 f3 G, Z
  14. , Y" k! b- ?0 v- q/ e& D* _
  15.     bsp_Init();        /* 硬件初始化 */9 |7 Y* X: z4 \0 x" z6 K
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    0 {6 }# Y( |" o5 i2 u) R. z0 m
  17. , m4 a8 z& }+ k% X5 f( k
  18.     PrintfHelp();    /* 打印操作提示信息 */
    5 [8 c2 P, }, \9 ]6 n2 B
  19. ' ^" q5 P5 W2 G: Q' x4 `7 n
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)/ ?8 c/ p' \$ j& w% G- J/ k
  21.     {
    0 a) B; D9 P  Q5 U  l
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */' I0 ^; B* p$ ^. }1 z% D; `
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) + 5 f; N- ]6 `4 P& U- I
  24. arm_sin_f32(2*3.1415926f*200*i/1000);# x9 K4 z! z! q2 R
  25.     }
    . r& f+ }4 m1 t  F: D
  26. & ~% T* j* n9 x0 I7 D' `+ u! q

  27. 2 S, u* N' h1 j/ s
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 *// l0 A# }5 t- S9 r1 a$ R0 y
  29. * a) u: G& z" D# ~! Z9 k
  30.     /* 进入主程序循环体 */
    / g( y2 ~6 C: D$ W( Q* S
  31.     while (1)! t' l. l4 X7 l& j7 s
  32.     {
    4 m+ b8 U& T7 n
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    # x% W" {* P' s+ w+ N5 t5 G
  34. 4 |0 \) F# E% F
  35. - g1 }. u) C1 u3 `( y. ~' M5 Q% D
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */
    + \9 r4 c0 n8 t: x) z8 t
  37.         {! V% N, P% ]  N. V( r7 c! N
  38.             /* 每隔100ms 进来一次 */
    / p: n$ v! N0 g1 L1 g9 U; E: o
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */6 Q# P3 a6 u& s9 V  f
  40.         }- c7 i( ]( p7 R
  41. 6 R; c6 _+ @4 N" l
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */8 o  S) _. v4 R+ e4 |1 R/ u
  43.         if (ucKeyCode != KEY_NONE)+ S2 Z; v& `+ ]0 C& G& x8 D
  44.         {6 w$ s5 h- u0 U/ I- L6 S! I
  45.             switch (ucKeyCode)
    ' W' W; ]. E& l, x* v2 y0 ^
  46.             {+ L1 Y( v0 ]  o7 J! |  k$ U  U) X2 t2 S
  47.                 case KEY_DOWN_K1:            /* K1键按下 */5 `/ s) i# c/ I& G9 \9 D/ B
  48.                     arm_iir_f32_hp();. A6 G2 i( H; f' F. V  Q; K& r
  49.                     break;4 y0 v, s" v& F$ s

  50. 4 z& w0 I' k) F9 M
  51. 4 [0 ~% ?) J( n: N+ I
  52.                 default:
    # x3 ^! K! a# K- _5 g9 O
  53.                     /* 其它的键值不处理 */) c6 G9 Y/ v  w; @- N
  54.                     break;! l/ L& X1 f. p  S5 I
  55.             }
    ( b" S# W0 Z4 S# n: D
  56.         }
    6 y& S. r3 ~: c! C# V: J3 d
  57. ' A5 ~$ |$ q. j8 h, J4 g$ Y
  58.     }
    7 a4 y2 Y$ W" |1 x0 a
  59. }</span></span>
复制代码
+ p+ W+ W9 O% n+ T" D
45.7 实验例程说明(IAR)
; Y; V! Q' U3 ~! ^( B配套例子:
+ F, t- r% \  E9 XV7-230_IIR高通滤波器(支持逐点实时滤波). h9 g6 j- K" L6 K# q! H
* G1 l. G- K, l: q
实验目的:/ v' b7 S. C4 W3 t; d
学习IIR高通滤波器的实现,支持实时滤波
2 E& A2 n/ z. r+ U  F7 F0 v
& Y: R9 R. N& w# L0 j% `! M
实验内容:
5 @+ v! B: [$ o启动一个自动重装软件定时器,每100ms翻转一次LED2。
5 o: C/ U; T* p按下按键K1,打印原始波形数据和滤波后的波形数据。
2 c& @5 F( j: c: Y6 h4 b, _4 I
+ b+ |+ A$ n; h! {3 G  W
上电后串口打印的信息:! {+ T$ h9 d. L' \

$ \1 e7 w: E* z% t2 L: |% a波特率 115200,数据位 8,奇偶校验位无,停止位 1。/ y# \8 ~# O0 H
3 x8 ]2 A: X9 d4 F& \- g
a66c11d15f6f8dd3c49ad9b587ab3a90.png
" D' S9 G. Z' A3 C

& B9 O! p9 ?" z' l2 RRTT方式打印信息:
# a* y, V- E! O* p* G
( D: s$ s3 w( I' b
5b42e93b5c59803fdc45c18e564e61f1.png

# v4 V( N) A# Z- W7 l' N' M- J, R0 Z+ W  a$ T+ _9 \7 `! j" f
程序设计:$ Q2 e; C5 R. i( }% c" e
7 X  n7 p' \9 K( s
  系统栈大小分配:
+ m$ D" ^' Y. E- e3 m0 x# H
, N! t) k. u0 x
a48a46f1265dd3824bc690f874e90613.png
4 r, m! o# A( u5 _# C8 K

/ H. ?! Z/ `# l- `  RAM空间用的DTCM:) r  G* ]: q. a( ~3 t7 K0 @
0 R* u" ]1 J1 s$ ^$ V
025eeb1d94bcb86c7bec22ed1e822f36.png

1 L4 u, _. w  _4 f! D7 b; E# R& l' e# t; s3 e2 i
  硬件外设初始化  W4 v* V, n+ F% e) B

* G# }6 W+ W; m% R4 F! C硬件外设的初始化是在 bsp.c 文件实现:
$ V5 ^4 a2 h% L- k/ T( s" c. C1 E
  1. /*
    4 i3 C; D* t4 k" X& p
  2. *********************************************************************************************************1 Z' ~7 M5 d( [1 H. y
  3. *    函 数 名: bsp_Init8 o  a. ]/ @0 Y7 p& Z7 D8 z
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次- J: o2 z' j! s- |+ T
  5. *    形    参:无) @& V$ Q% N0 q% ^; v7 g5 W  M% [
  6. *    返 回 值: 无
    6 H$ K8 y2 |! H) S" M
  7. *********************************************************************************************************  f: R9 }8 G0 n% w4 h
  8. */
    : G- ?% o# m2 l
  9. void bsp_Init(void)/ A6 L& s4 D( \3 ~
  10. {3 R4 E- I( O% \0 n. p
  11.     /* 配置MPU */1 P. ?5 r4 s2 `& Y. k5 B# `
  12.     MPU_Config();: G3 m2 ?/ D  J4 D

  13. 7 `* B' j+ p+ H/ P+ D1 D
  14.     /* 使能L1 Cache */
    / L$ Z1 |7 Q; h: g9 h0 l
  15.     CPU_CACHE_Enable();
    , K* x' I; O( `* p, s7 \
  16. ; H, @! n) b) f
  17.     /* ) V4 C/ X6 D' r1 y/ S
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:! m! Z+ ?& O2 U3 V4 _. d* d' k
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。+ C7 g3 z1 w' X3 s- R
  20.        - 设置NVIC优先级分组为4。6 K7 T* |4 ^" t; W9 L* F1 [: x
  21.      */8 Q  Q0 P) n$ z5 F
  22.     HAL_Init();# W/ Z! i1 a3 _! `0 b$ Q# H
  23. ) o2 e! b0 V* `) l7 P
  24.     /*
    ; y4 X- n, Y, S$ g/ o
  25.        配置系统时钟到400MHz
    ! J( t+ R- y& c1 I5 H& R
  26.        - 切换使用HSE。
    . n& F# M6 A1 G7 |( U& L
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    2 V' d. d$ Y% O5 o
  28.     */
    2 |6 {; ~: H' f' U; D3 b2 N
  29.     SystemClock_Config();* C6 S/ w. W0 i% [! S7 O
  30. 5 h2 B4 a1 S! o& ^
  31.     /*
    8 R. R" ?, A/ A  s
  32.        Event Recorder:
    1 g& w: k. q; [* M) }- N5 q
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    3 f" ^0 ]. B- |0 ~  |+ r  G
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章9 ~. d  y8 c- L8 k2 I. |! W$ k
  35.     */    5 U$ j; K3 N" o+ k' q6 Z
  36. #if Enable_EventRecorder == 1  % d% n, {2 z- [7 L+ I7 \0 Q
  37.     /* 初始化EventRecorder并开启 */
    ( Q4 x/ M+ V6 D8 k1 S8 p
  38.     EventRecorderInitialize(EventRecordAll, 1U);7 G' R! _  @& J: F
  39.     EventRecorderStart();, R0 x0 s; D& Q9 h7 [  \5 N; p  s
  40. #endif0 h7 o6 O+ m5 m$ M& _  _
  41. 8 R) s6 O) V8 N. p4 _
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    6 c5 K% h) [2 W8 z0 |6 K8 I' {
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */) R2 C" k3 v2 \( f2 _/ p
  44.     bsp_InitUart();    /* 初始化串口 */
    6 u, }2 s# X# ^4 N7 Z
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    ' r+ \" j7 I3 J' R& N
  46.     bsp_InitLed();        /* 初始化LED */   
    8 T5 O* r8 G+ r+ y9 F
  47. }
复制代码
$ ~2 O, d- J6 D* `3 W3 X
  MPU配置和Cache配置:
' F' P% i* |. u6 \; |6 @7 a
4 w* D) O) i4 ?1 s' v2 `数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
/ E3 T: ?4 k. x4 X. f  f  h" z; M/ c
  1. /*3 U$ i0 x  _$ i  c' b8 T! k3 I
  2. *********************************************************************************************************
      m4 E' ?, {5 Z, ]' x" V8 y
  3. *    函 数 名: MPU_Config
    2 E. m/ Q+ J3 l5 a9 Y
  4. *    功能说明: 配置MPU
    & `- ^9 m( b  |! _
  5. *    形    参: 无% A! H; [! P; C/ }' n0 d5 [0 z
  6. *    返 回 值: 无
    7 D6 `0 v& a' E/ v3 l- d  H
  7. *********************************************************************************************************
    ) Q" c/ s" c7 b9 v/ O9 q  v" Z1 L
  8. */
    & ?& _% T2 E9 j- N
  9. static void MPU_Config( void )
    ( B4 i. Q6 C3 E! h( o
  10. {
    % D# n& n3 u* ~  [0 R6 d
  11.     MPU_Region_InitTypeDef MPU_InitStruct;) S9 e. v; P- n5 W

  12. " I" G  P, K( L; ~3 G+ @
  13.     /* 禁止 MPU */
    ' N* b! K( h% d1 H6 t7 [4 ~
  14.     HAL_MPU_Disable();
    7 P; S- {+ P3 [; A- C, b
  15.   P2 q9 ?* V/ t1 `; V5 F9 Y' h- L: V$ }: r
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    2 X! Q) \- J3 f( H
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    : ^" b, b4 V9 n& z
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;1 g3 F# G* N/ [7 N& k( g
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    8 ~# ]& k& T. y
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;$ N) \- A+ F7 \
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    + H" H3 G% G: `/ \+ Y2 @
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;1 t$ z7 B' l( O! g0 S2 T
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    : F4 q4 O2 z2 G
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    8 r! E9 y( S& e- ~
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    ! ~. O1 G, T7 N6 A4 L8 O# q  b) B
  26.     MPU_InitStruct.SubRegionDisable = 0x00;: T+ z+ L9 ]' W# c* q4 k0 R  W
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;( P  c5 s  \" r: ?5 [
  28. 9 Z* U1 z( a, E! Y5 M" i, c  F
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);) n8 E% F! X: p$ h4 H7 ^6 t0 n* f
  30. + V4 L4 f% n4 r" d8 B& N6 ?4 m
  31. ! O3 K: `. p, M" e4 Z$ ]
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */6 T, I& x" s/ g3 M* k1 I# ~( q. V0 l7 S
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    8 G% x( M+ z! D# o6 E3 K$ W' G0 D
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    & x' L5 E. [# V$ J8 s
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    : u2 X, _0 m. ]# j7 E8 R% C
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;; r" ^! U$ a8 s" R: U
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    2 M7 y/ j3 Y, f& E
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      q- @0 y/ W0 K+ _) q3 E' h* e# U
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;0 P, S; f9 }, d. s
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    - w: Q/ c: |/ R4 q8 G6 ?: |5 k2 K
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;# }7 e" s, b/ T& n: B! a2 p
  42.     MPU_InitStruct.SubRegionDisable = 0x00;8 T: y% U' m$ ], P6 c; L; @' s
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    6 w0 a' `) [2 n6 q9 t: \

  44. 9 j! h4 B3 x' ]+ E' g, j) r8 |: g* n
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    - |3 ~5 q6 |# _$ d& P

  46. " W& L; L5 W' ?7 G( E2 r, ^6 M5 a8 ]
  47.     /*使能 MPU */" e9 m5 u( r  ?  h( u4 E% g  p
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);. a$ w/ V4 C, X* h* e2 i" k
  49. }
    , r* z- y* O. o6 V( X5 r" H  I2 m
  50. 7 g( l- w' F) T8 s1 D% M; Q
  51. /*& S" n' U) Y9 v! t
  52. *********************************************************************************************************7 S6 B' [* A  v
  53. *    函 数 名: CPU_CACHE_Enable* G6 c3 x% L3 V+ f+ f
  54. *    功能说明: 使能L1 Cache
    / K$ y% H# k% |5 y! {
  55. *    形    参: 无$ [' B% g( s$ D
  56. *    返 回 值: 无
    + E2 N$ h7 k) ~  k  z4 ^) k
  57. *********************************************************************************************************' E8 ]/ E, O, r7 G9 d
  58. */) F4 }6 }, z: `* L
  59. static void CPU_CACHE_Enable(void)
    + {$ U' k6 `2 K9 U' Q* Z; U
  60. {& v2 ^" l: U0 c% l
  61.     /* 使能 I-Cache */
    5 h/ j$ J% c: i; f! ]
  62.     SCB_EnableICache();
    ; }3 A0 P. A* G( U. L9 Y
  63. 1 f4 p% B8 r+ y) A- k. _! |: ^
  64.     /* 使能 D-Cache */& C0 \  u$ X9 ^. G
  65.     SCB_EnableDCache();: \+ ~& l# q# |% M- e
  66. }
复制代码

1 b  M- u! ^* n; Z' s  主功能:( X, a* ^  k. s9 b% l1 E

$ M' U; G# D" G* \' W主程序实现如下操作:# A1 h! Y) u6 H. c
" b/ C1 J& y  @% q$ Y) u
  启动一个自动重装软件定时器,每100ms翻转一次LED2。) `: g5 T  e4 H3 Z; A. F
  按下按键K1,打印原始波形数据和滤波后的波形数据。2 A7 k8 ?+ y8 L" v7 E
  1. /*
    0 P9 n$ {2 g7 B  l
  2. *********************************************************************************************************
    + m1 _* N! d# d& c& }! f' {
  3. *    函 数 名: main
    9 M; \9 Q3 i7 w6 {' j
  4. *    功能说明: c程序入口
    # A" w: z6 M" R( N% N( @) [
  5. *    形    参: 无
    ; E. b' c) f/ _' u- z: ~3 H6 Y
  6. *    返 回 值: 错误代码(无需处理)7 ~% ~5 u7 \* T5 C- ?  C/ q
  7. *********************************************************************************************************# r) U7 Z7 S. ?; c
  8. */3 R& U# ?3 U9 G4 q" ~  f
  9. int main(void)
    7 k6 Y# r2 l) t9 R! C4 m& Q
  10. {( ~3 b" u7 i5 H5 y
  11.     uint8_t ucKeyCode;        /* 按键代码 */, x0 v$ b8 [& U# D7 a
  12.     uint16_t i;
    * K, Y. s6 x# @# F

  13. ' z5 @- ]% z  Z8 Y' N" S8 o

  14. 3 n1 C* d1 ]3 B  I8 ?  ~! c
  15.     bsp_Init();        /* 硬件初始化 */
    / T) G* M/ a& I+ x3 X9 U( C
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    1 @6 D1 K2 \; `  U" q  h" v
  17. $ j, r- T/ {: r9 R: o0 o8 u9 b
  18.     PrintfHelp();    /* 打印操作提示信息 */
    5 N: }3 w6 I9 S0 V

  19. # `  ~5 a( x( C
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)* ^( z( l9 B7 P* q$ [
  21.     {
    2 f  C! P0 {& a0 Q6 e- g' g
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
    ; a2 p4 l3 `4 {1 a9 X$ i
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    / D2 j& u2 z0 O2 g5 m( l
  24. arm_sin_f32(2*3.1415926f*200*i/1000);+ G7 }; O% _: ?; w7 }3 R4 @
  25.     }9 d) m  o3 t, D
  26. * x; I( p2 O. C2 t

  27. 7 S! B% P0 t# j
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */) [; X+ P& n3 U+ @1 Z0 |8 C1 A
  29. ! d1 y) O& ?/ O3 O
  30.     /* 进入主程序循环体 */
      g3 Q1 A/ W9 ~* e' @7 L! l
  31.     while (1)( N# Q% L5 V* f6 q
  32.     {1 X$ l, `) {( c5 Q' u' @( m; @  ^
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    9 {' `, ?. x# x0 O# X
  34. 2 D+ {- l& Q: \6 e6 {9 Y

  35. # G7 G- z- M2 }0 f) }7 N! h7 U
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */
    6 d  ~6 a& _. z# B8 S
  37.         {9 k1 N0 X1 S% C; q! @8 }! Z4 o; s: g* ~
  38.             /* 每隔100ms 进来一次 */5 h9 ^7 P9 c& Y# K5 v
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    , w. g. x4 {( l, P
  40.         }
    * b# I1 _/ Q% l# f9 s6 {9 H

  41. " z1 L5 E* F5 H1 y6 R$ T$ r
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */! Q$ S+ F; N) ^# q+ U
  43.         if (ucKeyCode != KEY_NONE)
    ( Z* [* B/ M# ?
  44.         {
    * F% |1 E3 v8 |! N2 Y2 W7 @% a; \/ ^
  45.             switch (ucKeyCode)- w! Q3 g" l! v" _, k
  46.             {* x% S- }& d% X& T3 {3 r
  47.                 case KEY_DOWN_K1:            /* K1键按下 */
    ! L0 V. q% f/ A& O) Y+ q) t1 p& o
  48.                     arm_iir_f32_hp();; `7 c/ J# B' U
  49.                     break;
    9 G( H; q+ B  H- ^7 y4 A/ [* m
  50. 4 d4 e8 ~0 |4 F

  51. ' {6 |( I4 s2 n0 C' R5 Q( U' z- O
  52.                 default:  u" m/ D" `8 Z& l/ T$ n; g8 o
  53.                     /* 其它的键值不处理 */
    / s. \& L- m8 s
  54.                     break;4 b4 Q: I" W5 M2 W1 h
  55.             }7 l) N& m6 I" d
  56.         }
    ! e& a7 s( a! t. f; s0 F

  57. ! q* b! u% A' ^4 H* k( W! e
  58.     }
    ; P0 }- _" X" r1 ?( P3 Z
  59. }</span></span>
复制代码
45.8 总结: {& F8 p& f( r! r5 B
本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
- g4 A. g( A( L& |! `5 A' E3 V. a1 X+ Q

% G' d/ o$ E* V4 ~+ b' B+ c
7 O3 Q+ S8 [$ \) D
赞 收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版