基于FPGA的I2S 转TDM8 设计

Posted xiao小怪兽爱上凹凸曼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于FPGA的I2S 转TDM8 设计相关的知识,希望对你有一定的参考价值。

文章目录

一、概述

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

基于FPGA的VGA可移植模块终极设计

本文转载自:http://www.cnblogs.com/lueguo/p/3373643.html

基于FPGA的VGA可移植模块终极设计

一、VGA的诱惑

首先,VGA的驱动,这事,一般的单片机是办不到的;由于FPGA的速度,以及并行的优势,加上可现场配置的优势,VGA的配置,只有俺们FPGA可以胜任,也只有FPGA可以随心所欲地配置(当然ARM也可以,应用比较高吧)。

初学者就是喜欢看炫的效果,往往会忍不住想玩。尤其玩FPGA的,没玩VGA就感到跟单片机没啥提升,因此VGA的驱动也不得不讲。Bingo当年也是如此。挡不住VGA的诱惑,初学者问Bingo VGA问题的人也是灰常的多,也许一般教科书理论太强,实际应用不是很身后,在此Bingo用浅显易懂的语言来讲述VGA的驱动原理,以及通过设计一个可移植模块的应用来讲述。

二、VGA驱动原理

此处Bingo不参考任何资料,用当年已学的知识,用浅显易懂的语言讲述。

1、VGA接口

wps_clip_image-18353

最主要的几根线:

image

更详细的资料,请看 http://baike.baidu.com/view/10346.htm

2、VGA时序

VGA其实就是相当于一块芯片,跟单片机驱动IC一样,满足一定的时序,便能驱动起来。

(1)扫描轨迹

VGA的扫描其实很简单,大致轨迹如下所示:

wps_clip_image-8553

没扫描完一行,从新开始下一行;每扫完一场,重新开始下一场。相信你应该看的懂。

(2)行场扫描

以下是行扫描,场扫描HS,VS时序图

wps_clip_image-27336

如上如所示:VGA一直在扫描,没一场的扫描包括了若干行扫描,如此循环。

(3)VS时序深入分析

VS时序如下所示:

wps_clip_image-20360

可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

A~B:场消隐期 即同步,相当于还原扫描坐标吧

B~C:场消隐后肩 相当于准备开始扫描吧

C~D:场显示期 扫描中,数据有效区域

D~E:场消隐前肩 完成扫描,相当于准备同步

(4)HS时序深入分析

可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

A~B:行消隐期 即同步,相当于还原扫描坐标吧

B~C:行消隐后肩 相当于准备开始扫描吧

C~D:行显示期 扫描中,数据有效区域

D~E:行消隐前肩 完成扫描,相当于准备同步

wps_clip_image-20832

综上描述,我们只要知道每个时期的时间,便可以表示出VGA的时序。而FPGA的工作是由固定频率的时钟触发的,因此某固定时间可以用n次触发来表示。因此我们很容易就想到了FPGA常用的计数方法:比如说行扫描,我们计数0~H_total-1。用另一个进程将其划分为4个时期,安标注分配。其实这相当于状态机。

以下是固定分辨率1024*768 60fps下HS,VS的标准:

wps_clip_image-9084

用代码表示4个时期,如下:

// VGA_1024_768_60fps_65MHz

// Horizontal Parameter( Pixel )

parameter H_DISP  = 11\'d1024,

parameter H_FRONT = 11\'d24,

parameter H_SYNC  = 11\'d136,

parameter H_BACK  = 11\'d160,

parameter H_TOTAL = 11\'d1344,

// Virtical Parameter( Line )

parameter V_DISP  = 10\'d768,

parameter V_FRONT = 10\'d3,

parameter V_SYNC  = 10\'d6,

parameter V_BACK  = 10\'d29,

parameter V_TOTAL = 10\'d806

3、VGA电路

(1)三原色

VGA接口:R,G,B三通道,直接赋给数字信号,RGB,最多产生8种色彩。这是最基本的。电路如下所示:

wps_clip_image-11884

(2)真彩显示

a) 电阻网络

考虑到成本意识实现的简易方案,用R-2R电阻网络分流模拟DAC替换ADV7123视频转换芯片。见以下几个方案:

wps_clip_image-12612

DE1 VGA模拟电路

