HDMI解码芯片ADV7619调试总结

由于最近实验室有高速HDMI传输需求,所以不得不调试功能极多而传输速率高达297MHz,众多HDMI解码芯片中传输速率最高的一款。而关于这款芯片的使用记录又很少,所以就在此记录一下自己的调试心得和自己调试过程中收集到的相关资料及遇到的相关问题,方便后人能够快速的对该芯片进行配置。

整个调试分为这几个阶段:

1、查看数据手册,熟悉各个寄存器的功能,记录需要配置的额寄存器;

2、用verilog描述IIC协议,用来配置相关寄存器;

3、在2的基础上添加读相关寄存器的代码,并用chipscope读出相关位置的寄存器的内容,比较与写入的是否相同。

接下来我就按照这三个阶段的顺序来讲述以下自己苦逼的调试经历。

一、查看数据手册,熟悉各个寄存器的功能,记录需要配置的额寄存器

这里不得不吐槽以下ADI公司设计的ADV7619芯片,这款芯片的功能实在是太复杂了,所以手册中寄存器就有一大堆,实现一个基本功能至少需要配置50个寄存器吧,其中还包括一些ADI Required Write,这类寄存器用户不能更改,而且必须配置。如果你在找这款芯片的资料时,如果只找到了操作手册(ADV7619 Reference Manual)是远远不够的因为这个手册只是详细的介绍了各个寄存器的功能,根据这个你可以确定一部分你需要配置的寄存器;你必须找同时到ADV7619 Required  Manual(这个手册中有寄存器的地址映射以及各个模式下ADI Required Write的寄存器);同时你还必须找到ADV7619 Register MAP Documenntation,这个文档可以帮助你确定寄存器的映射关系。如果想更好更便捷的进行调试,你还需要HDMI_RX_FAQV1.0,这个手册中对ADV7619/ADV7611常见问题进行解答。对于调试有很大帮助。

在最初调试阶段由于缺乏寄存器配置经验,还进入一个误区,认为寄存器一定需要自己逐个查看,弄清所有寄存器功能,才能进行配置,所以就花费了一天半的时间看寄存器手册,基本熟悉各个寄存器功能。后来偶然一个机会点进去ADI官网,在ADI官网的中文论坛中找到了官方推荐的寄存器配置手册,我的天哪!!!简直不用努力了,为什么还要自己配置,官方推荐配置一定没问题呀,官方简直太贴心了,所以就按照官方推荐配置去配置寄存器。官方推荐配置的寄存器文档名称是ADV7619-VER.1.9c,大家有需要可以去官网下载。

所以我觉得以后配置什么芯片都要去官网找这个芯片的所有资料,同时还要去相关论坛看看大家在使用这款芯片时遇到什么样的问题及如何解决的。

二、用verilog描述IIC协议,用来配置相关寄存器

在用verilog描述IIC协议时,我犯了一个大错误。我们知道,在使用IIC进行寄存器配置时,IIC协议的工作过程如下: 1开始------>2发送器件地址------>3从机应答------->4发送子地址------>5从机应答------>6发送数据------>7从机应答------->8停止,这表示配置一个寄存器的地址完成, 然后继续循环2~8步。结果我在描述IIC协议的过程,为了偷懒,我想当然的认为进入一次寄存器配置开始状态,如果器件地址相同只需要发送一次器件地址,然后就可以进行所有的寄存器配置,当所有的寄存器都配置完毕,这时,才可以进入停止状态,错误的工作过程描述如下: 1开始------>2发送器件地址------>3从机应答------->4发送子地址------>5从机应答------>6发送数据------>7从机应答------->8停止,每次配置不同的寄存器仅仅重复4~7步,直到所有的寄存器都配置完毕,才进入第8步,在实际使用中,我发现这样理解是错误的,建议大家不要为了偷懒而这样做,这样做绝对会出问题,如果你这样做没出问题,请和我联系,咱们一起讨论一下这个问题。

