IIC协议VerilogHDL 代码

//
// Module Name:    IIC_CORE 模块 AT24C256,SCL为高电平期间锁存数据,所以主器件输出到ATC(写)时,
要在SCL为低电平的
//时候给SDA赋值,而从EEPROM读数据时,只要在SCL为高电平时读好了。
//
//------------------------------------------------------------------------------------------
/*IIC协议 I2C总线协议规定,任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器。
①总线空闲状态
I2C 总线总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各
个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平
拉高。
②启动信号(Start)
在时钟线 SCL 保持高电平期间,数据线 SDA 上的电平被拉低(即负跳变),定义为 I2C 总线
总线的启动信号,它标志着一次数据传输的开始。启动信号是由主控器主动建立的,在建立该
信号之前 I2C 总线必须处于空闲状态
③停止信号(Stop)
在时钟线 SCL 保持高电平期间,数据线 SDA 被释放,使得 SDA 返回高电平(即正跳变),
称为 I2C 总线的停止信号,它标志着一次数据传输的终止。停止信号也是由主控器主动建立的,
建立该信号之后,I2C 总线将返回空闲状态。
④数据位传送
在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时
钟的配合下,在 SDA 上逐位地串行传送每一位数据。进行数据传送时,在 SCL 呈现高电平期
间,SDA 上的电平必须保持稳定,低电平为数据 0,高电平为数据 1。只有在 SCL 为低电平
期间,才允许 SDA 上的电平改变状态。
⑤应答信号(ACK 和 NACK)
I2C 总线上的所有数据都是以 8 位字节传送的,发送器每发送一个字节,就在时钟脉冲 9 期间
释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK
简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对
于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉
低,并且确保在该时钟的高电平期间为稳定的低电平。
如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送
器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号。
*/
//------------------------------------------------------------------------------------------
/*本模块主always块的结构是:先写器件地址,然后写寄存器地址。寄存器地址写完后,判断读写EEPROM,
如果写就直接进行写数据,
如果读,则送读器件地址,然后送所读寄存器地址,然后再进行读数据/
/*
//------------------------------------------------------------------------------------------
module IIC_CORE(
						input clk,						//50MHz主时钟
						input rst_n,					//复位信号
						
						output	reg	scl,					//IIC时钟
						inout	sda,						//IIC数据
						
						input	iic_en,					//IIC使能
						input	rd_or_wr,					//读写标志:读1,写0
						input [6:0] device_addr,			//IIC器件地址
						input [15:0] iic_reg_addr,	//寄存器地址
						input [7:0]	iic_reg_data,	//寄存器数据
						output reg [7:0] read_data,	//接收数据
						output reg 	iic_done			//IIC完成标志
					);
//---------------------------------------------
		//分频部分
reg	[2:0] cnt;	// cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg	[8:0] cnt_delay;	//500循环计数,产生iic所需要的时钟

always @ (posedge clk or negedge rst_n)
	if( !rst_n ) cnt_delay <= 9'd0;
	else if( cnt_delay == 9'd499 ) cnt_delay <= 9'd0;	//计数到10us为scl的周期,即100KHz
	else cnt_delay <= cnt_delay+1'b1;	//时钟计数

always @ (posedge clk or negedge rst_n) begin
	if( !rst_n ) cnt <= 3'd5;
	else begin
		case ( cnt_delay )
			9'd124:	cnt <= 3'd1;	//cnt=1:scl高电平中间,用于数据采样
			9'd249:	cnt <= 3'd2;	//cnt=2:scl下降沿
			9'd374:	cnt <= 3'd3;	//cnt=3:scl低电平中间,用于数据变化
			9'd499:	cnt <= 3'd0;	//cnt=0:scl上升沿
			default: cnt <= 3'd5;
			endcase
		end
end

`define SCL_POS		(cnt==3'd0)		//cnt=0:scl上升沿
`define SCL_HIG		(cnt==3'd1)		//cnt=1:scl高电平中间,用于数据采样
`define SCL_NEG		(cnt==3'd2)		//cnt=2:scl下降沿
`define SCL_LOW		(cnt==3'd3)		//cnt=3:scl低电平中间,用于数据变化


//产生iic所需要的时钟
always @ (posedge clk or negedge rst_n)
	if(!rst_n) scl <= 1'b0;
	else if(cnt==3'd0) scl <= 1'b1;	//scl信号上升沿
    else if(cnt==3'd2) scl <= 1'b0;	//scl信号下降沿
	else scl <= scl;
//---------------------------------------------
		//读、写时序
parameter 	IDLE 	= 4'd0;
parameter 	START1 	= 4'd1;
parameter 	ADD1 	= 4'd2;
parameter 	ACK1 	= 4'd3;
parameter 	ADD2 	= 4'd4;
parameter 	ACK2 	= 4'd5;
parameter 	ADD2_1 	= 4'd6;
parameter 	ACK2_1 	= 4'd7;
parameter 	START2 	= 4'd8;
parameter 	ADD3 	= 4'd9;
parameter 	ACK3	= 4'd10;
parameter 	DATA 	= 4'd11;
parameter 	ACK4	= 4'd12;
parameter 	STOP1 	= 4'd13;
parameter 	STOP2 	= 4'd14;

reg[7:0] db_r;		//在IIC上传送的数据寄存器
reg[3:0] cstate;	//状态寄存器
reg sda_r;		//输出数据寄存器
reg sda_link;	//输出数据sda信号inout方向控制位		
reg[3:0] num;	//
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
			cstate <= IDLE;
			sda_r <= 1'b1;
			sda_link <= 1'b0;
			num <= 4'd0;
			read_data <= 8'b0000_0000;
			iic_done <= 1'b0;
		end
	else
		case (cstate)
			IDLE:	begin
					sda_link <= 1'b1;					//数据线sda为input
					sda_r <= 1'b1;
					iic_done <= 1'b0;
					if(iic_en) begin					//iic_en==1,开始操作			
						db_r <= {device_addr,1'b0};	//送器件地址(写操作)
						cstate <= START1;		
						end
					else cstate <= IDLE;				//没有任何键被按下
				end
			START1: begin
					if(`SCL_HIG) begin		//scl为高电平期间
						sda_link <= 1'b1;	//数据线sda为output
						sda_r <= 1'b0;		//拉低数据线sda,产生起始位信号
						cstate <= ADD1;
						num <= 4'd0;		//num计数清零
						end
					else cstate <= START1; //等待scl高电平中间位置到来
				end
			ADD1:	begin
					if(`SCL_LOW) begin
							if(num == 4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK1;
								end
							else begin
									cstate <= ADD1;
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase
								end
						end
					else cstate <= ADD1;
				end
			ACK1:	begin
					if(`SCL_NEG) begin		//
							cstate <= ADD2;	//从机响应信号
							db_r <= iic_reg_addr[15:8];	// 1地址		
						end
					else cstate <= ACK1;		//等待从机响应
				end
			ADD2:	begin
					if(`SCL_LOW) begin
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK2;
								end
							else begin
									sda_link <= 1'b1;		//sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase		
									cstate <= ADD2;					
								end
						end
					else cstate <= ADD2;				
				end	
			ACK2:	begin
					if(`SCL_NEG) begin		//
							cstate <= ADD2_1;	//从机响应信号
							db_r <= iic_reg_addr[7:0];	// 1地址		
						end
					else cstate <= ACK2;		//等待从机响应
				end	
			ADD2_1:	begin
					if(`SCL_LOW) begin
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK2_1;
								end
							else begin
									sda_link <= 1'b1;		//sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase		
									cstate <= ADD2_1;					
								end
						end
					else cstate <= ADD2_1;				
				end
			ACK2_1:	begin
					if(`SCL_NEG) begin		//从机响应信号
						if(rd_or_wr) begin	
								cstate <= START2;			//读操作		
								db_r <= {device_addr,1'b1};	//送器件地址(读操作),特定地址读需要执行该步骤以下操作
											
							end	
						else begin
								cstate <= DATA; 			//写操作
								db_r <= iic_reg_data;	//写入的数据	
							end
						end
					else cstate <= ACK2_1;	//等待从机响应
				end
			START2: begin	//读操作起始位
					if(`SCL_LOW) begin
						sda_link <= 1'b1;	//sda作为output
						sda_r <= 1'b1;		//拉高数据线sda
						cstate <= START2;
						end
					else if(`SCL_HIG) begin	//scl为高电平中间
						sda_r <= 1'b0;		//拉低数据线sda,产生起始位信号
						cstate <= ADD3;
						end	 
					else cstate <= START2;
				end
			ADD3:	begin	//送读操作地址
					if(`SCL_LOW) begin
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK3;
								end
							else begin
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase									
									cstate <= ADD3;					
								end
						end
					else cstate <= ADD3;				
				end
			ACK3:	begin
					if(`SCL_NEG) begin
							cstate <= DATA;	//从机响应信号
							sda_link <= 1'b0;
						end
					else cstate <= ACK3; 		//等待从机响应
				end
			DATA:	begin
					if(rd_or_wr) begin	 //读操作
							if(num<=4'd7) begin
								cstate <= DATA;
								if(`SCL_HIG) begin	
									num <= num+1'b1;	
									case (num)
										4'd0: read_data[7] <= sda;
										4'd1: read_data[6] <= sda;  
										4'd2: read_data[5] <= sda; 
										4'd3: read_data[4] <= sda; 
										4'd4: read_data[3] <= sda; 
										4'd5: read_data[2] <= sda; 
										4'd6: read_data[1] <= sda; 
										4'd7: read_data[0] <= sda; 
										default: ;
										endcase																		
									end
								end
							else if((`SCL_LOW) && (num==4'd8)) begin
								num <= 4'd0;			//num计数清零
								cstate <= ACK4;
								end
							else cstate <= DATA;
						end
					else  begin	//写操作
							sda_link <= 1'b1;	
							if(num<=4'd7) begin
								cstate <= DATA;
								if(`SCL_LOW) begin
									sda_link <= 1'b1;		//数据线sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase									
									end
							 	end
							else if((`SCL_LOW) && (num==4'd8)) begin
									num <= 4'd0;
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态
									cstate <= ACK4;
								end
							else cstate <= DATA;
						end
				end
			ACK4: begin
					if(`SCL_NEG) begin
						cstate <= STOP1;						
						end
					else cstate <= ACK4;
				end
			STOP1:	begin
					if(`SCL_LOW) begin
							sda_link <= 1'b1;
							sda_r <= 1'b0;
							cstate <= STOP1;
						end
					else if(`SCL_HIG) begin
							sda_r <= 1'b1;	//scl为高时,sda产生上升沿(结束信号)
							cstate <= STOP2;
						end
					else cstate <= STOP1;
				end
			STOP2:	begin
					if(`SCL_LOW) begin
					sda_r <= 1'b1;
					iic_done <= 1'b1;
					cstate <= IDLE;
					end
					else cstate <= STOP2;
				end
			default: cstate <= IDLE;
			endcase
end

assign sda = sda_link ? sda_r:1'bz;

//---------------------------------------------
endmodule 	  

你可能感兴趣的:(通信协议)