资讯专栏INFORMATION COLUMN

I2S总线

yunhao / 3773人阅读

摘要:一简介也叫,即总线又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专责于音频设备之间的数据传输,广泛应用于各种多媒体系统。位,这两个位用于选择标准,设置为,选择飞利浦标准。

一、I2S简介

I2S(也叫IIS,即:Inter IC Sound)总线, 又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专责于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。
特点
●支持全双工/半双工通信
●支持主/从模式设置
●8位可编程线性预分频器,可实现精确的音频采样频率(8~192Khz)
●支持16位/24位/32位数据格式
●数据包帧固定为16位(仅16位数据帧)或32位(可容纳16/24/32位数据帧)
●可编程时钟极性
●支持MSB对齐(左对齐)、LSB对齐(右对齐)、飞利浦标准和PCM标准等I2S标准
●支持DMA数据传输(16位宽)
●数据方向固定位MSB在前
●支持主时钟输出(固定为256*fs,fs即音频采样率)

二、I2S框图以及信号

I2S框图
STM32F4的I2S是与SPI部分共用的,通过设置SPI_I2SCFGR寄存器的I2SMOD位即可开启I2S功能,I2S接口使用了几乎与SPI相同的引脚、标志和中断。


信号
1,SD:串行数据(映射到 MOSI 引脚),用于发送或接收两个时分复用的数据通道上的数据(仅半双工模式)。
2,WS:字选择(映射到NSS引脚),即左右时钟,用于切换左右声道的数据。WS频率等于音频信号采样率(fs)。
3,CK:串行时钟(映射到SCK引脚),即位时钟,是主模式下的串行时钟输出以及从模式下的串行时钟输入。CK频率=WS频率(fs)216(16位宽),如果是32位宽,则是:CK频率=WS频率(fs)232(32位宽)。
4,I2S2ext_SD和I2S3ext_SD:用于控制I2S全双工模式的附加串行数据引脚(映射到MISO引脚),这两个引脚仅用于全双工模式。
5,MCK:即主时钟输出,当I2S配置为主模式(且SPI_I2SPR寄存器的MCKOE位置1)时,使用此时钟,该时钟频率为 256×fs,fs:音频信号采样频率。

STM32F4为支持I2S全双工模式,除了I2S2和I2S3,还可以使用两个额外的I2S,它们称为扩展I2S(I2S2_ext、I2S3_ext),其框图为:

扩展I2S (I2Sx_ext)只能用于全双工模式。I2Sx_ext始终在从模式下工作。I2Sx和I2Sx_ext 均可用于发送和接收。

三、帧格式

STM32F4的I2S支持4种数据和帧格式组合,分别是:
1,将16位数据封装在16位帧中;
2,将16位数据封装在32位帧中;
3,将24位数据封装在32位帧中;
4,将32位数据封装在32位帧中;

将16位数据封装在32位帧中时,前16位(MSB)为有效位,16位LSB被强制清零,无需任何软件操作或DMA请求(只需一个读/写操作)。如果应用程序选则DMA,则24位和32位数据帧需要对SPI_DR执行两次CPU读取或写入操作,或者需要两次DMA操作。24位的数据帧,硬件会将8位非有效位扩展到带有0位的32位数据帧。
4种帧标准:
1,飞利浦标准;
2,MSB 对齐(左对齐)标准;
3,LSB 对齐(右对齐)标准;
4,PCM标准;

I2S飞利浦标准帧24位数据,32位帧格式

I2S飞利浦标准,使用WS信号来指示当前正在发送的数据所属的通道。该信号从当前通道数据的第一个位(MSB)之前的一个时钟开始有效。发送方在时钟信号(CK)的下降沿改变数据,接收方在上升沿读取数据。WS信号也在CK的下降沿变化。在24位模式下数据传输,需要对SPI_DR执行两次读取或写入操作。比如要发送0X8EAA33这个数据,就要分两次写入SPI_DR,第一次写入:0X8EAA,第二次写入0X33xx(xx可以为任意数值),这样就把0X8EAA33发送出去了。
注意:从SD卡读取到的24位WAV数据流,是低字节在前,高字节在后的,比如,我们读到一个声道的数据(24bit),存储在buf[3]里面,那么要通过SPI_DR发送这个24位数据,过程如下:
SPI_DR=((u16)buf[2]<<8)+buf[1];
SPI_DR=(u16)buf[0]<<8;
这样,第一次发送高16位数据,第二次发送低8位数据,完成一次24bit数据的发送。

四、I2S时钟


上图中的I2SxCLK,可以来自PLLI2S输出(通过R系数分频)或者来自外部时钟(I2S_CKIN引脚),一般使用前者作为I2SxCLK输入时钟。
需要根据音频采样率(fs)来计算各个分频器的值,常用的音频采样率有:22.05Khz、44.1Khz、48Khz、96Khz、196Khz等。

当MCK输出使能时,fs频率计算公式如下:
fs=I2SxCLK/[256 * (2 * I2SDIV+ODD)]
其中:I2SxCLK=(HSE/pllm)* PLLI2SN/PLLI2SR。 HSE是8Mhz,而pllm在系统时钟初始化就确定了,是8,这样结合以上式,可得计算公式如下:

