I2S(Inter-IC Sound)总线,又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准。采用了独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真。
主操作或从操作
9 位可配置线性预分频器,以达到精确的音频采样频率(8KHz~192KHz)
数据帧格式可配置为 16 位、24 位或 32 位
数据包帧固定为 16 位(16 位有效数据)或 32 位(16 位、24 位、32 位有效数据)
可配置时钟极性(稳定状态)
发送模式下具有下溢标志(仅从机),接收模式下具有上溢标志(主/从机)和发送/接收模式下的帧错误标志(仅从机)
用于传输和接收的 32 位寄存器为两个声道分时复用
数据方向始终是 MSB 优先
支持 I2S 协议:
飞利浦标准
MSB 对齐标准(MSB 位向左对齐)
LSB 对齐标准(LSB 位向右对齐)
PCM 标准(具有短帧同步模式、长帧同步模式的两种方式)
利用 DMA 请求传输数据(32 位宽)
可配置 MCLK 时钟输出来驱动外部音频组件,其比率固定在 256×Fs(其中 Fs 为音频采样频率)
1
MM32的I2S总线简述
MM32F0160的I2S总线与SPI总线复用即SPI_I2S串行外设(串行外设接口与集成电路内置音频总线)。
I2S总线接口与SPI总线接口引脚复用关系如下:
SD:串行数据(映射在MOSI引脚上),用于发送或接收两次多路数据通道(仅在半双工模式下)。
WS:声道选择(映射在NSS引脚上),是主模式控制数据的输出信号,或从模式的输入。
CK:串行时钟(映射在SCK引脚上),是主模式串行时钟的输出,或从模式串行时钟的输入。
MCK:可选的串行时钟(映射在MISO引脚上),用于驱动外部音频组件(仅当外部音频设备需要时钟输入时使用,由主模式提供)。
2
SPI_I2S功能框图简介

图1 SPI_I2S功能框图
如上图1所示为SPI_I2S外设的功能框图,SPI_I2S通过“总线接口逻辑”挂载在APB和DMA总线上,TXREG和RXREG寄存器、主模式控制单元和从模式控制单元、主从选择控制、收发控制逻辑以及时钟生成及控制单元,8Byte的发送缓冲和8Byte的接收缓冲构成,时钟控制单元由Spbrg和Pclk提供时钟。
3
SPI_I2S外设的I2S时钟预分频器
I2SCLK时钟由系统APB时钟提供,I2S模块的预分频器电路结构如下图2所示:

图2 SPI_I2S时钟预分频器电路结构图
如上图2所示,当MCKOE位为‘0’时芯片不需要输出MCK时钟,预分频器直接将I2SCLK分频到CK;当MCKOE位为‘1’时芯片会输出MCK时钟,预分频器将I2SCLK分频后得到MCK,然后再经过分频处理才得到CK(分频倍数由CHLEN选择为 4或8)。
音频采样率一般常用 192KHz,96 KHz,48 KHz,44.1 KHz,32 KHz,22.05 KHz,16 KHz,11.025KHz,8 KHz。因此可根据 I2S 时钟分频器的电路功能式样,配置寄存器 I2SCFGR中的I2SDIV[8:0]、MCKOE和CHLEN 位来得到期望的音频采样率。
I2S 传输数据时,比特率计算公式如下表1所示(CK 输出一个时钟周期对应传输 1 比特数据,因此比特率 = CK频率FCK)。

表1
音频采样率(Fs)和 I2S 比特率的关系由如下的公式定义:
Fs = I2S 比特率/(通道长度×通道数)= FCK /(通道长度×通道数)
注:通道长度,即数据包帧长度,可配置为16位或32位;通道数为左右声道,值固定为2。
综上所述,根据I2SDIV[8: 0]、MCKOE和CHLEN位的配置情况, 得到音频采样率与FI2SCLK(APB时钟频率)的关系如下表2所示:

表2
4
SPI_I2S外设的I2S接口的音乐播放器
工作原理介绍
基于I2S接口的音乐播放器工作原理框图如下图3所示:

