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

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

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示
5 C# L3 B0 U9 w  o  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:7 P  f; u) J! Q7 u$ H, b1 ^
8 s+ M4 o, C2 c. ]9 d# k' H
  所有数据是在同一个采样率下依次采集的数据。
' S) L: f; l7 k5 k  每次过滤数据个数一旦固定下来,运行中不可再修改。& W" c- i4 u' I! _
  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。
  j$ Z/ }; \0 H$ r' m% ]$ g1 I; K( K7 w" q; N6 Y
45.2 高通滤波器介绍8 b7 L; t" Q3 K6 ~
允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。
  d3 a) Q* F* |& E! ~' N; C" N2 L; y# |7 o$ H& A5 A
c1e7dbbd579e8c1e535c9ce0678c3532.png

. |5 F. J! J4 [% O" t9 g& O. z* V
45.3 IIR滤波器介绍' |, [% o, o! R& l* d
ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。$ S3 d: H" E+ Q( f# J

# }- G' D1 d0 k' d  r直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:
; X* D  V4 T; x% B) [+ g
# y7 |, \( Y. B2 l2 F. Ry[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
  ]& K# o  K. Y) d
# m/ H- p. o  r: i  G% U直接I型算法每个阶段需要5个系数和4个状态变量。( m, ?6 i% W: L4 t

2 n& L# c, J* G+ _: x' V" I
7afa71129081e82218f8d501da73448e.png

% W4 T+ C8 d4 P0 q  b9 ~
* Q9 ]3 ?6 w3 V# U. _# g这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:* Q# j1 }% M4 c& ^4 r" f