wps_clip_image-1911

小马哥电路图

具体设计参考Bingo当年总结:

http://www.cnblogs.com/crazybingo/archive/2010/07/31/1789323.html

或者参考小马哥设计:

http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3582675&bbs_page_no=1&search_mode=2&search_text=VGA&bbs_id=9999

b) 专用视频转换芯片

利用专用视频转换芯片,ADV7120等,将数字信号转换为VGA RGB的模拟信号。ADV7120为高速D/A芯片,将数字信号转换为模拟信号输给VGA,电路如下:

wps_clip_image-19353

三、可移植VGA模块设计

Bingo玩VGA也算是比较早了,当年也是视觉的诱惑,以及唯FPGA独尊的优势。于是之后一发而不可收拾。本章Bingo将自己这些年最终优化的VGA驱动模块,发布至此。本模块所有代码均Bingo独家创造,请尊重版权哈。

本设计已经封装成模块,只要修改时序参数、扫描时钟参数以及在vga_display.v中添加显示电路,即可。方便移植,希望对大家有用。

1、模块划分

wps_clip_image-16944

(1)vga_design.v

工程顶层文件,例化各个模块。

(2)sys_ctrl.v

PLL时钟分配电路。

(3)vga_display.v

显示电路,根据时序,用于描述VGA的显示电路。

(4)vga_driver.v

VGA驱动电路,对时序,状态的约束。

RTL图如下所示:

wps_clip_image-19643

2、代码设计

Bingo例程以16bit RGB VGA驱动为例,不同位数的显示只要改一下vga_data即可。

前文以及代码讲述了那么多,此处不再贴完整代码,而是对代码中部分结构进行解析。

代码下载地址:http://blog.chinaaet.com/detail/21606.html

(1)vga_driver.v代码分析

a) 参数例化列表

#(

// VGA_1024_768_60fps_65MHz

// Horizontal Parameter ( Pixel )

parameter H_DISP  = 11\'d1024,

parameter H_FRONT = 11\'d24,

parameter H_SYNC  = 11\'d136,

parameter H_BACK  = 11\'d160,

parameter H_TOTAL = 11\'d1344,

// Virtical Parameter ( Line )

parameter V_DISP  = 10\'d768,

parameter V_FRONT = 10\'d3,

parameter V_SYNC  = 10\'d6,

parameter V_BACK  = 10\'d29,

parameter V_TOTAL = 10\'d806

)

这样写的目的是为了软件封装性,能够在例化的时候修改法分辨率,同时电路结构保持不变。

DISP,FRONT ,SYNC,BACK,TOTAL分别为显示期,消隐前肩,消音期,消隐后肩,总时间,各自对应各自的行场信号。

b) 行同步信号设计

//------------------------------------------

// 行同步信号发生器

reg [10:0] hcnt;

always @ (posedge clk_vga or negedge rst_n)

begin

if (!rst_n)

hcnt <= 0;

else

begin

if (hcnt < H_TOTAL-1\'b1)

            hcnt <= hcnt + 1\'b1;

else

            hcnt <= 0;

end

end

//------------------------------------------

always@(posedge clk_vga or negedge rst_n)

begin

if(!rst_n)

vga_hs <= 1;

else

begin

if( (hcnt >= H_DISP+H_FRONT-1\'b1) && (hcnt < H_DISP+H_FRONT+H_SYNC-1\'b1) )

            vga_hs <= 0;

else

            vga_hs <= 1;

end

end

如上所示,分析代码可以知道,行同步信号的计数状态机按照时序的划分,是以下过程:H_DISP,H_FRONT,H_SYNC,H_BACK,这似乎和上述分析的VGA时序不是完全吻合。但是VGA时序是一个循环,顺推H_BACK个时终域便可以得到以上时期划分,但是这样更方便后续坐标计数,因为Bingo此处这样设计,当然实际证明是完全可行的。

注意:(hcnt >= H_DISP+H_FRONT-1\'b1) && (hcnt < H_DISP+H_FRONT+H_SYNC-1\'b1) 只是因为后续坐标计算,就把时序提前了1个时钟已达到同步。

c) 场同步信号设计

同上。

d) 数据显示坐标以及输出设计

//------------------------------------------

assign vga_xpos = (hcnt < H_DISP) ? hcnt[9:0]+1\'b1 : 10\'d0;

assign vga_ypos = (vcnt < V_DISP) ? vcnt[9:0]+1\'b1 : 10\'d0;

assign vga_rgb  = ((hcnt < H_DISP) && (vcnt < V_DISP)) ? vga_data : 16\'d0;

这部分很容易理解,在显示期坐标根据显示的扫描而改变,在非显示期,坐标置零。

(2)Vga_display.v代码分析

a) 标准色彩定义

//define colors RGB--5|6|5

localparam RED      = 16\'hF800;

localparam GREEN     = 16\'h07E0;

localparam BLUE      = 16\'h001F;

localparam WHITE     = 16\'hFFFF;

localparam BLACK     = 16\'h0000;

localparam YELLOW    = 16\'hFFE0;

localparam CYAN      = 16\'hF81F;

localparam ROYAL     = 16\'h07FF;

定义当地的参数,目的是为了后续标准色彩调用的方便,没有其他作用。

b) 根据固定区域输出数据

