I2S总线是Inter-IC Sound 总线的缩写,其最初是由飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准。
在飞利浦公司的I2S标准中,既规定了硬件接口规范,也规定了数字音频数据的格式。
硬件方面,主要有三个信号,分别是:
1、串行时钟SCLK(有时也叫位时钟BCLK),即对应数字音频的每一个数据,SCLK都有一个脉冲信号。
2、帧时钟LRCK,用于切换左右声道的数据。一般LRCK为1表示正在传输右声道,为0表示正在传输左声道。
3、串行数据SDAT,用二进制补码形式表示的音频数据。
有时为了使系统间能够更好地同步,还需要另外一个时钟MCLK,称为主时钟,或系统时钟,其跟采样频率之间存在一定的比例关系。
软件方面:
I2S格式的信号无论有多少位有效数据,数据的最高位总是出现在LRCK变化(也就是一帧开始)后的第2个SCLK脉冲处。这就使得接收端与发送端的有效位数可以不同。如果接收端能处理的有效位数少于发送端,可以放弃数据帧中多余的低位数据;如果接收端能处理的有效位数多于发送端,可以自行补足剩余的位。这种同步机制使得数字音频设备的互连更加方便,而且不会造成数据错位。
/****************************************************************************************************/
以上内容摘抄自百科
/***************************************************************************************************/
实际项目中,采用了一片具备I2S格式的DAC器件,并用FPGA器件作为控制器件来控制DAC工作,就具体的DAC器件而言,需要控制的信号有
MCLK/SCLK/LRCK/SDAT/RST/MODE等信号。其与FPGA之间的连接不过多叙述,下面直接给出该部分实现的verilog代码,请有兴趣的自行参考。
module dac(
input clk_i, //clk in
input rst_i, //rst
input [2:0] da_ctrl, //dac sample rate control
output sclk_o, //sclk
output mclk_o, //mclk
output lrck_o, //lrck
output sdout_o, //sdin
output [1:0] mode_o,//mode
output reg da_rst, //rst
output da_done,
input [23:0] l_data_i, //left channel data in
input [23:0] r_data_i //right channel data in
);
/******************************************************/
reg da_sclk_r,da_mclk_r,da_lrck_r;
reg [1:0] da_mode_r;
reg [9:0] clk_div;
always@(posedge clk_i or negedge rst_i) begin
if(!rst_i) begin
clk_div <= 10'd0;
end
else
clk_div <= clk_div + 1'b1;
end
//这里采用时钟分频的方式来获得各时钟信号
always@(posedge clk_i or negedge rst_i) begin
if(!rst_i) begin
da_sclk_r <= 1'b1;
da_mclk_r <= 1'b1;
da_lrck_r <= 1'b1;
da_mode_r <= 2'b00;
end
else begin
case(da_ctrl[2:0])
3'b000: begin
da_mclk_r <= clk_div[0];
da_sclk_r <= clk_div[1];
da_lrck_r <= clk_div[7];
da_mode_r <= 2'b11;
end
3'b001: begin
da_mclk_r <= clk_div[1];
da_sclk_r <= clk_div[2];
da_lrck_r <= clk_div[8];
da_mode_r <= 2'b10;
end
......
default: begin
da_mclk_r <= clk_div[1];
da_sclk_r <= clk_div[3];
da_lrck_r <= clk_div[9];
da_mode_r <= 2'b10;
end
endcase
end
end
assign sclk_o = da_sclk_r;
assign mclk_o = da_mclk_r;
assign lrck_o = da_lrck_r;
assign mode_o = da_mode_r;
/******************************************************/
reg [ 4:0] L_count,R_count; //left and right count
reg send_done; //finish
always@(posedge da_sclk_r or negedge da_rst) begin
if(da_rst == 1'b0) begin
L_count <= 5'd0;
R_count <= 5'd0;
send_done <= 1'b0;
end
else if(da_lrck_r == 1'b0) begin
R_count <= 5'd0;
send_done <= 1'b0;
if(L_count < 5'd25) begin
L_count <= L_count + 1'b1;
end
end
else begin
L_count <= 5'd0;
if(R_count < 5'd25) begin
R_count <= R_count + 1'b1;
end
if(R_count == 5'd25)
send_done <= 1'b1;
end
end
assign da_done = send_done;
/*****************************************************/
reg [23:0] da_data_o; //output data
always@(negedge da_sclk_r) begin
if(L_count == 5'd1)
da_data_o <= l_data_i;
else if(R_count == 5'd1)
da_data_o <= r_data_i;
else
da_data_o <= {da_data_o[22:0],1'b1};
end
assign sdout_o = da_rst ? da_data_o[23] : 1'b0;
endmodule
构建仿真模块,进行verilog仿真,结果如下
从结果中,可以看出,预输出的24位数据为:left,0x555555;right,0x0ff00f。比对时序可以看出,输出正确。
I2S的控制时序如下图所示(摘抄自器件的datasheet)
在编写这份文档的过程 中,也有参考部分CSDN上的网友们给出的思路、代码等。相关内容可以在CSDN上一并搜索查看。