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