0 ]. I; T8 j( M! X8 |+ p% s' u' c6 Hy[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]
( O/ g7 o! [! G$ f, {1 n  y, W3 ^
比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。; @. p  v3 J7 p4 B
8 h% c1 b1 P# a: B9 g
高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。/ {" c$ I3 [5 Z: z
0 t1 z; n  z1 ^+ `0 L
75ec77cc1b971aa24bf16e169c67d8aa.png
2 `, J( O- d, v- V$ |

) D+ m% L2 C3 Y如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。
. C6 J  f& D1 G. _8 J! }9 ?. R$ c; l7 Z: q& M3 |& @7 d
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数% [: ?* c1 e8 O- @
前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
9 c! S- K" [! x: g$ M1 J
5 n* n6 C% }( D4 K% q下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:
! N6 i- W( v/ q# j7 S6 q! M! K" ?9 C2 q
4e2868a42a0de4b63d0a13d64952e470.png

; R6 I5 q  F; a+ y( b! g/ C6 u( ~4 X; L
filterDesigner界面打开效果如下:
% X2 M  h4 v( L3 g9 L: m6 I& [1 C0 B& `: ?( @5 j
b9bee9bb0a4ada395e69793b38c566fa.png

+ u0 B. k2 `. i! e% \/ ?; K4 @& m, V$ D# Q& m/ D3 v$ N3 d6 L5 B
IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:" g. u" c& @5 r; a4 G
5 Q, M# Z/ n( b1 t
84c4d0373d7c29786a6620c1050ee40f.png

3 ~2 A3 ]& m8 X5 K+ D
1 c, ?7 v3 u6 |- C% C: L+ R5 P点击Design Filter之后,注意左上角生成的滤波器结构:
9 w! H7 g' b) F- z/ R3 o# d3 _" r, ~% M3 A3 q* `2 F5 k. T8 B
e51daea41c797cdfd4a5c1857cc35eff.png
& Z- _; X2 ^# n! _4 P" H

$ n* P* e1 w; i& F4 `. U1 y0 N- N* J默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。. v- `- q; _7 l* _+ P- {: X8 O& \

8 Q7 u- N; }  M8 }! P转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:7 t5 V* Y5 ]/ V' u. ~

+ V( e6 A  a: e+ t7 }- m& Y
0328d6421fbe59b9299acd1c889e3dff.png

; J) ^1 m8 {# {6 b2 j
: h5 ?5 ^6 i) T  j! D  r转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):
; d, B: I; \# A/ s1 {) x
; x/ p) d2 M5 T* l
232d594c793c269063eb16a62ce71fff.png
; K% n( I; e  |7 R
7 @! f5 Q1 ^$ a& t8 C
第一项选择好以后,第二项选择Decimal:
. }5 I2 e( d( z- M; ^) Y' ]$ C; \9 _7 c! m7 X+ L) o8 \
31265a3570a3c4ff16c53050672e0e99.png

; n) k* }- ~4 N8 r) O, U( \* \$ i
. `( {* v7 o2 E& h- U2 B两个选项都选择好以后,点击Export进行导出,导出后保存即可:
( g' ~& i: z1 a6 O) P# t* [- K: j0 N4 J
a5e27f83f7223e9a95c29a47d1c79238.png

9 O7 I9 d4 G4 ^. a+ ^! P2 H. ^3 C1 t% r# Y  U1 O& {! T4 C) A
保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:  `& S0 [# |" k9 f

9 V% ~1 {) `$ `  u, {) ?% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
5 w' c  V" U9 p. |2 w% Generated on: 15-Aug-2021 20:38:331 l4 I. b: T8 q* P

, ]! Y* ?7 W, J6 W: o, G% O3 Y% Coefficient Format: Decimal6 A' I3 p' p" F8 R

. x  O4 A7 g& B. r8 `  b% Discrete-Time IIR Filter (real)                                                                 ; B! Q' S8 n: _: b# n4 O, y
% -------------------------------                                                                 
4 [2 x- z2 D# Q( a$ h: Q% Filter Structure    : Direct-Form I, Second-Order Sections                                      
0 o2 N" r! n0 R/ f% Number of Sections  : 2                                                                         " o2 I3 \  G  ?3 \1 Z% a; L
% Stable              : Yes                                                                       
/ `  M$ U* V* e" M% Linear Phase        : No                                                                        
% z# N' j; Y& X
- n- `+ c0 z: V9 l, U  G- u7 h+ D4 e4 `& Q! x
SOS Matrix:                                                                                       
. w, E) w* m: q% C" P& q1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
( ]* J  {1 E3 |& R1 h2 B1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
: V/ R7 X( ^0 M. {, t) P2 @/ m8 ~0 D3 q. r, [
Scale Values:                                                                                     , P! G2 W" V! x3 i, }. w% N
0.632277093897992026327870007662568241358                                                         9 [. n# \# j# C3 X% ?, J4 O
0.478258337927073562401147910350118763745                                                                                                               
) T% W' R% t7 O由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:! Y0 i8 c# l: K4 i$ U! P% p% o! I

& G/ x# K+ V! U6 @$ T. B+ YSOS Matrix:                                                  $ A! X5 X, w* _4 m
1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        
  ?3 T! R# h7 zb0  b1  b2  a0          a1                                                   a2: w3 ?& I" T1 b2 S1 R" P5 \
1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        
* Q2 U1 V( v( x6 S$ d3 N' xb0  b1  b2  a0        a1                                                     a2
6 k0 b" a. }, w# V' o注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:& b& s9 o: @( L
. @- v9 \( P6 t* b  ^
0.632277093897992026327870007662568241358                                                         ! o5 P8 D9 ?. ?, x, _# k
0.478258337927073562401147910350118763745  4 O- T6 ]$ h  k- |3 v
实际的滤波系数调用方法,看下面的例子即可。% |; S  a0 |( B1 f) }
4 T  V9 P8 {. a! j1 [  P
45.5 IIR高通滤波器设计
" V. h+ D- c* x
本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器2 d  I8 e, H( v1 z0 S; v5 ]! F2 h
6 X+ n7 z  _7 Q. i1 h6 w
45.5.1 函数arm_biquad_cascade_df1_init_f32* \: @$ l4 k$ ~( S
函数原型:1 @0 Y% L6 T1 ^
- [+ N; N0 s7 |1 h
  1. void arm_biquad_cascade_df1_init_f32(
    7 ?# x2 M8 g% R* j4 z. f
  2.         arm_biquad_casd_df1_inst_f32 * S,
    7 p# |7 t7 v9 e8 [6 |
  3.         uint8_t numStages,
    , U. m9 o) z7 Y8 }# C
  4.   const float32_t * pCoeffs,; ]. N9 y7 t6 n" u0 r6 m2 }: U
  5.         float32_t * pState)
复制代码

! c( B4 H7 t9 v0 \$ i- b4 j# o函数描述:4 K: G* b2 E8 j* M. W. @$ n
7 H3 ]0 S2 G* e& V. ~
这个函数用于IIR初始化。
6 Z: I1 F0 R1 L9 Z  ^4 E  o
. s' W% l: y, v' B8 I) O函数参数:
" B2 ~* S0 t) D/ K- ^6 G3 o( H) c
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
1 m7 h- s- |% D. o; S- ]5 I  第2个参数是2阶滤波器的个数。( r* K' k2 ?  E1 ^
  第3个参数是滤波器系数地址。: j( u& B/ [' e
  第4个参数是缓冲状态地址。& T' N6 g$ J3 d1 F
注意事项:3 ^$ A8 _! B' M
" x4 S# H4 F9 b1 m' D
结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):
" ]9 d, i1 n* N. w! X6 C4 Y* X+ t
" Y) J% p# k1 P) x
  1. typedef struct
    5 ?" L/ [+ s' C# ]
  2. {0 s6 H! r, v6 u$ y9 _# S
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */% ~5 V; o7 M6 |* ?) Z# k3 ^+ E4 ?  b
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */
    1 @; b" X- _, B& @) M/ a  n$ Q
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */" Z* Z. [' a& a% v% b$ m8 w
  6. } arm_biquad_casd_df1_inst_f32;
复制代码

4 y8 y+ N; C) j9 k" T) e  @numStages表示二阶滤波器的个数,总阶数是2*numStages。0 W8 y2 `2 |: M4 g4 K
pState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。7 m# ^  y  l& r% ?, x- P: R
参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:
2 g; r, o! o% H{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}
8 ^% C8 E0 i: ^7 r( q
7 l! [5 l  E; ~, m1 a" P先放第一个二阶Biquad系数,然后放第二个,以此类推。
3 s) s: d2 `9 A4 b& q  N7 B8 b
- T- F- j* s& M$ R% w- I4 o+ k45.5.2 函数arm_biquad_cascade_df1_f324 d+ ?1 \% l7 }; ?
函数定义如下:
2 Z7 p3 P. \  o+ {$ [# q
4 R& y2 l  ^) m3 J
  1. void arm_biquad_cascade_df1_f32(
    0 h* |9 B0 R- ~+ |2 a+ E- Q
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    4 V2 f  o2 q; w- g% q
  3.       float32_t * pSrc,
    $ f! \' _5 ]& ]
  4.       float32_t * pDst,
    ; t& s/ J( g  e% c3 N9 U, l- L; ]
  5.       uint32_t blockSize)
复制代码

& P1 N9 T" w) z函数描述:
: s: Z; M7 O. k2 B7 }' i2 r, G
4 {; q/ i4 e& f4 y8 i这个函数用于IIR滤波。
4 _4 J+ Q& _3 k& f4 t; X7 y, U% T# s& s* y& q' @! S4 \
函数参数:, X- B  |/ K, H# G: F6 X" }; s! V9 l

" V& z6 q% H# b1 ~5 D  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。5 z2 V  F3 b9 F  u* Y7 `7 h5 h7 \
  第2个参数是源数据地址。
2 N5 S, `2 S1 H* u8 j  第3个参数是滤波后的数据地址。$ s3 c  ~/ g  ^* g* X% g, e# n
  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。
! a5 g9 @& g1 c, R  [7 ]2 W45.5.3 filterDesigner获取高通滤波器系数
+ m% f) i+ y: T' D0 p" R设计一个如下的例子:4 v3 F3 X6 L3 O1 ]0 j& i9 `5 J
) p6 R; f$ v+ t% y- k% \; u0 J
信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:2 v" j# {; M5 D) n, G, R( I) I0 I! A

3 M; |" k7 x7 t, Z2 o2 E% L
7f8be339ea66c602e1b7ea4836e58544.png

: ?' O. q0 C6 O. l, G
, `( r3 {1 M$ A5 B; Y; B配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。
( S* n7 p+ h+ P  Z1 h% p( a) w% [1 H( g+ P
) C5 s; Y$ F! C* B, z45.5.4 高通滤波器实现( B. i: I5 [! h* j$ E; ~
通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。' c( Q2 b/ e. K2 I! F
; `( R2 T6 }6 M. c2 c. d# U. d4 @
  1. #define numStages  2                /* 2阶IIR滤波的个数 */; V8 t* u1 _+ B+ {0 q' `) b; p) R
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */
    , h4 a# _* B& f# _" p) Z* |7 Y
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */
    ) K" G: q' F1 s) [
  4. 9 @( {' `% |) N$ U  l
  5. " _# \/ I% f1 f( B/ g8 g" w
  6. uint32_t blockSize = BLOCK_SIZE;
    : ]9 J$ s/ A7 u7 x: u
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */+ |  z% ~* ^7 q, J. `
  8. 8 g7 V& Q" D6 m: A- c4 E- j
  9. " M! W& V6 P) c
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */% Q6 Y. o+ ^5 g( i; P" I2 H( \
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */7 J% M( _* ^' X! o+ A+ D8 g( E
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
    " x: `+ z# B; {

  13.   \1 p7 f, M( q( N7 M
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         5 v5 I/ N; J: K
  15. const float32_t IIRCoeffs32HP[5*numStages] = {9 B: S) _5 f$ X$ `$ s2 v
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   % K$ j# d) S& Z
  17. -0.544565360850816415627662081533344462514f,     
    / A( E: M: E6 T
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  
    2 s0 i0 q3 W6 @$ F
  19. -0.168318873843973093595849377379636280239                              0 O1 ~# n, J% y' n+ B$ ?% [: D7 p
  20. };                                               
    ! r/ g/ B  t3 x
  21. 4 ?& S8 ^; |) L
  22. /*
    - Y' K" K: L& x0 s  k* m
  23. *********************************************************************************************************
    / B3 ]3 A+ B6 V7 ]/ M" E
  24. *    函 数 名: arm_iir_f32_hp6 y9 f4 ]- e1 |, ]0 ]: f  z
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器7 M) i, G* G! ~/ l
  26. *    形    参:无* `% H7 D4 u* O6 T) a1 s
  27. *    返 回 值: 无
    - _" J( T1 a4 N5 Z: J2 d
  28. *********************************************************************************************************
    ( d/ `' M6 ~2 ]5 h, A- C3 B3 T' Z
  29. */9 Q/ m, u% D: p8 e) E
  30. static void arm_iir_f32_hp(void)
    5 ]: i/ F1 R9 ^3 d/ E2 K
  31. {
    7 \' J. t2 K# [1 |1 C
  32.     uint32_t i;
    0 l2 F" w" u% R+ f& h# X0 ~# X
  33.     arm_biquad_casd_df1_inst_f32 S;
    ) [; s% X2 t+ i
  34.     float32_t ScaleValue;& E" Y/ m9 Z6 K$ [! ?
  35.     float32_t  *inputF32, *outputF32;
    0 k9 u; P' K- i; l  C

  36. ; F$ A  |/ F. u- O- l2 v
  37.     /* 初始化输入输出缓存指针 */
    , q. ~* G. I4 t5 n
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];
    / [' J8 A, w; F8 Y. h8 J. C
  39.     outputF32 = &testOutput[0];
    3 f" x7 q; c) M4 z% q) A

  40.   D2 T- A# F, b. a8 u, g+ `8 o' T. F9 [: _

  41. + j+ ^, X. b/ J0 G& ~( q7 Z2 y
  42.     /* 初始化 *// j* g% j; c, |$ \( P, a
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0], 0 a9 D' E4 V4 h0 p1 W7 @( C3 {  c
  44. (float32_t *)&IIRStateF32[0]);
    ! c. f5 I$ i7 I- V
  45. 0 h* T& U4 ]3 _" D0 ]. K$ A
  46. 2 L3 s- Q8 r& K* s9 V
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    5 o. J7 D3 u$ Y$ a
  48.     for(i=0; i < numBlocks; i++)( C* w# _0 D* A# r4 R" ?: B
  49.     {: L/ v# w& }& b9 N
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),8 `4 G6 e6 ?% I# B2 O6 j7 l! l0 W
  51.   blockSize);
    6 ^2 o( C! J! Q( ]) D! W3 \" p
  52.     }' J. \/ @, M& j7 |+ O7 J5 d
  53. - G, P( O9 ^7 K9 C' K: s: T1 ^& b
  54.     /*放缩系数 */
    ; {) J! N6 {* }+ z6 y1 \
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f;
    - b4 _  V9 Q3 C
  56. ; b' \  d2 z( W' }# c
  57.     /* 打印滤波后结果 */
    ! s  A' f. u* T
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    9 ?  b. ]2 Q/ _
  59.     {! k3 }; |2 k( C& Y
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);5 e' }! n) A/ i/ Z4 v
  61.     }
    8 |4 G2 I4 \8 |( {: z; V
  62. }
复制代码
1 Y! }5 ?: |+ W
运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。/ H. D1 w" }! H" L/ W
8 C, t) J! [/ i
对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:
8 H; N$ U/ h) L
/ B7 z: f/ K. Q1 b8 s. q7 `
  1. fs=1000;             %设置采样频率 1K
    * a: I9 @4 v% D7 c# s' e
  2. N=400;               %采样点数      
    1 g7 V, L, E3 X+ k* f
  3. n=0:N-1;% Y6 Z- }) {" \+ |/ s, |
  4. t=n/fs;                %时间序列5 }$ B: W& r1 N* k% e3 r2 {
  5. f=n*fs/N;              %频率序列& ^, f6 E  k7 f. @% n
  6. ; M( v. U/ c. }( g! F" y
  7. x1=sin(2*pi*50*t);
    1 P( }8 y' Y: F  i) G4 @  g
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波# q9 r. s$ i) }5 E9 q
  9. subplot(211);) e, y% E# d* \0 V9 d
  10. plot(t, x2);
    ' T- |1 p% V- K+ {
  11. title('滤波后的理想波形');% b* J: M5 \, ?/ S" R2 F
  12. grid on;) V* n  ]9 ^; ~4 w7 p3 T

  13. ( L: d# c) B. c. ]( N7 m8 V
  14. subplot(212);" q1 ~* e2 S$ U6 m6 W: x
  15. plot(t, sampledata);3 X2 G* A. [& c! Y
  16. title('ARM官方库滤波后的波形');
    - @" d6 T* A. ^$ A6 n4 F
  17. grid on;
复制代码
% U& |- D& x6 y2 J: E
Matlab计算结果如下:3 g; T% x9 _/ E1 H4 \7 K

, h/ H1 N- J" s! F
91111ef8530bb9e40d9736ccc2c12424.png

* ]3 a2 A  b+ G, Z5 g
4 l0 L) B2 \7 ^9 }0 ]9 p从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:) _# N; W3 n2 ^. V2 Y' i+ H5 _9 r
- I* z' [9 g8 K5 O" K
  1. fs=1000;               %设置采样频率 1K- x- x2 j- v+ f- G9 a
  2. N=400;                 %采样点数      
    ! ?+ c7 v" m. S, T' A
  3. n=0:N-1;: k: ~+ H3 O4 c4 y; l) u
  4. t=n/fs;                 %时间序列
    5 u9 w( M5 Z% D* ]$ _8 A
  5. f=n*fs/N;               %频率序列8 r  t: e( X$ s( g" G
  6. ) A2 C" U, ^+ }% B
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成9 Q' s) \) e9 `& i, T- _, m6 l
  8. , ?$ ]% X) m# @
  9. subplot(211);
    . d( Q: A9 d: I1 n# [  w
  10. y=fft(x, N);                %对信号x做FFT   
    2 v3 I+ r! U9 b6 q) }5 B
  11. plot(f,abs(y));- k/ ?  I/ L$ Z. N# l
  12. xlabel('频率/Hz');
    + `! }. O8 y; L- b
  13. ylabel('振幅');
    0 ^2 e/ ?7 z" N; w/ J$ N
  14. title('原始信号FFT');! [0 t0 J% k* w0 f- ~, ^
  15. grid on;* \6 V, r" d9 M! E0 r

  16. 3 L9 {( q" V' z9 F4 `7 @6 ?  ~
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT+ e4 c! r! |5 i# Z
  18. subplot(212);                              
    % G8 l2 Z! a8 ?+ I
  19. plot(f,abs(y3));
    / o7 t0 d/ Q  w% M2 u
  20. xlabel('频率/Hz');
    " d9 W- d3 t: |
  21. ylabel('振幅');/ J* `6 ?' A  c% X2 T2 w& O* S8 Z
  22. title('IIR滤波后信号FFT');/ C6 a; \) G  j( Z3 Z
  23. grid on;
复制代码

! K* H* F: n) n- o+ B! sMatlab计算结果如下:+ `' c$ }  Z( U% J5 s, \
$ B5 {) ~3 p7 D4 X! L) s6 q
d13d47932e1b37bd5046b7ad31415509.png