fs= (1000 * PLLI2SN/PLLI2SR )/[256 * (2 * I2SDIV+ODD)]
fs单位是:Khz。其中:PLLI2SN取值范围:192~ 432;PLLI2SR取值范围:2~ 7;I2SDIV取值范围:2~255;ODD取值范围:0/1。

根据以上约束条件,便可根据fs来设置各个系数的值了,不过很多时候,并不能取得和fs一模一样的频率,只能近似等于fs,比如44.1Khz采样率,设置PLLI2SN=271,PLLI2SR=2,I2SDIV=6,ODD=0,得到fs=44.108073Khz,误差为:0.0183%。晶振频率决定了有时无法通过分频得到我们所要的fs,所以,某些fs如果要实现0误差,必须得选用外部时钟才可以。
为了方便可以将常用的fs建立对应的表格。

//表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODDconst u16 I2S_PSC_TBL[][5]={        {800 ,256,5,12,1},		//8Khz采样率        {1102,429,4,19,0},		//11.025Khz采样率        {1600,213,2,13,0},	//16Khz采样率        {2205,429,4, 9,1},		//22.05Khz采样率        {3200,213,2, 6,1},		//32Khz采样率        {4410,271,2, 6,0},		//44.1Khz采样率        {4800,258,3, 3,1},		//48Khz采样率        {8820,316,2, 3,1},		//88.2Khz采样率        {9600,344,2, 3,1},  	//96Khz采样率        {17640,361,2,2,0},  	//176.4Khz采样率        {19200,393,2,2,0},  	//192Khz采样率};

五、I2S寄存器

1、SPI_I2S配置寄存器(SPI_I2SCFGR)

I2SMOD位,设置为1,选择I2S模式,注意,必须在I2S/SPI禁止的时候,设置该位。
I2SE位,设置为1,使能I2S外设,该位必须在I2SMOD位设置之后再设置。
I2SCFG[1:0]位, 这两个位用于配置I2S模式,设置为10,选择主模式(发送)。
I2SSTD[1:0]位,这两个位用于选择I2S标准,设置为00,选择飞利浦标准。
CKPOL位,用于设置空闲时时钟电平,设置为0,空闲时时钟低电平。
DATLEN[1:0]位,用于设置数据长度,00,表示16位数据;01表示24位数据。
CHLEN位,用于设置通道长度,即帧长度,0,表示16位;1,表示32位。
2、SPI_I2S预分频器寄存器(SPI_I2SSPR)

设置MCKOE为1,开启MCK输出,ODD和I2SDIV则根据不同的fs,查表进行设置。
3、PLLI2S配置寄存器(RCC_PLLI2SCFGR)

该寄存器用于配置PLLI2SR和PLLI2SN两个系数,PLLI2SR的取值范围是:2~ 7,PLLI2SN的取值范围是:192~432。同样,这两个也是根据fs的值来设置的。

六、I2S初始化步骤

  • 1)初始化WM8978
    这个过程就是配置上一讲讲介绍的WM8978那十几个寄存器,包括软复位、DAC设置、输出设置和音量设置等。
  • 2)初始化I2S
    此过程主要设置SPI_I2SCFGR寄存器,设置I2S模式、I2S标准、时钟空闲电平和数据帧长等,最后开启I2S TX DMA,使能I2S外设。
  • 3)解析WAV文件,获取音频信号采样率和位数并设置I2S时钟分频器
    解析WAV文件,取得音频信号的采样率(fs)和位数(16位或24位),根据这两个参数,来设置I2S的时钟分频,用前面介绍的查表法来设置。
  • 4)设置DMA
    I2S播放音频,一般采用DMA来传输数据,这里我们用I2S2,其TX用DMA1数据流4的通道0来传输数据。并且,STM32F4的DMA具有双缓冲机制,这样可以提高效率。这里,我们将DMA1数据流4设置为:双缓冲循环模式,外设和存储器都是16位宽,并开启DMA传输完成中断(方便填充数据)。
  • 5)编写DMA传输完成中断服务函数
    为了方便填充音频数据,我们使用DMA传输完成中断,每当一个缓冲数据发送完后,硬件自动切换为下一个缓冲,同时进入中断服务函数,填充数据到发送完的这个缓冲。如下图所示:
  • 6)开启DMA传输,填充数据
    最后,只需要开启DMA传输,然后及时填充WAV数据到DMA的两个缓存区即可。此时,就可以在WM8978的耳机和喇叭通道听到所播放音乐了。

七、播放WAV文件代码

