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

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

[复制链接]
STMCU小助手 发布时间:2021-12-31 18:00
45.1 初学者重要提示6 ~* [" X5 I9 u, }& H1 K, R! D
  1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:+ }1 `4 F0 ^! _$ w& y, w' u7 [
/ U# P, Z) z8 L8 u; ~; Z+ k: d
  所有数据是在同一个采样率下依次采集的数据。+ I9 L! Y8 F. c' z2 T1 _. D7 L
  每次过滤数据个数一旦固定下来,运行中不可再修改。
- s% Y! W- ^( Y. r/ q  2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。
/ z, j* X- v  X" j3 @9 V+ _$ k/ Z8 I; I+ ~& Q# D  ?. G
45.2 高通滤波器介绍
8 q# T. a$ S. d; }) [. V$ c% n  P允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。
9 t1 D7 ]8 E& z% f$ q: ]% Q2 z0 z# h9 J6 N0 s9 Y
c1e7dbbd579e8c1e535c9ce0678c3532.png

/ j! G* K$ k/ Q4 ^3 u* m" V: i8 h5 Q, n
45.3 IIR滤波器介绍
. g' v& P% ^. |& BARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。
3 A# a0 Q+ Q; s  Y- b( q
7 N' x8 x8 y5 X3 B# K% Q- I直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:: k% \! H: z' ~. m3 F
, W7 O( i, w9 T
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
6 C* @6 z# E' @" E
) u; V0 @3 U0 D2 v6 l直接I型算法每个阶段需要5个系数和4个状态变量。  C( w( d  @9 A. D+ W3 ^) ^
% y1 l5 a. I* B. {: O
7afa71129081e82218f8d501da73448e.png

: {3 y0 ^0 {3 [. a% F' Z# g, Y
" ?% ~1 w* a2 I6 v; b这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:2 |- g4 b% G- `  A8 K( g

" }6 m) y4 Z6 F6 iy[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]4 U' T# @7 P6 b4 \2 P
4 j8 ^. J# Q  }6 [3 ?9 \- i  Q
比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。
+ w# ]" x. y; x" N; S$ C% K- D
7 e& c+ V2 z4 f4 O2 Q& |; ]& V高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。
/ S) Y) U( ?/ w/ ^% J( n. _: {
9 S; O, y, F* {/ ^% ]5 ^& O
75ec77cc1b971aa24bf16e169c67d8aa.png
- m$ e3 W5 m. t4 g% ?+ J) n* i

  K$ C- ]9 E) Q( D! @/ E2 ]8 V9 H如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。' v. r; N5 h5 f" y0 W5 S
