基于FPGA的I2S 转TDM8 设计

本文介绍了I2S和TDM两种音频传输协议的基本概念,并详细解释了从4路I2S信号转换成一路TDM8信号的设计思路与实现方法。文中包括设计框图与时序图,同时提供了关键的仿真验证过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

在音频应用领域,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个声道,具体可以看仿真时序图。
仿真激励代码:


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等等,只要时序搞明白了,用起来就简单了,大家可以自己研究研究,如有问题欢迎探讨。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值