//播放某个WAV文件//fname:wav文件路径.//返回值://KEY0_PRES:下一曲//KEY1_PRES:上一曲//其他:错误u8 wav_play_song(u8* fname){	u8 key;	u8 t=0; 	u8 res;  	u32 fillnum; 	audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));	audiodev.i2sbuf1=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE);	audiodev.i2sbuf2=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE);	audiodev.tbuf=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE);	if(audiodev.file&&audiodev.i2sbuf1&&audiodev.i2sbuf2&&audiodev.tbuf)	{ 		res=wav_decode_init(fname,&wavctrl);//得到文件的信息		if(res==0)//解析文件成功		{			if(wavctrl.bps==16)			{				WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度				I2S2_Init(I2S_Standard_Phillips,I2S_Mode_MasterTx,I2S_CPOL_Low,I2S_DataFormat_16bextended);		//飞利浦标准,主机发送,时钟低电平有效,16位扩展帧长度			}else if(wavctrl.bps==24)			{				WM8978_I2S_Cfg(2,2);	//飞利浦标准,24位数据长度				I2S2_Init(I2S_Standard_Phillips,I2S_Mode_MasterTx,I2S_CPOL_Low,I2S_DataFormat_24b);		//飞利浦标准,主机发送,时钟低电平有效,24位扩展帧长度			}			I2S2_SampleRate_Set(wavctrl.samplerate);//设置采样率			I2S2_TX_DMA_Init(audiodev.i2sbuf1,audiodev.i2sbuf2,WAV_I2S_TX_DMA_BUFSIZE/2); //配置TX DMA			i2s_tx_callback=wav_i2s_dma_tx_callback;			//回调函数指wav_i2s_dma_callback			audio_stop();			res=f_open(audiodev.file,(TCHAR*)fname,FA_READ);	//打开文件			if(res==0)			{				f_lseek(audiodev.file, wavctrl.datastart);		//跳过文件头				fillnum=wav_buffill(audiodev.i2sbuf1,WAV_I2S_TX_DMA_BUFSIZE,wavctrl.bps);				fillnum=wav_buffill(audiodev.i2sbuf2,WAV_I2S_TX_DMA_BUFSIZE,wavctrl.bps);				audio_start();  				while(res==0)				{ 					while(wavtransferend==0);//等待wav传输完成; 					wavtransferend=0;					if(fillnum!=WAV_I2S_TX_DMA_BUFSIZE)//播放结束?					{						res=KEY0_PRES;						break;					}  					if(wavwitchbuf)fillnum=wav_buffill(audiodev.i2sbuf2,WAV_I2S_TX_DMA_BUFSIZE,wavctrl.bps);//填充buf2					else fillnum=wav_buffill(audiodev.i2sbuf1,WAV_I2S_TX_DMA_BUFSIZE,wavctrl.bps);//填充buf1					while(1)					{						key=KEY_Scan(0); 						if(key==WKUP_PRES)//暂停						{							if(audiodev.status&0X01)audiodev.status&=~(1<<0);							else audiodev.status|=0X01;  						}						if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲						{							res=key;							break; 						}						wav_get_curtime(audiodev.file,&wavctrl);//得到总时间和当前播放的时间 						audio_msg_show(wavctrl.totsec,wavctrl.cursec,wavctrl.bitrate);						t++;						if(t==20)						{							t=0; 							LED0=!LED0;						}						if((audiodev.status&0X01)==0)delay_ms(10);						else break;					}				}				audio_stop(); 			}else res=0XFF; 		}else res=0XFF;	}else res=0XFF; 	myfree(SRAMIN,audiodev.tbuf);	//释放内存	myfree(SRAMIN,audiodev.i2sbuf1);//释放内存	myfree(SRAMIN,audiodev.i2sbuf2);//释放内存 	myfree(SRAMIN,audiodev.file);	//释放内存 	return res;} 

音乐播放就讲到这里啦!!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/122389.html

相关文章

  • STM32学习笔记 第二章 STM32资源介绍

    摘要:总线挂载的外设有等。外设地址映射片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,挂载低速外设,和挂载高速外设。 第二章 STM32资源介绍 2...

    G9YH 评论0 收藏0
  • 【程序员基础必备】计算机组成原理概念大全

    摘要:计算机组成原理是大学中的专业必修课,也是程序员需要掌握的专业知识。所以说计算机组成原理是非常重要的一门课程,本文总结了五十个组原中的重要概念,供大家学习。通道程序通道程序由通道指令组成,他完成某种外围设备与主存传送信息的操作。 计算机组成原理是大学中的专业必修课,也是程序员需要掌握的专业...

    OBKoro1 评论0 收藏0
  • 全志R528核心板和开发板

    摘要:产品概述是我司倾力打造的一款基于全志双核处理器的高性能核心板,可广泛应用于智能家居工业显示语控设备医疗电子等产品。 产品概述    R5是我司倾力打造的一款基于全志R528-S3A双核处理器的高性能Linux核心板,可广泛应用于智能家居、工业显示、语控设备、医疗电子等产品。   核心板体积小...

    nanfeiyan 评论0 收藏0
  • YodaOS开发套件征集活动

    摘要:为了能让感兴趣的开发者小伙伴第一时间体验到,联合社区赶在春节前发起开发套件试用征集活动,该套件是基于自研语音解决方案平台打造而成。与此同时,我们也同样提供高度定制化模块化的整体架构,开发者亦可方便地选择想要集成的模块对整个操作系统重新组装。 1月28日,Rokid为人机交互设备开发的开源人工智能操作系统——YodaOS正式上线,可用于智能音箱、智能家居、智能穿戴和车载等多种设备和场景。...

    canger 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<