Blog of Craftor never too late to learn

I2c总线设计(verilog)

2011-11-06

I2C总线设计(Verilog)

I2S是数字音频的接口,这里不用多说,请读者自己查阅相关资料。

本文中要设计的是FPGA与数字音频芯片的I2S接口时序。简单点说,就是通过FPGA向音频芯片写数据,通过的是I2S总线,因为这个总线比较麻烦,我在这里做成接口模块,其它模块直接拿来用就可以了。

提示,I2S总线的接口信号如下:

1、LRCLK:左右声道控制,高电平时,SDATA上为左声道数据,低电平时,SDATA上为右声道数据。(也有相反的情况,请参考不同的音频芯片的手册)

2、BCLK:跟SDATA上数据对应的时钟,上升沿采数据,也可能在下降沿采数据,请注意对应音频芯片手册上的说明。

3、SDATA:串行数据,一个BCLK对应一个。

时序图如下,WS就是LRCLK,BCLK就是SCK。

image

一、设计思路,数据流向,如下:

image

二、实现

左声道和右声道的数据,分别设计成两个FIFO即可。重点在于如何将两路数据拼装到一起,再转换成串行的数据。

三、设计

1、LRCLK和BCLK的产生

提示,如果数字音频的数据是16位的,那么BCLK就是LRCLK的16倍。即在一个LRCLK中,有32个BCLK,16个左声道数据,16个右声道数据。同样,如果数据是12位的,那么BCLK就是LRCLK的24倍。

部分代码如下:

// LRCLK & BCLK Generate

reg [7:0] lrclk_cnt = 0;
reg [2:0] bclk_cnt = 0;

always@(posedge clk) begin

	lrclk_cnt <= lrclk_cnt + 1;
	if (lrclk_cnt == 127)	audio_lrclk <= 1'b1;
	if (lrclk_cnt == 255)	audio_lrclk <= 1'b0;

end

always@(posedge clk) begin

	bclk_cnt <= bclk_cnt + 1;
	if (bclk_cnt == 3)	audio_bclk <= 1'b1;
	if (bclk_cnt == 7)	audio_bclk <= 1'b0;

end
  • 说明,如果音频数据的采样率是48KHz,那么,一般情况下,clk应该是采样率的256、384或者512倍。比较常见的是256倍,那么,这里的clk=44.8KHz*256=12.288MHz。

之所以用这种计数器的方式产生LRCLK和BCLK,是为下面的装入数据做准备的。

2、SDATA数据的载入
// DAC Data Assembling

reg [15:0] lbuf = 16'd0;
reg [15:0] rbuf = 16'd0;

always@(negedge clk) begin

	case(lrclk_cnt)
	// Left
	0:   audio_sdata <= lbuf[15];
	8:   audio_sdata <= lbuf[14];
	16:  audio_sdata <= lbuf[13];
	24:  audio_sdata <= lbuf[12];
	32:  audio_sdata <= lbuf[11];
	40:  audio_sdata <= lbuf[10];
	48:  audio_sdata <= lbuf[9];
	56:  audio_sdata <= lbuf[8];
	64:  audio_sdata <= lbuf[7];
	72:  audio_sdata <= lbuf[6];
	80:  audio_sdata <= lbuf[5];
	88:  audio_sdata <= lbuf[4];
	96:  audio_sdata <= lbuf[3];
	104: audio_sdata <= lbuf[2];
	112: audio_sdata <= lbuf[1];
	120: audio_sdata <= lbuf[0];
	// Right 
	128: audio_sdata <= rbuf[15];
	136: audio_sdata <= rbuf[14];
	144: audio_sdata <= rbuf[13];
	152: audio_sdata <= rbuf[12];
	160: audio_sdata <= rbuf[11];
	168: audio_sdata <= rbuf[10];
	176: audio_sdata <= rbuf[9];
	184: audio_sdata <= rbuf[8];
	192: audio_sdata <= rbuf[7];
	200: audio_sdata <= rbuf[6];
	208: audio_sdata <= rbuf[5];
	216: audio_sdata <= rbuf[4];
	224: audio_sdata <= rbuf[3];
	232: audio_sdata <= rbuf[2];
	240: audio_sdata <= rbuf[1];
	248: audio_sdata <= rbuf[0];
	endcase

end
  • 说明,至于在计数器的哪个值上将数据赋值,以上的代码都是经过仿真和实测的,读者可以自己仿真观察一下就知道了
3、FIFO数据的读取

第2节代码中可以看到,sdata的数据是从lbuf和rbuf中取的,那么下面的模块就是如何将数据从FIFO中取出,并放到lbur和rbuf中了。

// Fetch Audio Data From FIFO

assign lfifo_rd_clk = clk;
assign rfifo_rd_clk = clk;

always@(negedge clk) begin

	case(lrclk_cnt)
	125: 
		begin
			if(!rfifo_empty) rfifo_rd_en <= 1;
		end
	126: 
		begin
			rfifo_rd_en <= 0;
			rbuf <= rfifo_dout;
		end
	253: 
		begin
			if(!lfifo_empty) lfifo_rd_en <= 1;
		end
	254: 
		begin
			lfifo_rd_en <= 0;
			lbuf <= lfifo_dout;
		end
	endcase