接下来就是使用verilog描述IIC协议,这里我将部分代码贴出来,对于不同的寄存器的配置只需要在此代码基础上修改寄存器值和寄存器的个数即可,代码如下:

module iic_cfg_hdmi_out(
							output SCL,
							inout SDA,					
							input clk35m,   //system clk
							input rst_n,    //system rst						
							output cfig_done // 0: not config finished //1: finished		
				  );
				  
				  
reg	[7:0]	sub_address	[50:0];   //  IIC register address
reg	[7:0]	data_table	[50:0];   //  IIC write in data
reg   [7:0] device_address[9:0];
parameter i_max=50;	 					//配置寄存器个数

//parameter device_address = 8'h76;
initial	begin	
	 /////////////////////////IO寄存器配置device_address[0] = 8'h98;
	device_address[0] = 8'h98;////98
	device_address[1] = 8'h68;
	device_address[2] = 8'h44;
	device_address[3] = 8'h64;
	device_address[4] = 8'h4c;
		device_address[5] = 8'h99;////98
	device_address[6] = 8'h69;
	device_address[7] = 8'h45;
	device_address[8] = 8'h65;
	device_address[9] = 8'h4d;
	//////////////////////////IO寄存器配置device_address[0] = 8'h98;
	sub_address[0] = 8'hff;////////I2C reset
	data_table[0] = 8'h80;
	sub_address[1] = 8'hf4;///////CEC
	data_table[1] = 8'h80;///////////	
	sub_address[2] = 8'hf5;//////////INFOFRAME
	data_table[2] = 8'h7c;
	sub_address[3] = 8'hf8;/////////DPLL
	data_table[3] = 8'h4c;/////////
	sub_address[4] = 8'hf9;////////KSV
	data_table[4] = 8'h64;
	sub_address[5] = 8'hfa;//////////EDID
	data_table[5] = 8'h6c;
	sub_address[6] = 8'hfb;//////////HDMI
	data_table[6] = 8'h68;
	sub_address[7] = 8'hfd;//////////CP
	data_table[7] = 8'h44;
	///////////////////////////////
	sub_address[8] = 8'hc0;///////0x68////////device_address[1] = 8'h68;////ADI Required Write
	data_table[8] = 8'h03;
	////////////////////////////////////////////
	sub_address[9] = 8'h01;///////0x98//////////device_address[0] = 8'h98;///Prim_Mode =110b HDMI-GR 
	data_table[9] = 8'h06;//16-50hz///06-60hz
	sub_address[10] = 8'h02;//////////////////////////////////////////Auto CSC, RGB out, Set op_656 bit 
	data_table[10] = 8'hf7;/////RGB???
	sub_address[11] = 8'h03;////////////////////////////////////////////24 bit SDR 444 Mode 0
	data_table[11] = 8'h40;
	sub_address[12] = 8'h04;/////////////////参考晶振24Mhz及RGB与引脚关系
	data_table[12] = 8'h66;
	sub_address[13] = 8'h05;////////////////////////////////////AV Codes Off
	data_table[13] = 8'h28;
	sub_address[14] = 8'h06;////////hs vs正极性INV_HS_POL、INV_VS_POL should be set to 1 if not specified in Table 7
	data_table[14] = 8'ha6;	
   sub_address[15] = 8'h0c;
	data_table[15] = 8'h42;	
	sub_address[16] = 8'h15;
	data_table[16] = 8'h80;
	sub_address[17] = 8'h19;
	data_table[17] = 8'h83;
	sub_address[18] = 8'h33;
	data_table[18] = 8'h40;///////////98 33 40///////
	/////////////////////////////
	sub_address[19] = 8'hba;//////0x44////////device_address[2] = 8'h44;
	data_table[19] = 8'h01;
	sub_address[20] = 8'h6c;
	data_table[20] = 8'h00;
	/////////////////////////////////
	sub_address[21] = 8'h40;///////0x64/////device_address[3] = 8'h64;
	data_table[21] = 8'h81;