( O- }/ g# H! \+ C7 W( f4 G! [9 `; x, N9 B
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。
  V% L& P9 v% O3 W0 V  B" P; U6 P- k: |. e1 j7 y$ u2 D
45.6 实验例程说明(MDK)
  ?. \8 C) p3 T, s配套例子:& I$ y; w/ e* t- W: i/ B
V7-230_IIR高通滤波器(支持逐点实时滤波)
7 {) Z8 a" k7 t) |' \3 u0 n
) {! ~+ h" Z) l- t& N- M+ z实验目的:
$ ?+ s1 ^+ s" t  P! ?学习IIR高通滤波器的实现,支持实时滤波4 [0 f1 m$ |6 W

) V; X& T: j" E$ p0 T& U; J实验内容:7 D( u+ {) H  Y% Z; f1 z
启动一个自动重装软件定时器,每100ms翻转一次LED2。
+ o" m3 p  C! n* `' G按下按键K1,打印原始波形数据和滤波后的波形数据。3 h5 b9 _2 ~! [* D; H" ]

( h; e, h. C" D6 x使用AC6注意事项# G4 q3 T- H  D1 }" T: z9 c) x
特别注意附件章节C的问题0 I; v/ K$ Y2 r8 g0 N0 u
) p4 T- D, M; _7 X
上电后串口打印的信息:
5 V2 P: h' ~7 l( m2 g( R2 w1 B) K! \" B2 A. e; Q/ [' y
波特率 115200,数据位 8,奇偶校验位无,停止位 1。1 P& e& P' M! p  i) S5 L; l

