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

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

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示4 G2 ?5 b2 \& [- N% @0 y
  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:! w- f4 P9 v9 E& b8 Q4 q( \4 p

# P* X0 `% Z5 j/ P  所有数据是在同一个采样率下依次采集的数据。
% P9 {- \  W% q# Q$ F9 \. W  每次过滤数据个数一旦固定下来,运行中不可再修改。
/ K- Y0 f+ E: {1 S  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。
) a# ^" |6 a, ^& j, m5 O* T
6 d6 h1 }5 t  K" S45.2 高通滤波器介绍/ T' b3 W$ m1 `
允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。! ^8 g3 G2 E! S* N; I* }. g0 X
: c% a6 H5 C4 ]
c1e7dbbd579e8c1e535c9ce0678c3532.png

7 r$ D: c2 w$ s4 i+ j5 X
+ i. c( k; P" n) q4 V# @! G0 B45.3 IIR滤波器介绍5 q6 ~9 a) I9 ~) a
ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。* {1 t( E0 ]6 A: m4 C

  A4 ]9 W5 x' c! ~' b直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:  i) u+ s, Y' P- M$ H

* q4 s4 K* z2 K' ^y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
) Q5 i, A: A+ W& `4 n4 V# C* r2 a1 K% ?5 N
直接I型算法每个阶段需要5个系数和4个状态变量。1 {  b4 L: H" m9 J% a

* Q4 I+ p; g, P) Q0 N& G
7afa71129081e82218f8d501da73448e.png
: z3 c4 D* S( ?& H

7 ~! A( v$ T# S% j* O8 P9 l. Z6 G这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:
# V" x4 \) N! Q% N7 A1 d- a3 C; N$ o' X, P
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]6 c6 J/ N# B) o( _+ y
+ b; t; X0 B2 h3 J' L! x- j* @- w
比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。
5 O# c; E6 V; l' l$ n' c+ W% r
. P0 N7 ~, G+ _$ \- K" I, d高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。
2 m; `+ M2 T! M9 Y7 y
0 M" q2 S* V. ^# T- b. x1 J
75ec77cc1b971aa24bf16e169c67d8aa.png
! @0 k) \  H, K, e. l

6 R+ U% f" l* Q8 l6 j9 E如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。
; ~  k/ x. N: L7 ?  y" m) z" K- l+ q2 C, Q/ |# {
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数
0 G6 R; @* Z( j9 }6 O前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。/ d' N* ~( g6 C
- |" O: ]% y% O0 a% Y) r: }
下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:2 t) [% m% i2 L: }4 p. B
' C% }' G1 r# @. }9 v1 h
4e2868a42a0de4b63d0a13d64952e470.png

4 f1 y, ?( o1 j9 X4 [5 O2 c- h9 u9 q1 I/ j1 F7 M; n
filterDesigner界面打开效果如下:, N7 Z6 I6 L* o; \0 L, c

% s) }5 N: M3 b
b9bee9bb0a4ada395e69793b38c566fa.png
9 B' g; F  _+ T; |5 ]# @- ~

, ?* g3 B5 I" H& OIIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:
1 m/ |# }1 d+ e* D$ M" {1 q% l* p  o/ L/ n* @
84c4d0373d7c29786a6620c1050ee40f.png
; P( M1 v4 s+ [# H, C* V* a
& s5 S: v2 W; l& Y% k6 X$ o
点击Design Filter之后,注意左上角生成的滤波器结构:
. p6 w4 w5 Q3 ~4 J$ F) j0 k: M0 X' m; C  X1 X
e51daea41c797cdfd4a5c1857cc35eff.png

/ [$ P' H0 S4 ?; o! {- ]9 s9 J0 `" @: p, p- X
默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。6 W% c9 x1 }) V5 d' C
5 `  t1 q# @. H/ t2 }0 q& {
转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:" e  c- O" t! a! U

3 g0 @  B* w1 L# Y/ R1 |
0328d6421fbe59b9299acd1c889e3dff.png

# U3 c. _! a+ r, W' B' m, C; [; ^4 p
转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):
" c9 P6 T# W  ?  P1 t/ l$ Y/ Z$ V" ^  p
9 F5 I+ V& t8 M4 a* {
232d594c793c269063eb16a62ce71fff.png

' k* g* U, r& q! I; L/ n. o
; M( [- L* |7 l5 {3 h) t第一项选择好以后,第二项选择Decimal:
8 k/ U8 n, a/ K* f5 U3 K0 @  q8 U; U$ b% D4 g  a6 Q4 N$ G# X; a9 O  N, F
31265a3570a3c4ff16c53050672e0e99.png
6 Q  [  ?1 ?5 M8 x
( ~. I7 H% D+ V! L! F$ O' K
两个选项都选择好以后,点击Export进行导出,导出后保存即可:. P7 _. M4 D4 ~/ k
8 K* I) D) d0 S' j5 L+ f% l
a5e27f83f7223e9a95c29a47d1c79238.png
; d! f* M2 }2 }& Y' {* }" V  t
7 u" D; \( K. {& `; ]& ?+ }
保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:
# W# w! W9 e4 z  M
; e( s+ I2 D! V" b* M- A% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
- I5 q+ ]. M3 s% Generated on: 15-Aug-2021 20:38:330 `9 ^( O# H9 {9 M3 z( L4 |, o$ Q
3 r$ `" c, S; Q  F$ m
% Coefficient Format: Decimal% G% B' |) W+ A+ |/ p
! G' c; U4 {0 ~) k& J8 T
% Discrete-Time IIR Filter (real)                                                                 3 p* A1 Y8 W4 ]
% -------------------------------                                                                 
; m4 Z7 n0 c1 {; z, c1 ~2 h% Filter Structure    : Direct-Form I, Second-Order Sections                                      
; N2 @- ^; H7 \* O% Number of Sections  : 2                                                                        
2 e5 r; h+ M; u9 C/ @: ?/ v7 H" N% Stable              : Yes                                                                       
) ^! S# r" c0 _7 O% Linear Phase        : No                                                                        ! i) ^9 s  w. b0 O9 R6 d' n+ b& C