图3 I2S接口的音乐播放器原理框图
如上图3所示为I2S接口的音乐播放器工作原理框图:
MM32F0160作为Host MCU其SPI1接口用于驱动25WQ80存储器用于写入和读取存储的音频文件。
MCU端I2S2(SPI2_I2S2)接口工作在从机模式,MCLK不输出时钟。通过PWM输出12MHz的REF_Clock给NAU88C22音频编解码芯片MCLK脚,NAU88C22内部PLL合成稳定的12.288MHz作为内部IMLCK主时钟。NAU88C22 BCLK输出bit clock时钟到MCU端I2S2_CK作为音频采样时钟。I2S2_WS接口即FS用于分时切换左右声道。I2S2_SD接口即DACIN输出从25WQ80存储器读取的音频信号流DAC Stream传输给NAU88C22音频编解码芯片。
MCU端I2C_SDA和I2C_SCL接口用于设置NAU88C22工作模式和参数。NAU88C22 DAC输出可以选择从Speaker PA输出推喇叭或者从Earphone PA耳机接口输出推动耳机。
5
I2S接口的GPIO初始化
I2S接口GPIO的初始化代码如下所示:
voidI2S2_GPIO_Init(void)
{
GPIO_InitTypeDefGPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB,ENABLE);
GPIO_StructInit(&GPIO_InitStruct);
/*PB12I2S2_WS*/
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB13I2S2_CK*/
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB14I2S2_MCLK*/
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB15I2S2_SD*/
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB12AFI2S2_WS*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_0);
/*PB12AFI2S2_CK*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_0);
/*PB12AFI2S2_MCLK*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_0);
/*PB12AFI2S2_SD*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_0);
}
6
I2S从机的初始化
I2S从机的初始化步骤如下所示:
1
配置SPI_I2S_GCTL.SPIEN位为‘1’,开启模块使能;
2
配置SPI_I2S_GCTL.MODE位为‘0’,使模块功能为从模式;
3
配置寄存器SPI_I2S_I2SCFGR中的 I2SDIV[8:0]、 DATLEN 和 CHLEN 位,以符合希望得到的音频采样频率及数据包帧格式;
4
配置SPI_I2S_I2SCFGR.SPI_I2S位为‘1’,使能 I2S 传输功能;
5
配置寄存器SPI_I2S_I2SCFGR中的I2SSTD[1: 0]、 PCMSYNC 位,选择I2S传输时使用的通信标准;
6
配置SPI_I2S_GCTL.DMAMODE 位为‘1’,以启用 DMA 传输;
7
开启半双工传输许可, 即配置寄存器SPI_I2S_GCTL 中的 TXEN 或 RXEN 位为‘1’ (TXEN、 RXEN不可同时配置为‘1’)。
注意:从模式下发送时,在检测到WS的边沿之前,需要对寄存器SPI_I2S_TXREG进行1次数据写入操作;而且,从模式下接收时,在配置RXEN位为‘1’之前,需要一直维持WS输入信号在高电平。
I2S从机的初始化代码如下所示:
voidI2S2_Slave_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI2,ENABLE);/*EnableSPI2_I2SClock*/
SPI2->CCTL&=~SPI_CCTL_LSBFE;/*MSBfirstenable*/
SPI2->CCTL&=~SPI_CCTL_CPHA;/*Clockphaseselectstartsecondclock*/
SPI2->CCTL|=SPI_CCTL_CPHASEL;/*CPHApolarityselectstartsecondclock*/
SPI2->CCTL|=SPI_CCTL_SPILEN;/*SPIcharacterlength8bitdata*/
SPI2->CCTL|=SPI_CCTL_CPOL;/*Clockpolarityselecthigh*/
SPI2->CCTL|=SPI_CCTL_TXEDGE;/*Transmitdataedgefori2sbus*/
SPI2->I2SCFGR&=~SPI_I2SCFGR_MCKOE;/*I2Smasterclockoutputdisable*/
SPI2->I2SCFGR&=~SPI_I2SCFGR_CHLEN;/*Vocaltractlength16bit*/
SPI2->I2SCFGR|=SPI_I2SCFGR_DATLEN_32;/*Audiodatawidth32*/
SPI2->I2SCFGR|=SPI_I2SCFGR_I2SSTD_Philips;/*I2SSTDPhilips*/
SPI2->I2SCFGR|=SPI_I2SCFGR_SPI_I2S;/*SPI/I2Smodulefunctionselection*/
SPI2->GCTL&=~SPI_GCTL_MODE;/*I2SSlavemode*/
SPI2->GCTL|=SPI_GCTL_DW_8_32;/*double-worddataselectsignal*/
SPI2->GCTL|=SPI_GCTL_DMAMODE;/*DMAaccessmodeenable*/
SPI2->GCTL|=SPI_GCTL_TXEN;/*I2STransmitenable*/
SPI2->GCTL|=SPI_GCTL_INTEN;/*SPI_I2Sinterruptenable*/
SPI2->GCTL|=SPI_GCTL_SPIEN;/*EnableI2S*/
}
7
I2S发送DAC音频信号流
MM32F0163D7P的I2S2发送DAC音频信号流使用DMA中断发送,代码接口如下所示:
voidI2S2_TxData_DMA_Interrupt(uint8_t*Buffer,uint8_tdatasize)
{
DMA_InitTypeDefDMA_InitStruct;
NVIC_InitTypeDefNVIC_InitStruct;
RCC_DMA_ClockCmd(DMA1,ENABLE);/*EnableDMA1Clock*/
DMA_DeInit(DMA1_Channel5);
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&(SPI2->TXREG);/*SPI2_I2S2BaseAddr*/
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)(Buffer);/*Memorybufferformusicdata*/
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_BufferSize=datasize;/*LeftandRightchannelaudiobuffersize*/
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;/*memoryincrement*/
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;/*halfwordtransfer*/
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;/*halfwordtransfer*/
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;/*Normalmode*/
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;/*DMAPriorityMedium*/
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;/*Disablememorytomemorytransfer*/
DMA_InitStruct.DMA_Auto_reload=DMA_Auto_Reload_Enable;/*Enableautoreload*/
DMA_Init(DMA1_Channel5,&DMA_InitStruct);
DMA_ClearFlag(DMA1_FLAG_TC5);/*Cleartransfercompleteflag*/
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);/*EnableDMAChannel5SPI2_I2S2DMAtransfercompleteinterrupt*/
NVIC_InitStruct.NVIC_IRQChannel=DMA1_Channel4_7_IRQn;/*SetSPI2_I2S2DMAChannelNVICintterruptpriority*/
NVIC_InitStruct.NVIC_IRQChannelPriority=0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;/*EnableNVICIRQChannel*/
NVIC_Init(&NVIC_InitStruct);
DMA_Cmd(DMA1_Channel5,ENABLE);/*EnableSPI2_I2S2DMAChannel5*/
SPI_DMACmd(SPI1,ENABLE);/*EnableSPI_I2S*/
while(0==I2S2_TX_DMA_InterruptFlag)
{
}
}
I2S的DMA中断处理函数:
voidDMA1_Channel4_7_IRQHandler(void)
{
if(SET==DMA_GetITStatus(DMA1_IT_TC5))
{
/*I2S2DMAinterrupttransferflag*/
I2S2_TX_DMA_InterruptFlag=1;
/*Cleartransfercompleteinterruptflag*/
DMA_ClearITPendingBit(DMA1_IT_TC5);
/*EnableSPI2_I2S2DMAChannel5*/
DMA_Cmd(DMA1_Channel5,ENABLE);
}
}
8
MCU输出12MHz的PWM
给NAU88C22音频编解码芯片
配置MM32F0163D7P TIM1输出12MHz的PWM给NAU88C22合成12.228MHz时钟给NAU88C22的IMCLK和MCU的I2S,可参考MM32F0160_Samples中的TIM1 PWM输出例程。
9
SPI1接口的GPIO驱动25WQ80存储器
SPI1接口的GPIO初始化代码如下所示:
voidSPI1_GPIO_Init(void)
{
GPIO_InitTypeDefGPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOA,ENABLE);
RCC_GPIO_ClockCmd(GPIOB,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_0);/*PA15AFSPI1_CS*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_0);/*PB3AFSPI1_SCK*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_0);/*PB5AFSPI1_MOSI*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_0);/*PB4AFSPI1_MISO*/
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15;/*PA15SPI1_CS*/
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3;/*PB3SPI1_CS*/
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;/*PB5SPI1_CS*/
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4;/*PB4SPI1_CS*/
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
SPI1接口驱动25WQ80存储器,存储待读取的音频信号,经过SPI1读取后通过I2S2接口传输音频信号流到NAU88C22音频编解码芯片解码播放音乐,其初始化代码如下所示:
voidSPI1_NVIC_Config(uint16_tspi_baud_div)
{
SPI_InitTypeDefSPI_InitStruct;
NVIC_InitTypeDefNVIC_InitStruct;
/*SPI1NVICPriorityConfig*/
NVIC_InitStruct.NVIC_IRQChannel=SPI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority=0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
RCC_SPI_ClockCmd(SPI1,ENABLE);/*EnableSPI1Clock*/
SPI_DeInit(SPI1);
SPI_StructInit(&SPI_InitStruct);
SPI_InitStruct.SPI_Mode=SPI_Mode_Master;/*SPImastermode*/
SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;
SPI_InitStruct.SPI_DataWidth=8;
SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;/*Theclockislowinidlestate.*/
SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;/*Datasamplingstartsfromthefirstclockedge*/
SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;
/*SPIdataedgeadjustinfastspeedmode*/
SPI_InitStruct.SPI_BaudRatePrescaler=(SPI_BaudRatePrescaler_TypeDef)spi_baud_div
;
SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;/*DatatransfersstartfromMSB*/
SPI_Init(SPI1,&SPI_InitStruct);
if(SPI_InitStruct.SPI_BaudRatePrescaler<= 8)
{
exSPI_DataEdgeAdjust(SPI1, SPI_DataEdgeAdjust_FAST);
}
/* Enable Receive available data interrupt and transmitter empty interrupt */
SPI_ITConfig(SPI1, SPI_IT_RX | SPI_IT_TXEPT, ENABLE);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx); /* Receive enable */
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx); /* Transmit enable */
SPI_Cmd(SPI1, ENABLE); /* Enable SPI1 */
}
SPI1读写25WQ80存储器函数接口请参考MM32F0160_Samples中的SPI_FLASH_Interrupt例程。
10
I2C接口的初始化
I2C1主机模式驱动NAU88C22音频编解码芯片用于发送指令控制NAU88C22音频编解码芯片工作。I2C1接口的GPIO初始化代码如下所示:
voidI2C1_GPIO_Init(void)
{
GPIO_InitTypeDefGPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB,ENABLE);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_1);/*PB10AFI2C1_SCL*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_1);/*PB10AFI2C1_SDA*/
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;/*PB10I2C1_SCL*/
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
/*KeepthebusfreewhichmeansSCK&SDAishigh*/
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;/*PB11I2C1_SDA*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
I2C1接口作为主机驱动NAU88C22音频编解码芯片工作,其初始化代码如下所示:
voidI2C_Master_Mode_Init(I2C_TypeDef*I2Cx,uint32_tI2C_speed)
{
I2C_InitTypeDefI2C_InitStruct;
RCC_I2C_ClockCmd(I2C1,ENABLE);/*EnableI2Cclock*/
I2C_StructInit(&I2C_InitStruct);
I2C_InitStruct.Mode=I2C_CR_MASTER;/*ConfigureI2Casmastermode*/
I2C_InitStruct.OwnAddress=0;
if(I2C_speed>200000)/*morethan200K*/
{
I2C_InitStruct.Speed=I2C_CR_SPEED_FAST;/*I2Cfastspeedmode*/
}
else
{
I2C_InitStruct.Speed=I2C_CR_SPEED_STD;/*I2Cstandardspeedmode*/
}
I2C_InitStruct.ClockSpeed=I2C_speed;/*I2CSpeed*/
I2C_Init(I2Cx,&I2C_InitStruct);
I2C_Cmd(I2Cx,ENABLE);
}
I2C1设置从机地址代码如下所示:
voidI2C_Set_DeviceAddr(I2C_TypeDef*I2Cx,uint8_tdeviceaddr)
{
/*DisableI2C*/
I2C_Cmd(I2Cx,DISABLE);
/*Setthedeviceaddress*/
I2C_Send7bitAddress(I2Cx,deviceaddr,I2C_Direction_Transmitter);
/*EnableI2C*/
I2C_Cmd(I2Cx,ENABLE);
}
I2C1的读写数据和命令的操作参考LibSamples_MM32F0160 I2C例程。
11
编译I2S程序烧录烧录到开发板中播放音乐
编译MM32F0163D7P的I2S2程序并烧录到开发板中实现音乐播放功能。
实验1:播放三角波测试文件实测输出波形

实验2:播放48KHz/24BIT立体声WAVE格式音乐文件实测输出波形

-
接口
+关注
关注
33文章
9450浏览量
156164 -
数据
+关注
关注
8文章
7315浏览量
93997 -
音乐播放器
+关注
关注
0文章
71浏览量
16094
原文标题:灵动微课堂 (第262讲)|基于MM32F0163D7P的I2S接口的音乐播放器实验
文章出处:【微信号:MindMotion-MMCU,微信公众号:灵动MM32MCU】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
使用I2S播放音乐时,BLE不能被手机扫描到怎么解决?
【WRTnode2R申请】hifi 播放器
使用I2S播放音乐时,BLE不能被手机扫描到的原因?怎么解决?
使用I2S播放音乐时,BLE不能被手机扫描到怎么解决?
STM32的I2S接口能不能和CODEC的I2S接口同时录音、播放?
音频接口I2S实验
Windows Media Player音乐播放器代码
MP3/MP4播放器工作原理详细介绍
基于Android音乐播放器的研究
I2S接口工作原理_I2S接口介绍
基于MM32F5270控制器的I2S音频播放
基于MM32F0163D7P的I2S接口的音乐播放器实验

基于I2S接口的音乐播放器工作原理
评论