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