7 l; s# m' I' E2 [* s% i7 S
' C# M6 o6 [/ j0 \2 Z; ?/ aSOS Matrix:                                                                                       
, H( N4 M$ |# \! c1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
# S% @6 f. o- F$ e! ]1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
; [, n, s# t0 J; B, g
# g  k# F0 i1 O, Z5 d/ vScale Values:                                                                                     ) Z# I0 y3 t( y/ M/ d' _
0.632277093897992026327870007662568241358                                                         / ]8 `4 J* T5 m, Q/ {
0.478258337927073562401147910350118763745                                                                                                               
; C: q! h- [0 Y9 e+ w由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:
+ g5 [4 G' z* q8 W. U/ C1 P3 G7 \. [# D3 n- y
SOS Matrix:                                                  # V5 }  h+ S3 |% L4 k5 f1 Q6 {+ z* i
1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        
% S% {" T' i( z- S/ fb0  b1  b2  a0          a1                                                   a25 d" V, L: H: ?7 X4 ?, n
1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        / H' y# K9 C8 y6 y* A$ f4 B: f1 k# G
b0  b1  b2  a0        a1                                                     a2$ \$ z& T$ U2 E  x) x% z; N
注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:- W3 T$ H# c- K! }3 q
" T" C! v4 Z; y( z
0.632277093897992026327870007662568241358                                                         / w8 A8 c8 c: z5 \# B: p" V
0.478258337927073562401147910350118763745  * L* d2 _, [! k5 T# Q. E/ |
实际的滤波系数调用方法,看下面的例子即可。7 t3 t, [1 y9 k+ W. O* y
# q( j  C& \; v. `0 i( [
45.5 IIR高通滤波器设计

- }' S& X6 R9 _本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器
1 r3 l6 j! Q& i, i" U  X) D/ M5 Z, t
45.5.1 函数arm_biquad_cascade_df1_init_f32- a0 ~5 P1 t8 G) d& A- q& R
函数原型:% A" M8 P+ J/ B# a( Q( O2 |* C% O

( m  }1 Y  O: b$ }- Q
  1. void arm_biquad_cascade_df1_init_f32(/ B; }- T0 m; Z' }. c3 u  k
  2.         arm_biquad_casd_df1_inst_f32 * S,' Y* D6 U1 i" S
  3.         uint8_t numStages,* q: f4 L0 Z/ M, q) {
  4.   const float32_t * pCoeffs,
    $ b  Z# U) [1 |* ^7 v8 v/ E! t
  5.         float32_t * pState)
复制代码
4 ^  u  l. t; O- U  U. d
函数描述:
3 q9 b5 e( S6 }! q5 T7 ?# n& D
+ q% W4 [) u3 p4 B这个函数用于IIR初始化。; g; l2 L  R9 [7 v% }7 v$ s9 j
  b: J4 N& p4 t5 S4 a) e( Q4 p
函数参数:
$ p+ ]0 A0 K- ]1 G4 E% L' f4 W, }' i" S( |1 O* w) n
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
! u' V; W) h% e, n# ?  第2个参数是2阶滤波器的个数。
  j5 p/ [. W& G3 W. v) ]  第3个参数是滤波器系数地址。
. R& H' I! f6 C* W  第4个参数是缓冲状态地址。' _. B0 C4 S! K3 `. _
注意事项:
# A/ N3 Q& l6 e: ~: I1 g1 a
  R; g: v% U  K+ }结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):
# D+ `, m' k0 Z* ^, J9 @5 j9 T) P$ r) Q. Q
  1. typedef struct
    9 l9 p+ K# {) y2 h' p
  2. {
    . I. j! g, @* R; d; q1 U3 Q; Q
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */4 V# P5 r, n5 W; \* Y. X! E
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */4 t5 t3 X) @/ n0 {" y2 `" u# |
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */0 W  l3 i' r1 A7 q8 `! F. w
  6. } arm_biquad_casd_df1_inst_f32;
复制代码

  [7 O7 r/ c0 CnumStages表示二阶滤波器的个数,总阶数是2*numStages。
% a3 {  V3 K# W* @  JpState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。5 S" I- U4 ?/ z$ T3 ~2 m; P, d
参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:
  f2 ?! m& i6 l{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}  A! `$ B- z0 o# k, z4 Q" Z
1 C, |4 R! V, S
先放第一个二阶Biquad系数,然后放第二个,以此类推。
( K- [7 }6 ?6 n( n
$ ^% B5 E0 D( }( [& ?0 u45.5.2 函数arm_biquad_cascade_df1_f32
' F: P6 V3 i7 T3 C函数定义如下:5 m5 R+ [5 W- r3 `# p+ v
% I5 D0 A& k& M1 C- ]8 }0 f6 e
  1. void arm_biquad_cascade_df1_f32(: I$ \; s% `0 u: r! }
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    . ^9 s- l) l1 j; n5 q" W6 `, ?
  3.       float32_t * pSrc,/ s# [0 {  g# T2 B
  4.       float32_t * pDst,
    ! g- v5 n5 U! p/ d
  5.       uint32_t blockSize)
