I2S总线

一、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,ODD
const 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;
} 

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

  • 5
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
I2S总线接口的设计可以使用Verilog语言来实现。在设计中,需要考虑到FPGA与数字音频芯片之间的信号连接和时序。引用提到了设计一个FPGA与数字音频芯片的I2S接口时序,这意味着需要设计和实现与I2S总线相关的时钟信号(MCLK,BCLK)和数据信号(LRCK,SDATA)的生成和处理。 在Verilog代码中,可以定义输入和输出端口来连接FPGA和音频芯片。引用提到了常见的信号,如MCLK(主时钟),SCLK(数据时钟),LRCK(左右声道选择),SDAT(音频数据),RST(复位信号)和MODE(工作模式选择)。可以根据具体需求在代码中定义这些信号。 接下来,需要根据I2S总线的时序要求来生成时钟和数据信号。例如,可以使用计数器来生成BCLK(位时钟)信号,根据BCLK的边沿来采样和传输音频数据。还可以根据LRCK的边沿来选择左右声道。 随后,需要根据数据要求来处理音频数据。可以使用移位寄存器来将音频数据从SDAT输入并移位到输出端口。在代码中还可以实现复位功能,以及根据MODE信号来选择不同的工作模式。 在设计I2S总线接口时,还需要考虑时序同步和时钟域的问题,以确保数据的准确传输。可以使用FPGA的时钟域划分和时钟同步技术,以及适当的寄存器和状态机来实现。 总的来说,设计I2S总线接口的Verilog代码需要考虑与FPGA和音频芯片之间的信号连接和时序要求,并且根据具体的应用需求来生成和处理时钟和数据信号。可以参考引用中提到的时序设计和引用中给出的Verilog代码作为参考。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [[Craftor原创] I2S总线接口设计(Verilog)](https://blog.csdn.net/weixin_30527143/article/details/96956435)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [I2S DAC的Verilog实现](https://blog.csdn.net/snutqq/article/details/120347969)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

留小乙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值