always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

vga_data <= 16\'h0;

else

begin

if (vga_xpos >= 0 && vga_xpos < (H_DISP/3))

vga_data <= RED;

else if(vga_xpos >= (H_DISP>>3)*1 && vga_xpos < (H_DISP/3)*2)

vga_data <= GREEN;

else

vga_data <= BLUE;

end

end

如上代码,根据xpos坐标,来输出固定色彩。由于vga_driver模块已经完全设计好接口,因此vga_display.v模块就是简单的根据区域,输出设计的颜色。

(3) Vga_ctrl.v代码分析

a) 同步化解析

略。

b) 可控锁相环PLL设计

wps_clip_image-4070

本来可空锁相环PLL设计,Bingo想单独成章来讲述的,但觉得可能别的地方实际应用价值不是很大,最后便在此处阐明这样设计的原因。

由于VGA不同分辨率的扫描时钟不同,因此分辨率变化的时候,PLL的输出时钟是必要跟随着变化。但是Bingo觉得好麻烦,于是想了偷懒的一招:PLL IP定义,无非是用Quartus II GUI对PLL参数的设定,最后生成pll.v这个文件。而这个文件相当于是锁相环的驱动电路,故势必包含着参数的定义。实际如下:

altpll_component.clk0_divide_by = 1,

altpll_component.clk0_duty_cycle = 50,

altpll_component.clk0_multiply_by = 1,

如上图所示,每次修改参数,这三个变量会相应的发生变化。因此,何不把这三个参数作为例化的参数,可以再顶层直接修改代码来实现固定频率的输出?这样岂不是很方便?

因此设计参数例化接口如下:

#(

parameter DUTY_CYCLE = 50,

parameter DIVIDE_DATA = 1,

parameter MULTIPLY_DATA = 1

)

理论上是完全行得通的,这样设计的另一个好处就是电路的封装性更好Bingo实践证明,完全可行,因此在此处说明,要学会正确的偷懒呵呵。

(4)Vga_design.v顶层文件代码解析

a) Vga接口定义

//vga interface

output vga_adv_clk,

output vga_blank_n,

output vga_sync_n,

output vga_hs,

output vga_vs,

output [15:0] vga_rgb

如上所示,上面三个信号线是在使用adv712x视频转换芯片的时候才会出现,一般的电阻模拟电路,或者直接RGB3线驱动,可以直接删除相关信号以及电路。

b) 进一步偷懒法则

根据常用的几种分辨率,Bingo进行了总结,以下三种应用较多,因此进一步偷懒法则,围绕他们三者来(不在这三种以内的话,自己模仿着再写一个):

VGA_640_480_60FPS_25MHz

VGA_800_600_72FPS_50MHz

VGA_1024_768_60FPS_65MHz

Verilog语法也有define的用法,是否还记得C语言中,大工程的调试经常对相关变量的注释,注销来调整整个工程变量的应用,因此此处Bingo套用这种思维模式,定义以上三种模式的变量,通过修改注释define便可以轻松修改全局。具体如下所示:

//----------------------------------------

//vga parameter define

//`define VGA_640_480_60FPS_25MHz

//`define VGA_800_600_72FPS_50MHz

`define VGA_1024_768_60FPS_65MHz

`ifdef VGA_640_480_60FPS_25MHz