复制代码
1 W. S. C6 N6 R+ H3 L8 F- u
函数描述:
% W, x0 L- V9 ]3 U. R. m7 a9 S
. l8 `1 P/ a& E# R6 U5 F+ i这个函数用于IIR滤波。
( O& H8 i& Y  n7 Y
6 w/ U. O2 }/ j函数参数:+ J: h! s; g6 a) x1 j. C% }

1 _3 ~, |9 i' Y3 t6 ?7 E  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。4 O7 v2 _$ }) r
  第2个参数是源数据地址。
3 y+ x( |1 Y$ |$ D  第3个参数是滤波后的数据地址。
2 P8 H# Z! F7 A5 _( Z  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。5 [' d* Z% m9 K, E: e4 m
45.5.3 filterDesigner获取高通滤波器系数
/ I1 H5 y0 c/ W# I& G; t2 l设计一个如下的例子:# I- R' v" p  H0 w* _

7 x, q+ ~/ s% r' ]+ M信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:5 Q- m9 q  f! c( F) g

% J  V9 Q- T; @& t
7f8be339ea66c602e1b7ea4836e58544.png

* |: R1 Q2 A5 ~5 |* }6 w$ t% z
$ j4 M6 t  B6 e, p8 l4 U* g* p配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。4 e. g  C) D" N& }, T4 q

  H# E$ @$ e1 ?45.5.4 高通滤波器实现
) Q; p/ U' T" U7 [/ ]通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。" d  T' _" e; V$ s* b' a9 X

: @! ^* @# u) F1 @: U0 Y" Z! @/ G
  1. #define numStages  2                /* 2阶IIR滤波的个数 */
    % F4 j& Q# t; o1 l6 s) c4 Q. h
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */1 x. U' m  p$ f3 j; S( R3 D
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */7 R7 S. `* |$ Z8 g8 B
  4. - x+ {9 C/ ]" m
  5. % F8 I. \4 ]8 y( v7 P$ y
  6. uint32_t blockSize = BLOCK_SIZE;$ E4 W, Z5 k: w$ \5 Y% t2 `
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */
    8 ?" \+ t% a8 {1 q, E; J
  8. ' _9 d) }8 C. L, Q, c( {- z

  9. 4 w& I7 p- G8 Q2 u% v
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */
    4 n1 g  J7 P) {8 q. s0 t- z7 s
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */
    1 b  ^# b! }; e! Z  p6 [& Z" H
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */' S$ z# }# b3 U9 G" w
  13. - i* B: E* r( ]; `
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         # C4 v) ]& B  j- c
  15. const float32_t IIRCoeffs32HP[5*numStages] = {8 Z- Q( k: I- y% Q
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   1 ?7 v, f4 a' @& F  B$ j2 w
  17. -0.544565360850816415627662081533344462514f,     
    4 h5 A4 v% Q; b1 I
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  4 @' a# w( f; c6 p5 p$ a1 p2 U
  19. -0.168318873843973093595849377379636280239                              . V5 S4 l/ z: D, ]  C  Z. e5 }
  20. };                                               & L8 R; R, h2 |1 b  f0 Y

  21. ; v7 Q' y. }. Q% F; @
  22. /*
    / ^$ ?5 ]5 P+ ~- V- f6 w
  23. *********************************************************************************************************
    . J; V+ W5 x0 Z9 r8 g$ r. ]- {( _
  24. *    函 数 名: arm_iir_f32_hp
    & e, K8 s& y+ ^( g6 Q3 b
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器* ?/ S( T  g' b0 v( ?1 |* q! n
  26. *    形    参:无! V$ h2 }. U$ s2 j: U
  27. *    返 回 值: 无, ~* `. e( X" y+ P* u; R+ P+ l
  28. *********************************************************************************************************
    1 x* e7 B3 H4 k+ b2 _& [! Q
  29. */
    4 u5 C% s! `  g4 O1 S: Q3 D4 w
  30. static void arm_iir_f32_hp(void)
      N2 Y( _% l! D; s( L5 \5 q/ R: s9 O0 C
  31. {
    ) _" {9 ^$ y4 e& c1 ^( w3 X1 X9 N. B
  32.     uint32_t i;8 I! f3 y9 q# B) p& T
  33.     arm_biquad_casd_df1_inst_f32 S;
    & a& M- H% |: \: `5 D2 E4 }
  34.     float32_t ScaleValue;% _/ P' a/ y/ {) ^: J
  35.     float32_t  *inputF32, *outputF32;9 L' s6 m8 D0 g" z
  36. ; i8 \& O! H. Y" \& _( }6 l/ g  p5 Y
  37.     /* 初始化输入输出缓存指针 */
    # E) a# \0 B" R
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];0 e( b; F# c  `% H6 }" |
  39.     outputF32 = &testOutput[0];
    # v* O% J9 N/ t' ^

  40.   J4 e( ^3 k6 `$ o

  41. 9 F7 ?* M6 U% e# a( y4 F( N8 A& [3 `
  42.     /* 初始化 */& \7 {# u2 T5 Y% G3 n5 ^* s
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0], ' B8 D, z- p& A% w+ _: ]! U
  44. (float32_t *)&IIRStateF32[0]);
    0 ^; d. j! e& ^5 Z8 b3 i, ]5 v& s

  45. $ S' Q. F4 L' b
  46. 2 C8 j0 w9 K9 p
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    0 m1 l8 J) s! j. D
  48.     for(i=0; i < numBlocks; i++)
    & \8 W: z' ^& \
  49.     {
    " ?& q$ ]  s. C. Z/ J' s
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),! J4 g( D' u; Y' S4 u- c
  51.   blockSize);
    2 l  Y# X! X: ?8 ^2 M
  52.     }* H9 D* B+ Y1 T) f

  53. ) r4 [' ?/ D' B4 V+ z. ~
  54.     /*放缩系数 */
    1 ~# b) n. c5 n# P, O, g
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f; ! @" N5 l; e8 ~' X( Z& L5 \) @1 u" K
  56. 0 s/ h% ^( Q) R1 E( s& U
  57.     /* 打印滤波后结果 */
    ) D" `" G! V5 w1 Y; x9 |
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    % }* V' `( j- q2 d2 F
  59.     {
    8 U& s$ O- N0 d/ x2 R% N) b
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);
    " N' R1 C* q% e9 A% Q8 O
  61.     }# M! @2 u' b( A( [# U5 m
  62. }
复制代码