" x, c1 G* I6 q$ H% [3 y3 y
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数3 D) [+ u! o) K: X9 h" ?
前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
6 D+ J( i4 H' S& @; B" w
) W5 i# Z2 k5 {7 h下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:
8 H/ I' Y2 j. @  w9 {' x4 t; k" l
4e2868a42a0de4b63d0a13d64952e470.png
, [% |, X& {; O. z9 |% e5 k# A( `

3 T3 z3 a8 w, _! PfilterDesigner界面打开效果如下:
: y! }+ \& ]# x0 s) k& B
( N# x& u# o* j3 h3 o! B# c
b9bee9bb0a4ada395e69793b38c566fa.png

  p6 B# s$ P! Y- s# {3 I* V4 ]* F6 p* w6 y* G
IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:3 m' k% w% R) r7 B2 s
" }; l) N" {3 U0 L8 j! ~( j
84c4d0373d7c29786a6620c1050ee40f.png
# d6 _" V8 D6 r5 E# q
; L0 A) }( _" ?: W/ }
点击Design Filter之后,注意左上角生成的滤波器结构:, n8 B8 A& U% x* N& S' i
+ A3 p* B/ R2 U1 J) y; K$ A
e51daea41c797cdfd4a5c1857cc35eff.png

! q- Y# `& h, F% o' |$ \7 e+ ?5 Z- X
默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。- F5 h4 h4 t1 O/ u% Q

6 n" T) _; ]5 X. t$ P" u& H  k% J6 X" ~0 {转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:
' ~0 y8 l( M4 p% I1 B- m
. R/ }  T! Z( O( N
0328d6421fbe59b9299acd1c889e3dff.png

3 b5 F4 Y; f' H3 w* `7 q5 p9 P1 L( a; f( j, O& m* N8 M
转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):
2 [/ M+ r: L( V3 W$ T9 A4 k& t9 \8 o/ K( k4 p
232d594c793c269063eb16a62ce71fff.png
8 ^+ t; p1 W* L; |1 g+ Z3 \! s3 `
# H5 f3 |" A! n5 \
第一项选择好以后,第二项选择Decimal:* x! B" B9 Z, w6 u7 y9 R

. G- D1 b3 |( R7 A3 h
31265a3570a3c4ff16c53050672e0e99.png
, ^5 E4 ?$ F) r

$ a  [1 |. V* `9 J. U两个选项都选择好以后,点击Export进行导出,导出后保存即可:& F7 i% ?2 K. b/ ^8 K) g, A
% z) b! h; n( z3 ?/ A) F2 h6 `
a5e27f83f7223e9a95c29a47d1c79238.png

7 Y! d9 B: |. M) t3 V' C( R
; X! E0 R6 Q; ]保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:' T3 ^' A) u3 m! y7 Y% N

2 C8 Q& C- W  N6 k/ |1 k; U% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0." W* G; a0 F% o2 I* F- n# |( r7 R
% Generated on: 15-Aug-2021 20:38:337 d2 w, i. b+ x5 A' B

9 c2 a) H% Q8 y4 U2 s) J( }% Coefficient Format: Decimal. H/ _8 H2 R. i- [3 Q# y
6 @0 L- I2 ^5 W& T# p3 M
% Discrete-Time IIR Filter (real)                                                                 
( ~& A: N# {% e7 h/ @% -------------------------------                                                                 4 }# n$ v# ~/ W1 \3 u* \
% Filter Structure    : Direct-Form I, Second-Order Sections                                      ) C0 ^. m/ i2 y; \4 d( {
% Number of Sections  : 2                                                                        
9 x* G- ]0 x# S) m% ~% Stable              : Yes                                                                       
, k1 D! K$ c* c% Linear Phase        : No                                                                        ' t2 n! o' v, ^( {
6 I6 U) q7 w% q9 ]$ l5 I' s/ V

+ W( v& Q$ h* k: k* C8 iSOS Matrix:                                                                                       7 p2 \) }* W1 `. A
1  -2  1  1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514
1 b" b% o$ {5 r( b+ x1  -2  1  1  -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239
$ o. K, N/ S7 x* K% [6 l6 X# V1 p& \% ]' k
Scale Values:                                                                                    
1 O+ ^8 @' b% k0.632277093897992026327870007662568241358                                                         
7 z: E6 s2 N0 R6 C0.478258337927073562401147910350118763745                                                                                                               
! t6 D% c& G4 R, ~( h由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:! q% X4 ]# e9 b  e1 ~3 _

& X) A1 N' J& Z3 L+ {& [SOS Matrix:                                                  , S& a+ ]9 n  z& k8 _& K
1   2   1   1  -0.98454301474115180070612041163258254528   0.544565360850816415627662081533344462514        / t" F$ r/ Y6 y- C
b0  b1  b2  a0          a1                                                   a2
4 E  u- L+ {0 d# {; g5 E1    2   1   1   -0.744714477864321211519893495278665795922  0.168318873843973093595849377379636280239        
) X  @; a+ m4 T! A9 H7 Ub0  b1  b2  a0        a1                                                     a2
4 j& e# y5 u0 o0 S, N( l% i( l8 ^注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:
' h# y7 v4 ?( J4 f
( g- ?0 r$ H3 Z( l- i# \7 T0.632277093897992026327870007662568241358                                                         ! n! r2 h: K" b  c
0.478258337927073562401147910350118763745  8 K; F$ H# }2 v
实际的滤波系数调用方法,看下面的例子即可。9 g9 u; r( ?. R% i( w
; D* L  l. B! r* K* c
45.5 IIR高通滤波器设计

7 g$ @& F& [* q( k本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器
- O* ~; D0 h& N: `' ?5 P, n' k& t4 C. @/ Z
45.5.1 函数arm_biquad_cascade_df1_init_f326 F+ U% Y! g. z4 ~6 r/ d
函数原型:
9 n9 X: f- Z* z& j7 s$ }
; Y" _6 i: n1 h7 {3 D1 N$ Y
  1. void arm_biquad_cascade_df1_init_f32($ a# e6 ~+ {% }2 P& F1 }
  2.         arm_biquad_casd_df1_inst_f32 * S,# P6 B4 w' P2 @( W
  3.         uint8_t numStages,7 j9 f+ A: O3 ?1 W3 Z6 H7 O, g' o
  4.   const float32_t * pCoeffs,
    . p0 O& W# A8 U8 _
  5.         float32_t * pState)
复制代码

$ ]5 |. f, M! k* n. s6 l函数描述:
" z" s' v' q: Z2 n9 d8 `$ m; x& l5 D+ d0 r
这个函数用于IIR初始化。* O$ A3 V- B6 X+ y

$ z1 U+ |" T- K7 A6 @/ n1 j函数参数:" @2 I8 h7 \& r% B) \  `' j
- u* Z( Z9 S3 x$ q' b. [. t8 X
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。" |. H+ n4 e2 l9 I' R* |% M2 K$ w  X
  第2个参数是2阶滤波器的个数。
# {4 R* N; a: x  z" O  第3个参数是滤波器系数地址。
! O! h$ k+ }1 H: C  I! @  第4个参数是缓冲状态地址。9 C" v8 H/ `! S. u- m- }7 v
注意事项:( K. _+ P& f/ |
$ q. L! j( O! M8 k) d2 K% E
结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):
; k6 b9 K8 [( |: V& u- D' h" W) d# p' h
  1. typedef struct  x( T, l: |, {, E# X
  2. {
      M" B$ J7 I# @% X
  3.   uint32_t numStages;      /**< number of 2nd order stages in the filter.  Overall order is 2*numStages. */, U7 Y+ `$ w1 `5 v
  4. float32_t *pState; /**< Points to the array of state coefficients.  The array is of length 4*numStages. */
    , b7 R/ z5 T" {  e$ k  Y) a0 G
  5. const float32_t *pCoeffs; /**< Points to the array of coefficients.  The array is of length 5*numStages */
    ; W' P! N( X4 Y" C8 A# ?( v
  6. } arm_biquad_casd_df1_inst_f32;
复制代码

; j. n5 T9 g! l% snumStages表示二阶滤波器的个数,总阶数是2*numStages。+ Z) K  |7 ^. [# W2 Z
pState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。' b$ q& e$ E; ~) Z4 Q( O8 R
参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:: O) a1 m! t! |0 y& U
{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}8 n* P8 J" d/ Z$ T
3 m9 ^1 u& e, w: w3 ^% [" k
先放第一个二阶Biquad系数,然后放第二个,以此类推。
4 v* _7 I& O, R$ _) \0 Z; X
* p( X) e- o/ d' T% n5 Z0 M45.5.2 函数arm_biquad_cascade_df1_f32
5 x: [, t) B7 |& y; A& y1 [8 ^函数定义如下:% x* d% P' R: z

1 X3 I2 w' E: g0 A
  1. void arm_biquad_cascade_df1_f32(
    7 \+ _. Q1 @. K3 g7 l( e: C
  2.       const arm_biquad_casd_df1_inst_f32 * S,
    5 g. H& j; Y3 {
  3.       float32_t * pSrc,
    4 N2 F/ j& n2 k8 p, S; ]
  4.       float32_t * pDst,# o! _' D/ S/ N% x) r$ J0 t
  5.       uint32_t blockSize)
复制代码
0 }0 |. {# n+ J, |7 _
函数描述:/ y/ d4 |2 E( F* O  p6 ~5 u& F

: q& c3 w- ]8 N这个函数用于IIR滤波。
5 D+ s/ V- x6 U! h
" D* |( N/ F, s- y) n7 Z2 b* t函数参数:
% n# W* A2 n) h4 A. z: U+ p' v& i2 q0 N  a
  第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
* U% U/ u) x6 g! i4 S  第2个参数是源数据地址。
9 W' {9 p! S  q  第3个参数是滤波后的数据地址。
0 C' D1 h' Y7 A  第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。
! Q* I8 ~9 v5 o3 u* Q* e& P45.5.3 filterDesigner获取高通滤波器系数
2 w  U. c6 d9 p/ i5 j$ A8 s4 J设计一个如下的例子:5 R7 Z* M/ \* X7 i5 o9 X  M/ k
! Y8 q6 a! [! P8 U' [
信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:5 S" J/ F& ~9 t, m( h. x

8 z- `  V4 D3 ~. Z5 o
7f8be339ea66c602e1b7ea4836e58544.png

$ V) X# m" [+ Z9 f' d' O
2 b! b' O" ~9 f1 F0 r# D配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。
" B+ N9 U0 r' U/ p2 J" u% e; I6 E" Q% [% r. J
45.5.4 高通滤波器实现- t2 \. d. l$ P
通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。( m, Q% }' Z( C9 w( [$ E
+ W. |% h- F' U, w
  1. #define numStages  2                /* 2阶IIR滤波的个数 */
    5 c$ \7 O! H4 M. R6 D+ e
  2. #define TEST_LENGTH_SAMPLES  400    /* 采样点数 */
    : Y+ T$ K/ f( v
  3. #define BLOCK_SIZE           1      /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */
    6 X) S0 O  e: E7 o

  4. 9 x: h, k) Z& R9 o$ ?  F  q: V
  5. ' k9 S. I+ @4 v
  6. uint32_t blockSize = BLOCK_SIZE;
    3 \- l3 l; Z- c, p7 g
  7. uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;         /* 需要调用arm_biquad_cascade_df1_f32的次数 */
    , h3 C: {+ ~0 V: A. ?& a

  8. - u+ O; Q9 [& |
  9. " _, {4 S. h$ K$ e9 d/ G0 D
  10. static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */) b& X* ]1 n- s1 v6 U$ ?# X/ q
  11. static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 滤波后的输出 */
    1 B3 z/ `$ T' t6 w$ G; k6 Z
  12. static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */' e4 `+ m+ Q2 ]' }8 b! z
  13. ( s, x6 z! ]  f  Y3 Z
  14. /* 巴特沃斯高通滤波器系数 140Hz */                                                                                                                                         * Y4 e3 l0 l# [# p8 [, J2 h( w
  15. const float32_t IIRCoeffs32HP[5*numStages] = {+ j1 q7 r; l$ p6 O$ R1 H2 R4 q  m
  16.     1.0f,  -2.0f,  1.0f,  0.98454301474115180070612041163258254528f,   
    ) O. g& x! ]1 G8 t; ?
  17. -0.544565360850816415627662081533344462514f,     
    ! m" K! q- o$ X5 v0 P- U4 ?
  18.     1.0f,  -2.0f,  1.0f,  0.744714477864321211519893495278665795922f,  # U* Q, N* U, L! u" J, m8 g
  19. -0.168318873843973093595849377379636280239                              + F( J0 O6 k2 C! t' g
  20. };                                               
    & M' W6 W' ~& h8 C5 ~

  21. 5 ]# f7 \/ D2 U: i1 x
  22. /*
    + `! i4 B& O3 L9 E( p
  23. *********************************************************************************************************: P8 p& h* E* f5 D. s( B9 }
  24. *    函 数 名: arm_iir_f32_hp1 P: J: a. U: |
  25. *    功能说明: 调用函数arm_iir_f32_hp实现高通滤波器2 }: p: t/ o; D& Y9 w
  26. *    形    参:无
    * C' o8 i! [1 V% v5 R/ C3 v+ X
  27. *    返 回 值: 无
    1 v' l# M/ c) K0 M: F2 E
  28. *********************************************************************************************************( }% [2 n* o2 m9 x: W
  29. */
    5 b: b+ l( [) w6 k  s
  30. static void arm_iir_f32_hp(void)+ w+ w4 l+ M& ]
  31. {8 \, V8 z  Q( l# f  P/ t6 V
  32.     uint32_t i;
    1 \# |- U- v  q
  33.     arm_biquad_casd_df1_inst_f32 S;1 w# }7 }5 n" ]; @0 T
  34.     float32_t ScaleValue;8 g/ p; U2 L' U) y# A- \
  35.     float32_t  *inputF32, *outputF32;" B0 X  ?6 e( ?4 R

  36. 9 g6 J# M. R$ T  D. Q. j
  37.     /* 初始化输入输出缓存指针 */
    0 P% S/ M: K! N0 y4 v
  38.     inputF32 = &testInput_f32_50Hz_200Hz[0];
    9 g, M: V: F2 e) r2 t
  39.     outputF32 = &testOutput[0];% b9 I. h' }% x- u# z, \- {+ M

  40. 6 ?. n  o, t: C& u
  41. 2 a9 F' M' R1 ]
  42.     /* 初始化 */
    7 D" t! j! n; C9 F3 Y( H( z' e
  43.     arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0],
    / R7 h8 C4 N1 h# \$ Q
  44. (float32_t *)&IIRStateF32[0]);$ G! O5 `3 o' s$ G

  45. 1 h0 l4 i# u  t  q6 n9 e

  46. 5 D- J1 B5 x# I7 M
  47.     /* 实现IIR滤波,这里每次处理1个点 */
    9 q; J$ s, J4 F( w; ^
  48.     for(i=0; i < numBlocks; i++)
    2 s- e0 `5 ]8 `, n4 w# ?# p
  49.     {
    5 ]" Q0 {( E+ P, X4 p
  50.         arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),
    - j, D6 R* U; l: ~: U
  51.   blockSize);
    8 `* M2 w  Y! r) n$ w
  52.     }' N9 b# Z+ x8 o7 y% a) S
  53. ; {9 X4 Z  q' X& k
  54.     /*放缩系数 */
    2 g3 X; Y( b7 y& D
  55.     ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f; , M' K9 _+ C* O; o
  56. 0 q7 T8 O& Z* z
  57.     /* 打印滤波后结果 */
    - o( F# l' j. G8 j# H5 X8 ?8 t
  58.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    5 Q& S0 {+ o3 b$ }" @9 [( u) i
  59.     {6 L6 x& |( q# X+ p. U
  60.         printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);
    , R1 l( r- |9 u  S# ?' W% V7 H- r
  61.     }
    : Q3 A, x9 z& n5 r6 N6 H: c: d6 G6 N
  62. }
复制代码
. C0 r1 R5 t4 ]7 w4 r- W  S& F
运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。* N) M/ {2 K0 c+ d

% w. H$ w; j1 y- E" B4 a; l对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:6 Z, {7 E! t1 X! T5 y6 A3 }$ A

! C* k0 z9 Z; g
  1. fs=1000;             %设置采样频率 1K
    9 \6 L5 {! Y- K9 [3 k, a
  2. N=400;               %采样点数      ' e5 N& {: N0 y
  3. n=0:N-1;
    / ~& E; I9 X. J/ n5 P
  4. t=n/fs;                %时间序列
    + `5 i1 \$ l: F% b
  5. f=n*fs/N;              %频率序列, o* Q, K% b* w: G0 z9 V
  6. 8 ]9 g$ S/ B2 j6 h5 X
  7. x1=sin(2*pi*50*t);" @9 r; I/ P7 C
  8. x2=sin(2*pi*200*t);     %50Hz和200Hz正弦波, O& u$ v: a5 S5 _4 B
  9. subplot(211);! C% _. [. i4 @$ [1 Y& }2 n6 p" _' T
  10. plot(t, x2);; e) O: E; ~3 i- Q4 Q- f
  11. title('滤波后的理想波形');
    : K! }! v3 a' T+ ?# S* q* J! p
  12. grid on;
    ' }2 L2 u- q( `3 d
  13. 5 F; s4 I* y/ Q: ^: n8 A: G9 Q
  14. subplot(212);9 c9 B2 v0 W; T# K+ z4 \3 ^+ F
  15. plot(t, sampledata);
    4 C9 i  t0 e0 B
  16. title('ARM官方库滤波后的波形');7 x; @$ |  B+ k5 U
  17. grid on;
复制代码
, Z* g. U/ z3 x) b8 J8 W
Matlab计算结果如下:0 t! `% R- Q6 S! k4 A- [) ]4 d

7 e8 ^% p3 u( |+ \* x! R
91111ef8530bb9e40d9736ccc2c12424.png

1 {) N* M1 N7 A# q0 C4 W! C7 Q3 @) @( z- ?( I, p
从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:
: G' O3 R- S* r3 I& X, h  P/ R2 ~7 T% U. g% S
  1. fs=1000;               %设置采样频率 1K
    6 T6 S+ i1 o9 \# c2 O8 r# [
  2. N=400;                 %采样点数      
    , c. _1 V8 K2 e5 y
  3. n=0:N-1;$ X/ ]: P6 U% X' L* ?! C
  4. t=n/fs;                 %时间序列
    ! G7 a  u# \6 L9 w& ]2 V
  5. f=n*fs/N;               %频率序列
    0 z% g" P' L9 w" g& n; L
  6. ) `& n: n7 b& A8 D+ w
  7. x = sin(2*pi*50*t) + sin(2*pi*200*t);      %50Hz和200Hz正弦波合成; h) H, g" S4 r7 x& F0 h
  8. $ b4 _5 o5 L' h5 `4 G' V
  9. subplot(211);3 U. L# P8 Z- y
  10. y=fft(x, N);                %对信号x做FFT   9 V& m, ]+ Y  |
  11. plot(f,abs(y));# I) m# W) h7 c4 u6 w9 R
  12. xlabel('频率/Hz');7 e1 M: A7 V  q2 k& Z  P& A0 n
  13. ylabel('振幅');9 g! R, ?  G" T$ h! f8 d
  14. title('原始信号FFT');
    9 c, n* X  h) j  O
  15. grid on;
    ! U8 Z0 V5 I9 f- H" q+ C% F
  16. " K/ N1 M: }: I# u( Z" t( Q
  17. y3=fft(sampledata, N);    %经过IIR滤波器后得到的信号做FFT
    4 I9 `5 I8 w7 ]9 Z6 B
  18. subplot(212);                              
    # T: N' [2 X, T3 o- i8 s% H
  19. plot(f,abs(y3));, a( E5 \' }+ e, H; a% S
  20. xlabel('频率/Hz');
    + x# T! v2 n' L; G. B
  21. ylabel('振幅');
    - x3 }* Y3 G# F9 ~) b' q3 E+ ]
  22. title('IIR滤波后信号FFT');5 T% Z5 e) u+ S
  23. grid on;
复制代码

0 c( W8 j7 L5 E, }Matlab计算结果如下:
# \( {6 b- ^( ~+ B
  R  f$ p4 m: [4 c/ D& {
d13d47932e1b37bd5046b7ad31415509.png
5 W3 b. c' [/ z1 o  O; Q
5 ]+ q$ l! U3 B2 d; Z" V
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。
5 l0 I0 b/ a, V; a! z2 z# K
  \' E3 l7 M1 v* ]5 G/ t9 E45.6 实验例程说明(MDK), Q% S( H; k. T3 U+ y% K( F
配套例子:& C9 n- n+ L1 u6 u- I0 X7 r
V7-230_IIR高通滤波器(支持逐点实时滤波)
' E( r! U6 j; `% y% c) n5 L
. L: c4 m' l& x$ h9 ?% w! c实验目的:6 _/ g% @8 C* l% l# X& o
学习IIR高通滤波器的实现,支持实时滤波
+ Y' A+ Y6 B9 `: l+ S! g! O; a
- {, v9 o$ H+ `7 i1 k实验内容:
1 @+ H( M) \# B! \启动一个自动重装软件定时器,每100ms翻转一次LED2。
8 A8 Q/ ?( \$ C2 x按下按键K1,打印原始波形数据和滤波后的波形数据。
) Q' i# \4 P4 X* `7 l1 z1 n5 M" H3 p
使用AC6注意事项
9 |- u& J% _, ?- z+ x4 z9 _特别注意附件章节C的问题
3 B$ S  e# i& }2 U, r' g/ A* @  F  e( h  {  _
上电后串口打印的信息:9 Z3 e* ~- d* J! A  ~9 Z; Y

1 B0 E. a6 j* h  u4 j2 I波特率 115200,数据位 8,奇偶校验位无,停止位 1。# q7 v5 `  i6 X( ^

+ W9 h$ }, Z1 W. ~" q
262f2df9123b012595369835f600f93d.png

( {! q- q# f" b2 }% z. l2 d
' k2 x" t3 H8 t! I* o, X4 O# H1 Z3 TRTT方式打印信息:7 ~7 B' k; C; I0 a6 R( i; z4 `

9 }6 b/ e1 R5 o7 g
75dd602d2d2eb24a5dea611cc45ce934.png
) f# P  V, b9 r1 |: H; y
$ J3 E; A+ q- b3 R3 w
程序设计:3 k2 g3 \: m6 L) O

$ ~4 R1 {; ?2 D  系统栈大小分配:* ?: K1 }  d& A3 a' l; R2 `* P

# R/ L6 E- L4 ?. Y+ v3 ~- r. [
640570198f69835d8ac9eb982ccf39f6.png
' B3 U2 W( W' n+ D, H7 k4 Z
% p: t# X- `1 }! m
  RAM空间用的DTCM:* {" Y2 I; ?& `! j( ^& f8 y

* l: `+ ]4 ~7 X0 b, w
6efb3697225eacc369e069ab80d69ed2.png
+ d( n( f8 X! P( u

2 F+ }+ _! r! C" d. Y: q$ f* z4 V  硬件外设初始化! ?! S* n( V( K3 [! e0 ^  `5 `6 _9 Q. u5 }
$ m- J) k' `6 n" w) y+ A/ c; _
硬件外设的初始化是在 bsp.c 文件实现:; z: x) ~9 k' u( s1 d
1 n: a$ n/ h$ c# ]6 |
  1. /*
    ) v. d" @0 E$ D- v
  2. *********************************************************************************************************9 _* s# a0 K  s% _7 L; `3 u6 V  W/ }
  3. *    函 数 名: bsp_Init
    8 T3 B  m3 h( T2 W! k& W6 E
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    + G- u/ S9 G& ]% h
  5. *    形    参:无
    - c$ L* E: O/ J9 o. o/ h0 I2 [1 b: ~
  6. *    返 回 值: 无
    " i) @9 V2 X- W4 D' z, F) P% o2 J+ O
  7. *********************************************************************************************************& \; y' ]7 o; ^+ i
  8. */
    1 R& L* B' D: T$ [; R
  9. void bsp_Init(void)# P. R. ~, {* I( k, [
  10. {3 o- w! S4 _0 D, D, A2 R! ^! P
  11.     /* 配置MPU */
    0 {& E# b8 r9 p" Y$ Z) @
  12.     MPU_Config();7 D" c* @/ M) Q6 _* M4 w; f0 ]
  13. 0 p0 r. [3 ?' `5 D3 |, o3 \" ]
  14.     /* 使能L1 Cache */% ^. p, v. g5 l: ]) t
  15.     CPU_CACHE_Enable();
    & e! P+ r* b/ l# r. u1 |7 w$ t+ c

  16. ; w/ I* L1 d3 Q; `: s9 Z* b
  17.     /*
    0 H( U8 P+ Q8 h
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:- n0 J3 w0 C6 k) Y
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。/ m: w  g' R/ ?. W8 `% d# I3 E
  20.        - 设置NVIC优先级分组为4。
    / J2 X1 s( z; K" f( U' ^2 Q4 `
  21.      */
    . C; V8 s3 `! |" Y) w% d
  22.     HAL_Init();
    4 u1 k1 s8 [$ x$ w/ D8 n

  23. . r+ {5 P1 V4 N8 C3 [
  24.     /*
    5 |6 B6 W; R1 U0 V) K0 d4 d. w2 \
  25.        配置系统时钟到400MHz, w4 k7 w( P8 I  l
  26.        - 切换使用HSE。: g5 Q2 R) r. ?9 t8 }5 w# I
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    : ^/ m  F( {: }2 ^
  28.     */6 {- P5 \% ?7 p2 J* f
  29.     SystemClock_Config();
    6 v  s. h- Q! x# I
  30. 1 m* v4 `/ \# Z) {9 {& l! O
  31.     /*
    1 c2 `* y4 B  }! S) R
  32.        Event Recorder:
    " X- c9 N: [1 }  k" q: _6 _; ]
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。9 y7 V2 ^6 B7 I+ M) W' m
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    $ a0 k/ U( {! x2 n/ c# \
  35.     */    ; x2 W* q6 C: T8 v, u  w! i6 w
  36. #if Enable_EventRecorder == 1  8 L; f, d+ S! N/ ^  ?5 [
  37.     /* 初始化EventRecorder并开启 */
    3 v) a: T  W& a( G. ?
  38.     EventRecorderInitialize(EventRecordAll, 1U);7 f! S# Y9 c1 z- w7 v! K
  39.     EventRecorderStart();% b- f. i/ \' {
  40. #endif8 P9 |% z3 G( I) ^1 f4 k/ Z

  41. * h4 ~; |) r0 c: p
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    1 S) u0 j  H5 @! l8 B
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */' W- q0 p7 x5 ^/ s
  44.     bsp_InitUart();    /* 初始化串口 */- N# ]2 t1 L/ n' K/ S/ U9 e
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    0 |) c( S$ G' q' U8 j8 E+ E
  46.     bsp_InitLed();        /* 初始化LED */    4 H* N" U9 C: J! |7 E; H3 f
  47. }
复制代码

0 X: N' a2 l, D& S  MPU配置和Cache配置:
& V1 q& c0 y% `- Q: U( S/ v( g9 i5 C
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
) s% F: N# Q# G1 s" C* Q5 S- L2 @8 _/ ^+ ]: b% m
  1. /*% ~- S. ]- w# e4 V; J; p# C0 `, L3 H
  2. *********************************************************************************************************# w4 V' w. u( h) L3 q7 a6 H: v
  3. *    函 数 名: MPU_Config; K1 u& v) s. d8 D9 f
  4. *    功能说明: 配置MPU* e, |; d, c( }0 E) W: ]* p! ?) n+ u" f
  5. *    形    参: 无, m9 h/ _7 N: K$ i. Y! Z4 g  g
  6. *    返 回 值: 无
    ; y, H, o/ L# o3 G0 P
  7. *********************************************************************************************************/ D$ \* C' e' ]6 ]% d3 O+ O6 A7 U
  8. */9 `$ w( S3 ]: x# `
  9. static void MPU_Config( void )
    ) n( n6 L0 x. q5 B2 E1 E
  10. {
    " E( g, P3 a# x+ J: z' @% Z7 Y
  11.     MPU_Region_InitTypeDef MPU_InitStruct;8 g3 \; e; i) f; [

  12. # R2 d2 g) a" u8 C- W
  13.     /* 禁止 MPU */
    2 {3 i. u/ V1 U; }0 I0 c3 w. W' B
  14.     HAL_MPU_Disable();# d; g0 ^- C) V$ U

  15. 2 b8 d* m. G7 r& {  y& s
  16.     /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */- D) `+ G, e: [; U6 J9 A$ s& w
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    # @& K  T* x# s$ X
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;' v. H& k7 G4 E9 R
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;. Y4 t* A5 o3 }: t
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    6 m# D3 ^, I9 ?7 b
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT _BUFFERABLE;
    % M# K+ `) J8 M$ b
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT _CACHEABLE;
    5 A1 U8 L! w/ k; ?8 Z6 F2 Z: p& c
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
      B0 i% q+ [/ ~0 _! ^
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;  U4 E# x( X$ x$ c" t4 e( S
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;0 g. l2 Z- J% P" t% d
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    9 X. t3 H7 W1 V$ R
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;: b7 d' b. E) L. v0 D7 l) \, S
  28. 3 N! O5 k4 G) B6 s
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    0 z1 b  y+ e3 w4 J: B$ M
  30. # s1 b' H: s& |; I" i
  31. % t% M. L. \: W; F  }, @# m$ \
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    ' Q! F0 C* Z! Y' _8 H) w
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    + B0 [+ J+ ]% `. d
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    + X8 f0 e# A% ]% d+ @$ d: |
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    7 l* W7 r: I% t7 I: m: `
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    9 E% h6 e# y1 P, ^
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    6 _) C/ K% J: Q! ^
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    " x- [2 M& n9 i( M/ x
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;+ Z. K/ n: E3 j$ o! L$ I
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    2 J  C) v) M5 _/ X& a4 }
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    5 \" Q# j0 a5 q( O2 j8 d: i, J
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    , z0 n+ s' q. ]9 J+ A0 ^
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;4 y0 G! C8 [  z0 W! h

  44. 4 S; |% j+ W  ]7 F% e
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);  F$ c8 y' j. y) r3 U

  46. ! h' {' W3 G8 p6 N
  47.     /*使能 MPU */- e) G  ]0 h( X
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);2 |/ t0 @4 e& D. ]+ y9 \+ W
  49. }! ?9 e/ E6 C4 b+ ]. |! e* G
  50. * [  p- }1 v5 m4 T; p
  51. /*7 |; A# y- ?- I6 \: P& ]* O, {
  52. *********************************************************************************************************
    % l) W. y2 g; X1 L+ E  O3 p. d
  53. *    函 数 名: CPU_CACHE_Enable( m' G- a! j1 q, \$ C
  54. *    功能说明: 使能L1 Cache, @! Z/ Z6 l" u( z
  55. *    形    参: 无/ v7 n- N' X) Z, s$ [9 S
  56. *    返 回 值: 无
    / g5 K/ ]' X+ u: l1 `7 J! M- z
  57. *********************************************************************************************************, c& L2 Q; y4 K! Z
  58. */; {5 Q# e5 r" [7 g
  59. static void CPU_CACHE_Enable(void)
    * B8 L& V; R. \4 T8 x) y- E  v. V
  60. {
    " I0 [' m) m8 q: C' a
  61.     /* 使能 I-Cache */3 n9 @& x  m% J/ _9 z0 l( p8 i
  62.     SCB_EnableICache();
    ( s' z/ l4 d+ F8 x: h  f

  63. / O% q! _7 z5 D3 k0 g
  64.     /* 使能 D-Cache */
    : c# P1 ^0 c9 D8 R
  65.     SCB_EnableDCache();, O5 S0 v' ~8 g
  66. }
复制代码
* D/ U; Q7 V6 ]! ^
  主功能:% c/ k/ C% V+ B3 p$ @
2 `4 m# A8 R2 U" K: _+ J; s& C6 O
主程序实现如下操作:! q- L$ z/ Q9 y! u' S, o9 x  D5 K

# r$ \! u! ~0 Y( {% l/ J- h  启动一个自动重装软件定时器,每100ms翻转一次LED2。
6 N& S# U4 {" k: h/ _( e' x. ]9 r  按下按键K1,打印原始波形数据和滤波后的波形数据。' P- c8 @. ^5 y9 Y  ~
  1. /*0 Z) f- u% [  X* X. r9 b
  2. *********************************************************************************************************- F9 J" C& t! n2 Y( X: V$ J1 j6 `
  3. *    函 数 名: main
    % R0 @% E, ^% M8 N8 Z/ O
  4. *    功能说明: c程序入口
    . z( C( u2 X4 F& i) L5 ~1 m
  5. *    形    参: 无
    / ?, W+ l/ g5 h; T
  6. *    返 回 值: 错误代码(无需处理)4 T) n! A) m, I/ x2 e" e( C% T
  7. *********************************************************************************************************
    / n" ^  S1 C$ K/ D8 M. g8 z
  8. */' o. ^; i( v" p; m
  9. int main(void)
    1 w4 N1 L. x3 `# D* e
  10. {
    ' z  l* @0 w: H. l6 }
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    ; D7 d- E% [9 |  X
  12.     uint16_t i;
    & S" p' o3 Z  ~8 B: S. l4 H& j' _
  13. 0 ]! x( O* ^( E

  14. + H1 X7 i/ F: P4 N
  15.     bsp_Init();        /* 硬件初始化 */
    8 z) C. G3 F9 d! g8 G4 W
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    $ `' y- _& E; _' b3 r$ q& g

  17. ' _6 b/ X2 e% S
  18.     PrintfHelp();    /* 打印操作提示信息 */
    ! r3 d3 y# `. A5 m; O
  19.   v3 a1 O, b& U( n* v
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    & G9 e/ `. W4 Q4 u( V- F5 t3 b0 k
  21.     {
    : F! v" J. ~! G
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */- w8 {# m" d9 h3 [0 n7 U
  23.         testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) + - p0 R# W/ ~4 E0 W9 V+ q. _/ r
  24. arm_sin_f32(2*3.1415926f*200*i/1000);
    & J8 W- e' q* a/ H
  25.     }
    + e$ i" i) U/ G6 |5 @
  26. 5 n  R" I0 Q4 g  O0 x
  27. ' j2 j) ]- w) v+ [
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    - k0 S! D$ U+ y, }2 ~; O
  29. , Y- A6 Y  c( ?7 p5 W7 c
  30.     /* 进入主程序循环体 */
    9 @1 E% t6 Q. ^3 ?3 O
  31.     while (1)- @6 U  h- ~& Y0 c3 Q5 m
  32.     {
    " W& a! ?8 f0 ^! m& q
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
      q  z" ^0 q% E! b* T+ q! i
  34. 8 l( G0 b/ p6 k" ?. m% K- X

  35. , `# V: p' ]. [
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */4 l" ]7 y' G$ ~/ P% o+ e% {
  37.         {4 S: ]* j7 V" G2 R6 ]
  38.             /* 每隔100ms 进来一次 */9 O( \0 P, s, B( d6 H/ W
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */' X) L. }# ?* ~% |
  40.         }
    - y4 r" F$ P  k% w( p/ M
  41. 9 M6 C0 {5 U- w2 b. B& r
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */, X& D7 L% ]8 W
  43.         if (ucKeyCode != KEY_NONE)7 b* E/ c* T" T  {* c
  44.         {
    . K3 g1 A1 V$ m- T3 ^
  45.             switch (ucKeyCode)  o4 v  ~' z3 P! d+ T
  46.             {- o+ y$ P( k' {  b( o3 A! u  }
  47.                 case KEY_DOWN_K1:            /* K1键按下 */6 [1 S6 g) P, @9 G
  48.                     arm_iir_f32_hp();
    4 ?- X7 P4 Z+ v- ]$ U+ C
  49.                     break;8 |. s8 ]" q# O4 i2 F
  50. 7 r) |9 @) O- }
  51. . k+ p- }* L# t. C( }
  52.                 default:, J/ w; P9 \8 R  ~% L. [
  53.                     /* 其它的键值不处理 */0 M# i: A) i9 q5 O" b
  54.                     break;
    3 a; ^0 }% c) g  g/ G8 ]4 q" X, a! g
  55.             }3 d- b0 Y1 ]7 J4 J4 `* [
  56.         }) J, C$ c2 @8 H4 k" K

  57. ! [$ N; V: A9 w/ S5 V2 }- W% B
  58.     }3 c# ]5 C# P2 L& N1 N- x" W' A
  59. }</span></span>
复制代码

" r2 w$ M2 }: V6 @9 S# J45.7 实验例程说明(IAR)
; e1 `" x0 j; |( C0 H. ?4 d! E+ [配套例子:
  B& f3 C$ v8 f% NV7-230_IIR高通滤波器(支持逐点实时滤波)& i- k/ d1 G7 p. ]7 `! |

6 _; S  F; k( ^: w  v实验目的:
# ^' [3 _9 |7 v# U5 Y# r学习IIR高通滤波器的实现,支持实时滤波

9 u  u9 B- B$ q
) K" W& H1 U- _2 T. U实验内容:; q! M8 R  b7 L% ~) V4 I
启动一个自动重装软件定时器,每100ms翻转一次LED2。! W5 a5 s, ?! l2 h1 v, L" H, A# o
按下按键K1,打印原始波形数据和滤波后的波形数据。
) F8 u. N  [" f$ E( ~

( r4 O- f! p5 i* b5 i9 N- g6 I( n# h上电后串口打印的信息:6 L/ A' w8 |# f1 o) O8 b$ f$ \! O
/ x/ \- P! M. s: @) k
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
  a* C* O9 @  Q  v/ u5 k' U$ Q5 i) \) U! w4 M
a66c11d15f6f8dd3c49ad9b587ab3a90.png
4 [. f0 P  Q2 `2 T

