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

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

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示+ N' |  L) J% H$ j# {  r
  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:& Z8 q6 b9 A5 u; q9 O
2 q1 T0 X4 J5 ^$ l
  所有数据是在同一个采样率下依次采集的数据。  J. g5 p, [1 v& P% E7 k
  每次过滤数据个数一旦固定下来,运行中不可再修改。* i% R0 J" P% x; m. N( o- ]
  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。+ X; B# f8 D. `

" T5 d: w2 E# z7 K0 R/ K45.2 高通滤波器介绍2 Q3 X, A: G9 f' j: J! `9 t0 y
允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。
- b" B0 O0 y3 R) T& [; H" n6 d, r2 M& W! X
c1e7dbbd579e8c1e535c9ce0678c3532.png

, C+ `3 T8 c! ]  W0 O1 ^- q
" R9 D& i# _1 y8 a* K* j& N5 c45.3 IIR滤波器介绍0 t0 o$ D) i' h$ p2 h3 }7 t( n3 H4 X
ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。
" M/ ^0 v& B3 q, D* M: P9 K& i# P' L, a8 u( ]
直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:
; S! h) P/ o9 j5 ?; g2 c' K0 Y+ J! B, @
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
6 n3 t+ F2 q0 B; `+ x9 i0 Z/ |4 \& o* ^/ l8 D4 M6 U) ?
直接I型算法每个阶段需要5个系数和4个状态变量。& {& X# ~  N! m3 Q& {
! {3 u+ u% ?+ S! D! u7 y4 _
7afa71129081e82218f8d501da73448e.png
8 Z* z% T( C9 s$ z- Q2 s
% P3 {1 J4 S8 w1 [4 Q
这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:
6 C; k8 {1 w3 y& U2 O. a  G) ~9 s9 V- u- ~9 l
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]
  f2 Z: H7 s+ x1 {
; Q9 d, ~# c1 g3 ^比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。; s0 i: O% A& g( D: H) l

  g# z% e$ q# N  ?/ F7 a高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。
5 Y- b" k; v* f& o. @% Z) D5 r2 @( v/ g& F
75ec77cc1b971aa24bf16e169c67d8aa.png
" b  f0 \9 u( g) K9 O+ x
9 g0 c6 x" x* q. |- R5 O; C
如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。
" E3 `9 C4 p7 M
: ~* J3 M8 z0 q/ N8 T0 k; P45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数
5 e$ V7 g, q# Z8 H前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
) E! m5 `1 N" \4 j8 z# I0 q* `6 O
! ?+ B1 w7 N! L" s+ w下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:
7 I8 r" {: e4 W$ `; N2 c, R8 p. h9 S! P/ X1 [
4e2868a42a0de4b63d0a13d64952e470.png

' G% C* l7 l+ i% \& q% T- x8 f  z+ e* F; M- |
filterDesigner界面打开效果如下:1 {% W; _6 f  z# A
' N6 e* `- k# p" Z" f3 C% l5 L, o
b9bee9bb0a4ada395e69793b38c566fa.png

3 c1 o" U) \8 O/ o2 X3 \$ Z7 f" S3 \
3 _$ u- W. J) T0 f' oIIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:
. c  k* d4 ?; f) P0 C5 j
, G* j5 X/ T# s& h  w9 e
84c4d0373d7c29786a6620c1050ee40f.png

+ t; f, [9 \# H* ]
/ G+ w- L3 o, h, z+ s9 V' a6 _点击Design Filter之后,注意左上角生成的滤波器结构:, g: }& d: c3 Y! M& q

! L) }6 v8 M' C' m' L! I
e51daea41c797cdfd4a5c1857cc35eff.png
4 A: k' S  v8 c0 r' p+ Q' o
5 [: j2 a- A+ y( b) D& A& l
默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。
1 U" r1 c' ]* Y0 D
$ C$ A/ M* ~. V3 t, A转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:/ U9 }' K! Y3 e& O6 O' y& ?
/ U7 _( d: Q+ K/ l& }5 z
0328d6421fbe59b9299acd1c889e3dff.png
1 K8 N, C  S) w& y* c) ~3 b

. [& C* K) Y+ {. S转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):
2 Q2 S, E  o, u1 b7 O
0 M0 b  Z  q$ h5 I. P: m7 s
232d594c793c269063eb16a62ce71fff.png
/ Y/ _% l; |7 b! |% x/ l, h

4 a6 [. H/ Q+ D0 b2 B: |# G第一项选择好以后,第二项选择Decimal:2 m* d, ~' j8 w$ b8 F8 w

7 @. X' B) b' X% H
31265a3570a3c4ff16c53050672e0e99.png

8 U- a! x2 G0 J5 e# v# s: u, R0 i
两个选项都选择好以后,点击Export进行导出,导出后保存即可:! N6 A  n" H" D5 S

+ s' a! J; F% h7 y& v& E4 y1 z
a5e27f83f7223e9a95c29a47d1c79238.png

$ S1 v0 e1 j9 G& Y. A2 q5 Z8 F  v! u4 t- v% s$ V! V/ H2 j, r
保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:6 O- x2 {6 `7 a) N. q- z
0 K2 i& v9 a8 h2 p* E+ j& j
% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
8 A0 o; C1 _7 s7 d2 q6 e% K$ x% Generated on: 15-Aug-2021 20:38:33  |% D2 x( C& B2 I1 ~
/ f( A" o* [2 j, N+ l3 Y
% Coefficient Format: Decimal7 w0 J! o4 N5 g# B6 _
5 X4 S/ v/ B# F" J
% Discrete-Time IIR Filter (real)                                                                 
# M$ E$ _" h% q2 A9 a* I% -------------------------------                                                                 
7 d$ @; L, f7 ?( f% Filter Structure    : Direct-Form I, Second-Order Sections                                      
+ O6 n1 _+ h" u' S" F) R7 l% Number of Sections  : 2                                                                         # s; @& w; z3 A  F7 s
% Stable              : Yes                                                                       
: H: P8 R% r+ U9 g% _  O  F% Linear Phase        : No                                                                        - g. x! U& a6 x

" Q. D0 g% |$ C# h0 k  J
* e. J: y% [) O8 J* ]- [SOS Matrix:                                                                                       
$ X8 |" c8 ^8 e8 m1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
0 u' B7 i. y/ Q& ]0 b1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
' D! x/ Q# X8 s1 h/ ]2 d  g* p2 c9 D% |" u+ q8 r
Scale Values:                                                                                     . l- |1 J- f0 X) \5 _
0.632277093897992026327870007662568241358                                                         6 ^( f3 [5 m& f9 m7 H
0.478258337927073562401147910350118763745                                                                                                                / `. A* G: G$ Y
由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:' U, ]& n3 @+ L# D7 \
# X% A) |  m3 s+ M
SOS Matrix:                                                  
  ?# T8 u4 X0 }6 A, O2 t% g1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        
3 h# I8 y. H# u4 ?b0  b1  b2  a0          a1                                                   a2* ~+ m! H' _' L4 I9 t0 _- ?
1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        9 }, e5 N$ l* {  C+ ?! o' n$ {
b0  b1  b2  a0        a1                                                     a2
$ t4 m5 m$ |9 g# n注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:8 {) u; Z$ w7 ^2 {& m' Z* Q3 P
6 ^  Y% K4 m- p4 |  E# m
0.632277093897992026327870007662568241358                                                         : j3 \9 Q- M! B; w. p
0.478258337927073562401147910350118763745  
/ Q, Z3 w! \) p4 F1 ?实际的滤波系数调用方法,看下面的例子即可。2 W9 x1 ^3 Q* Y% b. S( I

/ f$ p  S  |+ n7 p1 N" g45.5 IIR高通滤波器设计
" o6 Y" j1 [' i* E( g$ m5 l
本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器% c  @2 @& C' V- s1 r% U

3 |4 l* T0 g! l45.5.1 函数arm_biquad_cascade_df1_init_f32
* H& ?9 a4 C* Z1 O0 I( ?函数原型:
$ ]; Y9 D4 e' B/ k& r
% z2 r# l3 z' h' \. h- C; u
  1. void arm_biquad_cascade_df1_init_f32(
    4 }* m3 O" i" e) H5 {0 z1 R
  2.         arm_biquad_casd_df1_inst_f32 * S,
    . a3 n7 b0 L' x) V
  3.         uint8_t numStages,; G6 d, ]# }2 a3 X
  4.   const float32_t * pCoeffs,- H7 X4 l) s9 {7 L$ |/ S
  5.         float32_t * pState)
复制代码

- @" ]1 X2 e! i! o: v/ |/ T1 ?函数描述:" E2 ]2 r8 |9 p  |; @5 d

