书接上文:https://www.cnblogs.com/feiniaoliangtiangao/p/11060674.html 和 https://www.cnblogs.com/feiniaoliangtiangao/p/11023636.html
请阅读完上面的两篇博文作为基础,再阅读本篇博文,如若已了解SD卡,内存管理,Fatfs,请跳过。
1.实验介绍
读取并解码SDHC卡里的WAV音频文件,然后通过SAI协议传输到WM8978播放
WAV介绍: WAV 即 WAVE 文件, WAV 是计算机领域最常用的数字化声音文件格式之一,它是微软
专门为 Windows 系统定义的波形文件格式(Waveform Audio),由于其扩展名为"*.wav"。 它
符合 RIFF(Resource Interchange File Format)文件规范,用于保存 Windows 平台的音频信息资源,
被 Windows 平台及其应用程序所广泛支持,该格式也支持 MSADPCM, CCITT A LAW 等多种
压缩运算法,支持多种音频数字,取样频率和声道,标准格式化的 WAV 文件和 CD 格式一样,
也是 44.1K 的取样频率, 16 位量化数字,因此在声音文件质量和 CD 相差无几。
WM8978介绍:WM8978 是欧胜(Wolfson) 推出的一款全功能音频处理器。它带有一个 HI-FI 级数字信号
处理内核,支持增强 3D 硬件环绕音效,以及 5 频段的硬件均衡器,可以有效改善音质;并有
一个可编程的陷波滤波器,用以去除屏幕开、切换等噪音。
SAI介绍:SAI可以说是I2S的强化版,但相差也不大,只是功能多了点,而I2S也只是比I2C多了一条声道线FS_A/B.
2.实验软件
keil5,Cubemx5.21
3.Cube配置
打开SAI功能,然后选择为主机模式,参照下面原子的例程配置参数。
//SAI Block A初始化,I2S,飞利浦标准 //mode:工作模式,可以设置:SAI_MODEMASTER_TX/SAI_MODEMASTER_RX/SAI_MODESLAVE_TX/SAI_MODESLAVE_RX //cpol:数据在时钟的上升/下降沿选通,可以设置:SAI_CLOCKSTROBING_FALLINGEDGE/SAI_CLOCKSTROBING_RISINGEDGE //datalen:数据大小,可以设置:SAI_DATASIZE_8/10/16/20/24/32 void SAIA_Init(u32 mode,u32 cpol,u32 datalen) { HAL_SAI_DeInit(&SAI1A_Handler); //清除以前的配置 SAI1A_Handler.Instance=SAI1_Block_A; //SAI1 Bock A SAI1A_Handler.Init.AudioMode=mode; //设置SAI1工作模式 SAI1A_Handler.Init.Synchro=SAI_ASYNCHRONOUS; //音频模块异步 SAI1A_Handler.Init.OutputDrive=SAI_OUTPUTDRIVE_ENABLE; //立即驱动音频模块输出 SAI1A_Handler.Init.NoDivider=SAI_MASTERDIVIDER_ENABLE; //使能主时钟分频器(MCKDIV) SAI1A_Handler.Init.FIFOThreshold=SAI_FIFOTHRESHOLD_1QF; //设置FIFO阈值,1/4 FIFO SAI1A_Handler.Init.ClockSource=SAI_CLKSOURCE_PLLI2S; //SIA时钟源为PLL2S SAI1A_Handler.Init.MonoStereoMode=SAI_STEREOMODE; //立体声模式 SAI1A_Handler.Init.Protocol=SAI_FREE_PROTOCOL; //设置SAI1协议为:自由协议(支持I2S/LSB/MSB/TDM/PCM/DSP等协议) SAI1A_Handler.Init.DataSize=datalen; //设置数据大小 SAI1A_Handler.Init.FirstBit=SAI_FIRSTBIT_MSB; //数据MSB位优先 SAI1A_Handler.Init.ClockStrobing=cpol; //数据在时钟的上升/下降沿选通 //帧设置 SAI1A_Handler.FrameInit.FrameLength=64; //设置帧长度为64,左通道32个SCK,右通道32个SCK. SAI1A_Handler.FrameInit.ActiveFrameLength=32; //设置帧同步有效电平长度,在I2S模式下=1/2帧长. SAI1A_Handler.FrameInit.FSDefinition=SAI_FS_CHANNEL_IDENTIFICATION;//FS信号为SOF信号+通道识别信号 SAI1A_Handler.FrameInit.FSPolarity=SAI_FS_ACTIVE_LOW; //FS低电平有效(下降沿) SAI1A_Handler.FrameInit.FSOffset=SAI_FS_BEFOREFIRSTBIT; //在slot0的第一位的前一位使能FS,以匹配飞利浦标准 //SLOT设置 SAI1A_Handler.SlotInit.FirstBitOffset=0; //slot偏移(FBOFF)为0 SAI1A_Handler.SlotInit.SlotSize=SAI_SLOTSIZE_32B; //slot大小为32位 SAI1A_Handler.SlotInit.SlotNumber=2; //slot数为2个 SAI1A_Handler.SlotInit.SlotActive=SAI_SLOTACTIVE_0|SAI_SLOTACTIVE_1;//使能slot0和slot1 HAL_SAI_Init(&SAI1A_Handler); //初始化SAI __HAL_SAI_ENABLE(&SAI1A_Handler); //使能SAI }
由上面的WM8979原理图可知,除了SAI的5条线传输信号,还有2条IIC的线用来控制WM8978
所以要手工配成输出模式,自己添加模拟IIC协议。
处此之外,请按照开头链接的例程配置SDIO卡和Fatfs,这些要用到。
4.程序讲解
由于该例程代码较为繁复,只能点出重点,细节可能会忽略
1,WM8979.C
芯片通过 IIC 接口(MODE=0)连接 WM8978,不过 WM8978 的 IIC 接口比较特殊:
1,只支持写,不支持读数据; 2,寄存器长度为 7 位,数据长度为 9 位。 3,寄存器字节的最低
位用于传输数据的最高位(也就是 9 位数据的最高位, 7 位寄存器的最低位)。 WM8978 的 IIC
读地址固定为: 0X34。
#include "WM8978.h" #include "IIC.h" #include "stdio.h" //WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存 //因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值 //写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值. //注意:WM8978的寄存器值是9位的,所以要用u16来存储. static uint16_t WM8978_REGVAL_TBL[58]= { 0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000, 0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF, 0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000, 0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000, 0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9, 0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100, 0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039, 0X0001,0X0001 }; //WM8978 写方法 // uint8_t WM8978_Write_Reg(IIC_HandleTypedef * iicHandle, uint8_t Register_Address, uint16_t Data_Byte) { vIIC_Start_Signal(iicHandle); //1. IIC_Start ; 起始信号 vIIC_SendByte(iicHandle, Slave_Address); //2. IIC_Send Device Address(W); 发送设备地址 if(!bIIC_ReadACK(iicHandle)) //3. IIC_ReadAck ; 等待应答 { vIIC_Sto