7 L% G* W! `, `! t/ vRTT方式打印信息:( W; v: F7 m: U& a$ p
1 ~: }" `6 M7 d% i! P3 n7 `0 L" l
5b42e93b5c59803fdc45c18e564e61f1.png
4 E5 g  }; ~9 ]! e) `; K4 H

( `/ p) [  ^* q, c程序设计:9 M, }0 ^9 c) [7 Z3 o

- F. i; j+ d$ c  系统栈大小分配:
0 q* C0 \' p/ B: p: N' z2 B( \3 O" C; ^2 t- [* M. P1 M
a48a46f1265dd3824bc690f874e90613.png

0 k" _- D9 B" H6 M2 j, {1 [5 a- m* p) D) e* O) D' t7 m
  RAM空间用的DTCM:
$ l4 B' _1 n$ x9 u
" x1 M/ Y2 Z" z3 ]3 H. H
025eeb1d94bcb86c7bec22ed1e822f36.png
6 @6 b! W( }* s; }0 F9 ?, ]; e4 v

* w. \9 x4 x6 N4 q  f  硬件外设初始化
# y( M# x! S& i& ^" |3 D4 D' w" m/ j/ a# T# ]9 W2 e
硬件外设的初始化是在 bsp.c 文件实现:% l% J& r, s5 m% _. s