: `: W; _- ]9 \+ h3 r这个函数用于IIR初始化。3 `" b+ |( m/ }' i
6 R' e# {$ u& T% ]
函数参数:( y4 l; x( j% \' Z8 C

* l" I$ v  _, g( ~  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
% F' D% P# y+ h. ?$ B7 e  第2个参数是2阶滤波器的个数。
. f6 {3 g$ j+ Y# H  第3个参数是滤波器系数地址。- d5 W$ t; O6 C, L: h! S
  第4个参数是缓冲状态地址。" h. d# c5 ?+ C+ F. r2 k
注意事项:
2 N4 k# I3 F8 n1 C  N. J5 u( N0 h) M  k3 q5 D: ~
结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):9 ~* E, b  J9 e8 D# l3 @3 q

- L! k* S5 `5 {. s, R* s
  1. typedef struct( @% Y; F. L, Z$ W" }, c, i
  2. {
    % M  h" f: _* r5 N$ s5 P1 v$ B) y
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */' \0 O% h' i$ f5 H% p% n
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */
    0 C" ]1 S0 S. D. P* c- D
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */
      c! s! }" R/ _; r0 w+ t
  6. } arm_biquad_casd_df1_inst_f32;
复制代码
9 Y. c( J' B; K# b0 X& k7 ]# S" v
numStages表示二阶滤波器的个数,总阶数是2*numStages。& d+ M: a+ y4 U& m' N
pState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。
$ i. K( I. G* n. \# @" W. O5 j参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:& ^4 L+ i$ U: [1 x1 m
{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}
3 h; \: ?  W! Y, Q9 W4 c0 J. r, ?: \, x
先放第一个二阶Biquad系数,然后放第二个,以此类推。
$ o, O# H9 j: n( U. B% G) S# M3 M" O  t# ^! I) a7 I
45.5.2 函数arm_biquad_cascade_df1_f32
9 x3 |. S3 y9 m+ `' X函数定义如下:
9 E( W$ e# Z' h( b8 P9 [% v' Z/ W3 l5 K
  1. void arm_biquad_cascade_df1_f32(  [' u* X& W4 x
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    % ~! i3 Q% X3 e! l: y
  3.       float32_t * pSrc,/ ^* L8 D! t$ G* j  l
  4.       float32_t * pDst,
    5 X8 j! q* `8 L5 }6 x7 q
  5.       uint32_t blockSize)
复制代码

% i6 Y! @+ n, ^  {5 j/ E5 O6 L函数描述:
+ U" h% Y1 e2 x% n& k. P+ r5 F  K& E
这个函数用于IIR滤波。* r3 X: |3 `, j6 T7 S- s+ b* M
( \& `- Y7 i: ~+ z) Z8 I4 P
函数参数:
: b8 Y& m7 K4 R4 f- `5 I
( x! m6 X; z1 @" f# ]  Y  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。! G- }" f3 s+ H8 N/ K" C7 ^. d
  第2个参数是源数据地址。
/ q7 M4 Q9 b8 N; P& q0 W  第3个参数是滤波后的数据地址。# [1 t1 C+ s" P  L6 p5 V" L
  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。0 q: q' R5 r" ^( a" W1 l
45.5.3 filterDesigner获取高通滤波器系数! g/ @, ?; j2 A
设计一个如下的例子:
# y! F. w$ `# F& ]8 U3 k0 s1 }8 s! a
信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:
; e4 Q" r7 D# Q! b3 V) \, c3 }$ H. F- j& V% D: }/ A
7f8be339ea66c602e1b7ea4836e58544.png

4 m& w) J1 a( H+ ]
# T7 i5 `$ |. m, e7 g/ u配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。
( ]% K3 J8 k2 a5 S$ h' ^0 @
7 j. p* v' H* d9 w$ [45.5.4 高通滤波器实现
7 Y: w1 M* _% y/ c: F通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。; H& \5 U3 E. m- c

. A! S+ c$ P! H+ r6 O0 v
  1. #define numStages  2                /* 2阶IIR滤波的个数 */
    ; k2 p# @6 p# e  W0 }, C/ B
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */8 G5 k6 `: P8 w& d3 @, |$ n
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */* C9 Y% Q  u$ N  j$ \5 u6 e
  4. / ^; T2 p# ]) K
  5. / O& _3 J& ~& Y' [! K' c) V
  6. uint32_t blockSize = BLOCK_SIZE;
    , W1 Q4 v* Q. j* g
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */
    - C4 B& Z& X3 d9 s3 s

  8. 3 Y; f/ c+ ^4 i4 C: E( Z# J) X
  9. $ {- Q: u; w7 m, y9 x) T( ~
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */4 O1 E/ `, S& @4 b9 f1 X! Y+ ?6 g
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */
    & A; y1 c4 E& Q9 a
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
    4 b" `3 D5 v  @" _$ p% u/ P4 k

  13. $ t  U' y9 r, R  l5 t& d1 z
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         
    * u/ G/ R, C" F' j  v
  15. const float32_t IIRCoeffs32HP[5*numStages] = {- v5 j9 _2 F: N! F# r+ E) B
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   6 M+ \* g$ ]+ l7 w. C) @
  17. -0.544565360850816415627662081533344462514f,     3 I( j4 o+ p+ `& j3 J
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  & f+ z7 G2 Z6 c( G
  19. -0.168318873843973093595849377379636280239                              
    ' B- J9 K: |5 b: M( d
  20. };                                               7 x# W' s2 @1 f
  21. 1 C* Q; d7 e: }  L0 M% X
  22. /*: x9 d( W2 Y+ v& H* ^' N
  23. *********************************************************************************************************5 q& N) S7 \- l( @
  24. *    函 数 名: arm_iir_f32_hp
    & g+ P6 \% Q  d/ r1 U, S' w! X+ m9 e5 s
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器
    . j1 ^, ^7 v+ O! Y8 v3 W" K
  26. *    形    参:无
    $ |# l( u9 [. n( E# g  C
  27. *    返 回 值: 无7 D/ }$ t8 c2 C3 M
  28. *********************************************************************************************************  Z  X2 U/ [' W% p( H: T# U8 C
  29. */
    ! \5 i0 V+ f& o( V) E- B5 N
  30. static void arm_iir_f32_hp(void)* Q5 J- H; n* `9 Q/ z9 |
  31. {
    4 N1 A' l4 \) ~; w7 K/ J0 @; b  V
  32.     uint32_t i;, s. d" T. I/ u7 `
  33.     arm_biquad_casd_df1_inst_f32 S;
    , p4 y, Y9 x/ b* R5 m) y. x" v& B
  34.     float32_t ScaleValue;
    + k. A: `4 x- e
  35.     float32_t  *inputF32, *outputF32;
    * h+ t  W1 d& Z1 I
  36. , L" p) F4 I; a, I- s# b  o# z
  37.     /* 初始化输入输出缓存指针 */5 E( S8 S7 K0 r! i7 C  F0 p
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];* \  y2 J3 M3 y: [" s4 K
  39.     outputF32 = &testOutput[0];
    9 \* W" I6 [$ f: d( ^. T

  40. . ?* ]; [3 T8 X0 a) q) d
  41. 8 E1 X& z$ n% O) I
  42.     /* 初始化 */
      x' j- ?- n/ d; l; ?
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0],
    ) I+ q! z/ a6 R/ W3 ]+ h7 d# U
  44. (float32_t *)&IIRStateF32[0]);
    + T( V8 x6 l( z; w

  45. 4 a% j, m6 i/ y/ P8 K, S" A
  46. 2 m7 d0 J" B# l' Y3 ~7 w
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    5 }5 x4 L1 k7 S
  48.     for(i=0; i < numBlocks; i++)0 ^; ]+ d. ?0 @+ ^3 ^
  49.     {
    , s) J* i0 w6 Q2 L+ e
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),) g* p! C* q' W% F3 a
  51.   blockSize);
    2 a& g. z0 d# ^( o, O& }( C' K; h
  52.     }
    : ]( t2 B6 Z; b0 C% H7 |
  53. 2 y9 I) C$ v$ A& S& f
  54.     /*放缩系数 */
    7 H5 a, C/ r2 D' w1 ]( K# Q( j0 a
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f; : k% N3 l3 h% }+ Y5 O

  56. $ ?0 r' o0 ~5 Y! u/ ?; q
  57.     /* 打印滤波后结果 */6 z& J% r( Q; }! ^0 |0 s
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    2 a4 \8 W) J0 P0 u" P
  59.     {& ^1 n9 W' C: _: q
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);
    5 i  W; q' C* A  c% D1 {; m
  61.     }
    ! u/ v  Q% l* e
  62. }
复制代码

! r( t6 |0 x: H) Z) X# W* \! Q运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。
% A5 E0 L* O# I. _3 D
! s" Z3 }+ ^4 s对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:! }# c/ S7 L. M; T9 ~" a" C

* u# f* T) `7 L+ k; j
  1. fs=1000;             %设置采样频率 1K
    : T; G6 |0 R' J
  2. N=400;               %采样点数      
    : p5 _6 N; x9 ?
  3. n=0:N-1;; @, ]7 O) N. `) B* H8 I* X5 E
  4. t=n/fs;                %时间序列3 m( `# D" r1 D% U+ Y$ V2 G. `
  5. f=n*fs/N;              %频率序列
    * {" @* D) [7 I) A

  6. 9 Z6 o  G, [  {$ H
  7. x1=sin(2*pi*50*t);4 I: B4 ?8 S) e2 e; J5 w
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波
    6 _, o6 v. D' h. F) f7 S" }' C
  9. subplot(211);2 A  v) o( P% ^9 F! v; s8 {3 D# \- K
  10. plot(t, x2);/ c9 q* g6 Y; w8 t3 [9 t7 g
  11. title('滤波后的理想波形');
    3 b" Q9 M+ v2 s
  12. grid on;
    ' P- I3 K7 v! k+ y
  13. 3 k8 o2 r' ^; E8 H- n& o* z' a$ e' `
  14. subplot(212);5 G& s- P* Q9 u5 A2 @( G
  15. plot(t, sampledata);
    * u8 q0 d1 `6 o! o8 k
  16. title('ARM官方库滤波后的波形');
    8 v2 e" _( Z0 D/ M+ [2 I8 H
  17. grid on;
复制代码
5 s2 H9 B5 G! P0 G* d- J; l
Matlab计算结果如下:# K; y5 A- o1 Y& A  ]$ o4 z% M

3 [' p! ?& e) d9 v& }+ N6 j
91111ef8530bb9e40d9736ccc2c12424.png

+ `. x! J# J- o
! W) V" H3 U; _; a从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:4 \4 E$ @+ r7 ~2 s, D
  X. Q" ]! r# R) h( I
  1. fs=1000;               %设置采样频率 1K
    7 _" b+ \7 ?! l
  2. N=400;                 %采样点数      $ ^; ]8 V- O7 \! G% B
  3. n=0:N-1;6 W- R- U% h" B9 {. U& f& a% O
  4. t=n/fs;                 %时间序列6 B/ X# `: @' j
  5. f=n*fs/N;               %频率序列7 ]+ p: b! i/ m: l' m8 z- _
  6. 7 H* H  s( r+ i) i. ^: @) E
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成0 a3 v: v4 ^+ r, \9 }* Q  ]
  8. . t* L$ |, O8 f, M+ T
  9. subplot(211);" ]. Y8 U% I7 C2 _
  10. y=fft(x, N);                %对信号x做FFT   
    * D9 h% h8 x! f/ ~
  11. plot(f,abs(y));
    ' V1 T0 s; ^' R9 J+ t6 I1 _
  12. xlabel('频率/Hz');. s( y" e2 V' D
  13. ylabel('振幅');
    9 Y# d6 G0 T$ r7 d( L# E' a* U
  14. title('原始信号FFT');
    4 h8 Y0 G* y  @' U) N# v
  15. grid on;9 c5 p7 Q$ f, v

  16. " W9 j% G; Z1 k! C6 j
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT
    / F; J7 ?* j, P" ^
  18. subplot(212);                              
    1 h, v! C/ r+ }% g9 l
  19. plot(f,abs(y3));+ w: O) {( B" E( W, D
  20. xlabel('频率/Hz');
    ; t2 n# g" ?# M1 s
  21. ylabel('振幅');; k- H1 J4 J: w8 ~& j' X
  22. title('IIR滤波后信号FFT');
    - C7 e1 q6 b, x+ k
  23. grid on;
复制代码
+ ]1 `+ }2 D/ z0 c. v
Matlab计算结果如下:
9 Z; z; W2 k+ q. o$ _8 j
- A; u; c2 ?8 \, X
d13d47932e1b37bd5046b7ad31415509.png

& ?( s8 P" ?( i/ z, A6 S! A+ g3 z  R* w' z' F
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。* I6 A. @7 D9 \/ ]" e0 v
% z, {' V4 x7 l9 W
45.6 实验例程说明(MDK)0 ^$ v: ?1 A# W) C+ K
配套例子:
* Y3 T) N7 m, x: x" |. w4 O4 nV7-230_IIR高通滤波器(支持逐点实时滤波)5 q+ g# F- p9 U* M! S; k# B3 K7 |

2 y/ j3 e9 j4 Q3 {. c实验目的:' b; j7 M/ [" o  b' X' H' v  P
学习IIR高通滤波器的实现,支持实时滤波8 O  _7 l2 P5 [: E

: w5 B9 D, G) v4 C3 e. H实验内容:
) }4 L3 A7 \( G" k' r2 o4 I! }启动一个自动重装软件定时器,每100ms翻转一次LED2。, r1 R6 n9 ]1 ?. x$ z/ T9 _
按下按键K1,打印原始波形数据和滤波后的波形数据。
: ^5 l1 x* B: p: B/ n5 \+ c: y+ a* N/ }! M
使用AC6注意事项
. @1 n, e5 K6 M2 d( k/ Z4 a特别注意附件章节C的问题* z" q6 u5 }' D. o# [
* m/ a$ c: E$ x6 ?% r* x0 Z
上电后串口打印的信息:
7 m5 M6 ^' C) N4 _
  g5 e. B# y( T4 s波特率 115200,数据位 8,奇偶校验位无,停止位 1。" X, \2 J, |4 I: U" O# j
$ J% @) ]3 o0 R# W- O9 v
262f2df9123b012595369835f600f93d.png

' E/ F1 ]0 p, [+ O& _0 p, N* ~% g( C( e6 s4 T9 [
RTT方式打印信息:
+ E+ }0 O- M" ~2 E* H5 Q" y4 f" N# V* W/ f$ f+ |- e; Q
75dd602d2d2eb24a5dea611cc45ce934.png

3 a0 N" a2 M5 K: [+ L" l$ w& s9 v; b/ e2 B4 w/ D; k  a
程序设计:
& P7 _# q1 _0 c1 F0 t4 |. L- y9 q: A2 D" F4 h& m
  系统栈大小分配:) S& e7 b1 q6 w+ p- s$ Z

/ Q+ h4 H. b* v& a) Q
640570198f69835d8ac9eb982ccf39f6.png
# L; Z# u2 Q6 r& e0 ~" L" V

4 ]/ R2 F, g6 f7 _" [( D- L: g- x  RAM空间用的DTCM:' b# `2 r( l7 L' I. w8 g
) @4 i7 N( u/ k2 N
6efb3697225eacc369e069ab80d69ed2.png
1 L$ L# V" U! @
, v3 z( {- V0 l0 d, I5 H0 s7 m* A) R
  硬件外设初始化- D# Z0 Q  K8 }3 Z
; h- {# J, Q) D7 l1 {
硬件外设的初始化是在 bsp.c 文件实现:
5 N, U4 |/ P) V) x! k$ k1 U( q
& O/ `7 y" R- b6 q4 ~, U
  1. /*/ ]2 o& F6 X' f) f* q" i
  2. *********************************************************************************************************
    7 R; o$ }1 `/ s% z1 Y1 s1 K) [; M1 R
  3. *    函 数 名: bsp_Init  @+ V7 q% w9 C7 i# [6 T
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次# ]9 E6 X, w% h6 X" K) o7 [
  5. *    形    参:无3 c. y2 @: }( }5 q
  6. *    返 回 值: 无
    ! D- p7 N+ R3 a: q% M6 f
  7. *********************************************************************************************************
    8 n: W5 D1 u2 {) K8 @, {2 o
  8. */
    : u0 }- q0 c- ~- @
  9. void bsp_Init(void)3 T4 }# R( Y8 C
  10. {' ^& r7 d: j) L! C5 k3 C
  11.     /* 配置MPU */
    " L8 \3 Q, ~/ m# z+ X
  12.     MPU_Config();
    5 R8 u8 O" Q# X( @  o3 I

  13. * _2 o$ v. Y, p
  14.     /* 使能L1 Cache */2 `2 L. S# ]) k/ J7 S8 T) l
  15.     CPU_CACHE_Enable();
    & c% m+ w3 O2 |' c2 C
  16. 9 W+ K! x6 o+ V( v
  17.     /*
    4 ~. X6 j& J5 P' `. L
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    2 S* e" t0 H$ y7 d
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    ; z+ q* c5 |5 _4 z2 T
  20.        - 设置NVIC优先级分组为4。1 s" g9 h. ~. Q* J$ e
  21.      */6 K1 h4 v4 _% q; P6 y, y: I
  22.     HAL_Init();1 G6 G* |8 T3 h, V6 n' o7 f' C

  23. ! q/ L3 z9 O# B# F
  24.     /* 3 e$ F$ h$ o6 u/ y
  25.        配置系统时钟到400MHz
    ) g1 y! X& g7 m4 g8 ~) G& x
  26.        - 切换使用HSE。  r' |0 u; b. R; \: d2 K8 H+ b! w
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。% ?9 f- |0 z' O" c
  28.     */
    2 V" R# F  W# _$ v5 G: i
  29.     SystemClock_Config();
      f2 z! H: o* B5 |! `" i

  30. - ^" I+ ^0 }) a+ j; x3 F4 d. s% ?
  31.     /* ) q1 E" N) ]2 y  ]7 T, [
  32.        Event Recorder:6 q% |- J( N/ f& S' S* z$ m
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。% k( Q+ s/ e. o) n3 A! l+ _
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章# |7 y$ I, J3 J. n3 C6 a
  35.     */    9 w1 S# b0 {' |$ K6 n
  36. #if Enable_EventRecorder == 1  ' [' m: Q- U- O, h: J! @" n
  37.     /* 初始化EventRecorder并开启 */
    # k" {; q0 k3 \
  38.     EventRecorderInitialize(EventRecordAll, 1U);) J0 H: g. b% h& X5 U% h
  39.     EventRecorderStart();
    0 ]2 Q$ Q) `3 d+ k
  40. #endif( m7 a. c# r, @# j! _
  41. * i2 I* e8 ^+ z& q9 Y* ~2 ~
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */* @" x+ U2 N5 ?$ V
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
    " Q+ h* b3 K* {" g- j
  44.     bsp_InitUart();    /* 初始化串口 */
    & c5 z! y; O) c5 a/ P1 c4 L1 l
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    7 b/ K2 o+ v2 P5 X' a# g" }
  46.     bsp_InitLed();        /* 初始化LED */    + i5 A5 g5 A- M
  47. }
复制代码
0 K+ i2 e1 m) \7 ^$ a) Q
  MPU配置和Cache配置:" @, J  j& s4 @% N+ D/ }

