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