一、概述
在音频应用领域,I2S和TDM的应用是最基础的应用,不管模拟转数字ADC的采样还是数字转模拟DAC的输出,都经常使用这两种协议,所以跨入音频领域,这些基础的必须先掌握;在本文章主要讲设计思想,针对I2S和TDM的时序只做简单的介绍,然后通过I2S和TDM之间编解码的仿真来熟悉这两种协议的设计思路。
二、I2S简介
I2S(Inter—IC Sound)总线, 又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准。在我们本设计中采用是I2S的标准协议。(下图是ADC的时序图)
主时钟 MCLK: 它是 LRCK和SCLK的参考时钟,一个系统应该使用同一的MCLK以保证时钟同步要求。常见频率256fs或512fs。这里的fs就是采样率LRCK的频率。本设计中MCLK为24.576MHz。
采样率 LRCK(也称WS): 用于切换左右声道的数据。对于I2S标准协议来说,LRCK为1表示传输右声道数据,为0则是左声道。在设计中,LRCK的下降沿是我们一个帧的起点,对于I2S来说,左右声道可以搭载两路数据,每2路数据为1帧。本设计中LRCK为48KHz。
串行时钟SCLK: 也叫位时钟BCLK,对应数字音频的每一位数据,该时钟都有一个脉冲。本设计中SCLK为3.072MHz。这里需要注意,不管我们实际音频数据的有效位是16bit、24bit或32bit,我们的串行SCLK都是LRCK的64倍,并且LRCK的下降沿一定是要和BCK的下降沿对齐的。
输入数据DIN: 对于I2s标准协议来说,我们数据是在LRCK下降沿到来后,第二个bck的时钟上升沿开始才是采样到有效数据。正常设计中,音频数据有效位都是24bit,因为32bit没有必要,本设计中也是24bit。
三、TDM简介
TDM即时分复用,它是强化版本的I2s。由于在我们实际应用中发现1组I2s只能传输2路数据,这不能满足我们的需求,所以TDM协议可以满足我们的需求。I2s说白了其实就是TDM2。我们比较常用的是格式是TDM8和TDM16。下图是我们DAC的时序图,是TDM8格式,LRCK的上升沿是一帧的起点,一个帧是8个通道。
主时钟 MCLK: 系统主时钟这里仍然是24.576MHz。
采样率LRCK: 这里LRCK的脉宽大小各个芯片的定义是有点差异的,我们这款DAC,LRCK的脉宽最小可以是1个BCK时钟周期,最大可以是255个时钟周期。本设计TDM的LRCK同样是48KHz。TDM8移位时钟BCK是采样率LRCK的256倍。
位时钟BCK: 同样的这里移位时钟bck的下降沿一定是要和LRCK边沿是对齐的。本设计BDCK为12.288MHz。注意TDM的一帧的通道数越多,我们BCK频率会越高,对时序的要求就越高,因为一个通道占用32个bck,采样率是固定的。
输入数据DIN: 这里数据的对齐方式采用的I2s的标准格式,也就是在LRCK上升沿后,每32bck的第二个bck的开始才是有效数据。
关于I2S和TDM协议的详细介绍可以参考:I2S/PCM协议及TDM模式详解,这里面有左对齐、右对齐等等相关。
四、设计框图
如图所示,为了能够符合边沿对齐的规则(LRCK的上下沿对齐BCK的下降沿),我们需要使用24.576MHz的时钟下降沿去做分频处理。我们一共是8路数据,4路I2S进行采样(每个AD 2路I2S)。然后进行TDM8编码,输出到DAC。
五、仿真验证
1.时钟分频
如下仿真图图所示,clk是24.576MHz,clkout0是采样时钟LRCK为48KHz,clkout1是I2S的位时钟BCK为3.072MHz,clkout2是TDM8的位时钟BCK为12.288MHz。(采样率LRCK的上下沿是和BCK的下降沿对齐的)
下面展示的是最简单分频处理方法的代码(干货)
:
module clk_div(
input clk,//24.576MHz
input rst,
output clkout0,//48KHz
output clkout1,//3.072MHz
output clkout2//12.288MHz
);
reg[9:0]cnt=0;
assign clkout0=cnt[8];
assign clkout1=cnt[2];
assign clkout2=cnt[0];
always@(negedge clk)
begin
if(rst)
cnt<=0;
else
cnt<=cnt+1;
end
endmodule
仿真激励代码:
module vtf_clk_div;
// Inputs
reg clk;
reg rst;
// Outputs
wire clkout0;
wire clkout1;
wire clkout2;
// Instantiate the Unit Under Test (UUT)
clk_div uut (
.clk(clk),
.rst(rst),
.clkout0(clkout0),
.clkout1(clkout1),
.clkout2(clkout2)
);
initial begin
// Initialize Inputs
clk = 0;
rst = 1;
// Wait 100 ns for global reset to finish
#100;
rst = 0;
// Add stimulus here
end
always # 20.345 clk = ~clk;
endmodule
仿真时序图:
2.I2S 转TDM8功能模块
关于4路I2S转1路TDM8源代码这里就不放了,想要看的可以点击如下链接获取:I2S转TDM8代码。
在激励文件中,定义的data_vld1~data_vld8这8个通道数据对应4路I2s的8个声道,具体可以看仿真时序图。
仿真激励代码:文章来源:https://www.toymoban.com/news/detail-417951.html
module vtf_i2s_to_tdm8;
// Inputs
reg reset;
wire MCLK;//TDM8的BCK
wire LRCK;
wire SCLK;//I2S的BCK
reg DATA1;//第一路I2s数据输入
reg DATA2;//第二路I2s数据输入
reg DATA3;//第三路I2s数据输入
reg DATA4;//第四路I2s数据输入
// Outputs
wire flag;
wire DATA_OUT;
/
reg clk;
reg LRCK_dly1;
reg LRCK_dly2;
reg [63:0] TMPA;
reg [63:0] TMPB;
reg [63:0] TMPC;
reg [63:0] TMPD;
wire clk_48k_negedge;
/定义的8个数据通过4路I2S发送出去(ch1/ch2第一路、ch3/ch4第二路、
ch5/ch6第二路、ch7/ch8第二路)
localparam data_vld1=32'hf111_1F00;//ch1
localparam data_vld2=32'hf333_3F00;//ch2
localparam data_vld3=32'hf555_5F00;//ch3
localparam data_vld4=32'hf777_7F00;//ch4
localparam data_vld5=32'hf999_9F00;//ch5
localparam data_vld6=32'hfBBB_BF00;//ch6
localparam data_vld7=32'hfCCC_CF00;//ch7
localparam data_vld8=32'hfDDD_DF00;//ch8
// Instantiate the Unit Under Test (UUT)
i2s_to_tdm8 uut (
.reset(reset),
.MCLK(MCLK),
.LRCK(LRCK),
.SCLK(SCLK),
.DATA1(DATA1),
.DATA2(DATA2),
.DATA3(DATA3),
.DATA4(DATA4),
.flag(flag),
.DATA_OUT(DATA_OUT)
);
///调用上面的分频模块
clk_div clk_div_inst (
.clk(clk),
.rst(reset),
.clkout0(LRCK), //48KHz
.clkout1(SCLK), //3.072MHz
.clkout2(MCLK)//12.288MHz
);
initial begin
// Initialize Inputs
reset = 1;
clk = 0;
DATA1 = 0;
DATA2 = 0;
DATA3 = 0;
DATA4 = 0;
LRCK_dly1=0;
LRCK_dly2=0;
TMPA=0;
TMPB=0;
TMPC=0;
TMPD=0;
// Wait 100 ns for global reset to finish
#100;
reset =0;
// Add stimulus here
end
always # 20.345 clk = ~clk;
always @(posedge SCLK)
begin
LRCK_dly1 <=LRCK;
LRCK_dly2 <=LRCK_dly1;
end
assign clk_48k_negedge =~LRCK_dly1&LRCK_dly2;
always @(negedge SCLK)
begin
if(clk_48k_negedge)begin
DATA1<=data_vld1[31];
TMPA<={data_vld1[30:0],data_vld2,1'b0};
end
else begin
TMPA<=TMPA<<1;
DATA1<=TMPA[63];
end
end
always @(negedge SCLK)
begin
if(clk_48k_negedge)begin
DATA2<=data_vld3[31];
TMPB<={data_vld3[30:0],data_vld4,1'b0};
end
else begin
TMPB<={TMPB[62:0],1'b1};
DATA2<=TMPB[63];
end
end
always @(negedge SCLK)
begin
if(clk_48k_negedge)begin
DATA3<=data_vld5[31];
TMPC<={data_vld5[30:0],data_vld6,1'b0};
end
else begin
TMPC<={TMPC[62:0],1'b1};
DATA3<=TMPC[63];
end
end
always @(negedge SCLK)
begin
if(clk_48k_negedge)begin
DATA4<=data_vld7[31];
TMPD<={data_vld7[30:0],data_vld8,1'b0};
end
else begin
TMPD<={TMPD[62:0],1'b1};
DATA4<=TMPD[63];
end
end
endmodule
时序仿真图:
对于I2S时序来说LRCK的下降沿是一帧的开始,DATA1~DATA4是4路I2S的输入数据,通道对应如图所示;对于TDM8时序来说LRCK的上升沿是一帧的开始,DATA_OUT是TDM8的输出数据,这里通道对应如图所示。
广大博友,关于I2S转1路TDM8的应用这里就介绍完了,可以拓展应用,比如:8路I2S转1路TDM16、8路I2S转2路TDM8等等,只要时序搞明白了,用起来就简单了,大家可以自己研究研究,如有问题欢迎探讨。文章来源地址https://www.toymoban.com/news/detail-417951.html
到了这里,关于基于FPGA的I2S 转TDM8 设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!