45.1 初学者重要提示
6 A# Z) a4 r% V* K8 @ 1、本章节提供的高通滤波器支持实时滤波,每次可以滤波一个数据,也可以多个数据,不限制大小。但要注意以下两点:2 }" g8 X) z# Q; i9 _6 x, A# q
" u0 R& y% X( V9 j( L
所有数据是在同一个采样率下依次采集的数据。
. Y5 w+ ~5 Q A5 @# L 每次过滤数据个数一旦固定下来,运行中不可再修改。
9 @4 p! R; q1 i; Z& \ 2、IIR滤波器的群延迟是一个重要的知识点,详情在本教程第41章有详细说明。IIR和FIR一样,也有群延迟问题。* h8 o& m, y6 a$ D1 y
) v5 W" y5 Y) Z! L
45.2 高通滤波器介绍* S7 P4 P, G, C
允许高频信号通过,而减弱低于截止频率的信号通过。比如混合信号含有50Hz + 200Hz信号,我们可通过高通滤波器,过滤掉50Hz信号,让200Hz信号通过。, S/ a5 ~+ v% _" e
% r; H. S* y& R2 Y& o5 a( h
- ~7 b/ z. |8 G* p
8 q' {5 t' X3 {" t45.3 IIR滤波器介绍' t, R+ w6 _3 {5 }; U W/ t
ARM官方提供的直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。
* T* I4 q: e1 E5 ]
0 p6 k( [! a3 f2 f直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成:' d3 _! _5 Y& C. }2 H6 m; D% j
. I; [- p- O5 {y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]6 A( I3 U* M8 j5 f/ w0 ^, J
0 I9 d6 j7 \) b1 ^; ^: e* ?
直接I型算法每个阶段需要5个系数和4个状态变量。
, e# w }% q5 K5 | s! ~. K& i
! V' z0 C& a9 z8 k7 X
/ N w" r \6 {1 i2 y
+ V1 l3 |4 t5 _$ [' _6 L这里有一点要特别的注意,有些滤波器系数生成工具是采用的下面公式实现:
* h( ?3 L5 B9 z) R- B; ^: |! B. s% ^) C# d. \
y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]
* Q& E' V9 w' \6 V$ H
5 h0 V/ q: p5 t- e% w/ n比如matlab就是使用上面的公式实现的,所以在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。; M6 c: s% N, L0 t$ V1 w/ K
% M& Y! Q4 m1 h高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。
$ z, a5 ^9 e0 z3 y: a) ?: q& F1 T: c% k9 U/ e% w# T2 V
4 i: U$ Y/ M& j8 d* [' |2 ^- L- H5 N; T- n
如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。* ^6 g- ^- P/ `7 ~7 C: b
9 J! y: N( c" f0 _% b
45.4 Matlab工具箱filterDesigner生成IIR高通滤波器系数9 {; X; }' n$ H) n0 G
前面介绍FIR滤波器的时候,我们讲解了如何使用filterDesigner生成C头文件,从而获得滤波器系数。这里不能再使用这种方法了,主要是因为通过C头文件获取的滤波器系数需要通过ARM官方的IIR函数调用多次才能获得滤波结果,所以我们这里换另外一种方法。
* A7 d! O( `: A3 ]5 j) e
8 Z- f' D7 P8 e6 ^9 k- y: f下面我们讲解如何通过filterDesigner工具箱生成滤波器系数。首先在matlab的命令窗口输入filterDesigner就能打开这个工具箱:
8 Y0 `3 m$ [: o! a/ s
5 C( q" V& L j, W; O: W$ D; A7 T( A+ ~( t1 z2 z9 p
* s. |2 x/ T! rfilterDesigner界面打开效果如下:
- g# p: S, S4 Z1 W' L9 J h7 I4 |/ \- a; p& f/ b0 E8 L+ v* H( L8 W
2 @" v1 U! _( j) \/ X) p
1 a% n m, N7 q* S: [IIR滤波器的低通,高通,带通,带阻滤波的设置会在下面一 一讲解,这里说一下设置后相应参数后如何生成滤波器系数。参数设置好以后点击如下按钮:
: ^ D1 Y; F7 o1 j6 J2 Z% r& r2 u# G. I$ D& k& w: t" [! Z
) c( D+ |3 T, r1 U4 n; |# E5 X6 L& t" J* t# M
点击Design Filter之后,注意左上角生成的滤波器结构:
' a. p6 B1 @! I7 v5 G/ |
* `" C4 }* ^& r& T+ @ h3 R& U9 I- m
0 w% o1 i y" r( `7 G: T默认生成的IIR滤波器类型是Direct-Form II, Second-Order Sections(直接II型,每个Section是一个二阶滤波器)。这里我们需要将其转换成Direct-Form I, Second-Order Sections,因为本章使用的IIR滤波器函数是Direct-Form I的结构。) X4 v# w1 K7 E+ v3 f
6 B. Y' k$ T( i" H. i) E" W2 O转换方法,点击Edit->Convert Structure,界面如下,这里我们选择第一项,并点击OK:, O, c4 I' p: S( G2 K! [$ m
$ x+ ]0 u- e: q$ y) n Y$ R; |
9 V' i8 U, Q2 G, G$ C' A# C& |5 U, X/ e6 }& [. j
转换好以后再点击File-Export,第一项选择Coefficient File(ASCII):3 n- H. X" {% ~
, T) ?8 ?" E. ^2 G( Y3 w* X$ B0 z( ]9 A# a: r7 l$ D
) S( x6 C6 o! C. F/ ?0 X0 y第一项选择好以后,第二项选择Decimal:
2 I5 u# [/ ?+ _$ n2 Y4 O+ f1 L% F* ? c1 D5 P
, n" U* i% p; Z5 {; c7 n
# p5 }4 u2 v, `3 L: ~两个选项都选择好以后,点击Export进行导出,导出后保存即可:
+ v2 [* l, h" h2 P+ l3 o: `' C+ r; J9 f* R& B# T/ j! i
: Y$ @4 K2 Z) c5 q' W7 [
* B. g; \0 X4 j& V, \0 i保存后Matlab会自动打开untitled.fcf文件,可以看到生成的系数:
3 v1 @7 z4 t& w1 b& [. s+ t" ?1 [& X, \" D
% Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.
, h2 I. c( a. ^& c& G5 I' Y% Generated on: 15-Aug-2021 20:38:33
9 ^* ?, Y) g7 l! r0 r9 a. v+ S/ @1 P2 l7 \% @/ f* M# W% m
% Coefficient Format: Decimal* c) M% J$ S9 J1 N
5 E g8 L6 b$ T+ u8 T
% Discrete-Time IIR Filter (real) 1 P8 A$ O* s Q( _8 E7 n; G
% ------------------------------- 0 S& l, n0 L! a' W! |2 p+ F
% Filter Structure : Direct-Form I, Second-Order Sections $ ]3 q) y; i( s% l. H
% Number of Sections : 2 " J3 H% A" s9 X* O7 E# N4 ?
% Stable : Yes , D3 d; m! {9 r4 U: n
% Linear Phase : No
: D9 ]/ S7 U: A$ ?0 ^# o
* w+ S( z) \# ^3 g( n6 [! {! v% i- z# t/ ?( H, X0 P2 l
SOS Matrix:
8 l( I9 M" D; M5 z/ D/ M4 y1 -2 1 1 -0.98454301474115180070612041163258254528 0.544565360850816415627662081533344462514
5 r" T3 D0 `7 }1 M5 Z) E1 -2 1 1 -0.744714477864321211519893495278665795922 0.168318873843973093595849377379636280239
2 \, J- S2 z% `3 p# q3 C2 L: L4 f* N5 P4 F
Scale Values: 2 v$ u" q6 i) |1 n w! o3 t
0.632277093897992026327870007662568241358 * H+ Z1 q+ _" D- r$ Z6 u# {
0.478258337927073562401147910350118763745
; Q; b G# A8 h5 }) `! n由于前面选择的是4阶IIR滤波,生成的结果就是由两组二阶IIR滤波系数组成,系数的对应顺序如下:
% h+ X3 j2 N7 B7 F# F% a; x
3 }; R! J$ O: p# O( z2 A- DSOS Matrix: - h8 D$ }6 n% Q+ c, S+ f
1 2 1 1 -0.98454301474115180070612041163258254528 0.544565360850816415627662081533344462514 ( O% m& n$ L; @" s5 H! G
b0 b1 b2 a0 a1 a2 E# K/ |5 ~, H3 F
1 2 1 1 -0.744714477864321211519893495278665795922 0.168318873843973093595849377379636280239 $ J% f8 L7 ?" L4 T G5 H0 z
b0 b1 b2 a0 a1 a2
, f( A: j" |0 D' v9 b- {注意,实际使用ARM官方的IIR函数调用的时候要将a1和a2取反。另外下面两组是每个二阶滤波器的增益,滤波后的结果要乘以这两个增益数值才是实际结果:
S/ F0 Q2 H5 i; D( g' B9 q% G6 W, O3 b4 n* R/ C- ~5 W
0.632277093897992026327870007662568241358
' E4 V& S w8 u% e0.478258337927073562401147910350118763745
1 m) K& }/ n5 O实际的滤波系数调用方法,看下面的例子即可。- X, c" X% L9 C' J
, }& |$ l; v. v
45.5 IIR高通滤波器设计/ ^+ H. p/ M; j+ ]: {' k* O$ G
本章使用的IIR滤波器函数是arm_biquad_cascade_df1_f32。使用此函数可以设计IIR低通,高通,带通和带阻滤波器' i0 J4 t+ t/ P+ [9 |8 ~! f2 L
5 e* M- ]6 h1 e5 @& L; t45.5.1 函数arm_biquad_cascade_df1_init_f32
* E) k( J" _' |& R函数原型:, r; d6 p" n+ S) u8 _
* s- I7 i3 Y' T) D! L' w
- void arm_biquad_cascade_df1_init_f32(
: @1 O. N( ?/ Y7 F3 L2 a5 Y6 `3 q - arm_biquad_casd_df1_inst_f32 * S,
# O# N5 N x! p" ], z( D - uint8_t numStages,
4 o* D% G* ^3 t - const float32_t * pCoeffs,
* L' \ h* M; @/ D2 M - float32_t * pState)
复制代码
' z, W5 ^' K6 B: ]. p$ x1 O函数描述: ]3 m! Q; ]* G4 @6 j' _1 u
5 {6 F( }9 A* d
这个函数用于IIR初始化。
( ?6 N6 B! ?- N; I8 a" K( i' ?2 [: u. o* C0 j, ]% L9 p$ Q
函数参数:3 C; Z1 b2 r2 j6 z! g
2 P/ u7 w. _1 m
第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。* d0 b* a h! H! U* y& `: \
第2个参数是2阶滤波器的个数。* q$ j7 Q8 v5 ]
第3个参数是滤波器系数地址。
/ N* G' G4 F) ?! D 第4个参数是缓冲状态地址。2 c2 l* S' z# [! q; k o+ s
注意事项: F0 e) F6 C+ [5 ~2 I! [6 @
, j- _+ v8 A) i7 i' F* y2 F结构体arm_biquad_casd_df1_inst_f32的定义如下(在文件filtering_functions.h文件):
8 O* r; S( j0 H& @) Z, |' S+ j6 N) ~+ I4 [: @2 X( b" B
- typedef struct
* D8 p4 B/ u: k - {$ [# K m6 a8 }9 h
- uint32_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */
' K- z1 T6 g0 j ^ - float32_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */$ z! m9 I0 p- X& a% p, X1 h( x
- const float32_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages */9 J" F! I! e6 Q, G- A* u, G
- } arm_biquad_casd_df1_inst_f32;
复制代码
/ u# I6 w$ r$ ynumStages表示二阶滤波器的个数,总阶数是2*numStages。
* m' @% x1 G3 ?3 F$ apState指向状态变量数组,这个数组用于函数内部计算数据的缓存,总大小4*numStages。
0 T4 ?7 ?2 [5 e: y参数pCoeffs指向滤波因数,滤波因数数组长度为5*numStages。但要注意pCoeffs指向的滤波因数应该按照如下的逆序进行排列:
8 J) }; d& O4 v p+ {) L7 c; M{b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}( ~ W( N) Y8 ~, W) j
, B5 O0 D2 _9 J' @7 M8 N先放第一个二阶Biquad系数,然后放第二个,以此类推。
0 @! X. p7 E1 K/ N7 k
# ?6 s# H+ L$ C- g45.5.2 函数arm_biquad_cascade_df1_f32
3 e( q9 w% u a- i函数定义如下:
" U3 X5 s! d! |" }8 b9 M# H7 {* Z0 r m+ E
- void arm_biquad_cascade_df1_f32(5 C+ E8 B9 }2 a& M5 E& f+ \/ R
- const arm_biquad_casd_df1_inst_f32 * S,
/ {6 {% |. Q" d: N7 t1 p, I - float32_t * pSrc,
# l/ s% S& y+ ~* G) z8 y3 ? - float32_t * pDst,) J2 F9 l' L2 O% R8 w6 O- B
- uint32_t blockSize)
复制代码
/ {, I: ^- v. a; `函数描述:: h6 l# m" p. r
9 L% l7 f( \ F; Y+ y8 i2 a k这个函数用于IIR滤波。3 m {& ]8 T# w4 x3 {# x2 C3 e* m5 }
" I4 k4 ~8 M0 n, A8 m' j2 s Q) m; C
函数参数:. x3 T1 X/ {4 w f
5 K+ x5 M3 V. e" q8 X: E5 v4 x 第1个参数是arm_biquad_casd_df1_inst_f32类型结构体变量。
6 t& @5 u+ H: c" r* h: f! {) Q1 B- u 第2个参数是源数据地址。
& h' j" B2 H0 d2 v 第3个参数是滤波后的数据地址。
4 b+ ?4 H) B: x! @ 第4个参数是每次调用处理的数据个数,最小可以每次处理1个数据,最大可以每次全部处理完。5 m& E+ b+ Y& q3 W% Q
45.5.3 filterDesigner获取高通滤波器系数
. `/ e- l1 U- H* L( |设计一个如下的例子:3 t3 w6 ~3 k3 P% L& K0 w
, q7 J% o6 X+ p' D; h信号由50Hz正弦波和200Hz正弦波组成,采样率1Kbps,现设计一个巴特沃斯滤波器高通滤波器,采用直接I型,截止频率140Hz,采样400个数据,滤波器阶数设置为4。filterDesigner的配置如下:
2 E% F( i6 ]- }7 O' t
) }. _2 \+ X9 s, _
7 ^- _" C f& |" U! h
8 @9 r# {1 O! @. o: [: O6 p配置好高通滤波器后,具体滤波器系数的生成大家参考本章第4小节的方法即可。
4 [7 l4 {, A1 W/ ]: J' `! ]( z9 \, x+ o+ z$ f
45.5.4 高通滤波器实现+ Y2 k$ ^/ R y8 D
通过工具箱filterDesigner获得高通滤波器系数后在开发板上运行函数arm_biquad_cascade_df1_f32来测试低通滤波器的效果。
$ Y7 Y3 E3 C$ [9 h1 b0 u( `+ h, v" w: N8 r- P
- #define numStages 2 /* 2阶IIR滤波的个数 */# @' i3 o6 }1 o& _. j
- #define TEST_LENGTH_SAMPLES 400 /* 采样点数 */
! y" B' t% f2 p3 x5 J4 @$ T' @9 F5 n - #define BLOCK_SIZE 1 /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */% p7 X1 x# o7 P r7 C+ H+ b* Z, m
8 N1 w9 o. @5 v, c) L5 j- Y
9 k: l- w, @" I7 a6 l& @! B- uint32_t blockSize = BLOCK_SIZE;
" Q; p$ Q+ n$ v+ X: G - uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE; /* 需要调用arm_biquad_cascade_df1_f32的次数 */! q# y# H3 j7 Y1 u: V$ L
- 1 _6 C! P0 r$ Z# h/ G5 C
5 k' C/ |2 i# q) U! H- static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采样点 */
; D. ~/ ^! Y! G, h - static float32_t testOutput[TEST_LENGTH_SAMPLES]; /* 滤波后的输出 */
7 M, g {) X5 S3 M1 a B; ]0 \ - static float32_t IIRStateF32[4*numStages]; /* 状态缓存 */
8 o% \) l( R3 k - $ z6 W! h$ `3 [) h( P
- /* 巴特沃斯高通滤波器系数 140Hz */
$ e8 l# {) J! Q2 X5 B* R" v* \ - const float32_t IIRCoeffs32HP[5*numStages] = {, ]; F" j( v) M( U0 }, X; I
- 1.0f, -2.0f, 1.0f, 0.98454301474115180070612041163258254528f, ; y" S% G" H8 ^' v" b- O5 W3 b5 U" p
- -0.544565360850816415627662081533344462514f,
" B5 r' k/ N3 U9 _ g# a& B - 1.0f, -2.0f, 1.0f, 0.744714477864321211519893495278665795922f,
1 w. E6 P# g$ k3 |4 p4 @ - -0.168318873843973093595849377379636280239 " J# d: ?! c* L7 n+ V+ b, }' k
- };
7 ]* D7 ]# d; {) _: A9 q# p - ) h A6 V4 ~& S8 @2 g; O
- /*
" E1 Z: `4 i" ^# M" y - *********************************************************************************************************& O0 e+ Z: v8 x, P4 d0 T
- * 函 数 名: arm_iir_f32_hp
: [$ d( t' Z6 P/ _" o5 i7 P - * 功能说明: 调用函数arm_iir_f32_hp实现高通滤波器
. Q n/ S3 l0 y/ g/ J5 D - * 形 参:无' S" e9 Q: E) n
- * 返 回 值: 无
7 Z. p( t3 T, q7 \3 P - *********************************************************************************************************$ ^7 v8 S' M" x0 i
- */, o/ Y( W1 N% U, c
- static void arm_iir_f32_hp(void)8 @2 t2 R. ?7 ?9 |. W1 p3 ^. Y, d
- {
6 d5 X0 G0 e& y - uint32_t i;- D4 n) |. ^, J& p6 ]5 P
- arm_biquad_casd_df1_inst_f32 S;
" _& h! F Z* X# a, W% F - float32_t ScaleValue;1 h- t9 d5 Q& m+ v6 W" \0 j
- float32_t *inputF32, *outputF32;9 S c* ], C `2 t t$ h) z( K
- & G1 L/ |) M# ?! H) s
- /* 初始化输入输出缓存指针 */3 X7 e+ L8 ~+ \$ Q$ b
- inputF32 = &testInput_f32_50Hz_200Hz[0]; n7 Y, X3 _7 y3 E5 s
- outputF32 = &testOutput[0];
8 p; b. k. Y- O' l& ]) v& a, ^ - 8 H$ S1 Y4 w" Z2 b" F, c9 D
- ! ~ c' Z3 S" v) m
- /* 初始化 */4 u$ {8 R3 D. U% Q/ x0 Z9 B7 D+ G
- arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32HP[0],
% t! t7 w) i! W( F" U# D* I - (float32_t *)&IIRStateF32[0]);8 W0 B* \! f8 r4 [/ z h* _
( c: C, ]6 |) g) O2 g
# W" a% y1 Q6 R+ t# \- /* 实现IIR滤波,这里每次处理1个点 */
3 x( a s7 y" \$ b$ v% j1 ]$ v - for(i=0; i < numBlocks; i++)* x2 {9 c5 m' N- s6 Y( V
- {
! ]7 @- P' K7 C - arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize), outputF32 + (i * blockSize),
, w5 I1 c8 n& }$ [. o3 g - blockSize);) q: \) @3 Q# n3 T# _* ^* W
- }7 B7 d; D' U% G @ z! H
- ~8 @+ M9 w, u0 d- /*放缩系数 */0 w% ^+ F$ t* ?2 ]6 Z+ x* F1 k8 \# L
- ScaleValue = 0.632277093897992026327870007662568241358f * 0.478258337927073562401147910350118763745f;
' q$ i: V( L1 I' W8 m$ f
& ?4 K% \, t7 ]0 D: O! D- /* 打印滤波后结果 */
$ f2 }: P& I2 h5 \, K+ d" N' Y* ^5 t - for(i=0; i<TEST_LENGTH_SAMPLES; i++)
1 f8 l7 Q/ T& R9 m - {
8 _* V- v# I3 Z( A. o: U2 j% R3 B" E/ Y- I - printf("%f, %f\r\n", testInput_f32_50Hz_200Hz<i>,</i> testOutput*ScaleValue);( i7 y; L' R8 E; y, @9 K" J
- }
2 n' n. T6 j" |& ] - }
复制代码
. P0 b) e2 S( p" D) i+ _* D, b" w5 G运行如上函数可以通过串口打印出函数arm_biquad_cascade_df1_f32滤波后的波形数据,下面通过Matlab绘制波形来对比Matlab计算的结果和ARM官方库计算的结果。, ^1 j3 I, d' F% v
) |4 E6 f/ l' W0 v p8 y对比前需要先将串口打印出的一组数据加载到Matlab中, arm_biquad_cascade_df1_f32的计算结果起名sampledata,加载方法在第13章13.6小结已经讲解,这里不做赘述了。Matlab中运行的代码如下:$ u5 h6 O- _, Z( u& `" Y
& M5 P+ L1 N7 q
- fs=1000; %设置采样频率 1K+ M- h# C( t8 J% U4 l
- N=400; %采样点数
% i1 Z/ D- l- g# t# h4 | - n=0:N-1;4 X' O7 B. w4 l7 x% T2 Q& @, {
- t=n/fs; %时间序列
6 |/ b% Z7 ^5 ?' D - f=n*fs/N; %频率序列- u/ H6 M2 K: a; o5 t: q
) W4 O. G. j& |) N1 o4 }- x1=sin(2*pi*50*t);! i/ z8 U, O' }1 k
- x2=sin(2*pi*200*t); %50Hz和200Hz正弦波+ m. D Z9 G/ [ J0 A
- subplot(211);* G9 R! Q; I$ g8 g
- plot(t, x2);
* A& z% P1 Q' _1 f# D5 N* K8 R1 G/ H - title('滤波后的理想波形');
* ^ }5 x! J1 k# D4 A# n - grid on;( C {- m1 J1 F3 P# ~ y( F: f
1 P$ Z+ M* j9 t- subplot(212); ?) j* Z8 p" N( u9 {' J0 b
- plot(t, sampledata);
% A. _9 i$ H: z q; \/ u - title('ARM官方库滤波后的波形');
/ O* O5 h% i) O- p - grid on;
复制代码
, s* G- q; @$ M% fMatlab计算结果如下:
' ^1 }5 M0 X( a r! h# T# r; O
( R8 S- Q x R0 X1 G+ ~7 r5 U7 N/ G- \
6 F1 _9 b% T2 a从上面的波形对比来看,matlab和函数arm_biquad_cascade_df1_f32计算的结果基本是一致的。为了更好的说明滤波效果,下面从频域的角度来说明这个问题,Matlab上面运行如下代码:7 D7 z5 M& A, d1 L& E2 U7 ~
" Z. r( Y' p- Q1 D1 U; q
- fs=1000; %设置采样频率 1K
/ G) a0 c6 P& e* @, x1 j - N=400; %采样点数
) I9 \' C$ M- o - n=0:N-1;+ i% V& E% b6 c
- t=n/fs; %时间序列
% m& b: f4 @1 X% } - f=n*fs/N; %频率序列
8 V2 R5 r! N! f
3 `1 h' ^, [+ Z, t; O$ p3 o) n, K- t- x = sin(2*pi*50*t) + sin(2*pi*200*t); %50Hz和200Hz正弦波合成, L0 E ~% c5 Y( ` o5 P' u/ @
- 4 e& P2 j1 U8 E( f. G( t! ?
- subplot(211);
: x; i( `4 P0 i! _( @) G* Z - y=fft(x, N); %对信号x做FFT
! U, W( c( _ n( Y) } - plot(f,abs(y));* {0 p9 Y( w* g5 w
- xlabel('频率/Hz');) I m) ?/ ?$ \0 ^" b
- ylabel('振幅');
& u6 v1 N/ ]3 Z3 D( R. Y# b - title('原始信号FFT');9 R2 W6 J* y4 h4 k3 ~. |. W
- grid on;8 A$ i* b; e% i/ M
& w5 O# b* Q Q# D- y3=fft(sampledata, N); %经过IIR滤波器后得到的信号做FFT/ l1 p9 U1 |* H% Z
- subplot(212); , _/ i0 b% k: D
- plot(f,abs(y3));; [4 ~2 g$ E+ W. @ c
- xlabel('频率/Hz');
7 {+ D- B' n+ G4 x: ~+ z6 e* n - ylabel('振幅');8 \1 k/ s; F" F8 M# ?
- title('IIR滤波后信号FFT');6 H. U$ C* { u# [1 u. U$ `$ S1 Z# l
- grid on;
复制代码
- F5 {) ~. l) q% q. f6 nMatlab计算结果如下:% i/ y2 y& `/ {, H& f" R# |( o
/ F) y9 J+ `2 d( f
( v: o N& O' f0 ^+ I6 \2 o; ^+ L
上面波形变换前的FFT和变换后FFT可以看出,50Hz的正弦波基本被滤除。' _* z8 u9 n: F u& V8 P
1 x+ a" [1 t. J" h1 X' n45.6 实验例程说明(MDK)/ C. C7 b9 d1 W4 x% g
配套例子:
. x+ ~" K7 p0 G2 d9 JV7-230_IIR高通滤波器(支持逐点实时滤波), @# x% c2 f8 y. q
+ U/ T9 D: U" y* o, X6 J实验目的:
6 E6 j- I/ R6 ]) c" O) |学习IIR高通滤波器的实现,支持实时滤波
0 v7 I: }) r# I$ W" t' \4 t
7 e* I% k5 T! r0 z6 p+ G实验内容:# a1 q( {7 |1 Q* P! C( m
启动一个自动重装软件定时器,每100ms翻转一次LED2。
! U, c+ U# z1 Q3 J& J0 d按下按键K1,打印原始波形数据和滤波后的波形数据。
8 ~) R+ n: o0 T& ~5 T6 ~; [
$ n; ~: i3 s) J( d9 b+ V5 @8 T) u1 D使用AC6注意事项
3 U" z4 ~% q8 e; E/ Q7 [ r: e特别注意附件章节C的问题" ^# F0 V3 a" u# M; V
# q' v4 T- A. B9 w; p
上电后串口打印的信息:
( R4 U8 v2 E, M2 p* J- L& k
. n. C: Z# X- u3 c$ H波特率 115200,数据位 8,奇偶校验位无,停止位 1。
6 `) f+ N& U4 D- v# m: \ y0 n* y0 |. R! L9 G
! {3 I' i2 F, I. j" ]
, |, Y2 o- I* B! ^& IRTT方式打印信息:
" L! k2 |# J% g! Q8 W" J
0 M" V5 t W% [3 G9 E0 u: \) x4 B6 Y7 w( b' s. j; A6 w
' e" O8 U4 r/ M0 N, [8 {0 r4 @( {程序设计:
s6 j0 F% }! I; s+ a% c. i( f f8 D6 j4 f! c
系统栈大小分配:- z, [7 u/ ^$ r1 u: E# `# U/ c
1 b& y z; a4 I. b7 S- V% u1 J0 d
- J2 E' L) o+ e. N6 l) T/ |6 y" n/ n& Z8 v( P7 K( x& m
RAM空间用的DTCM:
! z' ~& w7 U0 |/ u" ]3 ]! \" s$ ?& g4 T, y4 K' D% T. V
4 b( f! S' u0 W, i
: [8 O1 m$ T4 ^. ` 硬件外设初始化
2 J; D+ S0 W, O n3 [1 w
& y5 N" A; o; l% m8 G8 A硬件外设的初始化是在 bsp.c 文件实现:0 f8 W3 t) D4 O* z
& v ~, e0 }2 d# l6 |. m- /*5 a8 j4 S9 r, G7 ^( g4 |- {9 `
- *********************************************************************************************************
) ? [: y% f% A4 L9 _) a- Q/ e - * 函 数 名: bsp_Init
8 l0 d C' X- c. \) O! a+ E - * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
$ I' S8 ^5 m, M U. Q( m1 Y% y5 @ - * 形 参:无$ g- r k" R; j
- * 返 回 值: 无
; ]0 K3 E: t$ s" T - *********************************************************************************************************
) z* O3 Q; a- Y& @% j - */
5 ?. s, G6 f( n6 s - void bsp_Init(void): L( L- z. B/ u: K
- {
5 {5 j- a3 L7 r - /* 配置MPU */
+ ^# W/ ^* P g% j9 ^ - MPU_Config();
+ l) B1 @& h8 Q% N' t' S - 3 y( B5 f2 v8 F/ Q4 a8 e- C
- /* 使能L1 Cache */
, C- m( t/ O( p2 A7 p) W - CPU_CACHE_Enable();1 k0 I2 \/ {! b e) v$ k6 o
- % \) H5 y. l5 H& d6 D, j( G% h! a
- /* 2 u" i% M$ J5 s7 g$ N& b+ S$ r" C7 @
- STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
9 c- B* Z0 O, L1 R9 _9 p - - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。7 ^1 m4 e& O) O$ r5 H1 l
- - 设置NVIC优先级分组为4。5 w# }" S+ t6 j
- */- S7 t* Y! m7 _
- HAL_Init();* j5 B* O6 Z( b% N* [9 H
, z. _5 ?5 a/ z* p- /* $ x; ?* O" `* i
- 配置系统时钟到400MHz
3 r, V( Y# c, `3 e0 K2 l8 G7 q - - 切换使用HSE。
5 j( Y- C3 d$ q - - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
# L; `7 I8 p% ~0 {2 | - */
5 b2 C) ^' h, \" P2 y - SystemClock_Config();: X8 j+ k+ @9 \( \+ K+ h4 L
; Y. Q% _' g4 s3 [7 W- /* 1 n6 d& v# w& A- ~7 h. w1 ]& V
- Event Recorder:4 O' `' B" f2 q$ Q8 i5 l
- - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
# T4 K% D9 n9 l% ]' Y c& s4 k - - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章. W8 R/ W; |2 b/ P$ }- C2 B% d
- */
& Z8 ?1 P1 i* Y& Y - #if Enable_EventRecorder == 1 : _, X3 ?# ?0 \" [0 o
- /* 初始化EventRecorder并开启 */* t, V& Y6 O6 d
- EventRecorderInitialize(EventRecordAll, 1U);
1 [! k) n5 J5 }# B. I0 u - EventRecorderStart();
5 s+ V" U3 I& F A/ f3 _; _0 Q4 f; m) R - #endif
( @% H' b) h# G" O - $ X; {0 D7 Q7 i, L6 n
- bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
8 F0 V7 N: H3 i( s& R, m7 t - bsp_InitTimer(); /* 初始化滴答定时器 */5 ~: x- q6 l0 `
- bsp_InitUart(); /* 初始化串口 */
* U2 V! Z9 ]2 A5 Y: G) L - bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ : u' l R% o& g# b* B) w
- bsp_InitLed(); /* 初始化LED */
" i4 `: M1 w g! T9 f8 ` - }
复制代码 9 @0 O0 V8 c3 ~) |& T+ f
MPU配置和Cache配置:
3 ~* n, s. ^' o$ _
4 W1 J* S! \; T+ S- E数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。% v! g% g. H/ U" t% j7 _$ B" P+ G% Y
' G- q( I H- t q- /*0 M; f# J* p% v' [9 J
- *********************************************************************************************************
( W' q+ P: c4 D: \ x - * 函 数 名: MPU_Config0 ]2 _4 O) u9 w( G+ Y- A2 v9 r" b4 p
- * 功能说明: 配置MPU
[% o0 @; j! D: l - * 形 参: 无
5 p1 R% ^; x' {9 T" } - * 返 回 值: 无; h; k7 I) D3 _. L
- *********************************************************************************************************/ F/ f4 p3 v- R2 G+ H' I8 }$ `7 z
- */5 d7 B: L$ A* {, K, Z' E% n
- static void MPU_Config( void )
3 s" M" V0 b$ W. t' |$ N - {" |' M; x8 }* r4 E5 B
- MPU_Region_InitTypeDef MPU_InitStruct;
( ~$ ?0 x8 K8 }$ f7 _6 g - 8 E! j8 m u2 M; ]
- /* 禁止 MPU */
# E; ]/ j2 g/ T# [& {% V9 P - HAL_MPU_Disable();
: p, Y4 n- I6 ]. P4 e+ z
3 j: m' R( m' E A: m' x0 o- /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */
" l8 o& B$ {( z - MPU_InitStruct.Enable = MPU_REGION_ENABLE;% ~1 [: E1 |' x z% B4 a: M
- MPU_InitStruct.BaseAddress = 0x24000000;
* @9 P& m) x) y) f& c - MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;& i8 ~! ?# u! ]
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;9 v7 e3 ?4 z) V7 h/ ^7 \9 y
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT _BUFFERABLE;1 m2 _9 i* P) d3 M
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT _CACHEABLE;: G" U% @( J7 B+ Q5 Q9 r+ C1 Q' H
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
$ p# d5 Z* Y7 A& E9 a* C - MPU_InitStruct.Number = MPU_REGION_NUMBER0;
: R. r& x* M3 G& ]( A+ H5 j8 D - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;! f6 e/ M. b4 v
- MPU_InitStruct.SubRegionDisable = 0x00;# i# R) u8 ^& ]5 t8 P9 ]0 k
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;3 x Q1 {+ M: b( S- C( t( R
- 3 f( a+ o6 h& B% x- p
- HAL_MPU_ConfigRegion(&MPU_InitStruct);3 L# o9 x+ w0 ]6 D H3 H" K! E- T
4 D6 j6 x! P8 V/ L* `' O- / L& T5 |* ?- @6 Q; M
- /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
. ~, m4 `4 R; Y7 w; m2 T2 ` - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
; J/ }6 t* t9 Y* Q& m; Z - MPU_InitStruct.BaseAddress = 0x60000000;
$ p1 \ F* `. [ S) ?/ S0 ]7 p - MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
+ m8 G; v6 T; ] - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;* A# K) }( u, W2 W5 s6 S% l
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;& M6 {3 l+ x6 \% Z5 _5 @
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
( b" u4 V# J2 e7 R1 v) N; _ - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
. c" [' ^9 P" Z6 l7 Z) ~ - MPU_InitStruct.Number = MPU_REGION_NUMBER1;
/ _ t1 Y- }( \ G' n7 S - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
; l6 o' F/ b( R. W& H# G - MPU_InitStruct.SubRegionDisable = 0x00;+ O: ~& `/ U, q
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;9 p9 n9 ?; `% c
- $ P8 i1 u4 y5 [6 x1 N
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
# ^% y9 K- D/ c5 B - ' w. D+ z% _2 ?% A4 c- [
- /*使能 MPU */
' W* ?; @, S1 ~# e# A - HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
, n" T& G i+ v( t7 E - }
( Z& L5 }" j7 V8 }1 R$ v& x - $ x7 _ Q0 Y6 `# {, }
- /*
. B! V9 \5 e$ Z: m! m - *********************************************************************************************************
1 o6 {. z* _& m1 z - * 函 数 名: CPU_CACHE_Enable
& i0 E( R( B1 v X( L5 |, q! [4 u. K - * 功能说明: 使能L1 Cache
) i( |5 k( R& j - * 形 参: 无* f( ~. A( k; ?" j$ c0 Q* W
- * 返 回 值: 无. U. F+ h3 \) X
- *********************************************************************************************************
& K" ?3 t5 }1 y& @ - *// l. F: F& U6 Z8 y# V" g
- static void CPU_CACHE_Enable(void)
+ M: c$ |3 f' B( ]' r8 ^ - {3 E- a8 P7 I: P) N9 x
- /* 使能 I-Cache */
4 W+ h+ c2 q1 f: w3 H - SCB_EnableICache();& P, X5 _ H6 s; |
/ ?( G% i* o) X+ I- /* 使能 D-Cache */9 n5 ^: p) B5 ~7 J7 c) `9 j
- SCB_EnableDCache();
5 m' b/ q/ {! P3 k1 ^ - }
复制代码
C. l7 Z! U E& A+ A/ F 主功能:7 y3 A8 h: X4 Z+ N8 l
* T4 [3 e& Z6 N
主程序实现如下操作:
5 ~7 ]. L/ A6 ~0 Y6 A6 M: j2 G# K% z4 N0 [/ o0 q
启动一个自动重装软件定时器,每100ms翻转一次LED2。
. v, j: r( {; T7 i8 e 按下按键K1,打印原始波形数据和滤波后的波形数据。
3 Q4 @ s& m: W( Z- /*
; z* ?/ w' u$ s \( A; g5 V - *********************************************************************************************************
3 W+ }9 b$ q* `/ | h - * 函 数 名: main9 {, N" I4 _& f+ l6 u
- * 功能说明: c程序入口" u4 k( N- |7 D3 l" V4 e% d5 ?8 W T
- * 形 参: 无
4 y4 u( a1 T4 J7 V) S - * 返 回 值: 错误代码(无需处理)6 V) e$ h: h3 b D" M- B- }! R. u
- *********************************************************************************************************. O7 b J& o2 r" m* l c
- */7 h$ m8 Z) G) U/ ]# h( g
- int main(void)
+ C5 m4 s" j+ S8 ^0 q9 Y2 D; W - {, u( P. B( X& J
- uint8_t ucKeyCode; /* 按键代码 */
3 O: O+ W$ b% C/ ]4 p - uint16_t i;
$ n) H7 c" K+ j$ n7 ?% I" l2 s - 8 w' |, Z2 h8 f3 G, Z
- , Y" k! b- ?0 v- q/ e& D* _
- bsp_Init(); /* 硬件初始化 */9 |7 Y* X: z4 \0 x" z6 K
- PrintfLogo(); /* 打印例程信息到串口1 */
0 {6 }# Y( |" o5 i2 u) R. z0 m - , m4 a8 z& }+ k% X5 f( k
- PrintfHelp(); /* 打印操作提示信息 */
5 [8 c2 P, }, \9 ]6 n2 B - ' ^" q5 P5 W2 G: Q' x4 `7 n
- for(i=0; i<TEST_LENGTH_SAMPLES; i++)/ ?8 c/ p' \$ j& w% G- J/ k
- {
0 a) B; D9 P Q5 U l - /* 50Hz正弦波+200Hz正弦波,采样率1KHz */' I0 ^; B* p$ ^. }1 z% D; `
- testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) + 5 f; N- ]6 `4 P& U- I
- arm_sin_f32(2*3.1415926f*200*i/1000);# x9 K4 z! z! q2 R
- }
. r& f+ }4 m1 t F: D - & ~% T* j* n9 x0 I7 D' `+ u! q
2 S, u* N' h1 j/ s- bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 *// l0 A# }5 t- S9 r1 a$ R0 y
- * a) u: G& z" D# ~! Z9 k
- /* 进入主程序循环体 */
/ g( y2 ~6 C: D$ W( Q* S - while (1)! t' l. l4 X7 l& j7 s
- {
4 m+ b8 U& T7 n - bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
# x% W" {* P' s+ w+ N5 t5 G - 4 |0 \) F# E% F
- - g1 }. u) C1 u3 `( y. ~' M5 Q% D
- if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */
+ \9 r4 c0 n8 t: x) z8 t - {! V% N, P% ] N. V( r7 c! N
- /* 每隔100ms 进来一次 */
/ p: n$ v! N0 g1 L1 g9 U; E: o - bsp_LedToggle(2); /* 翻转LED的状态 */6 Q# P3 a6 u& s9 V f
- }- c7 i( ]( p7 R
- 6 R; c6 _+ @4 N" l
- ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */8 o S) _. v4 R+ e4 |1 R/ u
- if (ucKeyCode != KEY_NONE)+ S2 Z; v& `+ ]0 C& G& x8 D
- {6 w$ s5 h- u0 U/ I- L6 S! I
- switch (ucKeyCode)
' W' W; ]. E& l, x* v2 y0 ^ - {+ L1 Y( v0 ] o7 J! | k$ U U) X2 t2 S
- case KEY_DOWN_K1: /* K1键按下 */5 `/ s) i# c/ I& G9 \9 D/ B
- arm_iir_f32_hp();. A6 G2 i( H; f' F. V Q; K& r
- break;4 y0 v, s" v& F$ s
4 z& w0 I' k) F9 M- 4 [0 ~% ?) J( n: N+ I
- default:
# x3 ^! K! a# K- _5 g9 O - /* 其它的键值不处理 */) c6 G9 Y/ v w; @- N
- break;! l/ L& X1 f. p S5 I
- }
( b" S# W0 Z4 S# n: D - }
6 y& S. r3 ~: c! C# V: J3 d - ' A5 ~$ |$ q. j8 h, J4 g$ Y
- }
7 a4 y2 Y$ W" |1 x0 a - }</span></span>
复制代码 + p+ W+ W9 O% n+ T" D
45.7 实验例程说明(IAR)
; Y; V! Q' U3 ~! ^( B配套例子:
+ F, t- r% \ E9 XV7-230_IIR高通滤波器(支持逐点实时滤波). h9 g6 j- K" L6 K# q! H
* G1 l. G- K, l: q
实验目的:/ v' b7 S. C4 W3 t; d
学习IIR高通滤波器的实现,支持实时滤波2 E& A2 n/ z. r+ U F7 F0 v
& Y: R9 R. N& w# L0 j% `! M
实验内容:
5 @+ v! B: [$ o启动一个自动重装软件定时器,每100ms翻转一次LED2。
5 o: C/ U; T* p按下按键K1,打印原始波形数据和滤波后的波形数据。2 c& @5 F( j: c: Y6 h4 b, _4 I
+ b+ |+ A$ n; h! {3 G W
上电后串口打印的信息:! {+ T$ h9 d. L' \
$ \1 e7 w: E* z% t2 L: |% a波特率 115200,数据位 8,奇偶校验位无,停止位 1。/ y# \8 ~# O0 H
3 x8 ]2 A: X9 d4 F& \- g
" D' S9 G. Z' A3 C
& B9 O! p9 ?" z' l2 RRTT方式打印信息:
# a* y, V- E! O* p* G
( D: s$ s3 w( I' b
# v4 V( N) A# Z- W7 l' N' M- J, R0 Z+ W a$ T+ _9 \7 `! j" f
程序设计:$ Q2 e; C5 R. i( }% c" e
7 X n7 p' \9 K( s
系统栈大小分配:
+ m$ D" ^' Y. E- e3 m0 x# H
, N! t) k. u0 x4 r, m! o# A( u5 _# C8 K
/ H. ?! Z/ `# l- ` RAM空间用的DTCM:) r G* ]: q. a( ~3 t7 K0 @
0 R* u" ]1 J1 s$ ^$ V
1 L4 u, _. w _4 f! D7 b; E# R& l' e# t; s3 e2 i
硬件外设初始化 W4 v* V, n+ F% e) B
* G# }6 W+ W; m% R4 F! C硬件外设的初始化是在 bsp.c 文件实现:
$ V5 ^4 a2 h% L- k/ T( s" c. C1 E
- /*
4 i3 C; D* t4 k" X& p - *********************************************************************************************************1 Z' ~7 M5 d( [1 H. y
- * 函 数 名: bsp_Init8 o a. ]/ @0 Y7 p& Z7 D8 z
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次- J: o2 z' j! s- |+ T
- * 形 参:无) @& V$ Q% N0 q% ^; v7 g5 W M% [
- * 返 回 值: 无
6 H$ K8 y2 |! H) S" M - ********************************************************************************************************* f: R9 }8 G0 n% w4 h
- */
: G- ?% o# m2 l - void bsp_Init(void)/ A6 L& s4 D( \3 ~
- {3 R4 E- I( O% \0 n. p
- /* 配置MPU */1 P. ?5 r4 s2 `& Y. k5 B# `
- MPU_Config();: G3 m2 ?/ D J4 D
7 `* B' j+ p+ H/ P+ D1 D- /* 使能L1 Cache */
/ L$ Z1 |7 Q; h: g9 h0 l - CPU_CACHE_Enable();
, K* x' I; O( `* p, s7 \ - ; H, @! n) b) f
- /* ) V4 C/ X6 D' r1 y/ S
- STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:! m! Z+ ?& O2 U3 V4 _. d* d' k
- - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。+ C7 g3 z1 w' X3 s- R
- - 设置NVIC优先级分组为4。6 K7 T* |4 ^" t; W9 L* F1 [: x
- */8 Q Q0 P) n$ z5 F
- HAL_Init();# W/ Z! i1 a3 _! `0 b$ Q# H
- ) o2 e! b0 V* `) l7 P
- /*
; y4 X- n, Y, S$ g/ o - 配置系统时钟到400MHz
! J( t+ R- y& c1 I5 H& R - - 切换使用HSE。
. n& F# M6 A1 G7 |( U& L - - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
2 V' d. d$ Y% O5 o - */
2 |6 {; ~: H' f' U; D3 b2 N - SystemClock_Config();* C6 S/ w. W0 i% [! S7 O
- 5 h2 B4 a1 S! o& ^
- /*
8 R. R" ?, A/ A s - Event Recorder:
1 g& w: k. q; [* M) }- N5 q - - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
3 f" ^0 ]. B- |0 ~ |+ r G - - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章9 ~. d y8 c- L8 k2 I. |! W$ k
- */ 5 U$ j; K3 N" o+ k' q6 Z
- #if Enable_EventRecorder == 1 % d% n, {2 z- [7 L+ I7 \0 Q
- /* 初始化EventRecorder并开启 */
( Q4 x/ M+ V6 D8 k1 S8 p - EventRecorderInitialize(EventRecordAll, 1U);7 G' R! _ @& J: F
- EventRecorderStart();, R0 x0 s; D& Q9 h7 [ \5 N; p s
- #endif0 h7 o6 O+ m5 m$ M& _ _
- 8 R) s6 O) V8 N. p4 _
- bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
6 c5 K% h) [2 W8 z0 |6 K8 I' { - bsp_InitTimer(); /* 初始化滴答定时器 */) R2 C" k3 v2 \( f2 _/ p
- bsp_InitUart(); /* 初始化串口 */
6 u, }2 s# X# ^4 N7 Z - bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
' r+ \" j7 I3 J' R& N - bsp_InitLed(); /* 初始化LED */
8 T5 O* r8 G+ r+ y9 F - }
复制代码 $ ~2 O, d- J6 D* `3 W3 X
MPU配置和Cache配置:
' F' P% i* |. u6 \; |6 @7 a
4 w* D) O) i4 ?1 s' v2 `数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
/ E3 T: ?4 k. x4 X. f f h" z; M/ c
- /*3 U$ i0 x _$ i c' b8 T! k3 I
- *********************************************************************************************************
m4 E' ?, {5 Z, ]' x" V8 y - * 函 数 名: MPU_Config
2 E. m/ Q+ J3 l5 a9 Y - * 功能说明: 配置MPU
& `- ^9 m( b |! _ - * 形 参: 无% A! H; [! P; C/ }' n0 d5 [0 z
- * 返 回 值: 无
7 D6 `0 v& a' E/ v3 l- d H - *********************************************************************************************************
) Q" c/ s" c7 b9 v/ O9 q v" Z1 L - */
& ?& _% T2 E9 j- N - static void MPU_Config( void )
( B4 i. Q6 C3 E! h( o - {
% D# n& n3 u* ~ [0 R6 d - MPU_Region_InitTypeDef MPU_InitStruct;) S9 e. v; P- n5 W
" I" G P, K( L; ~3 G+ @- /* 禁止 MPU */
' N* b! K( h% d1 H6 t7 [4 ~ - HAL_MPU_Disable();
7 P; S- {+ P3 [; A- C, b - P2 q9 ?* V/ t1 `; V5 F9 Y' h- L: V$ }: r
- /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
2 X! Q) \- J3 f( H - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
: ^" b, b4 V9 n& z - MPU_InitStruct.BaseAddress = 0x24000000;1 g3 F# G* N/ [7 N& k( g
- MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
8 ~# ]& k& T. y - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;$ N) \- A+ F7 \
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
+ H" H3 G% G: `/ \+ Y2 @ - MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;1 t$ z7 B' l( O! g0 S2 T
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
: F4 q4 O2 z2 G - MPU_InitStruct.Number = MPU_REGION_NUMBER0;
8 r! E9 y( S& e- ~ - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
! ~. O1 G, T7 N6 A4 L8 O# q b) B - MPU_InitStruct.SubRegionDisable = 0x00;: T+ z+ L9 ]' W# c* q4 k0 R W
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;( P c5 s \" r: ?5 [
- 9 Z* U1 z( a, E! Y5 M" i, c F
- HAL_MPU_ConfigRegion(&MPU_InitStruct);) n8 E% F! X: p$ h4 H7 ^6 t0 n* f
- + V4 L4 f% n4 r" d8 B& N6 ?4 m
- ! O3 K: `. p, M" e4 Z$ ]
- /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */6 T, I& x" s/ g3 M* k1 I# ~( q. V0 l7 S
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
8 G% x( M+ z! D# o6 E3 K$ W' G0 D - MPU_InitStruct.BaseAddress = 0x60000000;
& x' L5 E. [# V$ J8 s - MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
: u2 X, _0 m. ]# j7 E8 R% C - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;; r" ^! U$ a8 s" R: U
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
2 M7 y/ j3 Y, f& E - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; q- @0 y/ W0 K+ _) q3 E' h* e# U
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;0 P, S; f9 }, d. s
- MPU_InitStruct.Number = MPU_REGION_NUMBER1;
- w: Q/ c: |/ R4 q8 G6 ?: |5 k2 K - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;# }7 e" s, b/ T& n: B! a2 p
- MPU_InitStruct.SubRegionDisable = 0x00;8 T: y% U' m$ ], P6 c; L; @' s
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
6 w0 a' `) [2 n6 q9 t: \
9 j! h4 B3 x' ]+ E' g, j) r8 |: g* n- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- |3 ~5 q6 |# _$ d& P
" W& L; L5 W' ?7 G( E2 r, ^6 M5 a8 ]- /*使能 MPU */" e9 m5 u( r ? h( u4 E% g p
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);. a$ w/ V4 C, X* h* e2 i" k
- }
, r* z- y* O. o6 V( X5 r" H I2 m - 7 g( l- w' F) T8 s1 D% M; Q
- /*& S" n' U) Y9 v! t
- *********************************************************************************************************7 S6 B' [* A v
- * 函 数 名: CPU_CACHE_Enable* G6 c3 x% L3 V+ f+ f
- * 功能说明: 使能L1 Cache
/ K$ y% H# k% |5 y! { - * 形 参: 无$ [' B% g( s$ D
- * 返 回 值: 无
+ E2 N$ h7 k) ~ k z4 ^) k - *********************************************************************************************************' E8 ]/ E, O, r7 G9 d
- */) F4 }6 }, z: `* L
- static void CPU_CACHE_Enable(void)
+ {$ U' k6 `2 K9 U' Q* Z; U - {& v2 ^" l: U0 c% l
- /* 使能 I-Cache */
5 h/ j$ J% c: i; f! ] - SCB_EnableICache();
; }3 A0 P. A* G( U. L9 Y - 1 f4 p% B8 r+ y) A- k. _! |: ^
- /* 使能 D-Cache */& C0 \ u$ X9 ^. G
- SCB_EnableDCache();: \+ ~& l# q# |% M- e
- }
复制代码
1 b M- u! ^* n; Z' s 主功能:( X, a* ^ k. s9 b% l1 E
$ M' U; G# D" G* \' W主程序实现如下操作:# A1 h! Y) u6 H. c
" b/ C1 J& y @% q$ Y) u
启动一个自动重装软件定时器,每100ms翻转一次LED2。) `: g5 T e4 H3 Z; A. F
按下按键K1,打印原始波形数据和滤波后的波形数据。2 A7 k8 ?+ y8 L" v7 E
- /*
0 P9 n$ {2 g7 B l - *********************************************************************************************************
+ m1 _* N! d# d& c& }! f' { - * 函 数 名: main
9 M; \9 Q3 i7 w6 {' j - * 功能说明: c程序入口
# A" w: z6 M" R( N% N( @) [ - * 形 参: 无
; E. b' c) f/ _' u- z: ~3 H6 Y - * 返 回 值: 错误代码(无需处理)7 ~% ~5 u7 \* T5 C- ? C/ q
- *********************************************************************************************************# r) U7 Z7 S. ?; c
- */3 R& U# ?3 U9 G4 q" ~ f
- int main(void)
7 k6 Y# r2 l) t9 R! C4 m& Q - {( ~3 b" u7 i5 H5 y
- uint8_t ucKeyCode; /* 按键代码 */, x0 v$ b8 [& U# D7 a
- uint16_t i;
* K, Y. s6 x# @# F
' z5 @- ]% z Z8 Y' N" S8 o
3 n1 C* d1 ]3 B I8 ? ~! c- bsp_Init(); /* 硬件初始化 */
/ T) G* M/ a& I+ x3 X9 U( C - PrintfLogo(); /* 打印例程信息到串口1 */
1 @6 D1 K2 \; ` U" q h" v - $ j, r- T/ {: r9 R: o0 o8 u9 b
- PrintfHelp(); /* 打印操作提示信息 */
5 N: }3 w6 I9 S0 V
# ` ~5 a( x( C- for(i=0; i<TEST_LENGTH_SAMPLES; i++)* ^( z( l9 B7 P* q$ [
- {
2 f C! P0 {& a0 Q6 e- g' g - /* 50Hz正弦波+200Hz正弦波,采样率1KHz */
; a2 p4 l3 `4 {1 a9 X$ i - testInput_f32_50Hz_200Hz<span style="font-style: italic;"><span style="font-style: normal;"> = arm_sin_f32(2*3.1415926f*50*i/1000) +
/ D2 j& u2 z0 O2 g5 m( l - arm_sin_f32(2*3.1415926f*200*i/1000);+ G7 }; O% _: ?; w7 }3 R4 @
- }9 d) m o3 t, D
- * x; I( p2 O. C2 t
7 S! B% P0 t# j- bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */) [; X+ P& n3 U+ @1 Z0 |8 C1 A
- ! d1 y) O& ?/ O3 O
- /* 进入主程序循环体 */
g3 Q1 A/ W9 ~* e' @7 L! l - while (1)( N# Q% L5 V* f6 q
- {1 X$ l, `) {( c5 Q' u' @( m; @ ^
- bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
9 {' `, ?. x# x0 O# X - 2 D+ {- l& Q: \6 e6 {9 Y
# G7 G- z- M2 }0 f) }7 N! h7 U- if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */
6 d ~6 a& _. z# B8 S - {9 k1 N0 X1 S% C; q! @8 }! Z4 o; s: g* ~
- /* 每隔100ms 进来一次 */5 h9 ^7 P9 c& Y# K5 v
- bsp_LedToggle(2); /* 翻转LED的状态 */
, w. g. x4 {( l, P - }
* b# I1 _/ Q% l# f9 s6 {9 H
" z1 L5 E* F5 H1 y6 R$ T$ r- ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */! Q$ S+ F; N) ^# q+ U
- if (ucKeyCode != KEY_NONE)
( Z* [* B/ M# ? - {
* F% |1 E3 v8 |! N2 Y2 W7 @% a; \/ ^ - switch (ucKeyCode)- w! Q3 g" l! v" _, k
- {* x% S- }& d% X& T3 {3 r
- case KEY_DOWN_K1: /* K1键按下 */
! L0 V. q% f/ A& O) Y+ q) t1 p& o - arm_iir_f32_hp();; `7 c/ J# B' U
- break;
9 G( H; q+ B H- ^7 y4 A/ [* m - 4 d4 e8 ~0 |4 F
' {6 |( I4 s2 n0 C' R5 Q( U' z- O- default: u" m/ D" `8 Z& l/ T$ n; g8 o
- /* 其它的键值不处理 */
/ s. \& L- m8 s - break;4 b4 Q: I" W5 M2 W1 h
- }7 l) N& m6 I" d
- }
! e& a7 s( a! t. f; s0 F
! q* b! u% A' ^4 H* k( W! e- }
; P0 }- _" X" r1 ?( P3 Z - }</span></span>
复制代码 45.8 总结: {& F8 p& f( r! r5 B
本章节主要讲解了IIR滤波器的高通实现,同时一定要注意IIR滤波器的群延迟问题,详见本教程的第41章。
- g4 A. g( A( L& |! `5 A' E3 V. a1 X+ Q
% G' d/ o$ E* V4 ~+ b' B+ c7 O3 Q+ S8 [$ \) D
|