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