, }5 _+ w! O4 k. W5 m' [数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
# L6 B' M# A, K0 y
$ u0 l8 M; i* ^! D, X4 J& G% @# e! p
  1. /*
    . |$ K6 l; z: A' x4 g; f
  2. *********************************************************************************************************
    . i$ ?1 O" \# k3 H+ C, Y+ `
  3. *    函 数 名: MPU_Config$ |( r% [0 s* x& h
  4. *    功能说明: 配置MPU
    1 \1 E- @# N' C
  5. *    形    参: 无# j! ~) q/ M3 ^2 I" s! |2 @3 M
  6. *    返 回 值: 无
    9 o6 G; B% d$ m( o" H3 f  k- `
  7. *********************************************************************************************************# p. ?/ K- @, J4 @0 |6 B: @& k1 l
  8. */
    2 Z# J% O. m8 T; Z- {
  9. static void MPU_Config( void )
    4 d7 c) K+ B. I* I6 ]
  10. {; B! J: F7 ~3 U" Z, a
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    / s2 W0 H7 C8 K9 q! ^

  12. 6 P8 w' K! T( i& S
  13.     /* 禁止 MPU */' X1 Z& t5 q' w1 a% H5 ?
  14.     HAL_MPU_Disable();/ K& e6 G9 Q( w$ i
  15. & _  V9 q, |4 l4 d2 W) }2 j
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
    9 T( @5 a0 \5 _8 F9 |
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;3 W1 x3 Q% P# ^/ m' C
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    ' t1 O0 c+ B4 p( q) w2 [
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;9 d  x- t$ P/ x; P" _
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    3 S2 F3 ]2 ~0 L
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;1 r. G# v) T3 @) Z
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;
    3 g* E6 K: i# E- o
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;" N" ?" _! ?8 ^# T2 P% A
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    ' z" X$ k0 C3 D3 t% b5 R7 I% z# w
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    4 U# m- \8 S( c% f3 J, ?5 r0 w6 l
  26.     MPU_InitStruct.SubRegionDisable = 0x00;# T6 K( s9 B% v+ o. l  `- }
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    ) K3 ^$ l* A' ^
  28. ) U/ Q4 m' L: u' x* t7 s
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);: J, W" N( p8 @3 g  l% D/ B
  30. . H% x7 f  W+ ]" T

  31. 7 f: f( Y2 L/ \
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */4 d" }6 E0 M8 @  f; G
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;3 C. U/ r4 H( s' P+ u* F/ ]
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;0 g8 t+ Q2 A5 L1 Y- p% k
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    / `4 f4 M6 }/ S
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    " o  ~+ i  j2 r0 M
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    ' P5 r3 A& A6 V$ Z0 e3 F7 D
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      U% n" w3 }& @* @; A& Y5 Y" ~
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    , _1 V' \; {/ ~1 R! }0 b
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;* b7 }( x) y* b& E7 _0 i' w( l
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    4 f8 Z2 j! r3 D0 n
  42.     MPU_InitStruct.SubRegionDisable = 0x00;9 K3 x1 j6 X. y' F: n# ?
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    0 A" S1 X- w/ o. H

  44. 8 r& e" `, i: p
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    / n3 }2 C$ y3 `3 P) `1 P

  46. ' s- [& a# P2 ~, P' T0 Z
  47.     /*使能 MPU */
    " e' e* d* R% w
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);2 K: y) X3 d, D& g
  49. }
    - L8 [7 f" J* U8 \0 z2 s6 e& c

  50. # ^5 s8 Z( [+ q# f6 v. X  M
  51. /*
    + I* q# m% |* _# r6 e# V
  52. *********************************************************************************************************
    ) r3 A6 R$ r6 n
  53. *    函 数 名: CPU_CACHE_Enable
    ( ~$ @$ }7 G& M' a+ C
  54. *    功能说明: 使能L1 Cache4 m& Z5 u8 c' e7 s7 {
  55. *    形    参: 无
    5 ~3 R# R: D# v/ j+ Y4 R
  56. *    返 回 值: 无
    & G4 c% {$ D# P
  57. *********************************************************************************************************
    , H( G' \# k0 L. H: H" |4 m
  58. */
    . a, P0 [, T4 A3 P
  59. static void CPU_CACHE_Enable(void)
    . P6 N1 T' F: D
  60. {9 p: r0 o0 X0 u5 E
  61.     /* 使能 I-Cache */9 T4 U4 G! M9 ^$ A, W
  62.     SCB_EnableICache();8 g1 c; X9 h8 S5 d' c  r8 \

  63. " q4 j3 x$ A/ Z2 ]
  64.     /* 使能 D-Cache */
    ; R0 w6 d' {1 L) k" ~0 B
  65.     SCB_EnableDCache();
    * v( W) g2 I) f) m5 H8 S' `6 ~
  66. }