/ J+ w$ _3 O: X) Y9 u. W
  1. /*& @7 w! V# [  J( B8 u/ z" n
  2. *********************************************************************************************************9 J0 l0 H( {5 e- `
  3. *    函 数 名: bsp_Init
    : [  [: o3 q8 X& I
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    3 O: }% e) H( k
  5. *    形    参:无( H! S8 B+ X* y! k
  6. *    返 回 值: 无
      \- B6 P! n6 K
  7. *********************************************************************************************************4 E# y% N# b( [  e
  8. */1 a( y8 Y9 i7 X9 N4 Q
  9. void bsp_Init(void)8 A1 J0 u8 z# M
  10. {
    ( h. y5 P' i( _1 ], Q$ v  D: e9 S
  11.     /* 配置MPU */
      C) w4 D3 u" s5 b0 I* Z8 R# d
  12.     MPU_Config();
    0 W, C( c# H  x" B3 P

  13. # i  i& Q8 M$ ^5 r, I! Z+ N' T
  14.     /* 使能L1 Cache */: U% q' P- G2 Q$ \
  15.     CPU_CACHE_Enable();
    ; z  P0 E) N2 p0 }9 @6 |: P
  16. / t6 E0 t6 z4 Z( ^+ X& N6 R9 J6 l
  17.     /*
    2 I, n& `9 Q9 |
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:" |& ?- b& B" M7 C/ Y5 V
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。0 l7 W" t, E+ @7 R7 h/ X6 }
  20.        - 设置NVIC优先级分组为4。% P7 k! m; m" p7 `8 M1 G
  21.      */+ y& R0 P8 w0 h9 C. d# x2 J# g0 v
  22.     HAL_Init();! ~  i' C4 @; E' j0 O9 q) h

  23. / M1 k, a7 Q1 |4 x6 a
  24.     /*
    3 N1 F# d  O1 W4 {0 s
  25.        配置系统时钟到400MHz; X5 `, C- u$ B* ~3 ~- _. ~
  26.        - 切换使用HSE。4 `4 L! h8 C! ^
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    2 N+ K& f! ~' m: g# f; E
  28.     */6 P7 `1 _+ d3 J% ?. X5 v
  29.     SystemClock_Config();& e1 M' r% C0 R5 `
  30. + T, q3 \8 r+ k( O: s" S( y5 s
  31.     /*
    % ~, t8 M0 y5 e5 ^0 Q# t
  32.        Event Recorder:
    ! ?$ [, x( [- K8 _; \/ G
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    4 w2 l; r1 k& [9 u7 h8 l
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章. B# b  m1 R* N# O1 _8 W9 p/ J
  35.     */   
    6 ], v$ H! g  I# O
  36. #if Enable_EventRecorder == 1  
    1 R' O- N. K  a* E5 S  A) q  q) G" [1 U
  37.     /* 初始化EventRecorder并开启 */
    7 f9 v# [1 B4 A# T; K. L
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    " Q7 Y1 q! U0 h, S
  39.     EventRecorderStart();' A; G' X# p# A; P! r" Y" V/ y6 }, J
  40. #endif
    5 a" `: r( ~0 x9 R8 d# v; m

  41. : j+ y: |7 |% n$ S4 k6 W6 w
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */3 u1 l/ C( @1 ?. k
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
    - J" J6 f7 W' r
  44.     bsp_InitUart();    /* 初始化串口 */  Z5 ~5 [, d* H; L
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    6 i- }1 ?( J% ]
  46.     bsp_InitLed();        /* 初始化LED */   
    $ M0 O! _/ k1 a9 X6 R
  47. }
复制代码
; ^( [+ e! N! f4 U
  MPU配置和Cache配置:% g7 K- {: h/ p- E( l* p1 }# z# Q

1 t% H% q/ ]3 U/ t% n数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
! e+ q) ^7 n8 Z7 U) K% |% S; w, U2 h
  1. /*
    ! Q( Q8 D( e( U
  2. *********************************************************************************************************
    0 _$ U" B) g# r& `6 I/ v- C
  3. *    函 数 名: MPU_Config! h% W1 h( x. Q+ G4 v- |/ S
  4. *    功能说明: 配置MPU6 r5 }/ `8 i' Z
  5. *    形    参: 无' O/ N" K  k" A2 [0 W
  6. *    返 回 值: 无
    % t  ^* W% ^9 `: e/ s
  7. *********************************************************************************************************  Y) O/ d; Y0 s. g! n
  8. */
    7 z2 [; j: ?8 V" r. c
  9. static void MPU_Config( void )' ]- l( Q6 H$ t7 {3 {1 Z$ T2 T
  10. {3 v% l% E2 L. e2 ?; q6 B
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    4 N' G! R# e9 D! l

  12. , [/ J" ?. X9 |4 v
  13.     /* 禁止 MPU */9 \2 @* B8 n8 d+ p, f
  14.     HAL_MPU_Disable();( s) h5 _( U* h$ [$ A1 }6 X" X+ B

  15. ) y, |' h) r4 f* G5 N/ R' l
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    . j# R5 \/ j' H" B# }
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;# u  U& Y1 S- ^; |/ V8 z
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;. @! [, B- X, e9 Z/ r/ p
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    8 G0 h. y: x9 v4 W) P
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    9 \8 `# o# o# d- h( `; H3 Y
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    7 N4 S' K* a0 e% X7 W
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;( T& _4 O/ a  u
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;. ^( F. a9 ~% }8 }
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;/ }7 m6 [$ k4 O# t
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;, E, i% T$ V1 j1 Y! d
  26.     MPU_InitStruct.SubRegionDisable = 0x00;" _. f$ n4 I$ @0 E$ C1 Y& K
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    ' U* r  H  n, ^4 Y% Q# p% F6 k

  28. 0 b, @6 t: c" l* |5 A3 L! M
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    & D) l; Z7 o. T0 F4 ]9 y: R/ B

  30. ! v' _/ v8 c  b  ~

  31. . e- Q6 g+ L( v3 }
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */9 d  [) w/ d9 O* [5 w4 _
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;9 A" ~. C3 r1 y& x- v9 B
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;0 s6 U8 t' ?( u9 y0 ~- J
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    , ~; o4 A, ^9 s. |* t' m  M
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    & M4 o, k% {. l8 W
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    2 W% m: Q4 N" v& s5 ]' f
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    ( B0 Y1 M* w1 \) v; J1 \% ]
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;0 a' T( U( M; b4 Y
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;) z- u. _+ l6 k, D, d/ H' G
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    + e3 r) R0 f; H/ X
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    + w8 L. t7 y: d
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;% I8 ?9 ^  {" g% |1 c
  44. 8 m# i' T, s6 s* {
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    & \$ g) y- M8 x& E1 f
  46. . k* x  r# J& l3 Z( k& Q# G
  47.     /*使能 MPU */# U1 r4 l0 b8 {6 |5 n7 r$ e) O
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    ( }( v1 T; D. y$ R
  49. }# r6 Q; \) g- }8 s: A
  50. ; n4 w4 \" p6 e0 R
  51. /*
    8 J5 x( t3 [2 D) A" [
  52. *********************************************************************************************************2 l! e8 K4 Z' u* o2 M7 v' \
  53. *    函 数 名: CPU_CACHE_Enable
    6 E9 t) s- @# G; I
  54. *    功能说明: 使能L1 Cache  v' ~: V) b/ D7 ]
  55. *    形    参: 无. z+ K8 \; `3 ^% i- I) t. w
  56. *    返 回 值: 无8 t( E. W/ ^2 ]5 C  i( q, G& _1 M
  57. *********************************************************************************************************
    7 A3 H8 o5 J; B/ J$ S  h
  58. */5 h) O3 Z0 o/ e. w% o" v
  59. static void CPU_CACHE_Enable(void)
      u9 S1 w) O( |6 q8 a. d# B$ w- ]
  60. {
    # E; J4 M- |; t) _, `8 {8 O. F3 Y
  61.     /* 使能 I-Cache */
    ) R) b, D. p7 z0 g8 k5 I. B
  62.     SCB_EnableICache();( d- o0 |4 c& O
  63. $ C/ C' U* R# H) O& `9 h
  64.     /* 使能 D-Cache */; @5 e1 Q* a* r& N( g4 \* D6 F% ~- e
  65.     SCB_EnableDCache();) \7 P: y9 |6 ^; Q, `2 C6 T" P
  66. }
复制代码
3 i! }! L# @4 S/ E
  主功能:- Q5 S; A! ^' l! g$ p5 f4 q; j

, d* m* H# d% N( @6 R主程序实现如下操作:( g9 o6 K5 p# v- z
! A- t5 V/ p7 o& C
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
9 h7 e" U& k- h: Z% ?  按下按键K1,打印原始波形数据和滤波后的波形数据。/ H9 `/ T& R! B3 c0 _) Y
  1. /*0 ^$ ~& m- G9 p8 _) R0 t% ^  P
  2. *********************************************************************************************************! F0 F, Q/ y$ n
  3. *    函 数 名: main
    " Y8 O  s$ ?  R
  4. *    功能说明: c程序入口
    ( \7 }- l* r. q+ a/ V# t) [/ _, N
  5. *    形    参: 无
    : }2 Z( M) b% ?5 U- b- B
  6. *    返 回 值: 错误代码(无需处理)
    0 G: w; y7 l9 w2 a* o7 P
  7. *********************************************************************************************************
    + \/ c1 Y1 E0 t) R. q
  8. */
    7 m0 K0 q8 g0 @! ~' F' A; D" F
  9. int main(void)8 O1 ~1 X  m2 ?" U
  10. {
    ! H( s) f1 j  `8 J
  11.     uint8_t ucKeyCode;        /* 按键代码 */# e+ |5 q  q  s+ h. ?& c1 |2 Q
  12.     uint16_t i;  U( M# q$ T% F
  13. 6 M9 d( o# w; K/ x+ W. p

  14. & }. Q/ s, U* ?
  15.     bsp_Init();        /* 硬件初始化 */8 ?) [2 u- A$ I/ {, y  W. n
  16.     PrintfLogo();    /* 打印例程信息到串口1 */
    # p# l+ }: X) U- {: r1 s
  17. 7 t0 `+ r( h/ X/ y+ t4 a
  18.     PrintfHelp();    /* 打印操作提示信息 */
    $ p6 h/ _7 ^3 C3 I5 D  |
  19. * V# n% H0 `7 Q. r. i
  20.     for(i=0; i<TEST_LENGTH_SAMPLES; i++)
    4 V) ?' Y  p/ s! Q
  21.     {
    ( D- h. L" t. I( W" \4 S8 [) ?
  22.         /* 50Hz正弦波+200Hz正弦波,采样率1KHz */; E' E: {6 n2 n* i5 L) F  |/ |! N: E
  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 p5 g% f, i1 ^# N% G
  24. arm_sin_f32(2*3.1415926f*200*i/1000);/ Y2 o; ?3 ^+ W0 t- x: c& w9 Z
  25.     }' _/ t( a+ }# [+ L, {0 ~

  26. , _4 ?$ z* A* d- j' A; E

  27. 1 O: e' V3 ?( n0 g
  28.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */# I  e* Y' c; r3 q# _( B* v7 z

  29. , L( a7 E) F5 m7 f
  30.     /* 进入主程序循环体 */
    % x4 D; y0 r* ]! x' L* W
  31.     while (1)
    ' e/ [; m* ?) Z4 v3 U# m) R
  32.     {1 o) o. L) ^1 X3 e1 ^
  33.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    % y! S- Q6 G$ v
  34. ) J+ T8 t$ \* L- k! L

  35. " _3 ?* d& }1 y2 K7 V
  36.         if (bsp_CheckTimer(0))    /* 判断定时器超时时间 */0 \: S$ R- l9 s
  37.         {
    # A( d* ^  q- o% W( S
  38.             /* 每隔100ms 进来一次 */
    % {8 r5 f7 Q' ?. l  Q& a& q* T5 l7 q. h
  39.             bsp_LedToggle(2);    /* 翻转LED的状态 */( Q' C4 V2 d: @/ A
  40.         }6 q9 Z/ _) {% h) U1 n. {

  41. 5 G- B* t5 W  c' O  i1 [  o. N0 q
  42.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */8 w; d6 ?- \3 F: X5 t3 v- E
  43.         if (ucKeyCode != KEY_NONE)5 I) I" N" D/ k) ?; K, Y9 f
  44.         {  y, i5 P2 v; s) X
  45.             switch (ucKeyCode)
    # x0 V4 P4 T8 G; b' F; Y
  46.             {* U/ u* y$ q7 C9 w/ G* c8 }
  47.                 case KEY_DOWN_K1:            /* K1键按下 */
    , F2 j8 t' K+ k. [! f) z% H
  48.                     arm_iir_f32_hp();5 H7 @7 B6 `9 f5 `" k
  49.                     break;6 p- T% F7 H! O1 J; D* n* `! L+ P

  50. , d: {7 ~; s# s. b( k: L6 M

  51. $ U7 y3 q* @( W2 D
  52.                 default:
    5 T! k! M2 m" z1 h# L9 q
  53.                     /* 其它的键值不处理 */) E1 k- n; Q  ]: ~3 D: l/ v
  54.                     break;
    - X8 v: G# v. M& u) J7 O
  55.             }
    4 X- M& f+ S5 \# @+ {
  56.         }  p/ C3 |, F: C7 q) H" P
  57. + o; |! l4 L% `: s# s. P
  58.     }- f. l* K' }, a  `# V% I6 O
  59. }</span></span>
复制代码
45.8 总结
3 x3 r5 c# b2 y本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。: G. U! t1 Y3 t0 J4 p9 k
5 W  y6 Y' W( p6 t
$ {$ b  p* l7 H. u

! f0 t4 E) C$ L( [3 v9 n+ W. G
收藏 评论0 发布时间:2021-12-31 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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