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