; G% ^1 m9 `' I8 {3 [
262f2df9123b012595369835f600f93d.png
8 n) m" \7 M& F) o, v6 {

3 c3 B5 i: K8 a2 }/ ]6 R, X! bRTT方式打印信息:
9 O( s: K/ J( ~7 p4 U) M. R  N" M6 f) [  l8 D2 I0 R
75dd602d2d2eb24a5dea611cc45ce934.png
$ j* a# g+ c7 ^
- f* s; h% x5 q! R
程序设计:' {# I* y. c, I4 u7 b! X

, g% e7 f* i- N1 d) W/ w  系统栈大小分配:: K; i/ X/ ]9 H
: Q+ q; V1 m3 Q+ S6 W3 r; |8 s
640570198f69835d8ac9eb982ccf39f6.png

$ o; ]6 B/ {$ V2 K% N/ j. a, R' u0 a8 u2 G4 ^1 A
  RAM空间用的DTCM:
1 h# I+ n+ q. f2 B% o3 V  C7 g5 B. t( t* _
6efb3697225eacc369e069ab80d69ed2.png
0 n5 l# L+ @$ s* y7 \8 z: J5 F1 h

/ A) B  P" N* B  硬件外设初始化
0 `; {& K) p0 p- [; f& c
% ?8 M( y1 l! k3 i* V硬件外设的初始化是在 bsp.c 文件实现:
, I3 x* u$ k* R; A* \1 g- @" E" ~6 }9 ?
  1. /*  C8 z9 g" n7 k. j, L, Y' R% b
  2. *********************************************************************************************************  I" h1 S' {$ |0 f& S1 R4 V
  3. *    函 数 名: bsp_Init! l# S: i. h+ Y5 t* @0 f
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    " Z" H) Q- }; S$ r$ |
  5. *    形    参:无! x' P& e5 u* y& L* J
  6. *    返 回 值: 无
      u# H/ o3 h: H( P* x. E
  7. *********************************************************************************************************
    5 V# f" m* H. W% R
  8. */  ]6 x7 ~$ w# j4 ]8 ^
  9. void bsp_Init(void)
    4 o) s3 h0 C: j
  10. {* m. w: L- R, I* B; U' X
  11.     /* 配置MPU */
    5 H7 D/ a# h' L' f4 y7 E
  12.     MPU_Config();6 k1 M0 W+ K4 f  z, w0 O1 c- O& v
  13. & p5 \( J+ A  T
  14.     /* 使能L1 Cache */' M+ s- ?/ c% o) V3 n$ Y! k
  15.     CPU_CACHE_Enable();1 D$ e/ ?- A( Z7 J$ r1 r0 x
  16. ( ?1 i* y* X' O) S( p1 }% X6 t+ |
  17.     /*
    ' k& p  e  P% A
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:) x2 O) V5 j9 j6 ~4 N1 z7 L+ Q: b
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    8 ]- R5 h; v9 t9 A* P3 C
  20.        - 设置NVIC优先级分组为4。9 g% g& q- K8 w4 D' O" b) F
  21.      */1 z1 V7 u$ Q+ a+ Q( b; [- a) O0 b" O5 D
  22.     HAL_Init();
    " x- S: f0 \2 F' ?
  23. ( _% w; q: k9 k
  24.     /*
    , c- D; [/ t; H1 R7 D- a; b+ E
  25.        配置系统时钟到400MHz0 M* }* b3 w3 N& \: x  m9 Y/ q
  26.        - 切换使用HSE。
    0 ^9 _& }1 F/ t, n
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    ' O+ K! U* E  s6 l
  28.     */
      n. H/ i0 J) p3 a: d
  29.     SystemClock_Config();: I0 ^7 j. w( d* W* i
  30. 0 F6 E7 S$ p$ `9 Q
  31.     /*
    & ~, v% K1 }; |; `, e7 L6 x% z
  32.        Event Recorder:
    ) ~  e  b) k  U' y  I
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    * x( Z1 e4 |% T( g3 R& r
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    & B+ z5 W& t1 i5 R" D$ T
  35.     */   
    ! f; a! [# F' ~( q" T/ C) k9 F
  36. #if Enable_EventRecorder == 1  
    # ]$ t& y# j! Z$ O, h/ ^* N
  37.     /* 初始化EventRecorder并开启 */
    ( y  }1 m4 D- w' G/ }- v1 I
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    ; d: i. M: P- f* e5 q6 D
  39.     EventRecorderStart();# L. I. o/ f$ M, P; @0 T4 ~
  40. #endif
      k* B" |. k/ ?# N0 g* |

  41.   N# o5 c! i6 A% N8 v4 U
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */; C9 Z  J" p0 ]& K
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
      S$ M: h' q  W! U2 q
  44.     bsp_InitUart();    /* 初始化串口 */9 k1 \1 l: ?& x: Z% P' P, w
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    : o4 [2 G7 W5 M3 d
  46.     bsp_InitLed();        /* 初始化LED */   
    2 `( ~: B% ?+ w1 ~, a
  47. }
复制代码

$ N9 B  f; f" r6 z1 @. c  MPU配置和Cache配置:
# F! d$ W$ i( t/ c  S1 ^8 m4 u9 D6 ^+ m4 @& `4 E6 R9 \
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。) n- R5 [5 R, V# ?+ I; S# n1 p

. u  s0 f4 \+ v1 M# \  t+ j
  1. /*1 O( D4 ]- W$ U3 t1 N2 d
  2. ********************************************************************************************************** S" N. d0 e- C3 J1 f; w
  3. *    函 数 名: MPU_Config
    3 r& {- T9 w4 H2 |1 f( o! a" {
  4. *    功能说明: 配置MPU* ^3 i+ q2 L; I- K1 m( M3 e
  5. *    形    参: 无6 J/ [5 o4 H1 F% P2 _
  6. *    返 回 值: 无6 t' \  _4 h1 k+ Z( |, E" Q) A2 ^
  7. *********************************************************************************************************
    / t+ Z2 N. l9 G8 J: F7 N  C
  8. */5 Q+ l* q7 p0 _( p: E4 w+ j; @
  9. static void MPU_Config( void )
    5 r6 A; ]9 I5 N0 h" P  K
  10. {
    + `1 L9 Y, O/ W1 L
  11.     MPU_Region_InitTypeDef MPU_InitStruct;" M; [" K& H( e3 m& l, A4 V* I9 H3 l
  12. - W9 b( F% r. P' s+ P
  13.     /* 禁止 MPU */; _2 y3 c3 ]" }* Q) u+ U" l6 q- r
  14.     HAL_MPU_Disable();3 D3 g0 Q( n9 }' \0 M9 V

  15. % ], n3 P& K8 V$ l( |
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    + {! b; d& w2 g% R/ F! E! N! K0 @
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;7 l# B% e+ r. `6 m. S- s
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    ; J4 F2 d; w, m1 \# n# j* L# V) u# E
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;4 `) U. x! s0 j0 V0 K7 D
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    5 M8 D. O+ ^- v& I  D; _
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;; X* J( T+ a  v9 b" ]6 m
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;+ d; d9 ~* J; N
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    4 o; S# z7 K* @
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;6 {# ?, j( C) N  S" U$ ]
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;$ s# H' F, n5 ]
  26.     MPU_InitStruct.SubRegionDisable = 0x00;5 z5 C$ y# V( _
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;: i. G. j, K5 R/ c3 e$ ], ^& R. X9 ^

  28. : d, q" x1 k) d4 ^8 b+ U+ h
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    ( w1 ^4 H/ s, q3 x# \
  30. 0 h& ?' {, ^$ ?$ w

  31. 1 p. @  b( A. @$ a" e( B
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    : B' t/ P% _8 `. C2 h7 X: k" Y
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;; l4 Q; Y) g0 M
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;+ ~5 y, Z' Y& v  b5 f9 ?5 _
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    : _8 @; k) J7 C0 L$ e
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    9 }2 C- h8 y1 A
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;' ~1 Q" j' A/ U* A9 I1 l* i: H
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    ( `! s+ x+ t5 v
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    * K' o3 v- }5 |" d
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;- R, l! p1 G" B8 n- s8 ~0 f, D
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    * @% ?* T+ m. m; r: K
  42.     MPU_InitStruct.SubRegionDisable = 0x00;) h( `* n4 f- n% t7 K& k
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    * G6 g7 R1 F- i1 C  ^! I
  44. # f' z; W. f' T3 o
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    1 j. B/ ~* E* b% p9 m: c
  46. 1 G: i; @' d$ l) O
  47.     /*使能 MPU */3 g, R9 ?$ s" H- V- P2 l8 {! p
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    / L% r% z. M: q2 {
  49. }3 T! b+ L( g" X1 [2 S% j: h1 B) R
  50. + K4 z" s$ l; m1 B4 a9 N( l7 Q8 r& i5 p4 k
  51. /*
    : v# W; p- U) D0 d% ?
  52. *********************************************************************************************************2 _& Q$ B' `* M5 `; L- {3 U( {
  53. *    函 数 名: CPU_CACHE_Enable
    ; r# c3 o7 O8 o; g
  54. *    功能说明: 使能L1 Cache
    $ _$ |8 {7 e! C6 ~# Y4 x0 X
  55. *    形    参: 无
    7 w8 B7 x! P4 p9 }9 P5 o9 _
  56. *    返 回 值: 无9 a  I, L# x$ l0 e% J
  57. *********************************************************************************************************6 ~* }3 I  J/ C1 N  [* B1 `4 r
  58. */' G1 g- ]/ _& p% E# t" j
  59. static void CPU_CACHE_Enable(void)6 L3 {! B- _, m: i5 n
  60. {
    3 H* T+ N- N1 {: j! {9 x% k
  61.     /* 使能 I-Cache */
    6 s; ~% n9 p2 h3 h3 \
  62.     SCB_EnableICache();8 G6 |: b/ t- ?2 `4 y! [

  63. * y0 u$ \" P! I, a# N4 j
  64.     /* 使能 D-Cache */
    2 ]* u5 R) d% A! z. V$ l8 ~& K, c
  65.     SCB_EnableDCache();. @1 W" L  H7 I, y
  66. }
复制代码
3 F, Z" _# B! T- A; {- K; N
  主功能:4 U4 P, O% E" Y% n; m6 @) n( ^
& b" H9 |& _. M
主程序实现如下操作:
% R, R5 G8 p: ?$ I" B; ]+ l5 G
5 y6 a2 q/ v+ y& K  启动一个自动重装软件定时器,每100ms翻转一次LED2。; t. j( O, ~: h% ^/ U
  按下按键K1,打印原始波形数据和滤波后的波形数据。
! e) ~- c4 j+ N, g
  1. /*6 q. L+ _. C$ r- W/ J( B+ t
  2. *********************************************************************************************************
    / ?( i2 a- q4 ^& E
  3. *    函 数 名: main
    4 U3 U, x# C6 E
  4. *    功能说明: c程序入口) Z" v. U6 r  l3 R
  5. *    形    参: 无
      v' H8 q( l; @1 a) y! m/ H4 m( Y
  6. *    返 回 值: 错误代码(无需处理)( V7 T8 s. g  n, T- g8 c8 A# ^
  7. *********************************************************************************************************
    * O5 h  z9 v4 r
  8. */' [5 {2 D7 p4 m: M5 ~( Z
  9. int main(void)
    ; u) m9 P* S4 o
  10. {& g) `' i7 g& |' B+ S& Z
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    + M/ @$ c* O! z* m
  12.     uint16_t i;/ G7 E1 u; u. C5 z1 D; L

  13. 2 p6 b" e9 P8 I- P

  14. : Z9 Q) @: j% d0 R
  15.     bsp_Init();        /* 硬件初始化 */; }7 i/ |0 P+ A
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    - n. Z0 i1 G$ H, I( ]

  17. " a6 m& V- a% U) Y# i* t
  18.     PrintfHelp();    /* 打印操作提示信息 */
    ( z% U( H0 b( E% D3 m

  19. ( ]0 r4 A# W) u
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)6 n0 @, X0 W, ~& c% t5 A
  21.     {
    7 ?* W. N& ^  J% W
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
    * u* m. h' {: P
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    2 _4 o; a5 }9 j& U* N: N
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    & r8 G( j* j/ H$ G- c
  25.     }
    4 a3 S, j) B" v/ Q

  26. ( s' \, X, P$ f; R, z! l

  27. ( b) k3 |. C7 X; ?4 ?! `
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    % Z2 P4 K0 \$ ?  W6 H0 Y6 p, @  A

  29. 9 D' F* f* o; u& p4 w
  30.     /* 进入主程序循环体 */
    - @  _  V" J" E1 L. @$ M
  31.     while (1)
    + _8 w. g' y# [( ^! W, f/ {
  32.     {
    1 m. P. g/ B  L: f( Z
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    + @+ `) U; r' |
  34. ' c" K4 H- T! h6 z! v/ q
  35. 9 B# b: b9 X: K& c  c. U
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */$ q  C) C! S! T; v
  37.         {
    ( r. ^( s0 a: x1 v' c
  38.             /* 每隔100ms 进来一次 */
    0 T; X8 S- m" c9 H2 ^# v
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    ( |+ n1 [5 l( w& L3 F
  40.         }
    " \/ C# ?( j# {4 ?$ H% R

  41. " A. n1 @2 @" i% M9 Y1 Z
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */1 {% q+ g! b. z/ v
  43.         if (ucKeyCode != KEY_NONE)
    + R. z) F  A0 A7 r# y- g2 H
  44.         {" N6 O  M6 S: w, P! P- g5 Y* c. b! K
  45.             switch (ucKeyCode)
    ( S: V: M1 }) |* o/ [0 z
  46.             {6 k" G: |( o7 S- T# i1 s
  47.                 case KEY_DOWN_K1:            /* K1键按下 */5 z5 ^" w: H  h+ I
  48.                     arm_iir_f32_hp();1 Q3 i# o9 b; L
  49.                     break;" Y7 T% g, y3 _) k3 q: m* _
  50. 0 c$ Y; G4 P) G) j  W! N2 s9 s; f7 U

  51. 2 t) l, s2 h# F- X9 v
  52.                 default:
    1 ?1 K5 C) _( {7 G( y) N
  53.                     /* 其它的键值不处理 */3 v3 \$ }6 O9 ^3 p4 i
  54.                     break;
    ( r1 F! }( L0 X9 p  J. Q$ ?
  55.             }  b+ `/ U) T: W
  56.         }
    ' Q% v" v$ w/ X$ X
  57. ( ^' p/ E# w1 E+ f/ w' \7 }8 j
  58.     }
    1 {/ P4 V5 U" i7 y! X# [
  59. }</span></span>
复制代码

# H" n% V! @& e/ q* c7 n45.7 实验例程说明(IAR)6 a. C* a6 T1 @& q3 x
配套例子:. P/ t! W2 w7 D6 Z, P7 W- [
V7-230_IIR高通滤波器(支持逐点实时滤波)
% A8 F, K0 m9 G6 ^3 p3 i7 E* r: ~( C' [7 f' S
实验目的:  g5 t, ]; Y( d9 \. @
学习IIR高通滤波器的实现,支持实时滤波

8 i  m) J# ?4 P; ]. J) m
3 @! `. m8 L; ]8 ?实验内容:$ F1 R  J9 N+ a2 n$ q& `
启动一个自动重装软件定时器,每100ms翻转一次LED2。3 ]9 f0 A/ B, `$ Q& W& p9 H
按下按键K1,打印原始波形数据和滤波后的波形数据。
* i0 u( B1 X1 O2 V; G: K4 Y: L
6 T4 U7 `( F( S* Z) j
上电后串口打印的信息:& \/ h+ C4 ^$ e8 ~6 I+ Q
4 U9 J- H1 j  |! p% B5 M; N% S
波特率 115200,数据位 8,奇偶校验位无,停止位 1。; a  c( h3 q3 Z7 ]8 a0 `6 v, t8 ^; |

8 I8 f$ H+ V/ ^) C+ x
a66c11d15f6f8dd3c49ad9b587ab3a90.png
7 o  z4 ~1 V! L9 ?

1 G9 J5 O7 k! W# ^; a5 `9 eRTT方式打印信息:0 }$ U* H( G7 Z7 A
  t1 L$ K" O3 g/ T, y
5b42e93b5c59803fdc45c18e564e61f1.png
, ?( A% f* @! x9 J; z6 [

/ u6 G$ J- L$ \% H程序设计:$ P+ k4 X- D; J3 V, ~9 Q( _: ^. v

  L8 y0 n7 C6 ^6 V8 R2 v9 T  系统栈大小分配:/ q; E# P4 g! g4 u: V1 K

  ~  [! t- d8 X
a48a46f1265dd3824bc690f874e90613.png

8 F8 G2 k1 e* D
, C) t1 K$ g8 u" [  RAM空间用的DTCM:. @- Z0 C- \! D  G1 l

9 u+ A/ M, C5 Q! S/ }+ z2 S
025eeb1d94bcb86c7bec22ed1e822f36.png
+ l: O- \; e# n5 t2 E. F

5 l9 r" L7 ?' V+ w7 J9 z! ?  g  硬件外设初始化
2 S( }: a% z5 c1 V1 }4 o) b* p2 G" l& a& Z) A9 {% [# e
硬件外设的初始化是在 bsp.c 文件实现:+ n8 `9 M, u! J! M* d

1 N7 y0 K9 K! n  x1 I+ P
  1. /*
    : }# t  T" N* x& z) f
  2. *********************************************************************************************************5 `* {' n- C+ m4 b$ N- t! X8 `
  3. *    函 数 名: bsp_Init
    9 f7 j, ~' T1 o2 D
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    : F* ?" O# c% Z0 R: }& e  G2 x
  5. *    形    参:无: d  x6 ~. q" H9 I$ J0 W0 F
  6. *    返 回 值: 无
    3 n( f" D1 O+ J: a! |" `% c4 a2 f
  7. *********************************************************************************************************, Z( g  B: i; [- @% M3 N
  8. */
    / o  i8 p  `- d; B) ~& d3 b
  9. void bsp_Init(void)4 O. Q7 c7 ^$ z* o
  10. {# {# w0 O( B, p6 q
  11.     /* 配置MPU */
      h6 v  P3 E# V  n3 H7 B5 O
  12.     MPU_Config();
    3 m0 m/ J0 `5 Q9 X/ I7 N/ z& I
  13. , Y& V& h1 D6 b1 y  W" Q: J3 g; }0 I
  14.     /* 使能L1 Cache */
    ( s) ?8 E) z7 G8 G
  15.     CPU_CACHE_Enable();
    - P3 F- i% N: i2 z4 z9 c
  16. * t8 G8 E3 I: J) i4 r
  17.     /*
    3 J5 P8 H6 [8 {7 ~4 n
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:( B1 r3 N& q' I! n' x- a
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    " M& d' q5 `2 V
  20.        - 设置NVIC优先级分组为4。- j8 g$ n+ Q) V) `
  21.      */+ ]0 E6 ^' I  A6 H8 h( q7 ?
  22.     HAL_Init();
    8 ]4 |) U! O/ [3 @

  23. 7 k* i  i. n. k
  24.     /*
    7 j- k% R$ M0 K( Q
  25.        配置系统时钟到400MHz5 J; Z2 L9 J8 N: m: t! @( w
  26.        - 切换使用HSE。4 f5 o+ {* j, ^' G- l
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
      ~3 Z6 A, B( n9 H
  28.     */, t# Y7 n; _7 `7 m' L. q1 M$ S: K
  29.     SystemClock_Config();
    ' w) h/ j" q0 P8 J

  30. ' d: |- V! a0 U  y5 D) K) [& l, E
  31.     /*
    + O4 L: _4 E' E2 Y# D
  32.        Event Recorder:
    - a3 J8 u  n) s8 J+ V
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。/ h3 j1 }9 Q7 _- ]$ G
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章1 `1 }! s5 }. ^: T: C  A# Q( h: g
  35.     */    $ v6 k6 |0 J9 s- X# [; P  q& u
  36. #if Enable_EventRecorder == 1  / O2 u* r# c$ s7 }$ Q3 S3 }
  37.     /* 初始化EventRecorder并开启 */
    * A& f7 `% n4 `) P# ?- A
  38.     EventRecorderInitialize(EventRecordAll, 1U);- c; l9 |. P; f" [9 O# D% p( C9 T
  39.     EventRecorderStart();
    & E0 `5 m% T4 j0 }2 s7 ?
  40. #endif
    1 Z( w) A, Q: B  D# e( ^9 p
  41. . w8 X0 P$ v* w5 F8 s, Q
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    # y* y7 W+ ~* o: d8 x
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */8 b  n+ ^! J( x" I1 u/ e
  44.     bsp_InitUart();    /* 初始化串口 */8 X& a, A7 r: Z9 P- _+ r8 E
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    3 R/ R' d3 j1 B2 g  X* f& U: {
  46.     bsp_InitLed();        /* 初始化LED */   
    8 r6 `; [/ X8 K4 T
  47. }
复制代码
6 X4 H$ X$ K" y, d1 [9 ~% i
  MPU配置和Cache配置:* ^. ^5 ?, T9 Z3 G0 b5 H

) `% T4 N3 t( s6 r1 ?& J( I9 P数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
/ y: s4 a. Z; G. P, q5 i% G
2 b% x/ n1 t4 o0 G- C
  1. /*
    7 q8 {. j: w$ r" Y% x
  2. *********************************************************************************************************
    3 U* M2 w9 i" m. l
  3. *    函 数 名: MPU_Config; i  X; P3 \3 b, t. r! _
  4. *    功能说明: 配置MPU. {0 E* r0 s9 E3 M  m9 j
  5. *    形    参: 无
    & G% B, _4 s5 z* Z) J8 N* s
  6. *    返 回 值: 无
    - z' H4 ~" m% w
  7. *********************************************************************************************************! ~6 [6 U9 \4 G$ e4 S
  8. */: n& w  {9 \3 b# w& V! f& {( [' u
  9. static void MPU_Config( void )2 e3 o0 B2 c, X! C$ B" w8 \
  10. {* ?1 `; {, v4 {4 e
  11.     MPU_Region_InitTypeDef MPU_InitStruct;+ C2 [7 l  D) z6 n6 `- ?. X6 e
  12. 6 q  i( d/ {1 K, e! R* S# _
  13.     /* 禁止 MPU *// w$ M: X& ~# x1 ?; ?
  14.     HAL_MPU_Disable();( y8 K# |) r5 ~+ F  N) {. r) k
  15. + M) X4 }" v; z8 G9 Y0 ?
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */' b1 x7 b# J' F8 l1 T& i; C
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    : ?9 T9 w( y, w" Z8 H0 m2 N/ P
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    $ k" {! C4 E# r9 Z3 j) o- k
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;0 l/ c& V4 ?" D; u$ U7 s. f
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 x/ t5 i; [0 U0 a$ K9 j
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    ( k0 S1 J* B, f9 c1 Q
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    / L( T* b* R" j6 |# p6 S
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    0 {4 ]& g; M+ R
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    0 E$ e% b0 g+ V
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    6 n+ A$ @( {+ S$ P  Z' O  P
  26.     MPU_InitStruct.SubRegionDisable = 0x00;' }* N" D+ B( x+ _1 o# c
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;- d6 @! v( q7 |+ F" b- Z' c! Z, b

  28. 6 c+ T3 s( n% n1 k
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);7 n; N/ B. y& j$ T* }, i
  30. 0 S! G# q$ z- `! n5 T: p
  31. 2 Z* `  _" E% R" V2 S+ L
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */& ]) S3 x2 @; O3 K0 r; M% \  h
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    + t: x8 n) m3 g7 ^& T! B3 }7 r5 ?
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    ! W$ z+ l! T+ [, k2 t% c& y8 F0 d
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    7 J/ p& G7 w7 `: L, k- a
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    4 e4 y+ l/ x) ~; l# N* K$ _5 R
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;/ o" P0 H9 @" ^& J) G
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    2 r. b+ w4 ~  M# C7 I
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    . r7 b7 x! u7 |
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;4 ?- b6 _# {7 Q: ?
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    8 T% @& n  h% e7 z
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    ; L$ d: _* {3 i1 F5 I4 g1 L) q& m
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;& m/ Z/ X( h# q" P  Z
  44. ; }9 E3 k0 O2 Y! `
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    & U0 a! O! U# p) u* s+ g

  46. , X/ o  j$ Q# R- q! t# C9 D: \
  47.     /*使能 MPU *// @5 t5 w5 ~1 |$ `/ n4 k) D1 ]' v
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    6 x( k' P" K) ^( H* \
  49. }8 V: z% r( r$ k

  50. " h! B% m) r5 M
  51. /*
    - e, _; ?! Y. k2 H6 `3 z# M
  52. *********************************************************************************************************( ]. e+ J* i# ?! H; f/ n! q( I
  53. *    函 数 名: CPU_CACHE_Enable
    ! I: A. w1 H3 M
  54. *    功能说明: 使能L1 Cache$ o3 P/ i. G6 \' g5 L
  55. *    形    参: 无
    - o% [( L# x- @  v; v4 R; ]
  56. *    返 回 值: 无
    # W# F6 M0 _6 V1 i& _+ I+ X7 e
  57. *********************************************************************************************************: E- z, ^( m- L7 P. p
  58. */( A" W* c* f. e. @, `8 L/ |( E$ w' S
  59. static void CPU_CACHE_Enable(void)! O' e  h1 L$ x$ L2 n8 u1 s9 C
  60. {
    - ]# P8 i5 h* q1 {# E3 J! ]& s8 |
  61.     /* 使能 I-Cache */
    % a3 h) M: ]4 H% T$ b
  62.     SCB_EnableICache();
    / e( ^7 ~, v8 p- d

  63. 7 g! {  j) u# `7 g7 Z; C
  64.     /* 使能 D-Cache */
    . N7 d" Q; F0 H- Z- K$ `! o5 K+ q& h
  65.     SCB_EnableDCache();
    & n0 @" J' A, Y* _
  66. }
复制代码
! t; f0 l$ n7 D
  主功能:: p2 g1 _) [- }8 P  Y

2 i2 F7 s* N) r1 S1 e主程序实现如下操作:
7 T8 w' B9 s. q  t3 @2 u1 Q! ^! R! b: o/ F9 ?, ^6 r+ J/ G
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
7 m; z. e5 s; D  按下按键K1,打印原始波形数据和滤波后的波形数据。3 U& \- K4 P4 w7 K
  1. /*! A) U+ @3 v# n6 K+ y
  2. *********************************************************************************************************
    . l4 E9 n+ \/ N! q
  3. *    函 数 名: main( x/ g) ]3 o2 Z
  4. *    功能说明: c程序入口
    ; r0 l: Y$ ^) B( O2 K: }2 D% X/ s8 T
  5. *    形    参: 无
    + w) z% a' l5 @0 v8 z
  6. *    返 回 值: 错误代码(无需处理)
    , y, u! h. @4 R" f9 ^2 k
  7. *********************************************************************************************************- G8 x& c8 N" x! y' j! R
  8. */
    $ G' \5 d9 r$ u& s7 ^& v
  9. int main(void)" `# P: d# X6 E& {5 }# Q
  10. {
    # o4 Q& i* O  n5 o. H9 d1 p
  11.     uint8_t ucKeyCode;        /* 按键代码 */7 D8 ^# |( Q5 ~: x
  12.     uint16_t i;% R1 T  x; ?( @. g% h5 Q
  13. % P! {& r$ X/ h; P- [) }7 _# ^
  14. + q: v1 d. A0 }1 x. W0 J
  15.     bsp_Init();        /* 硬件初始化 */9 d$ _" `- |6 [) E0 ]8 ^
  16.     PrintfLogo();    /* 打印例程信息到串口1 */& b2 q5 H" A  b: ^& Q
  17. - A8 F0 a9 ^3 h
  18.     PrintfHelp();    /* 打印操作提示信息 */
    , I0 P+ t, s3 O! f
  19. ( T6 N1 L0 t1 k1 B
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)( m; h% s/ I% |# I  F& ^) x
  21.     {
    * b, j& m* W) h5 o, w3 z- a
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */3 m! r: V/ U6 e0 C
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) + 8 K6 y* O! \! a, H
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    4 n& _8 A6 p' h
  25.     }- t3 R. W6 O6 i  E' t6 `/ a% e

  26. ) q& C" Z2 ]! n$ c
  27. + ^' u' i, I# |( g
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */9 h7 K$ Z. f& ]# J% {2 W* g
  29. , Z) P: ^) b" \" j6 _4 V
  30.     /* 进入主程序循环体 */
    & X. G2 P+ u8 Y3 z0 D" z; r
  31.     while (1)/ l) g- T  f- ^/ y
  32.     {3 b8 J* U7 r: Q5 O* F  I
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */& y; H9 U3 `" ~" ^9 ?
  34. * }3 n5 r  T/ c( x

  35. " G; ?  N- ^/ W! ?  f9 Z( H& _! m
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */& B8 ]; I+ t# f- a3 L$ T% B
  37.         {( `7 P0 F3 l/ F7 n
  38.             /* 每隔100ms 进来一次 */
    1 _2 V: P2 R6 h! I
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    9 F. Z9 |0 \: d+ J$ P% {: v
  40.         }
    0 s! E6 F# @7 y
  41. # O; o2 R- o" S0 K5 h( z
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */  S. G( x" I% ^: s4 E$ s4 s
  43.         if (ucKeyCode != KEY_NONE)
    # S# |% s) `3 y* v2 U- Z' ~
  44.         {
    * _8 J- s; L9 M9 T- j% R" q
  45.             switch (ucKeyCode)
    ! o$ V: U  n5 `$ t% r- Z1 @7 D
  46.             {
    ! g; o' B9 Z' S9 o6 t
  47.                 case KEY_DOWN_K1:            /* K1键按下 */
    + W2 e  R4 n4 I# g1 v8 V3 u/ B
  48.                     arm_iir_f32_hp();9 {. B$ v, _) B0 a4 {- Z5 K
  49.                     break;
    . w& [! [: e5 C$ F

  50. ! U- A# _9 Q4 k' f: f5 f6 m

  51. ( y% G2 a( t7 p* p2 M
  52.                 default:' S. f5 A/ l1 d) _; o
  53.                     /* 其它的键值不处理 */" @$ B" n; Z+ [" z2 H, h$ `" E( m9 k
  54.                     break;
    5 e, M0 |; o) ?1 s: h7 {
  55.             }
    3 `6 x1 q3 y6 W0 L' q$ \
  56.         }
    3 C; v3 Z* U3 j, }1 i
  57. ; _: D. Q8 j- k( z/ R( x
  58.     }: H0 B4 z3 n6 I) A
  59. }</span></span>
复制代码
45.8 总结
. P# f6 u3 f3 Y8 S% ?本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
" I: H, K3 Z8 M9 [) P5 N! |- g% ]2 I8 m& j9 j8 H

3 O( g6 |" U. O9 m1 q( a- t

! C. _+ k- ?" s6 N/ {! D2 z
收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版