本帖最后由 creep 于 2017-8-22 08:56 编辑
e: o% u. ^! [2 B
& w% K* J1 C' X' K( f STM32F769-DISCO 上面有个WM8994可以用于音频解码。WM8994具有高性能低功耗体积小等优点,曾用在早期的三星安卓手机上用到。下面的测试主要是使用WM8994播放WAV文件。/ W! b1 L( ~ {0 k% L3 `
3 I4 |- h, j5 [, H) |
& X0 g) X* q6 T
1、串行音频接口(SAI)" b( p8 o) t, C
SAI 接口(串行音频接口)灵活性高、配置多样,可支持多种音频协议。该接口适用于许多立体声或单声道应用。例如,它可配置为支持 I2S 标准、 LSB 或 MSB 对齐、 PCM/DSP、TDM 和 AC’97 等协议。将音频模块配置为发送器时, SAI 接口可提供 SPDIF 输出。SAI 通过两个完全独立的音频子模块来实现这种灵活性和可配置性。每个模块都有自己的时钟发生器和 I/O 线控制器。SAI 可以配置为主模式或配置为从模式。音频子模块既可作为接收器,又可作为发送器;既可与另一模块同步,又可以不同步。0 ^5 W) w L; `6 X' ~: T3 H' @
SAI 主要由两个各自带有时钟发生器的音频子模块组成,每个音频模块集成一个 32 位移位寄存器,该寄存器由模块自身的功能状态机控制。数据存储和读取都是通过专用的 FIFO 来完成。 FIFO 可通过 CPU 访问,也可通过 DMA 访问以减轻 CPU 的通信负担。每个音频模块是独立的。这两个音频子模块可彼此同步。SAI 中指定音频模块的 4 个专用引脚IO口( SD、 SCK、 FS、 MCLK)。
9 ^" ^( X! l% s2 c4 p- d
8 ? r8 o2 z! u5 ]3 T
5 ~0 {7 }" k$ }) R7 x, x/ E# x功能框图如下:$ `( a* H" }% i: O6 t( o7 c
1 e" ?* n; V# Q8 A' L9 U- P
$ _7 d: ~/ ]1 V3 U2 TSTM32F769-DISOC 上面的WM8994通过SAI1_Block_A和MCU进行解码数据通信,WM8994的初始化和各项配置则通过IIC实现。# T6 J6 F3 k# V' e4 K: A; y
* p, \0 ?( q' j4 ^+ n3 U& D' u8 U& v. E, K( N; {0 v% i
/ y+ C# W0 q3 v: ~; p* o2 Y
. H% N$ _$ z# D$ G1 }. @( O8 H
2、WAV格式文件
' M/ M3 Q+ \6 T8 g( |) uWAVE文件是计算机领域最常用的数字化声音文件格式之一,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio)。WAVE是录音时用的标准的WINDOWS文件格式,文件的扩展名为“WAV”,数据本身的格式为PCM或压缩型。简单的理解WAV文件有一个文件头和数据部分组成,文件头里面包含该WAV的详细信息。一个典型的文件头格式如下:
% q2 M4 q Z6 k* P4 p& |
7 {2 `$ Z5 a3 T8 M- Z
这个文件头共有44字节,不同的格式文件头可能长度不同,为了方便处理我使用开源软件Audacity将WAV的头文件格式都转为相同的44字节模式,其他的参数为立体声44.1KHZ.
( f4 K) s( G( A0 N. s: k- H
7 [0 x5 }# k) H
# @1 |1 G+ z* r% y$ n
2 G0 L* F% e, s
8 r& X+ @: G& F8 q7 m" \, S3、软件解码( n2 f! {+ k- g/ q8 i. W2 z
' l2 r: o( ?+ ^ 播放的WAV文件放在TF卡里面,文件系统使用开源的FATFS,为了支持较长的歌曲名字,需要在FATFS中打开长文件名支持模式。同时为了支持中文显示,我把汉字点阵放到了
: D" W7 Y5 N# [0 xSTM32F769-DISCO 的外置NORFLASH里面,具体的操作可以查看之前的帖子。
5 L) x/ B ^" n& k% O$ p/ Z6 ?: I! e# ^- G- ~% [
WAV的播放是使用DMA 循环模式把数据从SD卡搬送到codec进行解码,其中DMA使能了HalfTransfer和TransferComplete来不断的更新要解码的数据文件。
' ]/ f+ V8 G- p) L* W8 S( v最开始的时候先将要解码的缓存写满,然后开始DMA传输,当传输完成一半时在使能标记开始更新前半部缓存,等传输完成继续更新后半部分缓存,这样可以实现更新缓存和6 k9 x4 @; j& @2 Z- }4 a1 W- X
界面数据“同时进行”,当然这样做的前提是更新缓存数据速度要快于解码的数据,通过测试STM32F7完全没有压力。# a) i1 f, }$ F7 P* Q+ h
; G. _5 b6 O/ e, M& n2 A' |程序的流程如下:
/ a' s! s9 |, S: U# A- 先扫描SD卡找到WAV文件,将找到的WAV文件信息放入到一个链表中。
- 显示找到的WAV文件名称到LCD上,并更新要播放的文件(名称为绿色字体)。
- 根据预设的要播放的文件序号找到要要播放的WAV文件的信息,从SD卡里面读取填充播放缓存。
- 开始播放WAV后不断的填充播放缓存直到播放完成。
- 播放完成一个WAV文件后,根据播放模式(单曲循环、列表循环、随机循环)播放下一个WAV文件。
- 播放的过程中可以通过用户按键切换下一个WAV文件进行播放。
' ~: O/ O2 v A- U, V " O% m* y+ M, f% H Z8 q
程序下载后的:
/ p$ h' `3 \0 Y3 L; E5 m5 V, b" S0 n \+ L8 j
& } L P) m$ l* O. |
* U( ^( C0 I6 S
使用按键切换歌曲:9 m4 l% n) @* \
+ A7 v! @& F) A0 ]0 H
2 v3 n8 S% Q8 t- q9 {# c
- C3 s: E* F+ G6 Ymian 函数如下:
, O7 d0 X+ _5 k- _listfile_list *playfile;9 f7 A1 L# b4 I8 _ R
- CPU_CACHE_Enable();
1 }$ A* M( d2 g6 o& U8 s - HAL_Init();
( D( h! J( L5 W) d, Z - SystemClock_Config();0 x1 v! a4 L# K1 D- X% o
- USART1_Init();
4 V3 c0 d6 D- i- T/ q$ f - HAL_Delay(200);; C E) ?0 E. R1 R- E
- BSP_LED_Init(LED_GREEN);* F& Y% B9 ~* U) P. g! r
- BSP_LCD_Init();$ V3 C/ G* D) v3 o+ @8 _. X0 F
- BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS);; r2 g7 A. w* e' J2 t9 X H" K
- BSP_LCD_SetTextColor(LCD_COLOR_RED);! |% }) A R! a: v6 |' g2 |
- BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
2 N) |3 V' r* Z5 Q" X7 H8 O - HAL_Delay(500);
$ v$ G5 x* R+ ? - BSP_QSPI_Init();7 e4 r8 n6 @; N
- CopyFont2NorFlash();
" }/ M5 e7 K# @ - BSP_QSPI_EnableMemoryMappedMode();
& W7 M/ l+ G$ J3 o' I: Z$ C% H - CheckGBKFont4NorFlash();. b, a3 @6 t- n; h) W+ V
- BSP_LCD_Clear(LCD_COLOR_WHITE);
* M+ o% w$ l6 C" l6 ?+ M( P
6 t0 ^0 G: j2 p3 A9 o& H9 q- FatFs_SD_Init();2 H0 Q; V6 I: r2 B" S
- wavfilenum = ScanWavFiles("/");
! A p: z E% n' g# z: Y - PrintfWavFileTest();6 q4 ~& k; Z1 o; Q
- UpdateWavFileColor(playfilepos,playfilepos);+ ~' S" f. a9 C4 t( P3 g
- playfile = list_search(WavFileList, playfilepos);" E3 t. b0 e1 P
- : I' j. ]8 H0 [& J4 I; K
- if(playfile == NULL)while(1);* a; c, t7 C# O9 ? f5 F( X p: X
- + H& i [. v# ?, v; p7 ?8 n- |# M
- //´4 j: Q2 T% n4 s5 @+ I/ G
- wavFileLen = OpendWavFile(playfile->filename);
0 p: ]8 L; G l( O' `* Q - //
' r. c$ T: l! ?. E2 e8 \ - ReadWaveFile(BuffHead, PLAY_HEADER);
9 R& s C. Y6 K- ]% @ - //
- _8 E$ z. H1 G% f" m - ReadWaveFile((uint8_t *)PlayBuff, PLAY_BUFF_SIZE * 2);
* |! U0 a- H& V - Playback_Init();
, `- S% K9 `/ k8 c/ @
% j/ \0 v0 z5 c! S/ k, H- /* Start the playback */9 ^ \* [! k* G
- if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, NULL, 0))$ T! t5 `, p; v; o8 r
- {
* i7 m4 F& U7 B8 i - while(1);: n' B2 a% p. P" ^! s$ D7 w+ G% o
- }; g8 H; C4 x' C+ w' O
- : `4 c2 G' n# j3 [# u$ J' q9 [
- if(HAL_OK != HAL_SAI_Transmit_DMA(&SaiHandle, (uint8_t *)PlayBuff, PLAY_BUFF_SIZE))6 q! P! `# A, |& U# H8 `
- {
i0 n3 D* x2 L: B - while(1);
; s3 v7 Y6 U. K- @/ A. K - }
复制代码 需要注意的是程序里面默认播放是44.1KHZ立体声的WAV文件,如果是其他的格式的要在播放之前更新SAI的配置。
7 x3 Q3 a- e" G- SaiHandle.Init.AudioMode = SAI_MODEMASTER_TX;
! p, a9 Q/ P' [0 |9 A - SaiHandle.Init.Synchro = SAI_ASYNCHRONOUS;; {+ M0 Y, R( U) E- U) L
- SaiHandle.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;% U7 c+ A! N* Z) G
- SaiHandle.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;5 _* D% C# Z: `! ^2 l' ^2 j
- SaiHandle.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;$ l" _* C6 {6 N7 z* m) \+ W. ~, W
- SaiHandle.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
# W* J/ x. ]& d - SaiHandle.Init.Protocol = SAI_FREE_PROTOCOL;) e6 D5 q. ^) Q) u# J; N
- SaiHandle.Init.DataSize = SAI_DATASIZE_16;' E' v7 A0 X U! Q
- SaiHandle.Init.FirstBit = SAI_FIRSTBIT_MSB;
6 e1 X' f" |: T: {* X# K5 g - SaiHandle.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;- l' f; ~) A% P/ W
- * ]( |1 Y; f1 m u
- SaiHandle.FrameInit.FrameLength = 32;( z* J7 d( l5 e" M+ j
- SaiHandle.FrameInit.ActiveFrameLength = 16;& n( J2 q0 d- Y# r0 T" {0 u! j+ W
- SaiHandle.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
7 j; j L+ a. W+ a* H) C. O8 { - SaiHandle.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;" K. }: u% C' @3 j9 l% h+ B
- SaiHandle.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;+ _: b; H$ D) j4 H1 B2 t
/ s0 N+ R9 z' g9 ~* k7 B- SaiHandle.SlotInit.FirstBitOffset = 0;
5 ]) n/ T/ C; r9 ~ - SaiHandle.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;0 a0 P- a9 l# A; b3 Y, t" h% O+ d6 p
- SaiHandle.SlotInit.SlotNumber = 2;, {, s% M$ L9 ^# I; s M
- SaiHandle.SlotInit.SlotActive = (SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1);
* r* v. N* ^7 }3 Q* x
复制代码
( _7 w: e( J) V% L7 C4 u! g! R; w) o2 X) f" G$ J
& L2 w9 q3 g, i# }( ?6 J7 Q
2 _( T0 U& Z: U0 O/ j* h
8 y* A( G/ L0 j$ Z$ g* l' T4、最后/ A! g3 b+ c7 C/ R# l i
6 k' d7 r+ y, Q$ s7 n) W9 ` 使用STM32F769-DISOC配合论坛之前送的小音箱,播放音乐的效果和我手机电脑播放的听不出区别。我用手机语音备忘录录制了一段放在附件里面,感兴趣可以试听下。7 @5 v6 c6 L5 G; B
3 x u6 u+ y5 R' I( V! V" y
$ z- ~2 G' P! L; ^PS:附件的源码里面我默认关闭了汉字库下载到NORFLASH,如果是第一次运行程序请打开相应的宏定义加载字库。
! A/ `# {: l* o+ L4 Q' o/ I
( z$ J: G( C8 J! v# j$ s6 J; c
$ H; x( c. V, t) O. y! Z测试代码和文件:) T* j7 G" a+ {$ P+ s' a, P8 y
audio.rar
(3.89 MB, 下载次数: 378)
|
顶,好贴