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。

 

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