end
  • 说明,上面取数据对应的计数器值也是经过仿真和实测的,没有问题,读者可以自己仿真观察下

四、测试情况:

1、找一个mp3或者其它音频文件,48KHz的采样率以上,如果采样率不是48KHz的,通过Adobe Audition(原Cool Edit)软件调整采样率(升采样率会出现杂音,你懂的)。

2、用Matlab打开,可以看到在计算机上的音频文件的数据是经过归一化的。将他们转化成16位的二进制数(unsigned int类型的也一样),然后另存为二进制文件。

3、通过USB接口,将这个二进制文件发送下去。FPGA端连续不断的将数据输出即可听到声音。(软件通过USB发送数据下去的时候,最好将文件切成1K的段发下去,因为FPGA的FIFO缓冲区没那么大,USB发送数据的延时等待也要设置成200ms以上,不然数据流会断掉)

五、完整代码


module audio_ctrl(

	//---------------------------------
	// Clock and Reset
	//---------------------------------
	input 		clk,     // 12.288MHz
	input		rst_n,	
	
	
	//---------------------------------
	// Sound Data FIFO Interface
	//---------------------------------
	
	// Left Channel
	input        lfifo_empty,
	output wire  lfifo_rd_clk,
	output reg   lfifo_rd_en = 0,
	input [15:0] lfifo_dout,
	
	// Right Channel
	input        rfifo_empty,
	output wire  rfifo_rd_clk,
	output reg   rfifo_rd_en = 0,
	input [15:0] rfifo_dout,

	
	//---------------------------------
	// I2S Interface, Master
	//---------------------------------
	
	output reg 	audio_sdata = 0,
  	output reg 	audio_lrclk = 0,
  	output reg	audio_bclk = 0 

);

// LRCLK & BCLK Generate

reg [7:0] lrclk_cnt = 0;
reg [2:0] bclk_cnt = 0;

always@(posedge clk) begin

	lrclk_cnt <= lrclk_cnt + 1;
	if (lrclk_cnt == 127)	audio_lrclk <= 1'b1;
	if (lrclk_cnt == 255)	audio_lrclk <= 1'b0;

end

always@(posedge clk) begin

	bclk_cnt <= bclk_cnt + 1;
	if (bclk_cnt == 3)	audio_bclk <= 1'b1;
	if (bclk_cnt == 7)	audio_bclk <= 1'b0;

end

// DAC Data Assembling

reg [15:0] lbuf = 16'd0;
reg [15:0] rbuf = 16'd0;

always@(negedge clk) begin

	case(lrclk_cnt)
	// Left
	0:   audio_sdata <= lbuf[15];
	8:   audio_sdata <= lbuf[14];
	16:  audio_sdata <= lbuf[13];
	24:  audio_sdata <= lbuf[12];
	32:  audio_sdata <= lbuf[11];
	40:  audio_sdata <= lbuf[10];
	48:  audio_sdata <= lbuf[9];
	56:  audio_sdata <= lbuf[8];
	64:  audio_sdata <= lbuf[7];
	72:  audio_sdata <= lbuf[6];
	80:  audio_sdata <= lbuf[5];
	88:  audio_sdata <= lbuf[4];
	96:  audio_sdata <= lbuf[3];
	104: audio_sdata <= lbuf[2];
	112: audio_sdata <= lbuf[1];
	120: audio_sdata <= lbuf[0];
	// Right 
	128: audio_sdata <= rbuf[15];
	136: audio_sdata <= rbuf[14];
	144: audio_sdata <= rbuf[13];
	152: audio_sdata <= rbuf[12];
	160: audio_sdata <= rbuf[11];
	168: audio_sdata <= rbuf[10];
	176: audio_sdata <= rbuf[9];
	184: audio_sdata <= rbuf[8];
	192: audio_sdata <= rbuf[7];
	200: audio_sdata <= rbuf[6];
	208: audio_sdata <= rbuf[5];
	216: audio_sdata <= rbuf[4];
	224: audio_sdata <= rbuf[3];
	232: audio_sdata <= rbuf[2];
	240: audio_sdata <= rbuf[1];
	248: audio_sdata <= rbuf[0];
	endcase

end


// Fetch Audio Data From FIFO

assign lfifo_rd_clk = clk;
assign rfifo_rd_clk = clk;

always@(negedge clk) begin

	case(lrclk_cnt)
	125: 
		begin
			if(!rfifo_empty) rfifo_rd_en <= 1;
		end
	126: 
		begin
			rfifo_rd_en <= 0;
			rbuf <= rfifo_dout;
		end
	253: 
		begin
			if(!lfifo_empty) lfifo_rd_en <= 1;
		end
	254: 
		begin
			lfifo_rd_en <= 0;
			lbuf <= lfifo_dout;
		end
	endcase

end


endmodule


上一篇 Test

Comments