parameter DUTY_CYCLE = 50;

parameter DIVIDE_DATA = 2;

parameter MULTIPLY_DATA = 1;

parameter H_DISP = 11\'d640;

parameter H_FRONT = 11\'d16;

parameter H_SYNC  = 11\'d96;

parameter H_BACK  = 11\'d48;

parameter H_TOTAL = 11\'d800;

parameter V_DISP  = 10\'d480;

parameter V_FRONT = 10\'d10;

parameter V_SYNC  = 10\'d2;

parameter V_BACK  = 10\'d33;

parameter V_TOTAL = 10\'d525;

`endif

`ifdef VGA_800_600_72FPS_50MHz

parameter DUTY_CYCLE = 50;

parameter DIVIDE_DATA = 1;

parameter MULTIPLY_DATA = 1;

parameter H_DISP  = 11\'d800;

parameter H_FRONT = 11\'d56;

parameter H_SYNC  = 11\'d120;

parameter H_BACK  = 11\'d64;

parameter H_TOTAL = 11\'d1040;

parameter V_DISP  = 10\'d600;

parameter V_FRONT = 10\'d37;

parameter V_SYNC  = 10\'d6;

parameter V_BACK  = 10\'d23;

parameter V_TOTAL = 10\'d666;

`endif

`ifdef VGA_1024_768_60FPS_65MHz

parameter DUTY_CYCLE = 50;

parameter DIVIDE_DATA = 10;

parameter MULTIPLY_DATA = 13;

parameter H_DISP  = 11\'d1024;

parameter H_FRONT = 11\'d24;

parameter H_SYNC  = 11\'d136;

parameter H_BACK  = 11\'d160;

parameter H_TOTAL = 11\'d1344;

parameter V_DISP  = 10\'d768;

parameter V_FRONT = 10\'d3;

parameter V_SYNC  = 10\'d6;

parameter V_BACK  = 10\'d29;

parameter V_TOTAL = 10\'d806;

`endif

四、Display方案以及效果

1、彩条

(1)代码

always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

vga_data <= 16\'h0;

else

begin

if (vga_xpos >= 0 && vga_xpos < (H_DISP>>3))

vga_data <= RED;

else if(vga_xpos >= (H_DISP>>3)*1 && vga_xpos < (H_DISP>>3)*2)

vga_data <= GREEN;

else if(vga_xpos >= (H_DISP>>3)*2 && vga_xpos < (H_DISP>>3)*3)

vga_data <= BLUE;

else if(vga_xpos >= (H_DISP>>3)*3 && vga_xpos < (H_DISP>>3)*4)

vga_data <= WHITE;

else if(vga_xpos >= (H_DISP>>3)*4 && vga_xpos < (H_DISP>>3)*5)

vga_data <= BLACK;

else if(vga_xpos >= (H_DISP>>3)*5 && vga_xpos < (H_DISP>>3)*6)

vga_data <= YELLOW;

else if(vga_xpos >= (H_DISP>>3)*6 && vga_xpos < (H_DISP>>3)*7)

vga_data <= CYAN;

else// if(vga_xpos >= (H_DISP<<3)*7 && vga_xpos < (H_DISP<<3)*8)

vga_data <= ROYAL;

end

end

通过简单的对X坐标地址的分割,来得到彩条。这是应该是VGA初学者一开始最兴奋的几个界面吧。

(2)效果图

wps_clip_image-25343

2、花型矩阵

(1)代码

wire [19:0] vga_result = vga_xpos * vga_ypos;

always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

vga_data <= 16\'h0;

else

vga_data = vga_result[15:0];

end

通过x坐标地址和y坐标地址的乘积的值,取低16位,得到的数据有一定的规律。Bingo当年也是不小心发现的,仅此献给初学的孩子们,这个比彩条更帅气。

(2)效果图

wps_clip_image-756

以上是关于基于FPGA的I2S 转TDM8 设计的主要内容,如果未能解决你的问题,请参考以下文章

基于FPGA的嵌入式图像处理系统设计pdf

基于FPGA的呼叫设备verilog开发

什么是fpga

基于FPGA的智力抢答器设计

基于FPGA的LED点阵系统开发

FPGA课设-基于Xilinx Basys2开发板的除法器设计