///////////////////////////////////////
	sub_address[22] = 8'hb5;//////////0x4c/////device_address[4] = 8'h4c;
	data_table[22] = 8'h01;
	//////////////////////////////////
	sub_address[23] = 8'hc0;////////////0x68////device_address[1] = 8'h68;
	data_table[23] = 8'h03;
	sub_address[24] = 8'h00;
	data_table[24] = 8'h00;/////Set HDMI Input Port A (BG_MEAS_PORT_SEL = 001b)
	sub_address[25] = 8'h02;
	data_table[25] = 8'h03;
	sub_address[26] = 8'h03;
	data_table[26] = 8'h98;
	sub_address[27] = 8'h10;
	data_table[27] = 8'ha5;		
	sub_address[28] = 8'h1b;///////////////
	data_table[28] = 8'h08;
	sub_address[29] = 8'h45;
	data_table[29] = 8'h04;
	sub_address[30] = 8'h97;
	data_table[30] = 8'hc0;
	sub_address[31] = 8'h3d;
	data_table[31] = 8'h10;
	sub_address[32] = 8'h3e;
	data_table[32] = 8'h69;
	sub_address[33] = 8'h3f;
	data_table[33] = 8'h46;
	sub_address[34] = 8'h4e;
	data_table[34] = 8'hfe;
	sub_address[35] = 8'h4f;
	data_table[35] = 8'h08;
	sub_address[36] = 8'h50;
	data_table[36] = 8'h00;
	sub_address[37] = 8'h57;
	data_table[37] = 8'ha3;
	sub_address[38] = 8'h58;
	data_table[38] = 8'h07;
	sub_address[39] = 8'h6f;////////////
	data_table[39] = 8'h08;
	sub_address[40] = 8'h83;
	data_table[40] = 8'hfc;
	sub_address[41] = 8'h84;
	data_table[41] = 8'h03;
	sub_address[42] = 8'h85;
	data_table[42] = 8'h10;
	sub_address[43] = 8'h86;
	data_table[43] = 8'h9b;
	sub_address[44] = 8'h89;
	data_table[44] = 8'h03;
	sub_address[45] = 8'h9b;
	data_table[45] = 8'h03;
	sub_address[46] = 8'h93;
	data_table[46] = 8'h03;
	sub_address[47] = 8'h5a;//////
	data_table[47] = 8'h80;
	sub_address[48] = 8'h9c;
	data_table[48] = 8'h80;
	sub_address[49] = 8'h9c;
	data_table[49] = 8'hc0;
	sub_address[50] = 8'h9c;
	data_table[50] = 8'h00;
end
reg    	cfig_done_r=0;

assign 	cfig_done = cfig_done_r;
reg    	SDA_r;
reg    	SDA_link;//输出数据sda信号inout方向控制: 1 output 0 input	

reg SCL_r = 'd0;
assign	SCL	= SCL_r;
assign	SDA = SDA_link ? SDA_r : 1'bz;

reg [8:0]cnt_SCL = 'd0;//循环计数,产生iic所需要的时钟				  