复制代码

1 H9 q$ ^- o& [$ k& O9 c+ M# f  主功能:
6 L. Z* V8 L+ O: t1 k- \1 M( @  A" }- G) o! Y+ O# ^4 h
主程序实现如下操作:
1 [- e& i: q" h4 Q4 N% C1 U4 g0 M8 U* i0 g# q  n9 B! d
  启动一个自动重装软件定时器,每100ms翻转一次LED2。3 x- f; A- i9 [: q. ?+ m
  按下按键K1,打印原始波形数据和滤波后的波形数据。
% d0 \. o6 M4 |+ p- y/ I, H
  1. /*, T& i/ q; Y) g2 p
  2. *********************************************************************************************************' H0 X$ F3 k$ i/ W! q
  3. *    函 数 名: main5 o  h; g5 u% v: f* U" t' N7 o
  4. *    功能说明: c程序入口" m; ^7 g2 g0 h0 P: W
  5. *    形    参: 无' }* f$ I4 ^/ y7 L. p. N
  6. *    返 回 值: 错误代码(无需处理)  x9 }& m" Z$ m. I9 _
  7. *********************************************************************************************************4 c2 z5 |7 B5 I7 k6 C. z% ?" e0 l  @
  8. */$ L( C5 n( P  Y
  9. int main(void)" Q; Z1 |* D, c% x: }4 ]% ^
  10. {
    7 j- ?. B; i$ l. f" m0 w
  11.     uint8_t ucKeyCode;        /* 按键代码 *// O( r- F* w" H# l4 `
  12.     uint16_t i;3 S) \& K! _6 ~4 m) b) W% h; r: o

  13. " u! Y2 {: h4 d4 g7 |7 \; K
  14. 8 m7 s& F& S2 _
  15.     bsp_Init();        /* 硬件初始化 */
    8 P' B# o9 W+ S3 U$ B
  16.     PrintfLogo();    /* 打印例程信息到串口1 */- e, [9 s0 [  L% J; D
  17. " H9 v9 i0 B7 m7 e6 q' i& a
  18.     PrintfHelp();    /* 打印操作提示信息 */7 _- s- e/ q# t( |4 g- z6 {0 T% I

  19. . H3 K* n3 f1 ^1 ]
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    7 D, e+ }% [7 G9 B6 o1 \0 T7 u
  21.     {# k" V! i8 N6 D7 o; [
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
      t* j6 B6 o6 S. z
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) + 4 M& ^! s# }/ s% F1 A6 B) M5 V3 ~
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    ; X/ f# G, v' ~
  25.     }
    & G3 I, G6 M* T9 z$ m( s5 n$ T3 ]- E

  26. ) c  |9 I' P( \% b

  27. 7 U, u8 R/ p- N+ L
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    ' k1 c# v% n3 r  {

  29. 4 h; ]: i  Q7 o9 l3 @( B
  30.     /* 进入主程序循环体 */, F7 W" G  U+ I
  31.     while (1)# W9 O0 {* w' }( J8 w3 v* A% u) x
  32.     {+ @5 x( V7 y( r& A0 v. n3 w
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */$ c1 y4 c- p% f
  34. ' H& Y% {, G3 u& l: U# f
  35. & K& ?+ Z4 U7 h9 i
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */6 D( Z- D" }" [
  37.         {
    2 B9 y3 P) c5 x" \9 L
  38.             /* 每隔100ms 进来一次 */
    6 x$ g1 m! G; ^% X8 X' N+ l; m; m
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */  Q7 k* }3 Q6 S1 l7 [/ X
  40.         }
    7 ^* T6 c' A' F; g$ K* p( c) z
  41. 0 x. f+ J+ p  F$ v! f, y6 d! _
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */5 J, Y2 ?4 Y4 f1 C. _1 p6 z
  43.         if (ucKeyCode != KEY_NONE)
    9 e$ }0 A# [! N$ G( W+ n
  44.         {
    % H& N% K& Y+ S* ^6 t! C3 _  u
  45.             switch (ucKeyCode)7 d# n; k" ^7 x  e, k
  46.             {
    & l3 d+ j! I! i% Z& ~' |1 R: N
  47.                 case KEY_DOWN_K1:            /* K1键按下 */! h7 r; J/ r: [3 U
  48.                     arm_iir_f32_hp();
    ) c; v7 ~& G- l. S% |( h# Z- e/ ^
  49.                     break;
    ( F) {) o% T, H( u' r* w0 y. u2 x! X

  50. 5 k+ U) v3 b( ^7 y9 h
  51. ; T1 H% i6 g0 K" E
  52.                 default:0 V- ]' X# s4 O9 y
  53.                     /* 其它的键值不处理 */
    3 f  Z; e/ i* u7 L2 Q/ w& d
  54.                     break;1 B7 g! G6 z1 Z5 J- u% O
  55.             }
    ; M* s2 i; ?" c. Y! E
  56.         }
    * K+ K! C' u: U
  57. ; {  @# M  W$ P0 T; m, b
  58.     }
      ~  L( A# j" c" \: p6 y' O2 {
  59. }</span></span>
复制代码

6 V8 G" |& Y1 t6 p8 n0 E45.7 实验例程说明(IAR)! G8 k- g2 _6 U# _& G
配套例子:; C8 K. d9 `  t& d+ ]: p: S
V7-230_IIR高通滤波器(支持逐点实时滤波)
9 B% b+ C3 Q% Z& d! @" W& `& `% z; F- O5 G$ {; J0 z: t
实验目的:
# a$ `1 o3 E2 K  w" Y! Q学习IIR高通滤波器的实现,支持实时滤波
7 R2 {4 T% b9 D4 F. A
( X3 ]$ U$ ?* @+ N$ S* G& s: ?2 a
实验内容:
7 W# ]9 f' U; [/ u启动一个自动重装软件定时器,每100ms翻转一次LED2。
  i# B" L4 E0 x7 o) v按下按键K1,打印原始波形数据和滤波后的波形数据。

3 Y+ |' x% ^. N: x* ]: D% g
  s3 S% s( L& l/ q! @/ {上电后串口打印的信息:
; [% Q2 Q! A' \% c4 `
6 H- ^9 G* c# c2 D波特率 115200,数据位 8,奇偶校验位无,停止位 1。
0 N& N) Z  o) _7 O/ V, q3 d: L5 s( e4 I
a66c11d15f6f8dd3c49ad9b587ab3a90.png

2 ]% E' J0 Y& b8 Y% n; u$ T# v6 U1 ]( P% w2 i
RTT方式打印信息:
: d7 @! I0 [. m3 `* w0 ~, B2 x, u
7 Q& R5 y+ p9 j  w* ]6 s
5b42e93b5c59803fdc45c18e564e61f1.png

6 p" @: g0 c* P0 |( d3 F0 w9 N' V3 ^# |& B# v# h" J. {
程序设计:3 O2 Z4 M% K7 A4 R3 m
/ i4 b0 N. v+ i
  系统栈大小分配:
5 T- _2 ?, y8 d. P8 |2 o, n
! ?% y. _& Q4 W
a48a46f1265dd3824bc690f874e90613.png
/ w% ]' J* @& `3 E( L

6 k+ E2 h/ h* `& c& q  RAM空间用的DTCM:
1 L& T3 z) z. u8 Y2 I% i, ^3 [+ N6 |% |
025eeb1d94bcb86c7bec22ed1e822f36.png

; F% O* s+ l0 ]  c: ]
# o% o: t4 t& d! e3 C, {+ A9 ]  硬件外设初始化
: A2 `1 v$ b2 n
+ P$ e! @# x( h0 Y2 I+ o8 D硬件外设的初始化是在 bsp.c 文件实现:9 g3 T6 B2 [! b* b- a
7 ^0 I" ^0 Y  d# F; M! K. W; v
  1. /*
    1 L7 R- J' W- k
  2. *********************************************************************************************************) M: t4 o1 j( c! R/ e" s9 x. X/ [
  3. *    函 数 名: bsp_Init
    ' L! i# s8 I' ]- X$ S5 _2 U
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    7 {( j  J- A+ B
  5. *    形    参:无
    - \6 t) Y% {5 k9 K
  6. *    返 回 值: 无/ o8 \& U% p, R* {7 d$ _: w
  7. *********************************************************************************************************% _/ v8 a8 S) ^/ V: O1 [3 R: k, a
  8. */
    8 P4 P3 K5 Z/ v+ d7 z
  9. void bsp_Init(void)
    , Q% o9 J6 ?5 m" r/ D1 L
  10. {0 V8 l' l. ?0 z0 l
  11.     /* 配置MPU */
    2 {. d+ {' Z( N! [
  12.     MPU_Config();9 q/ D0 @& s5 a" B$ h% w  _
  13. 4 p' o9 t* i" q: B
  14.     /* 使能L1 Cache */
    / u% i/ u% h8 F0 J: r$ A
  15.     CPU_CACHE_Enable();
    ; j, _$ L9 j+ i! i/ m4 U0 g

  16. ( X  f7 h! \; s$ T
  17.     /*
    4 D/ L: l  `- i
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:3 t# ^$ v- A* n
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    4 n$ y5 q6 `2 ]
  20.        - 设置NVIC优先级分组为4。
    $ t1 n$ F2 l( G4 J) m6 p* j
  21.      */2 Y3 ?/ m3 n+ g& Z$ Z. ^
  22.     HAL_Init();1 I; n9 @8 F9 [; [% W7 T
  23. , h6 O) q& k  P9 u! t& H, P
  24.     /*
    $ L; I& U3 Q8 ~! z# X* S$ [" h
  25.        配置系统时钟到400MHz
    4 }2 S7 c0 h/ ^1 }
  26.        - 切换使用HSE。
    % i! F) s  G" R% |0 m) x
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。+ i. K1 U7 A4 q# O0 G& j7 B  j
  28.     */: H5 }; ]. A) R0 E) z
  29.     SystemClock_Config();
    % q6 E  L/ M8 v6 E/ }7 K" R' A

  30. / E7 Y/ s! Y$ w/ x
  31.     /*
    0 m) n2 j- g# Z7 ]/ Y& u- r% Q
  32.        Event Recorder:
    . h. m& i1 g7 s9 ~8 r6 P6 Z4 J, P
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    # R/ {$ W! J4 A& D8 i7 c
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章7 _+ X6 [& r( n. x2 J& I7 Q9 n. l
  35.     */   
    ( Y; @3 j$ G1 I5 \
  36. #if Enable_EventRecorder == 1  8 B: f7 s+ L# o0 f+ x! G3 z
  37.     /* 初始化EventRecorder并开启 */
    6 M% v- t' R) N( B
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    . k: X+ N2 K1 Y. i
  39.     EventRecorderStart();) w( W5 K$ f# y
  40. #endif  ]/ A% M* G/ ^0 G( ?1 l

  41. 0 `* j' [1 J+ }9 s4 Z1 g
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    0 y9 ~- B0 v4 u/ F' r: w
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
    * i, x( p6 Z( e6 G
  44.     bsp_InitUart();    /* 初始化串口 */, f2 k: e3 s  x  m8 ]' J) F
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    4 Z9 B; a) u# o8 h7 h& r
  46.     bsp_InitLed();        /* 初始化LED */    * \# K6 y( `" z& I. |- \! P
  47. }
复制代码
- W8 N" [) v2 R0 U( ^# N2 A
  MPU配置和Cache配置:
( v% ~7 o4 p; O9 T" S0 I9 b
; L1 j7 n7 U4 g- V& y" Y数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。5 P; T: A' ^! y

" d. w8 Z4 k+ {. B0 A1 L3 U& `$ L' N' H
  1. /*9 E# ]. M: b2 M5 B
  2. *********************************************************************************************************
    ) d0 [% W8 n$ V8 l, o2 T8 I8 ~$ j' {" r5 N
  3. *    函 数 名: MPU_Config/ \% |! ]' `0 h7 t* O
  4. *    功能说明: 配置MPU1 k5 j' ~) u2 i4 n' h( K5 |; x
  5. *    形    参: 无. i# b% r0 V$ h$ a3 y, w
  6. *    返 回 值: 无
    ) l2 `" K6 G. Q6 d. K6 U" K8 T# y/ a( u
  7. *********************************************************************************************************& F; w7 [, b8 |0 m( ?
  8. */8 e/ a0 x9 }1 M+ V+ z% A& ?% b
  9. static void MPU_Config( void )
    & W- ?5 o: E: u& U/ u- c
  10. {
    + H$ d' O% l0 f" l
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    2 \: }$ p1 l# J7 O/ e# B

  12. ) ]6 {+ B0 m$ c- U$ C
  13.     /* 禁止 MPU */
    " Q+ z6 p7 F- X) f
  14.     HAL_MPU_Disable();
    ) d8 A& X0 N" [& V1 P$ r

  15. $ [; f9 F8 l3 E4 ~# b  w
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    - C% G2 B6 z9 E1 M" K
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    / a/ y1 a4 j1 y8 `; u
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    - p) N0 |2 K0 o* o# H: z
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;: q9 p$ |- ?* T2 `% y) t
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    8 N+ v$ I8 i! P* T0 j* ~
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;% d* [, }7 a" M+ q; A
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;. a3 J- d  L* o' j& U4 }, k
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    5 Z0 P5 ~' y+ n
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;5 k" _9 q# g5 w& n) ]+ k
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    9 O& n+ F5 Z" k: N+ h6 |# R: k
  26.     MPU_InitStruct.SubRegionDisable = 0x00;$ O; T0 E# u, o( r9 S+ Q7 ]% N
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    8 p" d5 U$ ^. B1 l' i5 I3 X

  28. 7 w* z. C2 R& T% m
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    ' E  d. f' p" p5 s+ ?1 {8 u

  30. , B' P' U. r5 ]+ e' W6 A( C
  31. ! B& Q2 v% w$ r* q; j1 }
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */- H4 M$ `* y6 X# {! n
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;5 e5 W+ d5 [: T& n0 K
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    # w) }1 t$ }3 @- C3 U; h2 W
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    4 R' X2 i' ^" @2 G% Z  o; N
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;; `2 y; i+ d: N
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;, P8 A- M" T6 M( D, T
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    6 M9 J. `7 }/ A, X& z( ~
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;7 G: R; o$ B! C5 t4 r- g% q
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;$ c% q0 Q5 E$ c6 N: G$ l
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    / E/ A) W5 Q. x4 }  @! f
  42.     MPU_InitStruct.SubRegionDisable = 0x00;% L5 o" j& T# E- R: B+ y
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    + A" ?; D* q+ ?' a6 U% Y# L* x

  44. 5 H( d& G' ~( p2 T" Q
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    : |) Y- j& F1 ?  w; K, J: @' |  T

  46. 4 v0 t/ f0 v- n" F& \" x, b  x) Y
  47.     /*使能 MPU */
    : P  V) a. C6 @' [  N# `! g
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);9 O) R+ v, r' Q
  49. }( k5 h5 T  [% F3 }+ J6 d

  50. % M4 t* a0 p' A7 \/ Z9 Y! u
  51. /*% m. }1 Q3 Z1 e3 P
  52. *********************************************************************************************************) m$ S  q& X9 i3 h1 J  }" i/ |
  53. *    函 数 名: CPU_CACHE_Enable
    1 p  g4 _- r3 ^/ d) a
  54. *    功能说明: 使能L1 Cache2 _( \7 F9 W: \, w, H5 t3 H
  55. *    形    参: 无' Q/ U2 r" W' c* o2 k
  56. *    返 回 值: 无9 z5 z5 \" S7 T
  57. *********************************************************************************************************' t6 j8 M! Q* L5 ~* I7 `
  58. */! Z+ v, M6 Q% Q/ b! L( E
  59. static void CPU_CACHE_Enable(void)
    2 Q$ o7 ]( I9 F' \; P5 ~7 E2 H
  60. {
    . M7 d5 L( M- l9 k2 B/ [3 y
  61.     /* 使能 I-Cache */' O* j, {5 r7 o/ G0 {
  62.     SCB_EnableICache();
    - D- m- m$ j1 Z2 t$ P4 K$ Y
  63. $ f+ v8 d4 @8 M  s* p- k+ H
  64.     /* 使能 D-Cache */
    , g/ S; a' S' g
  65.     SCB_EnableDCache();
    - I2 f9 W5 f1 o4 P2 R* N# I- \  A
  66. }
复制代码

9 x6 s1 k) Z8 ^1 A- ]  G  主功能:
9 D# Y! Z5 V: E7 k
) D& K2 Q& r# h主程序实现如下操作:+ u6 }: |5 e0 G& p& E& Y

- f9 {; i* d0 z3 [* q: k8 X9 {3 L2 J  启动一个自动重装软件定时器,每100ms翻转一次LED2。1 y) p4 Z8 ?# |
  按下按键K1,打印原始波形数据和滤波后的波形数据。
% c: H7 t5 i" B5 [9 ?( q. z7 ^% X
  1. /*, i4 m* `: e! [7 G! r6 z. X, d
  2. ********************************************************************************************************** i5 g. L5 H, A; w
  3. *    函 数 名: main
    7 g' y8 O3 _  I) T/ v
  4. *    功能说明: c程序入口5 i% L# S  m, G
  5. *    形    参: 无/ l5 `9 Z3 y4 J9 V+ ?0 e( D+ V8 ^
  6. *    返 回 值: 错误代码(无需处理)
    3 `4 X. {  \! {. H! k" z" c
  7. *********************************************************************************************************5 d* K6 r1 T1 Z7 c
  8. */% x2 Q% Y% G+ L5 j; N1 ^
  9. int main(void)
    & W8 N1 |: N6 g  ?) `* j
  10. {
    ! B! n" J( x8 l* h4 L9 y% ?# g
  11.     uint8_t ucKeyCode;        /* 按键代码 */8 A2 L9 f9 B3 L1 v& A
  12.     uint16_t i;
    # i9 D2 o, B) W0 K+ e9 o3 J

  13. 7 ^) C7 ^4 C. l; Y- G" ~( R2 V
  14. 7 b1 E7 g2 @5 o, E2 O
  15.     bsp_Init();        /* 硬件初始化 */
    : ]# m; H' W* i4 i
  16.     PrintfLogo();    /* 打印例程信息到串口1 */. y# \6 d; t8 q) m  q

  17. 2 i. d! Y. Y; k6 F' J
  18.     PrintfHelp();    /* 打印操作提示信息 */
    , a* r6 n$ E3 _# S! L

  19. 7 `* {$ b% B# ?: {0 X' }
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    & c0 L6 ]# c  T1 i. C
  21.     {2 j, y* L  b, C
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
    : ^, h5 w6 N$ N# y7 D5 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) + " t1 x7 b& Y# Q3 b! C
  24. arm_sin_f32(2*3.1415926f*200*i/1000);# {5 X8 J9 T% `) W0 b, V
  25.     }
    % ]2 v7 t; f# L& |
  26. , S, c1 c" V$ p6 P& l1 ?

  27. 2 k1 f7 y, U: S) W* _6 a% a3 h
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
      ]- b6 k. o: v2 M$ ~1 W

  29. 3 [2 N/ F% ~( R
  30.     /* 进入主程序循环体 */" k, R/ D9 i9 w' A, O
  31.     while (1)
    ( K: c3 N$ B, v; _3 D
  32.     {
      [5 @' a7 W6 d8 N% Q% g3 F1 z! Q
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    0 ~- f4 Z+ X% m" W  C- }

  34. * j* D% F: L7 @

  35.   x: \4 l, ^" L, f
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */% z  N+ K9 F# q/ w+ R
  37.         {9 p  B& c$ u3 s+ w2 M5 f4 }
  38.             /* 每隔100ms 进来一次 */% _4 X+ D* v) O* a1 F$ d2 A# F- H
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */
    " |' i& t# J$ m; v  W1 f8 Q
  40.         }6 ?: Z8 ^9 I6 b

  41. : ^# f9 ~$ \, A0 W3 n5 s
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */3 f+ S* k, X. O, L6 X( p- d6 ]* t! Y
  43.         if (ucKeyCode != KEY_NONE)$ u  F1 c, o/ l" e. D$ ?6 @$ D; {# n
  44.         {# K7 _8 y0 k- `# y/ W+ F0 r" S
  45.             switch (ucKeyCode)( V0 s1 j3 }/ W% ^. q) ^
  46.             {, D' D7 W/ p2 K
  47.                 case KEY_DOWN_K1:            /* K1键按下 */1 b/ K* Y; v( E9 k  ]# z
  48.                     arm_iir_f32_hp();% J2 n2 X6 o0 M
  49.                     break;$ g% C9 @# v6 I; T* j; y

  50. $ a" L# r2 R$ ^7 w( b. C8 n7 }
  51. ! s/ c: R; h$ ]3 p8 F) i
  52.                 default:
    * D* p$ e/ P- Y; L7 p
  53.                     /* 其它的键值不处理 */
    ' x3 Z% [/ S: a& f" e4 U
  54.                     break;) Y: N2 i" S& q6 [
  55.             }
      {8 J% X8 t5 f0 r$ Y$ F6 ^
  56.         }$ _* K0 c6 @$ x+ B

  57. ) l+ s8 l% R1 ~+ v% _
  58.     }
    " N. @; z+ ~9 d3 X6 V. ]1 D
  59. }</span></span>
复制代码
45.8 总结
' t, J, C0 T, q$ n4 p) w. N9 q7 E本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
: z& e+ m7 @3 ]* g' ?& j' N, j- W, E5 Z# C

7 h9 G/ J: F2 B0 H5 H( c  g
3 Z) i5 D; b" T/ h
收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

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