3 [6 R7 {% N" g; {9 u运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。; G6 w/ d) z# D. j

1 n' ^1 \( c, }( b# b对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:9 @* S$ E3 h1 N1 _) @
" x6 c9 j! L; i4 U; K* S
  1. fs=1000;             %设置采样频率 1K6 M( f7 j2 h8 G' i. H
  2. N=400;               %采样点数      3 W; S2 z$ C/ ^$ W* M
  3. n=0:N-1;6 b% q0 r6 ?$ r4 r6 m: Q+ o( X
  4. t=n/fs;                %时间序列
    ' w0 b* @2 A" X% I4 ?
  5. f=n*fs/N;              %频率序列
    8 w* c2 j. S" }8 }/ p
  6. 3 x8 a+ [2 s4 a& f
  7. x1=sin(2*pi*50*t);/ A4 {' y5 S* K& z+ T
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波
    ) Z0 n( {3 m0 r0 x" u, g
  9. subplot(211);( ^' D7 T% T  p' {" J8 |3 r
  10. plot(t, x2);  W! f5 W( u( p
  11. title('滤波后的理想波形');
    6 z0 ]$ H( \  X" W
  12. grid on;( e' K; Z# Z. U  }2 F( [8 y

  13. . K# A3 d4 i' m) j5 r5 y( f" d1 G2 ]
  14. subplot(212);
    ( B7 z7 t' j+ }/ K
  15. plot(t, sampledata);
    ; N0 Y5 p  m, R( f
  16. title('ARM官方库滤波后的波形');/ k* T- d7 j% e/ m
  17. grid on;
复制代码
& l6 ?; o+ Z7 `( Z5 `
Matlab计算结果如下:  g  D: y- z) l
0 n9 I. Q7 M/ u0 ^5 @- @7 ^
91111ef8530bb9e40d9736ccc2c12424.png

1 z8 x) |1 L6 o5 @8 Q1 ~8 B; D( {
/ r, @8 q5 j- \# E( `. N从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:
8 _6 T! E7 k" f$ j. [6 ]2 K1 K6 `7 K' j' B% f$ K% |$ E
  1. fs=1000;               %设置采样频率 1K7 i* e! b2 G5 |2 x  q
  2. N=400;                 %采样点数      
    ) R0 i% P, S8 l1 l; u
  3. n=0:N-1;+ i  m! A+ J, S- L! U! i9 l
  4. t=n/fs;                 %时间序列$ t. a9 `! E" w& }6 E% a
  5. f=n*fs/N;               %频率序列
    6 Y; c/ G$ O8 P; A. K

  6. - T' |& l6 q& A# ]/ f6 c7 n( ]
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成! m9 S% M- I+ c5 `- K& b# C- o
  8. 6 s/ W: c$ {; |2 N6 _& y
  9. subplot(211);7 Y( y" `$ V9 z6 _2 d' k4 q5 ]
  10. y=fft(x, N);                %对信号x做FFT   
    . r- D: [0 i7 s, j# d; w0 R2 q
  11. plot(f,abs(y));
    ' y+ J9 ?* @9 A% k' v' L) ^; x
  12. xlabel('频率/Hz');6 o6 H' k, H) y9 d8 |
  13. ylabel('振幅');& Y! N8 M* [9 \) U# _
  14. title('原始信号FFT');0 \; x. C! h2 M6 x7 k. b0 X  m
  15. grid on;
    / E$ j3 O+ E0 P$ b/ Y

  16. - v$ Q3 o, f5 a2 R) T
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT  M7 K) }* x% B  _& C3 P8 m
  18. subplot(212);                              
    ' L, R0 q- _8 Z
  19. plot(f,abs(y3));
    # u. C- G2 r. L6 x+ ^: T
  20. xlabel('频率/Hz');
    4 c. h1 K% b" y% i+ \, w
  21. ylabel('振幅');
    2 _! m2 @% k( W. N) v9 u0 Y* [
  22. title('IIR滤波后信号FFT');
    & w3 u( h: a. _) {/ s
  23. grid on;
复制代码

0 u6 z; f: Y2 QMatlab计算结果如下:( X3 k+ e( ?6 I( ~" y' I6 c
7 I' p- W- L& K1 i! g' H
d13d47932e1b37bd5046b7ad31415509.png
7 Z) b; X) s5 x

1 l+ t$ I) o6 {上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。0 U7 x* O9 q+ [' I- L

1 Q9 H3 l) W4 ]45.6 实验例程说明(MDK)
4 l2 N, ~9 B3 }2 e配套例子:4 Y  B; x/ n7 u+ ]7 S' b" H! E9 Q
V7-230_IIR高通滤波器(支持逐点实时滤波)) ]  V+ r  t) A; Q7 z+ w
& c: Z+ R2 }7 x; N
实验目的:. T9 D# F: I( z6 R3 M+ o
学习IIR高通滤波器的实现,支持实时滤波# i. l. E3 a3 A+ G5 _' x: F

7 P6 Y7 f( O) ^2 y/ g实验内容:2 a: ?. r- w$ @9 ?
启动一个自动重装软件定时器,每100ms翻转一次LED2。/ M9 c& g$ o. U' U3 t
按下按键K1,打印原始波形数据和滤波后的波形数据。
/ P5 k3 V. i$ V9 c; j: T8 k$ P) Y3 N) T3 \4 l0 R
使用AC6注意事项# O9 U/ g9 Q9 \. {) O- r0 R" `. D
特别注意附件章节C的问题
% M5 q  e3 [  P' d& m2 F: m+ ^% d) H' j, u( R( z5 \
上电后串口打印的信息:/ `/ Z0 J8 Z% J$ |  D6 S3 t4 V1 M

1 ?; @' C/ Z1 h- g波特率 115200,数据位 8,奇偶校验位无,停止位 1。* v4 H8 s+ C! u6 v1 v( F

( ~% d$ g5 s% `- y3 Z8 x& I9 f* b6 E
262f2df9123b012595369835f600f93d.png
- ?2 {) W" }6 S  Z9 W! v. {

' u# W. m( P# r% z5 E6 JRTT方式打印信息:
& ]# \  h1 C) b$ k' Z% P6 A* L. s0 @* ]& V2 P9 D+ E
75dd602d2d2eb24a5dea611cc45ce934.png
' T& i: O3 ?! w  E

7 o" N) w9 K4 b% y程序设计:) O+ ]0 g) h- R

3 u8 Y$ l& W& v  系统栈大小分配:0 h, ?8 r: ~1 l2 ~" A2 ^- R2 N
  l6 J! w3 g, B0 b
640570198f69835d8ac9eb982ccf39f6.png
, A& g8 {4 D' ~

, u! M" k! j% d  X8 D' t3 ^" f  RAM空间用的DTCM:# T* O: U. r; X" f; {

2 y, w7 D" s; v; d! i3 a
6efb3697225eacc369e069ab80d69ed2.png
' [9 `! Y5 E& R) F
' ]! l( C2 X! q
  硬件外设初始化9 x5 {0 r# R% N" D. L
( y- o) }) l2 ]4 V5 _( b6 Q: l
硬件外设的初始化是在 bsp.c 文件实现:
. H3 P1 S" W$ P$ O0 q9 S3 s
! q; R6 B1 w; n
  1. /*# \$ k$ `7 _4 `" N0 ^+ }
  2. *********************************************************************************************************
    ; M- G: [, m2 D6 Q& \, L
  3. *    函 数 名: bsp_Init
    2 z& h( M/ [0 t, E# k. m# L" F
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次- s2 @) s3 Y8 _. x. C8 F8 v! i4 W$ G
  5. *    形    参:无
    2 a$ s) d; b, a% Z0 h$ `; i
  6. *    返 回 值: 无4 P" X& Y/ A6 O  i) D
  7. *********************************************************************************************************: }8 D' o4 {! t" U
  8. */
    ! O* m9 `! V/ P" j+ L8 K( `
  9. void bsp_Init(void)$ Q! q; G+ D1 D& y0 P# d3 L
  10. {
    6 {+ m9 w1 f$ w
  11.     /* 配置MPU */
    ) ]5 t2 P9 a- B& n9 r5 S9 n# b  }
  12.     MPU_Config();
    ( z+ m- R# i* m; _$ \  B9 j+ W

  13. / P1 R# e* B8 W# p6 |; V
  14.     /* 使能L1 Cache */
    9 _) J- _4 U7 v9 \% e$ V
  15.     CPU_CACHE_Enable();* ~  W% V" `5 z& E9 V, J
  16. 9 b1 \1 V4 U2 t( _5 R
  17.     /*
    8 D2 Z, B" c4 Y" L6 E
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    % h' ?9 U, N' U  q6 s+ K; Y
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。  }& `2 ^: _. U$ d
  20.        - 设置NVIC优先级分组为4。/ R- N+ \) n! V8 R2 k& U
  21.      */( d! t' A! t. ]$ N& Q  m
  22.     HAL_Init();
    3 s  k- m: a/ t5 p

  23. 9 b# o; h- W. F" T
  24.     /*
    . [. q: g+ n, ^4 e, A7 h. t' D2 D  _
  25.        配置系统时钟到400MHz0 Q  \1 N7 M9 S' d; m
  26.        - 切换使用HSE。& }- B+ [  ~0 }/ Y  y* o) E7 k
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    4 d& U; h9 d0 U& ~* ?2 C/ Q1 P
  28.     */
    6 |1 T7 z! s1 m1 ?9 \
  29.     SystemClock_Config();& k% V" I% O- A5 p$ {! z9 c* u

  30. - N1 ?% Z2 X' a  b4 ?
  31.     /*
    4 r* A1 z6 E8 Y3 b$ ?
  32.        Event Recorder:
    # l; m( K4 c* W. O" y
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    : ?/ ^) e, s. D6 m6 Q5 _- j0 d# Y
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章; S, c1 l' t7 Q% w
  35.     */   
    6 I8 T& E. y# u9 T5 c8 H
  36. #if Enable_EventRecorder == 1  ' S: X  H; b1 R* O7 s) O( @1 \! u. D
  37.     /* 初始化EventRecorder并开启 */" g6 x# g5 s  P6 ~
  38.     EventRecorderInitialize(EventRecordAll, 1U);9 L5 F/ N, m( [6 b) ~" o
  39.     EventRecorderStart();% J( i9 a/ H7 ~: q3 y
  40. #endif
    3 _& G4 g$ a/ O8 E4 Y

  41. % q' s2 I; O9 p9 |6 \
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */- e2 O3 Q: D, ]8 J& T
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
    4 B5 [0 o: }4 x! b& _& F/ A0 [
  44.     bsp_InitUart();    /* 初始化串口 */
    % e3 Q4 n# g9 Y0 S
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    % j# w$ q! o6 o+ I: v
  46.     bsp_InitLed();        /* 初始化LED */    0 ~6 H5 l* \- i$ x
  47. }
复制代码
; I! `7 I8 j, h  Q. d6 z0 s6 M
  MPU配置和Cache配置:4 _6 m' n  R2 p' a! r) C0 e7 ^

+ e( C- `" e% P; l8 m8 Z0 i数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
  y2 S& X3 A6 [
/ ^5 s& X/ R* y* K. e: s  A: k( }
  1. /*& n" G6 F+ f5 N$ `( h8 O, ?& `& F
  2. *********************************************************************************************************+ L0 m' S" t5 a
  3. *    函 数 名: MPU_Config3 l7 M! N  {$ b1 f7 f* v* d7 x# C
  4. *    功能说明: 配置MPU9 l+ h# ^$ b; @0 S; j0 ?( P) c
  5. *    形    参: 无
    ) o8 E5 e. r2 Q8 T% J
  6. *    返 回 值: 无
    ; Q7 ^! ^! w- b8 ?
  7. *********************************************************************************************************
    7 c. v6 |3 W3 G: a7 T0 t; l
  8. */  N6 n0 Z$ G- p: ~
  9. static void MPU_Config( void )0 B( v: L% b! T! r9 o
  10. {; J! l: O# ]* [% m. O3 V3 W! ?- ]
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    % R8 {/ ^" ?4 ^8 @# Y
  12. ' j& B6 B) j/ j: K
  13.     /* 禁止 MPU */% W% l3 z! i5 U2 A4 l9 G9 Q! w. Q* C
  14.     HAL_MPU_Disable();1 b) V8 i; t6 i4 ]$ K

  15. 1 D6 y0 I( E0 k* u
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    % n1 {; m: O  p# q1 O/ m$ }  W
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    0 f: R; a; F  o- C1 X
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;! T* X8 j/ r; q5 x& y4 i6 i7 B
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;( B8 U4 f4 x( A: l( s3 k  c9 i
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    ' g& _& `, }8 I& `7 C
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;
    & m7 d! i8 G+ W2 J+ B$ W* n
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;4 M, ~& f5 p, G8 F' Q  G, _
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    1 B6 C6 c5 s, S% B# K& K( L  }
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;8 \- U: m+ M. _% o9 `
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;8 x. x5 O/ @$ \7 X/ J# {. U
  26.     MPU_InitStruct.SubRegionDisable = 0x00;) u* V( `; b5 D( E) |
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;- q% U- K6 g5 l- w

  28.   V6 D) [- S4 M& ?; ?
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);' B+ Y( e* W: P& w, }) K2 A
  30. 3 y$ a2 U. l, C6 ~9 t. p
  31. " ]; ~9 ^' H/ d! y$ ]" }
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    7 J' \0 ~! i( S" h4 Z6 ]
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    ; N$ h5 R& W; o7 I4 i
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;0 Z+ {4 p* u% _
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
      F& [& b9 w3 N) U' C
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    * b. O# d1 ~) s/ L2 C
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    8 m2 m2 D% }, a
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    : Q0 U$ p/ u+ p
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;  q: l* Q" H& W+ {
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    9 \5 v. }/ K: H3 I1 `& w% [
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;+ k, p; \) f' \* Q* X& b0 M% o( J
  42.     MPU_InitStruct.SubRegionDisable = 0x00;+ F, W% r4 ~. n: u  p  O. G
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    . T# c" R: K/ L$ n  k

  44. & {3 O; A! H. @/ Z' U' B
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    1 }: Z5 T0 C( F6 l# O
  46. ' m% W- Q: A2 T
  47.     /*使能 MPU */
    ( _2 ^2 T1 f: T6 }3 B' L
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);* t# O( g4 l& n! O
  49. }7 t3 Q0 U: d  [% d

  50.   F+ C9 l: m& J
  51. /*. q. ~& F3 d3 \
  52. *********************************************************************************************************
    2 n9 T* X# ]# P: [6 T
  53. *    函 数 名: CPU_CACHE_Enable% h: p6 ~( ~) v; h
  54. *    功能说明: 使能L1 Cache
    5 Q6 Z3 h; `! Q. @, f. k
  55. *    形    参: 无
    $ j) D6 }3 W$ e, z# `
  56. *    返 回 值: 无3 `' U$ S4 A+ o$ ]
  57. *********************************************************************************************************
    ) ~- q) F! x' _
  58. */, s8 g6 ^6 P' y
  59. static void CPU_CACHE_Enable(void)5 I/ e0 o. ^, D' y
  60. {
    : o$ }, q" ~( Y, p
  61.     /* 使能 I-Cache */
    * V% W& r1 \! e2 R" s
  62.     SCB_EnableICache();" ?; L" H4 G, G- X4 Q5 m& @  Z) B
  63. 7 w" P. ~+ E1 l4 n) I. H
  64.     /* 使能 D-Cache */+ [  f3 N/ k. N4 d6 K9 J& l
  65.     SCB_EnableDCache();
    . e: J7 a; _* g! u+ Z* y' M! \
  66. }
复制代码
7 V+ T; W) r1 L# Q
  主功能:
6 _; y$ m+ B+ e# @4 _' ?1 @. h. ^5 u$ K0 R
主程序实现如下操作:
$ n4 |3 U% r$ v/ t4 `) _5 b8 ?5 Z
. t/ Z6 f! D% l  m  启动一个自动重装软件定时器,每100ms翻转一次LED2。
4 x% q) V  D; m: q5 b  按下按键K1,打印原始波形数据和滤波后的波形数据。# B8 F0 {9 d) w& v9 z, k4 [
  1. /*. i, U7 J/ X0 a' [5 q
  2. *********************************************************************************************************  {+ P9 V  y, ?4 l% i# R/ a; ~  `
  3. *    函 数 名: main
    1 F* {' |& T( G
  4. *    功能说明: c程序入口
    7 H( n; D8 _7 K
  5. *    形    参: 无  K- C5 i- f1 m1 N9 M: [3 m
  6. *    返 回 值: 错误代码(无需处理)
    + X/ ?: w' [8 D) r" _
  7. *********************************************************************************************************9 b* o. M+ U: g  ]
  8. */; `$ `# R$ i1 E. g0 C# B% \
  9. int main(void)
    ) w# B1 ^% X6 _0 w# z0 X; ~/ C; A
  10. {
    % O2 q5 }7 ~4 ?( i/ o5 E$ ^" ?
  11.     uint8_t ucKeyCode;        /* 按键代码 */) `! s! R: P" d- W; H6 z' \0 r
  12.     uint16_t i;" F( U; }5 h, ?

  13. ; K% _5 M- x( R& O- C
  14. 8 z* u$ ~0 g! [3 W
  15.     bsp_Init();        /* 硬件初始化 */
    $ P1 d. `! B  F3 C7 \% J
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    " g& K) L; v0 U, \' l
  17. % ?# |) B3 a5 |' J
  18.     PrintfHelp();    /* 打印操作提示信息 */, x) ?5 ]) D' K! v8 |
  19. $ T9 H$ E+ v1 y0 h
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    8 b/ }+ }& o/ [1 c6 Y
  21.     {
    % c0 i' d% ^/ O& X
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */. u" g9 j+ D$ S  F& F2 \
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    , E2 _0 J' Q& z
  24. arm_sin_f32(2*3.1415926f*200*i/1000);* M+ n6 q. X3 h" h
  25.     }
    , O) B2 ?/ w8 C+ S/ J( ]

  26. / t2 }1 B" k% T( ]& f' x

  27. . t! K( W+ J) m  B
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */" b& X! ]- M; C) m/ `

  29. 5 R7 e. Z  J  ^; ~
  30.     /* 进入主程序循环体 */
    8 J+ T+ H! o% J0 v7 \
  31.     while (1)
    0 y8 Z4 I* N% C* W
  32.     {
    : C/ ?+ K1 k+ Y' e1 G
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */$ T5 z& O1 `. H! R: b$ V5 e
  34. , M7 [- r7 A, Q( m8 m4 y

  35. 1 k; \; k/ W, }1 i; A
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */3 w5 {+ T) L: X  w* N. S
  37.         {2 n/ ^8 W1 z5 E/ Z1 t
  38.             /* 每隔100ms 进来一次 */; S, [* E1 N5 a, Y
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    / e0 h; U5 B$ w# {. J0 G+ C
  40.         }
    & J2 I8 f' Y0 s5 Y* N3 `: ~0 q; W
  41. 1 g! i" o4 w& F& E% T
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */# E0 O: |3 h1 z' B6 b5 \3 l( u5 H
  43.         if (ucKeyCode != KEY_NONE)
    ' I1 b( g8 ^* [4 V' g! {$ L
  44.         {6 E* K3 W0 L9 A1 Z" P* B
  45.             switch (ucKeyCode)  ?( P& X) m% H' m( x
  46.             {
    $ Z, k- V) \/ W+ ]7 O
  47.                 case KEY_DOWN_K1:            /* K1键按下 */8 E! ?4 B; f, `, ?4 K$ b3 O
  48.                     arm_iir_f32_hp();1 o0 c7 q0 D9 l% E6 k+ d, S# e
  49.                     break;
    6 I/ @1 s& x" c: G( H

  50. $ A7 l0 t6 G) {9 G% f) K

  51. ( G: \: }9 E! K4 x* n3 ?" N$ |
  52.                 default:
    / ?1 \  X- [. x$ S( k
  53.                     /* 其它的键值不处理 */
    # F& U, y3 ?/ s1 b, Z: _3 K! V
  54.                     break;/ b. ]0 L" m/ f# A+ e2 y5 y3 N
  55.             }1 v9 b3 C; [& w% W9 e
  56.         }2 n9 P) g/ x  E/ r4 U6 V

  57. 5 j8 x' v6 v7 Q1 `9 F! X& _6 y
  58.     }
    9 V2 D# i1 x, }/ e1 E: R
  59. }</span></span>
复制代码
2 m1 v) Y4 g0 L; m' x
45.7 实验例程说明(IAR)6 j  g, }# {- ~# V
配套例子:
; s& v: e& {9 `. U4 Q( _( bV7-230_IIR高通滤波器(支持逐点实时滤波)  M4 U3 r( a. L- u* L1 X: H3 v* t
3 k5 S* R% @. Z/ u' p
实验目的:
6 d$ g0 j5 P- S! Y, n$ f, R学习IIR高通滤波器的实现,支持实时滤波
! d. _" u3 n+ [5 e6 D' Y4 h( q
8 `1 k$ |3 {( S
实验内容:
7 U1 K# ]( s4 a& E+ b1 U启动一个自动重装软件定时器,每100ms翻转一次LED2。
* ~/ r2 E! z+ W8 V1 Q! W按下按键K1,打印原始波形数据和滤波后的波形数据。

+ r* k1 Q7 s( M+ O2 i
$ k) h0 d/ V. z9 ^5 J- P上电后串口打印的信息:
* x# [; C' `- M7 X8 i) r- u+ D* h& o6 b
波特率 115200,数据位 8,奇偶校验位无,停止位 1。3 b; h1 ?# Q) R* L# M. B2 n
0 ~1 S: M$ C5 i" f+ e
a66c11d15f6f8dd3c49ad9b587ab3a90.png
5 _+ w. A6 o/ q4 b8 y+ g& r
/ a+ i- ^$ w3 c1 ~3 `% P
RTT方式打印信息:
* U/ p( m0 o5 C: n! k9 V/ T2 j1 ^& p  |6 S. d
5b42e93b5c59803fdc45c18e564e61f1.png

1 o. Q# `# K" [) q( u) N8 R5 i5 n, V' Q- C
程序设计:. C$ O' o  ?4 Y* j

/ @: Q9 c0 u  @/ ~1 `. K4 B5 j  系统栈大小分配:
4 c6 U+ d/ r0 }' u+ W! H* p0 T. @3 F  [0 y: M- a  Z: v2 _3 U4 u! w; i0 D$ P
a48a46f1265dd3824bc690f874e90613.png
; O8 `$ O0 b" b, W
6 W! f0 E9 p; A* C
  RAM空间用的DTCM:
6 Q7 ?+ D# o1 f
$ t! {2 J$ T: t! m1 P4 T3 S0 |
025eeb1d94bcb86c7bec22ed1e822f36.png
; ?- W2 w0 b& y1 z5 X: c
. D9 l+ T& I% z$ M- I
  硬件外设初始化
5 F6 k" v# Y( Z+ L! r
& a0 \5 D! E6 C3 R7 f' c硬件外设的初始化是在 bsp.c 文件实现:
4 p# Q- A9 h1 a" t" h: ?8 v! x* i; x) v1 R. j! a( P/ G, P
  1. /*
    0 w. Y: a# i% P7 G1 n5 n- j
  2. *********************************************************************************************************6 {4 o+ z+ P" D
  3. *    函 数 名: bsp_Init
    . q! z4 o! `$ m' L' D" D
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    8 m  S% v4 ?5 _# f% j
  5. *    形    参:无3 C0 ^: V! ~1 C! D- P4 ~+ }
  6. *    返 回 值: 无1 @) p5 S, J( n: q: E
  7. *********************************************************************************************************2 H3 D* z3 j; s; h% N- q0 n
  8. */; n& ]5 p$ F) J( g7 d
  9. void bsp_Init(void): R7 U9 A# i) V% G, q
  10. {
    * G5 n' D3 Q7 y* a( ^7 a, `
  11.     /* 配置MPU */
    7 R4 `( I3 v. g  ~
  12.     MPU_Config();) F9 y# t) h( A& E/ e! Z8 H+ B

  13.   e/ ~/ P: T# d5 f/ y, w
  14.     /* 使能L1 Cache */
    4 S9 a$ p* t: d# _8 P
  15.     CPU_CACHE_Enable();) W/ b9 l* I/ r& o  F7 R

  16. 8 C+ b& l6 b1 J; Z) K
  17.     /* - C, {% j% |; x0 k
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    ) Y0 j9 A5 ~! o* o  G
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。* h1 x, c8 {* s$ y$ R& B/ J2 B5 l7 t* Y
  20.        - 设置NVIC优先级分组为4。
    + p. |, Q9 d% P4 E5 o
  21.      */
    / X* t* S5 @5 r6 r7 k! h1 u; ^0 m
  22.     HAL_Init();
    / q0 k9 b+ s4 g, a+ T, G+ C, T
  23. # F5 n2 R5 l/ h: q
  24.     /* $ s1 Q. |4 E# u9 q; c) t/ y. q. ^5 q9 D
  25.        配置系统时钟到400MHz6 j4 K0 I' y! N- o! D6 M
  26.        - 切换使用HSE。6 k1 J; {5 N( `. N' ?% M; K# ]
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    1 n- T$ r; ^# O- E, B) X9 Z
  28.     */
      G* y1 M3 z9 n. {$ ?) Z
  29.     SystemClock_Config();! s, K& E( R2 o+ A; P

  30. 1 G/ w$ O; [) Y7 k
  31.     /* ! \0 u; L" T) k
  32.        Event Recorder:+ t( {5 t9 T5 a& R4 p% c
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。, T& ?% c5 D' m7 i4 t7 e
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章0 ]9 j6 z4 f$ U
  35.     */    ( Z* ~; I# m6 b" U' D& H5 G) \
  36. #if Enable_EventRecorder == 1  
    6 {: O0 l& Q* Y3 ~& H4 f3 U
  37.     /* 初始化EventRecorder并开启 */
    9 X; E/ y  \6 M
  38.     EventRecorderInitialize(EventRecordAll, 1U);: h& b% b# Q7 o) P! S
  39.     EventRecorderStart();
    3 Y& t5 b: |+ ]" `0 h
  40. #endif* F* \  H5 c* x# G) w: q  q
  41.   [, |: T+ ?+ \  W$ [
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    - W" }0 w0 [  Y$ m7 D6 j# W
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */; D; m% \" E& k! F" L  m# m4 H
  44.     bsp_InitUart();    /* 初始化串口 */
    ) z( s0 k9 c6 N
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    " t& I) V5 r& ?
  46.     bsp_InitLed();        /* 初始化LED */   
    : @2 n" |5 {8 @
  47. }
复制代码
) C" ^6 y7 j: l0 F+ h; V
  MPU配置和Cache配置:7 V6 _9 Q0 B: Z% y7 t9 r. }# R9 {$ U

, w% q. K( {" p+ ]& X8 ?- s& s数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。: Q6 `$ |* b, S
! z9 g9 Y& Q0 B3 x- x0 V9 p7 F% N
  1. /*
    5 k( T0 R# j/ ]& W- [
  2. *********************************************************************************************************6 g2 w/ `, [1 ^( M1 {8 Z
  3. *    函 数 名: MPU_Config, Z& ?/ W8 A  I* t- }
  4. *    功能说明: 配置MPU  o2 q' y& @: y9 U' O3 `! }
  5. *    形    参: 无
    3 d7 f2 g) C% F
  6. *    返 回 值: 无7 D3 S+ R9 T+ S! U7 A1 t  M3 M
  7. *********************************************************************************************************$ ^+ w1 D8 J& F6 o
  8. */9 d4 Q9 x' \2 B# S! {6 Y% e
  9. static void MPU_Config( void )
    : w% E4 w" \. R9 n6 [+ I
  10. {0 p, y9 {$ T$ j( ~
  11.     MPU_Region_InitTypeDef MPU_InitStruct;+ l7 S3 |8 M9 V. n- a/ E
  12. / i8 D3 @& T/ `5 J& H5 ?% e' J
  13.     /* 禁止 MPU */1 Y$ u* P) Y5 r' M
  14.     HAL_MPU_Disable();
    6 s: O3 p9 H. ]1 ]0 `

  15. % ?( w( N1 z, Y) X) u
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    + T4 j  j! T, M8 l/ q
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;6 d9 x0 s, j( l
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;8 U) k. N* C" V: Y- U
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    ; F4 ?7 d, c& i0 @2 c
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    1 ~  y" c, L0 }+ |* u0 K* ~8 c
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    : r) W9 A+ w6 b- e& Z$ F3 W, F0 ]. x! @
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    ! c" E9 Z% t/ `+ w- ?5 t* l1 _
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    7 O: _1 O; ?. L6 r5 c( R. d  k
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    : W3 c& d# q9 U. u5 }
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;  G' S+ p5 a8 x. t
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    3 C0 I- ]* k3 P& Z9 }5 i' V
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;& w4 e2 P+ g( Y1 n8 {# i1 V# P! |

  28. 3 X6 T5 ?0 Z$ V& `* d
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    8 z; M& E3 A* i/ D" T! \0 @: t" q
  30. : {- F8 I/ q5 E5 b
  31. ! r( e+ j9 z1 {, B  {
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */$ ^: v1 \; f1 E% }9 s9 S3 k
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    . o' |# A1 f& {/ I# x
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    2 A+ J& f& c- R* C* O& s% `1 r
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    : N$ ?! q3 B# L. a, l* y2 n$ q- `
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;8 ~/ ?# i9 @9 L: w! |! D0 ?+ f
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    9 Y# e, }2 K7 v  h* F; S
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    " j/ C' S- J6 ^* i! J
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ; s6 @+ s, v- W) Q7 }7 Z
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    ) f0 y. h+ C) m! t) S; |1 q5 d+ s( s
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;2 c8 O  m2 Z" X% X7 j. |# L
  42.     MPU_InitStruct.SubRegionDisable = 0x00;4 E/ N0 O& y+ {; u, l
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;/ `9 @$ D0 N% b

  44. 7 d  m% V; K. e8 p; u+ `6 E
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);) `% R: l" ?! x

  46. ' n1 A: z$ D) i& }
  47.     /*使能 MPU */
    ! p6 ^0 c  u' g
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);6 m/ \; c, A/ m2 _' `6 i
  49. }6 N$ d; }6 l& {, {3 o& c
  50. ; o8 q- z  }7 K- u- b. x' u
  51. /*: z  `2 B- V) k: Y
  52. *********************************************************************************************************
    4 h/ n1 Y$ Y; S, M
  53. *    函 数 名: CPU_CACHE_Enable
    9 s& z$ r3 T) K! V. j6 M
  54. *    功能说明: 使能L1 Cache
    + S# ~0 Z' o7 C" l# q; n  _
  55. *    形    参: 无4 g4 C: ]# y4 p
  56. *    返 回 值: 无
    8 {- L4 J* `( a# \# \
  57. *********************************************************************************************************4 F/ ^% D2 u/ X) v8 H6 W
  58. */% `! _( k5 Z7 F
  59. static void CPU_CACHE_Enable(void)
    5 T6 a  a2 _8 _/ d: w4 w" G
  60. {1 J5 `, v! a) Y/ R5 x" A/ L
  61.     /* 使能 I-Cache */
    5 H4 Q3 q% Z( ~8 g/ H
  62.     SCB_EnableICache();' m0 |# [; o# v; V( X5 M7 l+ l
  63. 2 V: O/ c3 R) f" R. b
  64.     /* 使能 D-Cache */: N3 d3 J6 b3 L
  65.     SCB_EnableDCache();
    % t. K0 t& E* M0 M
  66. }
复制代码

: T& b0 Q0 `8 N: a6 v; t6 x  T' Y4 U  主功能:
3 G7 ]* m7 f& h( i# j! K, B( j5 q5 d% Z+ y$ i2 M1 {" W/ e
主程序实现如下操作:  A$ A* D) B- t
5 X2 y& f$ U* G3 o( c# R
  启动一个自动重装软件定时器,每100ms翻转一次LED2。0 v$ S4 v( ]& n$ D7 w. ?% N
  按下按键K1,打印原始波形数据和滤波后的波形数据。
% ?/ F- a8 f+ ?( O
  1. /*, C! x( K6 L+ f5 z! `4 @
  2. *********************************************************************************************************/ q- g  H' v  W& T. U7 _. {
  3. *    函 数 名: main3 v; m* t4 o0 R* g
  4. *    功能说明: c程序入口+ ^5 B0 x. C# X$ L/ B+ M, z+ A
  5. *    形    参: 无; U. N& x- s# b* o9 E0 [9 r
  6. *    返 回 值: 错误代码(无需处理)( v* c) h- u1 v
  7. *********************************************************************************************************
    - }' ~7 W) w4 l& M
  8. */6 r$ }2 i! r2 r6 o( u
  9. int main(void)# Q8 n8 e/ g4 ?
  10. {- G" D, P& g: }7 R  z
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    2 i$ r, M0 K+ J! ~
  12.     uint16_t i;1 H8 H& B! Y0 I

  13. 1 J0 O% e" }# S# B8 ]
  14. 2 J9 X6 r% A, g/ p7 ^& ~
  15.     bsp_Init();        /* 硬件初始化 */
    ( z' P  P7 u# V$ K
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    ' Y! B" R. b6 T

  17. 2 P5 U0 }- |1 h. @
  18.     PrintfHelp();    /* 打印操作提示信息 */5 H5 g( A0 q8 `
  19. 9 e# A  y$ y- F
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)6 s2 X+ t. ~( Y' ?- ^/ [& r. @
  21.     {
    : ]4 E% S* y2 M1 t1 \- |. j: w7 n
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
    1 n( A" A* E1 G1 l
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
    " C! L2 m0 j* u+ N7 H
  24. arm_sin_f32(2*3.1415926f*200*i/1000);- R! A* Q! q- V" g* k2 Q
  25.     }
    " u  V' L* M6 w  [

  26. ' h/ k; C9 ~" J5 x* E$ k/ R' B

  27. 5 u! r0 R4 g* w
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    9 [6 q7 ]/ P5 e
  29. - D0 u) U8 \& m5 ~
  30.     /* 进入主程序循环体 */
    6 x1 D( M: E& K* G: Z
  31.     while (1)7 h4 L! D( ~! ]9 v
  32.     {
    ; |  T( c7 `* W6 V" W* e
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */5 J3 W; [! Y1 p( d3 E

  34. ( {0 S( A) e3 m6 V7 b( T4 N- m
  35. ) k3 y! O  H4 N# v
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */
    , G% u' X5 ?9 r8 M$ F7 e0 @
  37.         {! o/ V# l) E# H" u) I
  38.             /* 每隔100ms 进来一次 */* [3 F: S: g4 R$ y& _
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    ' p0 P* [: \! I! L! z$ M
  40.         }
    3 l9 e; u3 l& W+ \5 E

  41. - i; v, W8 s* S( l$ c5 D% v6 R& r. G
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    $ \* J8 |6 c$ ?
  43.         if (ucKeyCode != KEY_NONE)9 b) E# s8 ?) l* B6 t$ Y9 T
  44.         {
    & ?& L8 X! m) W5 s' I% n
  45.             switch (ucKeyCode)  B& ?, J% Y  P! T- {
  46.             {) @. A8 Q, Y; p2 L& P0 p, S  s
  47.                 case KEY_DOWN_K1:            /* K1键按下 */
    : J* f- Y/ {, {% ]4 |
  48.                     arm_iir_f32_hp();
    # b0 \% t. _, A( w' i# V! r
  49.                     break;" \* H) |& p  {+ N9 L

  50.   S7 h3 O7 r& ?0 u
  51. 0 |& |* J3 o! F
  52.                 default:; n8 d. d4 ~+ n  q
  53.                     /* 其它的键值不处理 *// {; O, X, Q9 ^0 U' b/ B
  54.                     break;
    - a% I6 O7 f) K
  55.             }
    2 n  v9 m+ |( B! ?! a
  56.         }; k! m% b5 A) e( _7 u& k1 S) r% [
  57. 1 T- k1 |2 k/ C5 Z
  58.     }. q2 G% z7 l; h0 v2 l
  59. }</span></span>
复制代码
45.8 总结9 ?6 r$ V: G" g* E. o" m8 S# Q
本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
8 p' O' Y/ Q# r: C
5 |* A, z! w- y4 x+ Z. o, f( |
& i# x) b) t8 V( b/ r7 b! N. Y) Z

+ @+ W( B# x; L9 A! t1 N8 i2 A
收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

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