本帖最后由 creep 于 2017-8-22 08:56 编辑 0 Y, H3 o" \; x7 D, {
8 g+ B* G: T! @) p' K; S8 ^
STM32F769-DISCO 上面有个WM8994可以用于音频解码。WM8994具有高性能低功耗体积小等优点,曾用在早期的三星安卓手机上用到。下面的测试主要是使用WM8994播放WAV文件。
, p3 i0 u8 _: ^6 q: o( q4 ]5 w0 Y) W. b( r1 ^! n
1 _& Z3 Y A- Q& u
1、串行音频接口(SAI)& c) B( K0 f2 X5 `( M
SAI 接口(串行音频接口)灵活性高、配置多样,可支持多种音频协议。该接口适用于许多立体声或单声道应用。例如,它可配置为支持 I2S 标准、 LSB 或 MSB 对齐、 PCM/DSP、TDM 和 AC’97 等协议。将音频模块配置为发送器时, SAI 接口可提供 SPDIF 输出。SAI 通过两个完全独立的音频子模块来实现这种灵活性和可配置性。每个模块都有自己的时钟发生器和 I/O 线控制器。SAI 可以配置为主模式或配置为从模式。音频子模块既可作为接收器,又可作为发送器;既可与另一模块同步,又可以不同步。8 A7 l, K/ Q; K( B( F
SAI 主要由两个各自带有时钟发生器的音频子模块组成,每个音频模块集成一个 32 位移位寄存器,该寄存器由模块自身的功能状态机控制。数据存储和读取都是通过专用的 FIFO 来完成。 FIFO 可通过 CPU 访问,也可通过 DMA 访问以减轻 CPU 的通信负担。每个音频模块是独立的。这两个音频子模块可彼此同步。SAI 中指定音频模块的 4 个专用引脚IO口( SD、 SCK、 FS、 MCLK)。/ k# _/ z; C& v8 s( z
+ _+ R, p# B- w0 x0 Q2 @8 z% w
9 K- h; `' l+ `2 {) H* n7 M, ]功能框图如下:
2 b- D* B9 _6 T4 H$ x- g
5 [4 z% A& @7 m( g9 S4 {4 E& H" L z# E/ g7 f: }" I% ?$ N
STM32F769-DISOC 上面的WM8994通过SAI1_Block_A和MCU进行解码数据通信,WM8994的初始化和各项配置则通过IIC实现。
% `1 U! ^. Q3 [4 B n' N, {
: t" r( E) N# V* j9 c4 Y" L6 {
A- u( D$ J4 E- ~! q7 ]
; F/ N( A t1 s
- s1 t y0 V$ i8 }2、WAV格式文件
) n- O9 m/ {' P7 c7 `9 g1 C" NWAVE文件是计算机领域最常用的数字化声音文件格式之一,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio)。WAVE是录音时用的标准的WINDOWS文件格式,文件的扩展名为“WAV”,数据本身的格式为PCM或压缩型。简单的理解WAV文件有一个文件头和数据部分组成,文件头里面包含该WAV的详细信息。一个典型的文件头格式如下:4 a$ a) T5 N' P$ y7 f) b
0 C) \: q/ P5 r9 u 这个文件头共有44字节,不同的格式文件头可能长度不同,为了方便处理我使用开源软件Audacity将WAV的头文件格式都转为相同的44字节模式,其他的参数为立体声44.1KHZ.
; T8 l5 b6 t" o1 c" n$ y, O4 b( @4 C6 y- o$ X, {* P
$ g; S/ g0 e' V* P( Z3 T
) J$ [! W# n& w* N+ b/ x1 l$ I2 q; P* V$ v8 a
3、软件解码# h( u2 R: i: w
8 d% I5 W, m. S; ~* v
播放的WAV文件放在TF卡里面,文件系统使用开源的FATFS,为了支持较长的歌曲名字,需要在FATFS中打开长文件名支持模式。同时为了支持中文显示,我把汉字点阵放到了
1 U. {' P4 p, Z! @: K0 KSTM32F769-DISCO 的外置NORFLASH里面,具体的操作可以查看之前的帖子。
8 X# H `& X, C) Z& |0 \; W" ]! {4 b7 |- O1 J! d& P
WAV的播放是使用DMA 循环模式把数据从SD卡搬送到codec进行解码,其中DMA使能了HalfTransfer和TransferComplete来不断的更新要解码的数据文件。- M: I) _6 c3 Y
最开始的时候先将要解码的缓存写满,然后开始DMA传输,当传输完成一半时在使能标记开始更新前半部缓存,等传输完成继续更新后半部分缓存,这样可以实现更新缓存和
4 r) E) `9 N/ j( Z界面数据“同时进行”,当然这样做的前提是更新缓存数据速度要快于解码的数据,通过测试STM32F7完全没有压力。
! t9 L) E' O2 b1 n4 F4 s. Y8 h& z- ~+ S2 J6 s6 W
程序的流程如下:1 I, |: t# u* L6 u6 [) `, B/ ] I
- 先扫描SD卡找到WAV文件,将找到的WAV文件信息放入到一个链表中。
- 显示找到的WAV文件名称到LCD上,并更新要播放的文件(名称为绿色字体)。
- 根据预设的要播放的文件序号找到要要播放的WAV文件的信息,从SD卡里面读取填充播放缓存。
- 开始播放WAV后不断的填充播放缓存直到播放完成。
- 播放完成一个WAV文件后,根据播放模式(单曲循环、列表循环、随机循环)播放下一个WAV文件。
- 播放的过程中可以通过用户按键切换下一个WAV文件进行播放。2 Y+ @* w# v& c. H& Z
6 c" M: Z. \ z0 @0 O程序下载后的:' t$ @( z) E' T: b% U. x( e* R( r
) R9 W& G# l/ e2 v
5 N# N# w* P7 ~
3 ?. u1 |; v# d& O5 H1 K; |
使用按键切换歌曲:, g: d) e1 ^, q. g0 ]) a) j
9 Q6 v! C9 G- c
( ~# t4 i1 H6 k+ f
+ w) N. ~; D& H x/ d
mian 函数如下:$ q/ |5 z; N' M) y) {* L
- _listfile_list *playfile;
, Q9 l. ?+ V7 k5 x9 L6 ?& i - CPU_CACHE_Enable();
3 ` Z2 L/ ~) L+ f% C) A6 D( Y - HAL_Init();
/ Y0 m+ i- k) {# j5 F - SystemClock_Config();
- e1 |( k+ Z! ^$ D! Y! ?+ r - USART1_Init();5 O: N& X) C: C, }
- HAL_Delay(200);* V/ D& q5 [. j9 x; \# `9 @9 J" D
- BSP_LED_Init(LED_GREEN);" a0 M4 K' y# a! Q
- BSP_LCD_Init();
7 | d$ R* h- W- _" Y - BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS);
1 y+ ]$ D6 i$ W: p - BSP_LCD_SetTextColor(LCD_COLOR_RED);: h j% {) k5 \" \* `+ j& @
- BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
+ K# `' J+ G* J - HAL_Delay(500);
# R8 A9 I% T* \/ q9 R! _" M - BSP_QSPI_Init();3 y! |2 a( N! K. `6 J+ b
- CopyFont2NorFlash(); M, ]& X* K8 f' a9 W: k0 ~
- BSP_QSPI_EnableMemoryMappedMode();) F2 _3 w5 b$ I) N; @) I% l
- CheckGBKFont4NorFlash();4 Z& ?6 M' W/ C4 d
- BSP_LCD_Clear(LCD_COLOR_WHITE);' J& a, B0 p; Z/ {
- 2 F; s3 R9 n, F1 H
- FatFs_SD_Init();
. b! }+ ]! B# c - wavfilenum = ScanWavFiles("/");
0 ?; Q0 A- k' G. B* C2 z - PrintfWavFileTest();
% P" x2 i9 _" _ - UpdateWavFileColor(playfilepos,playfilepos);
" }! w& O- }1 S% W6 r( {- f - playfile = list_search(WavFileList, playfilepos);
# e4 q* K" g& j8 |, ~: F0 h - " P, U$ u) R7 D% {1 l! i* K
- if(playfile == NULL)while(1);" O8 v: A$ z9 W( d2 c
- ; J; A' p1 h1 T9 y+ E. r
- //´
6 E$ \% ]7 g( j& q0 X# [# b m - wavFileLen = OpendWavFile(playfile->filename);: W9 S! v9 ^& B/ R! x0 Q
- //( p2 i" z, D6 N" t, p* }
- ReadWaveFile(BuffHead, PLAY_HEADER);
. U6 g$ ?( W2 }1 y - //
! H' S, |" d! q* i; p* G7 i - ReadWaveFile((uint8_t *)PlayBuff, PLAY_BUFF_SIZE * 2);$ ^7 Z6 S$ q/ W, ?* @+ j
- Playback_Init();' I% |; h4 _' o4 Y! H
; T: |9 n4 F1 o8 U- /* Start the playback */
7 u) ~8 v3 y( h9 Q$ C - if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, NULL, 0))
9 e2 F& T9 ], M2 e - {
2 ^; I1 J, p+ I K - while(1);
* N S. d4 A( ~ q# ?4 T - }5 j$ W$ m" V% J+ I6 L$ p
, z7 Q( _* W+ T7 A! o \6 p9 g- if(HAL_OK != HAL_SAI_Transmit_DMA(&SaiHandle, (uint8_t *)PlayBuff, PLAY_BUFF_SIZE))
/ Z) e s0 u5 Y: f9 {1 _/ N - {
% r& L+ R* ]$ q1 H - while(1);
: c7 Z( i9 }6 f" Q8 g, E - }
复制代码 需要注意的是程序里面默认播放是44.1KHZ立体声的WAV文件,如果是其他的格式的要在播放之前更新SAI的配置。5 h8 F! L+ q- J2 Z. N& i0 s
- SaiHandle.Init.AudioMode = SAI_MODEMASTER_TX;
1 l8 p7 W' } X$ O7 R$ J! f5 v - SaiHandle.Init.Synchro = SAI_ASYNCHRONOUS;
. s' v& v( u/ Z- y. E) v' P - SaiHandle.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
! T1 D* g9 ~, E' {# ?! z! | - SaiHandle.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;# V, Z: `8 k/ S. P8 S
- SaiHandle.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
: a9 c) k3 `6 c0 w2 L, G( u - SaiHandle.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
# J; H; M+ D* Y/ S9 R' B: F - SaiHandle.Init.Protocol = SAI_FREE_PROTOCOL;
/ ]( i0 ?3 C* F4 ` - SaiHandle.Init.DataSize = SAI_DATASIZE_16;
+ H: _7 F4 b' g) } - SaiHandle.Init.FirstBit = SAI_FIRSTBIT_MSB;
. D) ]$ p' I/ l3 C4 ?1 }0 T - SaiHandle.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
I. k8 V/ g, S* E( m6 m
2 J% h L2 q- P6 i% m- SaiHandle.FrameInit.FrameLength = 32;
+ r Y" G& p F# v - SaiHandle.FrameInit.ActiveFrameLength = 16;7 \+ y9 o' o, I1 c r. G
- SaiHandle.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;! s8 B( z8 `0 q" s
- SaiHandle.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;' I* F3 O; T- N
- SaiHandle.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;
, }+ F9 N6 q* z' S& b7 B, w$ r
; d) G8 b! b, a5 G, x- SaiHandle.SlotInit.FirstBitOffset = 0;* S% Y' l5 A% R# @
- SaiHandle.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
6 q7 q: L" K, s - SaiHandle.SlotInit.SlotNumber = 2;
; A. o+ p6 e0 {9 f+ j9 _ - SaiHandle.SlotInit.SlotActive = (SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1);
* o+ K) r" K$ f
复制代码 + S" W/ s2 L* t3 f& S" I
. Z/ O; X- z" |4 F: z; v0 x
* l5 G R' ^6 t' s8 V) H8 [7 ~' P* L5 s2 u" x
$ E. {* m$ b1 g1 L4 n' R" E" u
4、最后
9 _! @) v/ J) }+ e" r1 j/ y# s! M6 x
使用STM32F769-DISOC配合论坛之前送的小音箱,播放音乐的效果和我手机电脑播放的听不出区别。我用手机语音备忘录录制了一段放在附件里面,感兴趣可以试听下。
& I u- @" S5 |8 U0 ^* M
9 K0 h2 Y# l' I @: F2 ^
. G0 E6 @" r/ a) d; G& oPS:附件的源码里面我默认关闭了汉字库下载到NORFLASH,如果是第一次运行程序请打开相应的宏定义加载字库。
3 y* w9 x, l- m- H; [0 w5 ?( i7 W, C4 F. W3 B. d: G( u
' [- l1 G& m% y3 f/ J
测试代码和文件:& C* Z# i' c8 a2 a( Q& Z: g$ r7 \3 l
audio.rar
(3.89 MB, 下载次数: 370)
|
顶,好贴