马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?快速注册
x
音频录制以及播放
0 S5 {( N- {$ n) [7 I2 W1 \1 p2 ]4 y6 d+ L" D
一般的音频应用中,往往需要支持音频的拾取输入以及音频的播放输出。LPC5411x具有I2S音频接口以及双通道PDM数字麦克风接口,其中数字麦克风接口支持芯片深度睡眠时的语音激活,非常适合于音频,尤其是低功耗音频的应用。
6 a) P; Q; K! ~! B% U7 K2 L
6 m/ Y* `; Y% P1. I2S
$ D$ W+ p# W0 k( I0 |6 `: N6 H) T
( C, P6 ^; F" KLPC5411x内置了8个Flexcomm接口用于支持串行外设,其中Flexcomm 6和Flexcomm 7可以配置成I2S接口。每个I2S接口只能支持音频的半双工传输,即I2S数据口只能配置成输入或者输出。万利的LCP5411x开发板板载了一颗WM8904音频编解码器,支持Line in输入以及耳机输出,通过Flexcomm 7连接音频输出流,Flexcomm 6连接音频输入流,从而构成I2S的全双工传输。! R/ {: R4 ]" }- Y$ T% m
. `. m' l3 M7 ~$ d* xI2S的初始化过程如下:
* V+ D5 H. Q9 Y2 x: X G: ~( Q: ~
# V+ l+ e8 W+ Q j0 W. o4 t% ja.   在pin_mux.c中初始化I2S的功能引脚。
3 l* z( u8 c. S7 d: d) G3 C
2 S/ {5 m8 _$ ?: }5 t# K// I2S FC6
& Z O) u7 b5 a# }5 r0 h8 }- w, h( o9 c7 Z' e' |4 o* u5 d5 R
uint32_t i2s_config = {
" _3 d7 a* ^3 {3 o5 b( K4 b) F8 N6 @0 s# g; Z6 h4 V- h. p
IOCON_FUNC1 |1 O% B1 \: _$ g m' s2 H
9 J" K4 P; r& e* U, t
IOCON_MODE_INACT|
( ]4 N4 ^* Y2 c- d f
' A# i. J* [, Z/ ~IOCON_DIGITAL_EN|- u5 h& m& Q4 e. c
& a- ?, X% T# Z: N8 |2 dIOCON_INPFILT_OFF
. k4 z! r; M0 o1 c0 y# N' h
, Y: A( M( b$ b) \: O; t* n9 P* E# [};     5 H3 M; L* f3 Q: L" ?
( _* y) i1 x+ k7 j
IOCON_PinMuxSet(IOCON, 0, 5,i2s_config); // PIO0_5 DATAO
0 R- Z8 ~4 @* u7 a# F# G- Q$ i% l8 j/ \% B8 c9 H9 E
IOCON_PinMuxSet(IOCON, 0, 6,i2s_config); // PIO0_6 WSO
! i% M% J" l/ l& B/ B ?8 B l. P* {( j% R0 j
IOCON_PinMuxSet(IOCON, 0, 7,i2s_config); // PIO0_7 BCKO( n( K% |7 l5 i( ~0 x) z a2 _
. U& ^ l) W0 V# p* n. ?// FC7: Y6 h, f$ p* L7 Z
( S6 r) V. T7 n0 i7 P. M& {* l% q
i2s_config = IOCON_FUNC4 |   IOCON_MODE_INACT |: q/ v* i" E: P+ ~
4 q4 L- D A7 M: mIOCON_DIGITAL_EN | IOCON_INPFILT_OFF;    7 s, t0 i* r" q: A3 G: v/ {2 R
" E9 U0 q& R. J
IOCON_PinMuxSet(IOCON, 1, 12,i2s_config); // PIO1_12 BCKI
- ]+ a/ G& n8 ~3 g
' U7 `& C- Z0 \( x; HIOCON_PinMuxSet(IOCON, 1, 13,i2s_config); // PIO1_13 DATAI. k1 A- h; k& [
: {7 i, x6 z' W! [5 i0 KIOCON_PinMuxSet(IOCON, 1, 14,i2s_config); // PIO1_14 WSI: a% L8 J! a) ]1 w- e$ u! b/ Z2 A
* b5 }9 }; s+ V, z3 ?
IOCON_PinMuxSet(IOCON, 1, 17,i2s_config); // PIO1_17 MCKL9 U( U( Q; t0 i9 z
& J# I6 j) E6 i' w4 K* f; I+ B
b.   设置I2S外设时钟,使用PLL时钟,24.576MHz。
! {" L" ]5 k/ @ H& S; V. q& d' r4 \& d, {8 T |
const pll_setup_t pllSetup = {/ y3 f1 O. x4 Q- z; O4 C& R
) P5 f, K D- [4 `8 a
.syspllctrl =SYSCON_SYSPLLCTRL_BANDSEL_MASK | SYSCON_SYSPLLCTRL_SELP(0x1FU) |SYSCON_SYSPLLCTRL_SELI(0x8U),
$ F1 ^! {* s! {) L8 c
7 S: F9 P) ?" }$ T; X, w# ~.syspllndec =SYSCON_SYSPLLNDEC_NDEC(0x2DU),
2 _. X6 l+ {0 H4 y4 A: x7 d1 s0 i/ w4 e) S) O5 q
.syspllpdec =SYSCON_SYSPLLPDEC_PDEC(0x42U),
8 J6 S* P7 t' w. |# F1 b; S3 _
, F3 c; [4 p4 T9 ^% X.syspllssctrl= {SYSCON_SYSPLLSSCTRL0_MDEC(0x34D3U) | SYSCON_SYSPLLSSCTRL0_SEL_EXT_MASK,0x00000000U},; _5 d/ X3 P# h9 P
6 P$ P/ s! e% H: ]/ c
.pllRate =24576000U,& t) K4 Y% j) V7 V& g
" q# X6 ^% ^( [6 }6 R+ V6 z
.flags =PLL_SETUPFLAG_WAITLOCK};
. Z# E" b. y: o: n- \2 S3 L3 r5 H2 j
+ @1 q0 u4 O* H }1 c 
) r( G" o+ T, d9 t& `8 @6 s6 i: a: r7 j7 ~# E/ q
/* InitializePLL clock */
f% W0 y9 C8 W. {( }; S5 R8 j7 `! M
CLOCK_AttachClk(kFRO12M_to_SYS_PLL);
/ s0 Z ~" w% M7 s% G" z E& R* O6 m4 V5 N
CLOCK_SetPLLFreq(&pllSetup);$ N- e+ A$ [9 @0 W4 x. O
5 }1 U- N8 o1 g; A- i / l; }1 _+ D9 C7 W0 h( j g
5 t, x% `. D( _8 f6 Y9 \
/* I2S clocks*/
: ^: ?4 @* \5 g* k* O/ T7 E' A1 [' ?1 `; o3 v( ~
CLOCK_AttachClk(kSYS_PLL_to_FLEXCOMM6);: u! g4 }4 m9 G) q* l8 V" I
. t5 v0 G* C* Q* e `7 ECLOCK_AttachClk(kSYS_PLL_to_FLEXCOMM7);2 o* P/ b" F$ b- c- l
8 m* B3 X. q. i) p: `6 x
c.   设置I2S的主时钟MCLK输出,1分频,MCLK输出24.576MHz时钟。6 a W7 E2 K( L o+ j0 Y6 R
0 u: K4 p5 R6 N$ A+ a/* Attach PLLclock to MCLK for I2S, no divider */
+ M+ N3 z% W5 z' Z$ r& `
1 ]( x6 y( X% o! A- D$ XCLOCK_AttachClk(kSYS_PLL_to_MCLK);0 ?( }$ L- ^/ }9 l% W- i
2 C% ^6 O1 I8 \0 f% Y; U) C
SYSCON->MCLKDIV= SYSCON_MCLKDIV_DIV(0U);
% N H' a1 m3 P9 L; S% c( H
" @, o& T4 c6 J. X/ ]SYSCON->MCLKIO= 1U;
0 Q4 v2 O. ^1 t6 X" N& h) j
5 b3 s, V2 U- w8 }d.   设置I2S1为主机模式,相应的数据格式,并初始化。I2S1的寄存器基址对应Flexcomm 7的寄存器基址,在主机模式,由主机产生位时钟、帧时钟这些同步时钟,通过I2STxConfig.divider分频决定I2S的位时钟,在48分频时,位时钟为24.576MHz / 48 =512KHz,16位长,双通道对应的采样率为24.576MHz / 48 / 16 / 2 = 16K。' R- g( v2 A" y( P
- L# D% |; w& ^# D* P+ Q
   /*
P% ^/ V9 d, Z: S) E, c) v2 U1 t+ V( r
    * masterSlave = kI2S_MasterSlaveNormalMaster;
( d2 t3 C/ }% t5 J
# y8 w7 k9 w8 T5 |* \    * mode = kI2S_ModeI2sClassic;
; E+ S4 r F) }% k$ C7 u7 a1 S
/ [, y' ]* I6 t4 e* \/ R6 E) d    * rightLow = false;
/ c' k6 p' j }5 @& \, g" e% u" f Y( L6 t. _
    * leftJust = false;" x: C9 |2 k% h( |5 u, P' a9 I
% b) C0 ]1 U! ^+ R
    * pdmData = false;# d- g2 P5 U$ p. J7 v+ ?
) y+ C- }$ w, Y# H9 b
    * sckPol = false;
' e, u$ I- G$ g: p
T7 S" k8 e( |+ N8 H$ E    * wsPol = false;/ _0 F: t/ K; t6 J8 |9 M/ p+ m
5 ~; G) v! q7 {4 J9 ^7 O
    * divider = 1;
8 T& l2 z2 U5 |+ n3 W- [. l# t7 B
h4 e; a" W( q% n+ T* j8 x0 n    * oneChannel = false;
! B( m5 i1 C- O& @) U2 p' b
- a* M/ q$ [2 k1 ~    * dataLength = 16;6 m( T3 w4 [. H$ p' F
$ N2 {1 E2 W2 C' Z! S4 ?3 d0 i7 p
    * frameLength = 32;
) s/ v0 Q/ w: m3 b3 n4 l/ `/ C% M4 @" P q+ }! _& o7 s
    * position = 0;( m1 T/ `0 F6 R+ e2 \
9 s2 H/ a0 [: B" y( ?' [! @4 Q/ g/ w    * fifoLevel = 4; \, T1 v4 a1 v" K8 ^1 o( {% }
l" W; R) z1 p0 r- B- D    */' `5 Q8 G/ I+ s) g Y7 l$ v* \
% F+ k* X# j1 G7 Z t8 ?6 {( Q. |
I2S_TxGetDefaultConfig(&I2STxConfig);4 p( |: {+ J: Q3 s; d- u, Z
' W8 h2 i7 Z* B9 o2 [$ `
I2STxConfig.divider = 48; //24576000/16/2/48=16k; H( U) H# p: G2 j2 b) I: q
, E0 R; g2 d6 s8 C
I2S_TxInit(I2S1, &I2STxConfig);
" c$ d" B* ]( Y: \! V& i" g/ w* A' h" K3 ^' W9 G
e.   设置I2S0为从机模式,相应的数据格式,并初始化。I2S0的寄存器基址对应Flexcomm 6的寄存器基址,在从机模式,从机的位时钟、帧时钟同步到主机的位时钟、帧时钟。
3 E3 w+ E/ J% \5 J
; c% C' `& r. E2 g2 {   /*4 I5 X; K: c0 h L0 K
' J A+ Z. E2 u; I" j+ S
    * masterSlave = kI2S_MasterSlaveNormalSlave;
7 ?8 Y" V8 h6 X& z6 b. Y, h: `
* \0 L# u0 l! A3 S" u' ^    * mode = kI2S_ModeI2sClassic;
" ~9 E) ]' w5 N' ~1 \
' M2 L+ K( _. V# M! b) `$ i6 ~    * rightLow = false;' f1 w2 S( d7 D Z/ J5 s! `) K) ?
( g: m) l( ^1 f' A    * leftJust = false;
! o' w' g0 a& D5 _" F/ j/ [; o
- N( q" ]9 D- m9 k3 D2 k8 [    * pdmData = false;
/ u1 z9 [6 B- _+ j0 y# V2 o F2 F9 p5 R# l
    * sckPol = false;$ L2 x7 U \0 {9 j- @
$ Z4 ~; ~/ M( |& V% n& r: P" t
    * wsPol = false;
4 s5 J- u4 i* |. v) j$ I1 ^. e0 ]" v7 G
    * divider = 1;
" y( ?4 W$ k) [2 b2 M+ p! y7 O! y- k% p# g
    * oneChannel = false;% A) i- Z3 T1 c" e9 A" }
) g6 Z4 [) V- [2 x" R2 x* M1 l    * dataLength = 16;( n# u* `) I( m5 n9 {' k
" Y9 o2 ]5 F- x9 d% [    * frameLength = 32;. L9 r" }) D* g, N8 {3 s i
2 H2 e4 J! h* D    * position = 0;
* S' p" \ |5 P: v" E) y9 b+ I4 w5 f5 K
, g' g; v" Q0 I0 g6 \5 C* q    * watermark = 4;) z, r2 y; r0 r- H/ o7 a( R c
! c @2 B8 l! f    * txEmptyZero = false;
" @! M+ z# d D j9 D: y0 n. H( [& v
    * pack48 = true;
& E& _7 g9 V2 u7 }& \- W& i( L# `) J% _1 Y
    */
9 O( l% v/ r) x+ ?; b8 R3 Q4 h d6 t+ b$ f& i+ u1 M
I2S_RxGetDefaultConfig(&I2SRxConfig);+ ?. `: W0 h9 M7 q* c$ i
% m% r5 K7 B" W7 @1 W) [8 T
I2S_RxInit(I2S0, &I2SRxConfig);( O0 d" I, [8 z
& s; D+ r; t! F& d! n" Mf.    设置I2S的DMA通道传输,DMA传输可以减少CPU对音频流的参与处理,从而降低CPU的负载。通常音频数据的处理以帧为单位,方便音频编解码,打包传输等处理,当DMA完成一帧缓存传输时,通过中断通知CPU处理下一帧缓存的DMA传输。5 z0 a5 u, E: N$ L' o7 T1 J5 ^
- S' E: t9 _" Y( G& C9 W# J7 o
DMA_EnableChannel(DMA0, I2S_TX_CHANNEL);
8 s1 `+ D2 k! q, m: k5 y) _, N* P5 \4 |* o7 N
DMA_EnableChannel(DMA0, I2S_RX_CHANNEL);
- w4 w: q3 `9 l" F! _
5 i. G! S1 S l/ [ g( F1 IDMA_SetChannelPriority(DMA0,I2S_TX_CHANNEL, kDMA_ChannelPriority3);- [0 \, k+ d Q2 e' f7 X" `
/ S9 X2 M4 b& [* o; v# T% _, HDMA_SetChannelPriority(DMA0,I2S_RX_CHANNEL, kDMA_ChannelPriority2);
- o% c+ S+ M- p) H* q6 r
7 w" z' Q1 i6 J0 [$ [& i& L/ `DMA_CreateHandle(&I2STxDmaHandle,DMA0, I2S_TX_CHANNEL);0 ?. g- U- @9 T
, g' U; J. {: o gDMA_CreateHandle(&I2SRxDmaHandle,DMA0, I2S_RX_CHANNEL);2 v) c$ G- q2 Z( ~" X" w( c& y0 X
- w! h% J+ O! `' E# j) H! G+ V
I2S_TxTransferCreateHandleDMA(I2S1,&I2STxHandle, &I2STxDmaHandle, I2STxCallback, (void*)&I2STxTransfer);- A! ]+ |/ f8 G0 T2 f! o* \
2 c: ?* d* t6 E$ x
I2S_RxTransferCreateHandleDMA(I2S0,&I2SRxHandle, &I2SRxDmaHandle, I2SRxCallback, (void*)&I2SRxTransfer);5 { v* l: \0 {# |3 D2 S. M2 G1 Q+ w
2 t- u, S( F' k1 q. x. mg.   I2S准备就绪后,可以通过I2S_TxStart()函数启动输出帧缓存到音频输出流的DMA传输,通过I2S_RxStart()函数启动音频输入流到输入帧缓存的DMA传输。
4 Q5 J2 a' w7 y4 [, ~) M) S: x& ^4 f- D/ z& b" K- @# V
void I2S_TxStart(void)
; w$ R6 f( ]$ \, {$ w3 T: p( u
- x' z& j K' w; I" B' `' e6 T{2 V! F/ R* d1 g9 z- [
) u/ }5 d9 K- N. M6 \$ KI2SState.TxReadIndex = 0;( o7 g+ j$ L+ j3 B! g! K* `0 h! u% R
1 O: T* e) @5 Q7 G3 c: s. n
I2STxTransfer.data = (volatile uint8_t*)I2SState.TxBuffer[0];
+ A2 _( G) P: e! q" f
" A! S& X' |& mI2STxTransfer.dataSize =sizeof(I2SState.TxBuffer[0]);, I. Y( z% `# ~# L4 z3 [
) Y7 Z0 ^9 L' e4 P9 d }& S2 B, o* w
I2S_TxTransferSendDMA(I2S1,&I2STxHandle, I2STxTransfer);
9 k" ~: u z" K) @- ]$ H+ T# h7 @& n/ T6 s: P3 d7 v( N/ ]( `
I2SState.TxWriteIndex = 1;
]) ~* U( d7 V& ]5 i& u# _( j+ Q7 w: t
I2S_TxTransferSendDMA(I2S1,&I2STxHandle, I2STxTransfer);
' }: c. d* Z5 m9 o) ~# M$ Z, T1 f' l( g
}& ?" O1 b' E8 e
" Z& U8 R% ]. ~7 Z2 G$ C 
# s4 H5 ]% B# x- {2 t: N$ m R
. H( t# d# i* F+ g; Qvoid I2S_RxStart(void)
1 V, C. ~' K C5 N& A& }: b" q, p4 `
{, }* l9 Q: g5 l' R
9 o3 c% ^1 M% x1 v O
I2SState.RxWriteIndex = 0;- }1 h: G3 q& T0 \
: \' o+ R& S3 Y, V( R Q
I2SRxTransfer.data = (volatile uint8_t*)I2SState.RxBuffer[0];6 t9 D# Q& [ U8 M- c- {4 q
8 M- T, y8 x& P; V! AI2SRxTransfer.dataSize = sizeof(I2SState.RxBuffer[0]);6 e8 E9 N% \' ^9 l% N
( R7 _( o" ~+ K$ L* [8 [: iI2S_RxTransferReceiveDMA(I2S0,&I2SRxHandle, I2SRxTransfer);) g1 ~ g+ U0 r( k7 }
. v: r0 h# @" ^9 L8 t1 q
I2SState.RxWriteIndex = 1;
- u# a; R+ Q( G: Q! O: ?
$ k) i( v0 g! u( ~& G- F$ x* y6 lI2SRxTransfer.data = (volatile uint8_t*)I2SState.RxBuffer[1];
5 x" Q& w X3 Z# L* O! W" N% i/ S( {0 o' |3 ?
I2S_RxTransferReceiveDMA(I2S0,&I2SRxHandle, I2SRxTransfer);
2 N; g' T/ m5 K! }4 q
( t* Y, F6 M3 F' Y5 i7 ^" ]; c}
z1 M, {3 U; T' [- C- G# R
' Z1 ^! {: b( l5 f% J1 p/ c, Q2. 音频编解码器$ L7 x2 o& f' ]9 @, U
( u& |; q1 ^ j! e1 m' Z开发板板载了WM8904音频编解码器,通过I2C设置WM8904内部寄存器,设置为从机模式,相应的数据格式。/ K2 g( @5 |+ z* s/ Q0 R- y; b
7 N: V$ n9 {( T; G; t
a.   在pin_mux.c中初始化I2C的功能引脚。
3 S% D* _$ I5 g: X8 s [* d# E D1 b. d. | D! C
const uint32_t i2c_config = (% p8 ]' x* J! f( [1 B/ o9 b
$ |/ U8 F% X Y E: ?IOCON_FUNC5 |+ q1 r p3 z+ S- ?) ]( h' S. A
% {/ t& [* {- ?5 f- P
IOCON_DIGITAL_EN|
5 T# Q4 g! z0 Z( E* }& \ E
3 i& h3 R+ V9 l. r' v+ {IOCON_INPFILT_OFF|( p5 V: z( o: ^
$ n) h+ X8 T4 Y* yIOCON_OPENDRAIN_EN$ `9 M, [% X. a9 l' A
. V- F0 |# K8 D);
( h. T( d/ G4 f: f7 v7 H+ d& Y; m# X# T; o5 u
IOCON_PinMuxSet(IOCON,1, 1, i2c_config); // PIO1_1 SCL
5 I3 {% \0 v$ v% ^) r
: v* o6 Z0 v! @0 LIOCON_PinMuxSet(IOCON,1, 2, i2c_config); // PIO1_2 SDA
. }0 [4 o" ~; t8 e6 b6 h( E4 D3 @7 @/ w, I) d) _6 }$ B, S
b.   设置I2C外设时钟,使用FRO时钟12MHz。
% s+ `7 Y- s$ [2 \& `
" v2 V( Q$ U3 {/ j/* I2C clock */
4 J& a S5 }! \2 [: x8 }( E* Y
# ` `) C( m8 I& h6 o9 E/ b# ]' q+ ~CLOCK_AttachClk(kFRO12M_to_FLEXCOMM4);
* ?" `5 U4 a0 w
! ?0 C2 f8 d+ _% d/* reset FLEXCOMM for I2C */
( u9 a6 r; n3 ~6 k. M% L1 a
# h7 n2 |2 h7 `; n) ]RESET_PeripheralReset(kFC4_RST_SHIFT_RSTn);      
3 Z; J" F3 D8 H. {8 ]8 Q
* E N! j0 o0 v0 e1 c+ rc.   设置I2C为主机模式,相应的波特率,并初始化。7 _- s. x1 O6 I/ A" d J
- F7 r' |- H- L$ ?/ J) s4 ?
   /*
! F8 t! ]2 B, }# y" K9 ]. r* ]6 ], g, C' v; o! W8 W N
    * enableMaster = true;
, E# G7 {6 ]2 d: M' z' w; k( t, @
' [3 H/ G, u7 ^1 A& X! o0 Z) y% X    * baudRate_Hz = 100000U;/ d- k1 R* B( l- D, z9 j
9 Q, H; Y2 B# g1 e    * enableTimeout = false;
^# _9 h5 e7 k0 o+ d2 E0 f1 d' f
1 @9 y) ?8 v. M* ~1 ~2 R    */
G/ v Z- u7 t. }5 ^1 S
' |. u9 x1 K" t: t% J6 vI2C_MasterGetDefaultConfig(&i2cConfig);
& r* r- |6 {, n" d3 W1 E5 n! P' o5 D o+ {% ^6 V, d6 @% H. Z/ j
i2cConfig.baudRate_Bps =WM8904_I2C_BITRATE;
$ I( D# Y& m3 c. ]: O
9 j, k4 Y4 y, Z* l" |/ `/ yI2C_MasterInit(I2C4, &i2cConfig,12000000);
% W4 Y4 G* ~( V; e6 `$ m6 Y2 s$ n( U& S
d.   初始化WM8904音频编解码器。
8 j0 V3 m! R6 o7 g$ t" u2 o% D, S- u( C6 D- j: e
WM8904_GetDefaultConfig(&codecConfig);+ _% ~! x6 w! N6 t! p% M' F |
1 I( ~' K0 Z) FcodecHandle.i2c = I2C4;9 ?$ `% z4 H9 Q! J: T6 t( |
3 i7 X- ^- @" o$ S7 Fif (WM8904_Init(&codecHandle,&codecConfig) != kStatus_Success) {
* v1 ^/ D# Z: U: x6 R$ H! h. V" U+ U" C/ S2 `7 v& Q1 f
PRINTF("WM8904_Initfailed!\r\n");
4 l8 P' O4 j! y( j$ T% R$ W' v
: T3 Z* a$ i( c& [+ rreturn;
4 Y7 h# J- {0 G) {- F5 h+ }1 [" }6 d6 w; w* k5 ~
}
p1 s) x _2 n# A% V3 d/ |+ @& e+ X
/* Adjust it to your needs, 0x0006 for-51 dB, 0x0039 for 0 dB etc. */, D2 U1 d2 O$ ~% N! \
" \& |, y Q8 N9 @8 }( M5 V( d+ ]
WM8904_SetVolume(&codecHandle,0x0030, 0x0030);
7 v: B# K# K' k5 F! ^8 N
2 t4 h3 A$ ]- O! a% H" W8 i3. DMIC2 ^7 l1 I1 r/ R7 H6 E) f
( x) H8 ]0 P# C+ M$ X, J
DMIC (digital microphoneinterface)支持PDM输出的数字麦克风接口。开发板板载了SPH0641LM4H数字麦克风,从数字麦克风获取的PDM数据可以通过一系列的滤波器还原出相应的波形,再采样获得对应的PCM数据。PCM采样率由DMIC时钟以及过采样率(OSR)决定,采样率公式如下:
E" o) R3 A" U/ o2 B" A% t$ R; K7 k/ c- t; C' Z! f/ u
4 C9 ~- h+ c8 }0 F, |1 Y8 j; l; Y4 m I+ {
例如,DMIC的时钟800KHz,过采样率OSR为25,在2 FS模式,对应的PCM采样率为16K。
+ x' E5 N1 Z, w; w( b! ^( _2 k! n, Z t( ^5 X R5 A
a.   在pin_mux.c中初始化DMIC的功能引脚。- V) @' s5 l6 E" V$ ^
6 s6 ~2 o2 ?4 K- `. Mconst uint32_t dmic_config = (- h8 f. B2 N4 `/ V- ~ G- f
7 A- K3 L* Q/ Y4 M3 M: UIOCON_FUNC1| $ {, ^" h* G( X2 o8 }% D/ H/ j
k9 t, H, D( a @( N$ Z8 u+ G pIOCON_MODE_INACT|
. y; `8 T. v. b8 Q: G5 S4 q# D
$ y7 X# ~! Z$ N/ Q1 E2 XIOCON_DIGITAL_EN|3 T5 X: I5 {0 n* b. M
4 x) j5 V2 Z% }4 }5 l0 v
IOCON_INPFILT_OFF/ Z: Z& \! F( V' L7 l6 k$ y( I) o
: i8 |! o$ Q7 }/ o3 M
);
: B6 i) W- ?) o% h- N% j0 |$ ^) \# } Z4 @1 L
IOCON_PinMuxSet(IOCON,1, 15, dmic_config); // PIO1_15 PDM0_CLK9 y0 W& c6 K% ^* n# M3 S
# A- [! L8 k6 ^& [5 p: [
IOCON_PinMuxSet(IOCON,1, 16, dmic_config); // PIO1_16 PDM0_DATA
& e* j: `/ W2 ]$ P( X# _( [) l* W9 L% Z7 q
b.   设置DMIC外设时钟FRO时钟12MHz,相应的接口时钟800KHz。" }5 r& n) Q8 t2 f5 p! A4 f. g9 e
# b% K `: ~+ C [& T2 |
/* DMIC uses 12MHz FRO clock */) }$ }4 |+ \9 A# s. t
6 x/ }2 L( z; ] ECLOCK_AttachClk(kFRO12M_to_DMIC);5 w3 a2 {; {4 `! X1 d
2 d; w) P, Y! c$ b* x, j: S* B9 W 
4 F" B: q0 @2 P
& `4 D9 N: F, L% [/*12MHz divided by 15 = 800KHz PDM clock*/
; a" b3 T6 d1 @* P0 t8 o" D, L& u0 K6 d6 [+ b
CLOCK_SetClkDiv(kCLOCK_DivDmicClk, 15,false);
0 T( w% F& ?' |: s$ ~, ]
' H" `6 |: p1 ]9 ^c.   设置DMIC过采样率,相应的增益,16位数据,并初始化。. X1 c8 ? @% B# T h
3 H; U; d8 k: m0 n( n# N
dmic_channel_cfg.divhfclk =kDMIC_PdmDiv1;3 Z& O( l$ n$ G. Q* @% T6 |1 n
5 F# N+ k6 K y" {3 {; m
dmic_channel_cfg.osr = 25U;
4 E7 {/ _$ L7 u5 y- v
( k) h0 H8 m* k" ^$ E1 idmic_channel_cfg.gainshft = 3U;
5 z# L4 [- S4 q4 n7 M" t
: o7 F! d) j9 @' qdmic_channel_cfg.preac2coef =kDMIC_CompValueZero;6 I' L7 u" l* Z& x6 m* Z) H) ^
) J% X1 L, z5 H% u( fdmic_channel_cfg.preac4coef =kDMIC_CompValueZero;' {* x% E \, g" \: q3 B6 ]
' z. V% N2 s! H* [& gdmic_channel_cfg.dc_cut_level =kDMIC_DcCut155;
/ t& |9 _4 A7 F* n+ Q% j9 K$ o6 y
dmic_channel_cfg.post_dc_gain_reduce =1U;
6 G5 [! |7 R' [* v, s
/ c5 D7 {, d6 `. J( zdmic_channel_cfg.saturate16bit = 1U;
( K* V' q& n' w L j- Z6 Y
, q. r' I d, ]( u' N8 R, J+ wdmic_channel_cfg.sample_rate =kDMIC_PhyFullSpeed;
: L5 ^! H2 M7 a: J
9 K' D0 v; P6 g7 ]; ]" MDMIC_Init(DMIC0);7 E" g( b" y" D1 {/ _
: R, E, q' b- k; m, I, XDMIC_ConfigIO(DMIC0, kDMIC_PdmDual);( D& b) F7 W" U8 |
! i1 z' B- J, i# `' y
DMIC_Use2fs(DMIC0, true);
* H9 ?" l* s! l" m$ d( c% N8 n: O" U" C+ b0 C' A
DMIC_SetOperationMode(DMIC0,kDMIC_OperationModeDma);
& \1 L' C9 M0 |% }, A7 z9 B# U
DMIC_ConfigChannel(DMIC0,kDMIC_Channel0, kDMIC_Left, &dmic_channel_cfg);
6 h% g, n R, p6 L8 T
( _/ z- v) s% f- X, {5 i3 }DMIC_FifoChannel(DMIC0, kDMIC_Channel0,FIFO_DEPTH, true, true);
1 C) R0 O1 [& K+ |: `3 @# F, j$ H9 R/ n/ }5 B9 @6 a( g
d.   设置DMIC的DMA传输通道。
/ T9 P: R5 B# D& e
1 h1 b+ }2 N, ^, |DMA_EnableChannel(DMA0, DMAREQ_DMIC0);
4 Q* A1 \2 k! H5 w- [ n. M0 T% C4 o) r0 y" H+ |5 T5 g
DMA_CreateHandle(&DmicDmaHandle,DMA0, DMAREQ_DMIC0);
6 q7 V- e/ y4 S& I1 n+ G9 t5 @4 U0 s/ L4 c4 R1 b/ D# V( ]
DMIC_TransferCreateHandleDMA(DMIC0,&DmicHandle, DMIC_Callback, &DmicTransfer, &DmicDmaHandle); 
& A3 b( k% k5 ^: d
4 g* x' A8 ^* b+ a' sDMA_SetChannelPriority(DMA0,DMAREQ_DMIC0, kDMA_ChannelPriority2);3 }' {1 U. ^7 W" q1 O
# T1 H: q a" Q( ee.   DMIC准备好后,可以通过Dmic_Start()函数启动DMIC音频输入流到帧输入缓存的DMA传输。
9 l7 ]1 z" l* z. x/ u
; m* _8 |9 |8 t3 r2 Q4 H* |8 tvoid Dmic_Start(void)4 `5 n- r6 ?+ B3 K4 ?7 B
/ z8 Q& o$ x; u8 V: H{2 G0 x. `0 P0 U+ {1 U
9 i- w* Y: n% }3 S% m' _6 k
DmicTransfer.data = (uint16_t*)DmicState.Buffer;
0 q; ~9 }: w( w3 V4 {; e3 i3 h e- y* Y3 j" W2 q
DmicTransfer.dataSize = sizeof(DmicState.Buffer[0]);) t1 u0 \' S$ n/ Z+ V1 r' \8 v; m7 G
# N2 m" P# b- h! p! j0 i
DMIC_TransferReceiveDMA(DMIC0,&DmicHandle, &DmicTransfer, kDMIC_Channel0);( p# n+ @" J8 Z( E2 G" i% D
4 v: t! R/ j7 j; x! a+ k6 ]2 nDMIC_EnableChannnel(DMIC0,DMIC_CHANEN_EN_CH0(1));           
3 `4 \ Q7 X% i% Q _% K- K' ]4 t/ D' s% F: u# z
}, t8 h( G- l" D
, t1 S& m8 \4 E: L; n) T) v
4. 应用例程
6 b8 l! g, _9 F
$ @- w5 ~( ^' _6 c9 q Hmain函数例程实现把音频编解码器输入流DMA传输到音频输入缓存,音频输入缓存不经过任何处理,直接输出到音频输出缓存,DMA再把输出缓存传输到音频编解码器输出流中,实现音频编解码器Line in的回放。也可以把数字麦克分的输入流DMA传输到麦克风输入缓存,并拷贝到音频输出缓存,DMA传输到音频编解码器输出流中,实现数字麦克风的拾取回放。& {5 X( X" F0 a3 F1 e
" H. N: `6 u. I" ^3 m* _int main(void). f! G. h( E! v5 e6 I4 U
: p1 z6 m: @- H{    5 ~ _8 l g* B9 Z
; c5 o5 m4 C$ ?$ X7 J! @
int i;" I" N! X2 P, t2 X4 Z0 P
8 g! i4 G/ b0 m! Vint Select;
" O3 y$ s+ |0 u. z9 k$ v( g3 i) ~+ N K4 p& s( k/ J
      
@. w6 K- o7 }+ s+ J9 F3 r4 c* U( o4 ~# k
/* Board pin, clock, debug console init*/0 N2 G( K5 p) H1 f1 m
4 `. ?( j) N+ f A3 o
CLOCK_EnableClock(kCLOCK_InputMux);( h3 [/ Z" ? F# S
! G" O2 ^! U5 n0 w) \' i. j3 [
CLOCK_EnableClock(kCLOCK_Gpio0);/ b1 f9 H/ U% h
3 a' k) ?5 G1 ~6 t4 Q4 F
CLOCK_EnableClock(kCLOCK_Gpio1);
2 {5 D; g1 E* Y2 K, W7 z, L' c- j' q' Z; ]0 x
 , |+ d+ `0 t& m) p
9 H# B1 R0 G( K. T/ _2 R' N
/* USART0 clock */
, `8 B: @* ^ ~5 ]/ m/ F& W3 O! X5 o
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);( q1 _. O6 q- b# d6 R2 b+ P
6 _& d5 ^$ P/ B. }" u
BOARD_InitPins();0 e3 W6 J8 t3 z$ u
/ ]. a/ Y3 _9 Z# g
BOARD_BootClockFROHF96M();
& S+ v. ^' |6 W+ z" ^+ D( x" k# |5 t6 [
BOARD_InitDebugConsole();
' a) w. ]; G3 C( Y! B
1 j7 j2 @/ U8 B7 p7 @" i      + B# D. s5 J; I' ]1 l
& E* `6 B1 w" z; I: c
Gpio_Init();- V9 x4 w/ i: \
; h7 \/ U2 C0 r. Y/ xDMA_Init(DMA0);
* ?$ v% a; K0 f) a; P+ F
1 ~6 q$ |7 J+ h& \3 @" g3 {& BI2S_Init();+ G# r2 _' c! Y2 |( }6 i' O6 r" U
0 @! g( P' \. B$ s! g" J
Codec_Init();- T0 o! @' B3 @/ v
# i6 B+ E# y6 m
Dmic_Init(); 2 X- `! E/ i- w+ _
& g+ I( N# h7 B! W8 r
I2S_TxStart();; \& r* d! R2 L- _
: l9 W- r7 V+ M. h      
% A: L2 A# A3 X6 p0 o! @+ ?
. u) G8 d m' K/ p+ TSelect = 1;; L$ T( A/ j& @: l4 w1 z
( P Q5 \* ^& g) K7 [4 t; eif (Select) {
% E3 S7 W t; ^+ Y; O- ?" b( P- H* |1 Q+ v7 M$ h6 `, c6 ^
// codec linein' |$ v! O4 `! I: s: ^
$ N$ ^4 `$ M0 o4 u& w" X% xI2S_RxStart();% a0 W8 L' O% J* l
+ h' a3 ~7 z7 j P6 V$ G( A- k$ n0 e
       while(1) {
, g9 @! {9 @: Q3 p2 S" C/ n0 h, E" Y @ h# x' l; U% o
       if(I2SState.RxEvent) {* \% `; {4 h& r* I& n! z
$ ~6 Y- n2 T @( J* v8 M       for(i=0; i<AUDIO_FRAME_SIZE; i++) {
6 e# c& D! v; B, k: \1 x
, |4 `% j* N2 bI2SState.TxBuffer[I2SState.TxWriteIndex]= I2SState.RxBuffer[I2SState.RxReadIndex];+ [% M7 d4 \; ?" j5 U
3 \" R- Y+ s* B4 s       }% C8 T6 Z8 O9 n; O
! K! e! A- ~( ~, O
       if(I2SState.RxReadIndex >= AUDIO_NUM_BUFFERS-1) {8 g, g2 R9 t1 u
9 ^) @2 `) `/ r( @! L" d              I2SState.RxReadIndex= 0;7 M) r+ g. U1 U0 A- s8 G
0 C$ ]# ` E7 C0 f
       }else {
& f, o4 q$ V/ T" T7 X& l/ |/ g5 j( b1 ^9 X
              I2SState.RxReadIndex++;) L% O& u" m( W( T% _2 G% `
" Y" @# f9 }3 r8 }! n9 y
       }
6 ~; L8 [$ G( @% W( z f& i1 ?. q% j% ]9 @
       if(I2SState.TxWriteIndex >= AUDIO_NUM_BUFFERS-1) {2 X& H- b; ]; _1 v1 q! I
6 a& p* O' b' S$ E, X; G              I2SState.TxWriteIndex= 0;8 c+ _8 U4 v, F7 Y& f+ r
% O6 b, k0 V7 M4 P: K& A
       }else {
) k2 K/ M; ~' t+ G4 \' O+ B% D3 ?5 r- D1 ]
              I2SState.TxWriteIndex++;9 P" t; |7 v7 a- h
% w: x7 d3 t/ w7 a+ z
       }
: g( N7 t' _7 q$ E, v
0 i) F4 x/ {* _& z$ n8 V/ E              I2SState.RxEvent= 0;( t; z2 \0 E1 F4 I0 s, S8 M+ q {
5 s+ d" I7 h8 f
       }
/ j; s5 S @3 i3 _& o9 p4 E* J. p2 g; m3 P' T
       }7 S. V+ C8 H8 ~0 v0 ]3 T" g% N
* b2 d ?5 _8 S} else {
) L( a6 R5 f' a! w7 ]& d4 C. f" s ]3 @) L7 c6 c/ a$ J: e+ S
       //digital mic
* q0 t0 @* a/ S" ]' L; [4 x/ C3 s/ i! f9 d* i$ E. `5 X7 J
       Dmic_Start();2 e) n8 \* D5 C4 R
& j' ^" k# }+ ^5 n) f; f* ?; P       while(1) {
: \' M3 L2 w) f2 Z0 A5 Q
$ X$ z- E3 ^: i: ?% O       if(DmicState.Event) {; q# `- d/ w: b- L# c9 H% R5 F1 X
2 P+ \& x. C4 L- W" E              for(i=0; i<AUDIO_FRAME_SIZE; i++) {
% W1 t9 B$ f4 G! m. ]4 U; b( } X& R/ e3 \& k/ w6 d
I2SState.TxBuffer[I2SState.TxWriteIndex]= DmicState.Buffer[DmicState.ReadIndex];
Y( d( l) T1 a% w" U9 K' I4 s5 f' D
              }
/ b G h/ Q9 Z# }/ i9 {! y+ P
# Y0 [0 A. z3 @# l" R              if(DmicState.ReadIndex >= AUDIO_NUM_BUFFERS-1) { [4 }4 L/ G" n# L( P$ ?
# E) M& o7 E# D( Q3 M$ A0 h
                     DmicState.ReadIndex= 0;
A8 e" P( N+ v
$ x! C5 m/ n2 b7 E% n% C! A              }else {7 H" S7 C0 `- f, o; |: @
4 N3 ]) ?( ]1 Z& j. B
                     DmicState.ReadIndex++;
6 ^$ z' K, x1 W* c' }) S
5 w; `, k; e" E1 a+ w+ x; V              }& P: E/ J2 x+ k T
* f6 m. @+ |2 d              if(I2SState.TxWriteIndex >= AUDIO_NUM_BUFFERS-1) {
" f- ]8 {8 d: O/ F: v( M
& q( @% c D8 Q# \& t5 r                     I2SState.TxWriteIndex= 0;
% c( t9 J f/ |) F9 Q
3 f0 i9 _! H e6 y1 i4 x! u, Q8 ~              }else {
5 {6 P* S! i, ?, X0 ^% k/ q
: G6 F$ g+ ~' N! h! \                     I2SState.TxWriteIndex++;; t4 [/ H/ p! ~5 f
1 H3 h# i' x* c# x
              }+ _1 w6 \: d. h, l/ p
0 { h% o ?1 r& X              DmicState.Event= 0;2 z* x* P/ R4 C# D3 I6 ?. w E6 {6 H2 L
8 k) g# R9 x- ^) F8 n6 [# f       }
1 n9 B2 R5 [& V8 F& g" q' X+ J7 y9 } Y' B9 q4 }
       }+ m% q5 t# Q, d0 d
4 [/ k+ a8 c" C# n
}
9 Y( M8 j, q1 Y6 q) o8 c( n
& ]0 R+ a, p, v+ K' g' T4 [}
- ~7 @% a5 Y: y+ y7 O--------------------- 7 G# X# q) Z* r; e
作者:huang20083200056 0 c; \3 w! K. N& C
来源:CSDN 9 r% |# i# C2 A% V# k
原文:https://blog.csdn.net/huang20083200056/article/details/78508453
' S' K! \0 U/ Q* c6 D% R$ |' V+ n版权声明:本文为博主原创文章,转载请附上博文链接! |