always @ ( posedge clk35m )
	if(cnt_SCL == 9'd349)	   //周期为20us,即100KHz
		cnt_SCL <= 9'd0;
	else 	
		cnt_SCL <= cnt_SCL + 9'b1;//时钟计数;

	
`define SCL_pos cnt_SCL == 9'd87         //scl上升沿
`define SCL_hig cnt_SCL == 9'd174 	     //scl高电平中间,糜谑采样
`define SCL_neg cnt_SCL == 9'd262	     //scl下降沿
`define SCL_low cnt_SCL == 9'd349	     //scl低点平中间,用于数据变化


always @ ( posedge clk35m )    // 产生100k SCL signal
	if(!cfig_done_r) begin
		if(`SCL_pos)
			SCL_r<=1'b1;    //scl信号上升沿
		if(`SCL_neg)
			SCL_r<=1'b0;    //scl信号下降沿
	end
	else
		SCL_r<=1'b1; // config finish ,scl output 1;	

reg [3:0]	 state; //状态寄存器
reg [4:0]	 step;  //输出8位数据串行计数
reg [7:0]   data;  //在IIC上传送的数据寄存器

parameter IDLE		   	=	4'D0,    //the state during the IIC writing 
			 START			=	4'D1,
			 SLAVE_ADDR    =   4'D2,
			 ACK1			   =	4'D3,
			 SUB_ADDR		=	4'D4,
			 ACK2			   =	4'D5,
			 DATA		   	=	4'D6,
			 ACK3			   =	4'D7,
			 PAUSE			=	4'D8,
			 OVER   	      =   4'D9;

reg			[7:0]	 i;//连续输出寄存器地址和数据计数

always @ (posedge clk35m or negedge rst_n) begin
	if(!rst_n)	begin
			data 		<= 16'd0;
			SDA_link 	<= 1'b1;
			SDA_r 		<= 1'b1;
			step 		<= 5'd0;
			state 		<= IDLE;
			i			<= 0;
			cfig_done_r <= 1'b0;
	end
	else case(state)
		IDLE:	begin
				SDA_link 	<= 1'b1;
				SDA_r 		<= 1'b1;
				i			<= 0;
				cfig_done_r <= 1'b0;
				state<=START;
		end
		START:	begin
			if(`SCL_hig) begin
				SDA_r 	<= 1'b0; //start low
				data		<= device_address;
				state 		<= SLAVE_ADDR;
				step 		<= 5'd0;
			end
			else 
				state<=START;
		end
		SLAVE_ADDR:	begin
			if(`SCL_low) begin//low level for transmit data
				if(step==5'd8)	begin
					state <= ACK1;
					SDA_link <= 1'b0;//after the 8 bits data,the sda become input
					SDA_r <= 1'b1;
					step <= 5'd0;
				end
				else 	begin
					step <= step+5'd1;
					SDA_r <= data[5'd7-step];	
					state <= SLAVE_ADDR;
				end
			end
			else 	
				state <= SLAVE_ADDR;
		end

		ACK1: begin  //responsion
			if((`SCL_hig) && (SDA==1'b0)) begin
				state <= SUB_ADDR;
				data <= sub_address[i];				
			end
			else 
				state <= ACK1;
		end			
		SUB_ADDR:	begin
			if(`SCL_low) begin
				if(step==5'd8) begin
					state<=ACK2;
					SDA_link<=1'b0;
					SDA_r<=1'b1;
					step<=5'd0;
				end
				else begin
					SDA_link<=1'b1;					
					step<=step+5'd1;
					SDA_r<=data[5'd7-step];	
					state<=SUB_ADDR;
				end
			end
			else 
				state<=SUB_ADDR;
		end
		ACK2:	begin
			if((`SCL_hig)&&(SDA==1'b0))	begin 
				state <= DATA;
				data <= data_table[i];					
			end
			else 
				state <= ACK2;		
		end
		DATA:	begin
		if(`SCL_low) begin
			if(step==5'd8)	begin
				state<=ACK3;
				SDA_r<=1'b1;
				SDA_link<=1'b0;
				step<=5'd0;
			end
			else begin
				SDA_link<=1'b1;
				step<=step+5'd1;
				SDA_r<=data[5'd7-step];	
				state<=DATA;
			end
		end
		else 
			state<=DATA;
		end		
		ACK3:	begin
			if((`SCL_hig) &&(SDA==1'b0)) begin
				state <= PAUSE;					
			end
			else 
				state<=ACK3;
		end
		PAUSE:	begin// sent the stop signal 
			if (`SCL_low) begin
				SDA_link<=1'b1;
				SDA_r <= 1'b0;
				state <= PAUSE;
			end
			else if(`SCL_hig) begin
				SDA_r <= 1'b1;//scl为高时,sda产生上升沿(结束信号)
				if(i==i_max) begin
					i <= 0;
					state <= OVER;/////OVER
				end
				else	begin
					i <= i+ 8'd1;
					state <= START;
				end
			end
			else 
				state <= PAUSE;
		end
		OVER: begin
				cfig_done_r <= 1'b1;
				state <= OVER;
		end
		default:state <= IDLE;
		endcase
	end				
endmodule

这里使用的是一段式状态机写法,至于为什么用一段式状态机,当然是一段式状态机描述IIC协议最为简单咯。如果大家有用三段式状态机描述IIC的代码,又比较简单的,欢迎一起交流,互相学习。

三、在2的基础上添加读相关寄存器的代码,并用chipscope读出相关位置的寄存器的内容,比较与写入的是否相同

不要认为使用上述代码将寄存器配置好就一定不会出现问题。如果一次就配置好,芯片就能正常工作,当然是最好的。但是,如果寄存器按照上述代码配置了,但是芯片不能正常工作,那么我们该怎么办呢?总不能不做了吧,说不做就不做,你考虑过老板的感受吗?所以,那么我们应该怎么做呢?最好的方式当然是将写进的寄存器读出来看看读出来的与理论上写进去的是否一致,如果一致,那你就要返回去看数据手册,看看是不是有什么什么寄存器遗漏了。那么,如何寄存器中的值呢?整个操作过程与写过程一模一样,只不过在读寄存器地址时,器件地址的最低位要设置为1(写的时候器件地址要设置为0)。我们调试的思路一般是先将写寄存器然后再读,如果不写直接读有什么意义呢?先写后读寄存器的verilog代码描述如下(可以与上面的只是写入寄存器的代码进行比较看看有什么异同):

module iic_cfg_hdmi_in(
							output SCL,
							inout SDA,					
							input clk35m,   //system clk
							input rst_n,    //system rst		
							output reg[7:0]data,
							output reg SDA_link,
							output cfig_done // 0: not config finished //1: finished		
				  );
				  
				  
reg	[7:0]	sub_address	[50:0];   //  IIC register address
reg	[7:0]	data_table	[50:0];   //  IIC write in data
reg   [7:0] device_address[9:0];
parameter i_max=50;	 					//配置寄存器个数

//parameter device_address = 8'h76;
initial	begin	
	 /////////////////////////IO寄存器配置device_address[0] = 8'h98;
	device_address[0] = 8'h98;////98
	device_address[1] = 8'h68;
	device_address[2] = 8'h44;
	device_address[3] = 8'h64;
	device_address[4] = 8'h4c;
		device_address[5] = 8'h99;////98
	device_address[6] = 8'h69;
	device_address[7] = 8'h45;
	device_address[8] = 8'h65;
	device_address[9] = 8'h4d;
	//////////////////////////IO寄存器配置device_address[0] = 8'h98;
	sub_address[0] = 8'hff;////////I2C reset
	data_table[0] = 8'h80;
	sub_address[1] = 8'hf4;///////CEC
	data_table[1] = 8'h80;///////////	
	sub_address[2] = 8'hf5;//////////INFOFRAME
	data_table[2] = 8'h7c;
	sub_address[3] = 8'hf8;/////////DPLL
	data_table[3] = 8'h4c;/////////
	sub_address[4] = 8'hf9;////////KSV
	data_table[4] = 8'h64;
	sub_address[5] = 8'hfa;//////////EDID
	data_table[5] = 8'h6c;
	sub_address[6] = 8'hfb;//////////HDMI
	data_table[6] = 8'h68;
	sub_address[7] = 8'hfd;//////////CP
	data_table[7] = 8'h44;
	///////////////////////////////
	sub_address[8] = 8'hc0;///////0x68////////device_address[1] = 8'h68;////ADI Required Write
	data_table[8] = 8'h03;
	////////////////////////////////////////////
	sub_address[9] = 8'h01;///////0x98//////////device_address[0] = 8'h98;///Prim_Mode =110b HDMI-GR 
	data_table[9] = 8'h06;//16-50hz///06-60hz
	sub_address[10] = 8'h02;//////////////////////////////////////////Auto CSC, RGB out, Set op_656 bit 
	data_table[10] = 8'hf7;/////RGB???
	sub_address[11] = 8'h03;////////////////////////////////////////////24 bit SDR 444 Mode 0
	data_table[11] = 8'h40;
	sub_address[12] = 8'h04;/////////////////参考晶振24Mhz及RGB与引脚关系
	data_table[12] = 8'h66;
	sub_address[13] = 8'h05;////////////////////////////////////AV Codes Off
	data_table[13] = 8'h28;
	sub_address[14] = 8'h06;////////hs vs正极性INV_HS_POL、INV_VS_POL should be set to 1 if not specified in Table 7
	data_table[14] = 8'ha6;	
   sub_address[15] = 8'h0c;
	data_table[15] = 8'h42;	
	sub_address[16] = 8'h15;
	data_table[16] = 8'h80;
	sub_address[17] = 8'h19;
	data_table[17] = 8'h83;
	sub_address[18] = 8'h33;
	data_table[18] = 8'h40;///////////98 33 40///////
	/////////////////////////////
	sub_address[19] = 8'hba;//////0x44////////device_address[2] = 8'h44;
	data_table[19] = 8'h01;
	sub_address[20] = 8'h6c;
	data_table[20] = 8'h00;
	/////////////////////////////////
	sub_address[21] = 8'h40;///////0x64/////device_address[3] = 8'h64;
	data_table[21] = 8'h81;
///////////////////////////////////////
	sub_address[22] = 8'hb5;//////////0x4c/////device_address[4] = 8'h4c;
	data_table[22] = 8'h01;
	//////////////////////////////////
	sub_address[23] = 8'hc0;////////////0x68////device_address[1] = 8'h68;
	data_table[23] = 8'h03;
	sub_address[24] = 8'h00;
	data_table[24] = 8'h00;/////Set HDMI Input Port A (BG_MEAS_PORT_SEL = 001b)
	sub_address[25] = 8'h02;
	data_table[25] = 8'h03;
	sub_address[26] = 8'h03;
	data_table[26] = 8'h98;
	sub_address[27] = 8'h10;
	data_table[27] = 8'ha5;		
	sub_address[28] = 8'h1b;///////////////
	data_table[28] = 8'h08;
	sub_address[29] = 8'h45;
	data_table[29] = 8'h04;
	sub_address[30] = 8'h97;
	data_table[30] = 8'hc0;
	sub_address[31] = 8'h3d;
	data_table[31] = 8'h10;
	sub_address[32] = 8'h3e;
	data_table[32] = 8'h69;
	sub_address[33] = 8'h3f;
	data_table[33] = 8'h46;
	sub_address[34] = 8'h4e;
	data_table[34] = 8'hfe;
	sub_address[35] = 8'h4f;
	data_table[35] = 8'h08;
	sub_address[36] = 8'h50;
	data_table[36] = 8'h00;
	sub_address[37] = 8'h57;
	data_table[37] = 8'ha3;
	sub_address[38] = 8'h58;
	data_table[38] = 8'h07;
	sub_address[39] = 8'h6f;////////////
	data_table[39] = 8'h08;
	sub_address[40] = 8'h83;
	data_table[40] = 8'hfc;
	sub_address[41] = 8'h84;
	data_table[41] = 8'h03;
	sub_address[42] = 8'h85;
	data_table[42] = 8'h10;
	sub_address[43] = 8'h86;
	data_table[43] = 8'h9b;
	sub_address[44] = 8'h89;
	data_table[44] = 8'h03;
	sub_address[45] = 8'h9b;
	data_table[45] = 8'h03;
	sub_address[46] = 8'h93;
	data_table[46] = 8'h03;
	sub_address[47] = 8'h5a;//////
	data_table[47] = 8'h80;
	sub_address[48] = 8'h9c;
	data_table[48] = 8'h80;
	sub_address[49] = 8'h9c;
	data_table[49] = 8'hc0;
	sub_address[50] = 8'h9c;
	data_table[50] = 8'h00;
end
reg    	cfig_done_r=0;
assign 	cfig_done = cfig_done_r;
always @ ( posedge clk35m )
	if(cfig_done)	begin
		sub_address[17] <= 8'h6a;/////98
		sub_address[2] <= 8'h6f;/////98
		
		sub_address[39] <= 8'h04;////68
		sub_address[41] <= 8'h05;//////68
		sub_address[43] <= 8'h07;////////68
	end
	else	begin
		sub_address[17] <= 8'h19;
		sub_address[2] <= 8'h44;
		
		sub_address[39] <= 8'h6f;
		sub_address[41] <= 8'h84;
		sub_address[43] <= 8'h86;	
	end		

reg    	SDA_r;
//reg    	SDA_link;//输出数据sda信号inout方向控制: 1 output 0 input	

reg SCL_r = 'd0;
assign	SCL	= SCL_r;
assign	SDA = SDA_link ? SDA_r : 1'bz;

reg [8:0]cnt_SCL = 'd0;//循环计数,产生iic所需要的时钟				  

always @ ( posedge clk35m )
	if(cnt_SCL == 9'd349)	   //周期为20us,即100KHz
		cnt_SCL <= 9'd0;
	else 	
		cnt_SCL <= cnt_SCL + 9'b1;//时钟计数;

	
`define SCL_pos cnt_SCL == 9'd87         //scl上升沿
`define SCL_hig cnt_SCL == 9'd174 	     //scl高电平中间,糜谑采样
`define SCL_neg cnt_SCL == 9'd262	     //scl下降沿
`define SCL_low cnt_SCL == 9'd349	     //scl低点平中间,用于数据变化


always @ ( posedge clk35m )    // 产生100k SCL signal
//	if(!cfig_done_r) begin
		if(`SCL_pos)
			SCL_r<=1'b1;    //scl信号上升沿
		else if(`SCL_neg)
			SCL_r<=1'b0;    //scl信号下降沿
//	end
	else
		SCL_r<=/*1'b1*/SCL_r; // config finish ,scl output 1;	

reg [3:0]	 state; //状态寄存器
reg [4:0]	 step;  //输出8位数据串行计数
//reg [7:0]   data;  //在IIC上传送的数据寄存器

parameter IDLE		   	=	4'D0,    //the state during the IIC writing 
			 START			=	4'D1,
			 SLAVE_ADDR    =   4'D2,
			 ACK1			   =	4'D3,
			 SUB_ADDR		=	4'D4,
			 ACK2			   =	4'D5,
			 DATA		   	=	4'D6,
			 ACK3			   =	4'D7,
			 PAUSE			=	4'D8,
			 OVER   	      =   4'D9;

reg			[7:0]	 i;//连续输出寄存器地址和数据计数

always @ (posedge clk35m or negedge rst_n) begin
	if(!rst_n)	begin
			data 		<= 16'd0;
			SDA_link 	<= 1'b1;
			SDA_r 		<= 1'b1;
			step 		<= 5'd0;
			state 		<= IDLE;
			i			<= 0;
			cfig_done_r <= 1'b0;
	end
	else case(state)
		IDLE:	begin
				SDA_link 	<= 1'b1;
				SDA_r 		<= 1'b1;
				i			<= 0;
				cfig_done_r <= 1'b0;
				state<=START;
		end
		START:	begin
			if(`SCL_hig) begin 
				SDA_r 	<= 1'b0; //start low
//				data		<= device_address[0];/////////////////////////器件地址
				state 		<= SLAVE_ADDR;
				step 		<= 5'd0;
			if(!cfig_done_r)begin
				if((i <= 'd7) ||((i >= 'd9)&&(i <= 'd18)))
					data <= device_address[0];
				else if(i == 'd8 || i >= 'd23 )
					data <= device_address[1];
				else if(i == 'd19 || i == 'd20)
					data <= device_address[2];
				else if(i == 'd21)
					data <= device_address[3];
				else if(i == 'd22)
					data <= device_address[4];
				else
					data <= data;
			end
			else
				if((i <= 'd7) ||((i >= 'd9)&&(i <= 'd18)))
					data <= device_address[5];
				else if(i == 'd8 || i >= 'd23 )
					data <= device_address[6];
				else if(i == 'd19 || i == 'd20)
					data <= device_address[7];
				else if(i == 'd21)
					data <= device_address[8];
				else if(i == 'd22)
					data <= device_address[9];
				else
					data <= data;				
			end
			else 
				state<=START;
		end
		SLAVE_ADDR:	begin
			if(`SCL_low) begin//low level for transmit data
				if(step==5'd8)	begin
					state <= ACK1;
					SDA_link <= 1'b0;//after the 8 bits data,the sda become input
					SDA_r <= 1'b1;
					step <= 5'd0;
				end
				else 	begin
					step <= step+5'd1;
					SDA_r <= data[5'd7-step];	
					state <= SLAVE_ADDR;
				end
			end
			else 	
				state <= SLAVE_ADDR;
		end

		ACK1: begin  //responsion
			if((`SCL_hig) && (SDA==1'b0)) begin
				state <= SUB_ADDR;
				data <= sub_address[i];				
			end
			else 
				state <= ACK1;
		end			
		SUB_ADDR:	begin
			if(`SCL_low) begin
				if(step==5'd8) begin
					state<=ACK2;
					SDA_link<=1'b0;
					SDA_r<=1'b1;
					step<=5'd0;
				end
				else begin
					SDA_link<=1'b1;					
					step<=step+5'd1;
					SDA_r<=data[5'd7-step];	
					state<=SUB_ADDR;
				end
			end
			else 
				state<=SUB_ADDR;
		end
		ACK2:	begin
			if((`SCL_hig)&&(SDA==1'b0))	begin 
				state <= DATA;
				data <= data_table[i];					
			end
			else 
				state <= ACK2;		
		end
		DATA:	begin
		if(`SCL_low) begin
			if(step==5'd8)	begin
				state<=ACK3;
				SDA_r<=1'b1;
				SDA_link<=1'b0;
				step<=5'd0;
			end
			else begin
//				SDA_link<=1'b1;
				step<=step+5'd1;
//				SDA_r<=data[5'd7-step];	
				state<=DATA;
				if(cfig_done_r)	begin
					SDA_link<=1'b0;
					data[5'd7-step] <=  SDA;
				end
				else	begin
					SDA_link<=1'b1;
					SDA_r<=data[5'd7-step];	
				end
			end
		end
		else 
			state<=DATA;
		end		
		ACK3:	begin
			if((`SCL_hig) &&(SDA==1'b0)) begin
				state <= PAUSE;					
			end
			else 
				state<=ACK3;
		end
		PAUSE:	begin// sent the stop signal 
			if (`SCL_low) begin
				SDA_link<=1'b1;
				SDA_r <= 1'b0;
				state <= PAUSE;
			end
			else if(`SCL_hig) begin
				SDA_r <= 1'b1;//scl为高时,sda产生上升沿(结束信号)
				if(i==i_max) begin
					i <= 0;
					state <= OVER;/////OVER
				end
				else	begin
					i <= i+ 8'd1;
					state <= START;
				end
			end
			else 
				state <= PAUSE;
		end
		OVER: begin
				cfig_done_r <= 1'b1;
//				state <= OVER;
				state <= START;
		end
		default:state <= IDLE;
		endcase
	end				
endmodule

 这样,就可以使用Chipscope抓取这些要读的数据,比较读写数据是否一致。

注意:有些芯片的寄存器配置需要复位一定长的时间,在配置芯片时,一定好好看数据手册,如果复位时间不符合要求,那你IIC写的再好也没用咯。

祝大家都能正确的调试出IIC。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章