基于FPGA的EEPROM 24LC04的读写

1.实现的功能:向相应的地址写一个字节;随机读取某一地址的数据;
2.语言:Verilog;
3.基本知识:
IIC协议:关于IIC协议的接收文档网上很多,推荐网址:https://wenku.baidu.com/view/838dc456ad02de80d4d840c8.html添加链接描述
特别注意的地方:这张表中时间单位为us的参数都得注意
这个条件不满足,通信很可能会失败
接收应答信号时钟高电平应满足的时间条件;当主机接收的应答信号为低电平表示从机正确接收到数据,反之通信不成功传输数据要求:数据在时钟电平时期改变,高电平保持稳定不变;每次传输一个字节且从高位开始传输
EEPROM AT24LC04:
控制字节:器件地址+读写命令单个字节写入和页写入当前地址读模式;随机读模式;连续读模式4.设计思路:
IIC读写有多个过程状态,可以使用状态机的思想来实现;
在这里插入图片描述
我的设计中涉及到三个时钟:系统时钟50mhz;IIC时钟也就是scl为250khz;用于驱动iic时钟为:4250khz=1mhz;
在这里插入图片描述在这里插入图片描述
从上到下:系统时钟;驱动IIC时钟;SCL;SDA;
之所以产生一个1mhz的时钟是为了保证每次scl=0时sda数据改变,acl=1时sda数据稳定不变;这也就是为什么是4
250khz了,这个4倍关系这么来的,当scl时钟为100khz,则需要4100khz时钟来驱动。
start信号:
sda拉低大于4us时再拉低时钟信号产生第一个时钟stop信号:
sda应拉高至少4.7us
5.设计中遇到的问题及感悟:
a)起始信号和停止信号sda满足的时间条件很重要,否则通信很容易不成功;
b)随机读数据的时候:切记切记切记要
**发送两次起始信号和器件地址***,笔者由于看手册不仔细把第二个起始信号忘了发送,导致读取数据错误、通信不成功;
c)调试过程:先用modelsim仿真,符合时序要求时再上板,quartus ii可以使用虚拟逻辑分析来观察读出的数据;同时可以使用示波器一路看时钟,一路看数据线;
往地址05h写入0xCD,并读取该地址数据,结果也为0XCD在这里插入图片描述

在这//IIC驱动模块
module I2C_Driver(
		 input						sys_clk_50m	,
		 input						sys_rst_n	,
		                	
		 input						iic_en		,	//IIC使能
		 input						wr_control	,	//IIC读写控制信号,0:写 1:读
		 input						addr_control,	//地址字节控制,0:1字节地址,1:2字节地址
		 input[15:0]				iic_addr		,	//IIC字部地址输入
		 input[ 7:0]				write_data	,	//写入IIC器件的数据
		   
		 output reg[7:0]			read_data	,	//读取IIC器件的数据
		 output reg					iic_done		,	//一次IIC操作完成信号
		   
		
		 output reg					ACK			,	//应答信号
		 output reg 				iic_scl		,	//IIC时钟输出
		                	
		 inout  						iic_sda			//IIC数据线(双向)
		                	
);  
  
parameter   SLAVE_ADDR 	= 7'b1010000							;//IIC器件地址
parameter	Write 		= 1'b0									;//写
parameter	Read  		= 1'b1									;//读
parameter   CLK_FREQ   	= 26'd50_000_000						;//IIC模块的驱动时钟频率(CLK_FREQ),即系统时钟50MHz
parameter   I2C_FREQ   	= 20'd250_000 							;//IIC的SCL时钟频率为250kHz
parameter	CLK_Devide 	= (CLK_FREQ / I2C_FREQ) >> 2'd3	;//分频系数


parameter	WRITE_FLAG	= 1'b0									;//写标志位
parameter	READ_FLAG	= 1'b1									;//读标志位


//localparam define
localparam  iic_idle     = 8'b0000_0001;          	//空闲状态
localparam  iic_sladdr   = 8'b0000_0010;          	//器件地址
localparam  iic_addr16   = 8'b0000_0100;          	//发送16位字地址
localparam  iic_addr8    = 8'b0000_1000;          	//发送8位字地址
localparam  iic_data_wr  = 8'b0001_0000;          	//写数据(8 bit)
localparam  iic_addr_rd  = 8'b0010_0000;          	//器件地址读
localparam  iic_data_rd  = 8'b0100_0000;          	//读数据(8 bit)
localparam  iic_stop     = 8'b1000_0000;          	//结束I2C操作

reg         sda_dir     ;                      		//I2C数据(SDA)方向控制,1:out,0:高阻,可以输出
reg         iic_sda_out ;                		  		//SDA输出
wire			iic_sda_in	;								  	//SDA输入



reg[7:0]		current_state;				
reg[7:0]		next_state;

reg         state_done1;                     	//状态结束
reg         state_done2;                     	//状态结束
reg         state_done3;                     	//状态结束
reg         state_done4;                     	//状态结束
reg         state_done5;                     	//状态结束
reg         state_done6;                     	//状态结束
reg         state_done7;                     	//状态结束

reg         wr_flag		;                    	//读写标志
reg[15:0]	addr_buff	;								//写地址地址缓存
reg[ 7:0]	wdata_buff	;								 //写数据缓存
reg[ 7:0]	rdata_buff	;								//读取数据缓存


reg[11:0]	sys_clk_cnt	;								//系统时钟计数器
reg[ 5:0]	dri_clk_cnt	;
reg			dri_clk		;								//驱动iic时钟


assign  		iic_sda 		= sda_dir ?  iic_sda_out : 1'bz; //SDA数据输出或高阻,高阻状态时数据线的状态不定这是就可以由从机来控制数据线的状态,主机负责读取状态
assign 		iic_sda_in  = iic_sda ;                      //SDA数据输入


//对系统时钟计数
always @ ( posedge sys_clk_50m or negedge sys_rst_n )	begin
	if( !sys_rst_n ) 
		sys_clk_cnt <= 12'd0;
	else if( sys_clk_cnt == CLK_Devide - 1 )
		sys_clk_cnt <= 12'd0;
	else
		sys_clk_cnt <= sys_clk_cnt + 1'b1;
end

//获得驱动IIC时钟,IIC时钟250khz,驱动IIC时钟为4*250khz=1Mhz
always @ ( posedge sys_clk_50m or negedge sys_rst_n )	begin
	if( !sys_rst_n ) 
		dri_clk <= 1'b1;
	else if( sys_clk_cnt == CLK_Devide - 1 ) 
		dri_clk <= ~dri_clk;		
	else
		dri_clk <= dri_clk;
end



//(三段式状态机)同步时序描述状态转移
always @ ( posedge sys_clk_50m or negedge sys_rst_n )	begin
	if( !sys_rst_n ) 
		current_state <= iic_idle;
	else
		current_state <= next_state;
end
//组合逻辑判断状态转移
always @ ( * ) begin
	case( current_state )
		iic_idle   : if( iic_en )//1							//iic使能之后
							next_state <= iic_sladdr;		//进入器件地址状态
						 else
							next_state <= iic_idle;								
		iic_sladdr : if( state_done1 ) begin//2
							if( addr_control == 0)			//进入字地址为1字节状态,直接发一个字节的地址
								next_state <= iic_addr8;
						   else 									//进入字地址为2字节状态,先发高字节地址
								next_state <= iic_addr16;
						 end
						 else
							next_state <= iic_sladdr;								
		iic_addr16 : if( state_done2 )	//4						
							next_state <= iic_addr8;		//进入地址低字节
						 else
							next_state <= iic_addr16;
							
		iic_addr8  : if( state_done3 )	begin	//8				
							if( wr_flag  == 1'b1 )						//进入读数据
								next_state <= iic_addr_rd;
							else  								//进入写数据
								next_state <= iic_data_wr;
						 end
						 else
							next_state <= iic_addr8;							
		iic_data_wr : if( state_done4 )					//写数据,16
							next_state <= iic_stop;
						 else
							next_state <= iic_data_wr;							
		iic_addr_rd : if( state_done5 )
							next_state <= iic_data_rd;		//写地址读取数据,32
						 else
							next_state <= iic_addr_rd;								
		iic_data_rd : if( state_done6 )						//读取数据,64
							next_state <= iic_stop;
						 else
							next_state <= iic_data_rd;							
		iic_stop   : if( state_done7 )						//停止信号,128
							next_state <= iic_idle;
						 else
							next_state <= iic_stop;							
		default	  :   next_state <= iic_idle;			//空闲状态
	endcase
end

//时序电路描述状态输出
always @ ( posedge dri_clk or negedge sys_rst_n )	begin
	if( !sys_rst_n ) begin
		iic_scl			<= 1'b1;		//时钟线拉高
		sda_dir 			<= 1'b1;		//数据线输出
		iic_sda_out		<= 1'b1;		//数据线拉高
		addr_buff 		<= 16'd0;	//字地址缓存
		wdata_buff 		<= 8'd0;		//写数据缓存
		read_data 		<= 8'd0;		//读数据寄存器
		rdata_buff		<= 8'd0;		//读数据缓存
		wr_flag 			<= 1'b0;		//读写标志位
		dri_clk_cnt 	<= 6'd0;		//IIC驱动时钟计数器	
		state_done1 	<= 1'b0;		//各个状态结束标志位
		state_done2 	<= 1'b0;
		state_done3 	<= 1'b0;
		state_done4 	<= 1'b0;
		state_done5 	<= 1'b0;
		state_done6 	<= 1'b0;
		state_done7 	<= 1'b0;
		ACK				<= 1'b0;		//应答标志位
		iic_done 		<= 1'b0;		//IIC计数操作
	end 
	else begin
		dri_clk_cnt <= dri_clk_cnt + 1'b1;
		state_done1 	<= 1'b0;
		state_done2 	<= 1'b0;
		state_done3 	<= 1'b0;
		state_done4 	<= 1'b0;
		state_done5 	<= 1'b0;
		state_done6 	<= 1'b0;
		state_done7 	<= 1'b0;
		addr_buff  		<= iic_addr		;		//将字地址缓存
		wdata_buff 		<= write_data	;		//将待写进从机的数据缓存
		wr_flag 			<= wr_control	;		//获得读写信号
		ACK				<= 1'b0;					//应答标志位
		iic_done 		<= 1'b0;					//IIC结束标志信号为低电平
		case( current_state )
			iic_idle  : 	begin
									iic_scl 		<= 1'b1;		//空闲时:时钟线和数据线拉高
									sda_dir 		<= 1'b1;		//输出
									iic_sda_out <= 1'b1;		//拉高
									dri_clk_cnt <= 6'd0;		//IIC驱动时钟计数器
									if( iic_en )  begin
										wr_flag 	<= wr_control;	//获得读写信号
									end
								end
			iic_sladdr :	begin
									case( dri_clk_cnt )
										6'd0	:	iic_scl <= 1'b1;			//拉低数据线准备
										6'd1	:	begin
														sda_dir 		<= 1'b1;	//输出
														iic_sda_out <= 1'b0;	//数据线拉低,发送起始信号
													end
										
										6'd2	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd3	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd4	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd5	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd6	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd7	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd8	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										
										6'd9	:	iic_scl		<= 1'b0;		//拉低数据线准备
										6'd10	:	iic_sda_out <= SLAVE_ADDR[6];//器件地址
										6'd11	:	iic_scl		<= 1'b1;	//拉高锁存数据
										6'd12	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd13	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd14	:	iic_sda_out <= SLAVE_ADDR[5];//器件地址
										6'd15	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd16	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd17	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd18	:	iic_sda_out <= SLAVE_ADDR[4];//器件地址
										6'd19	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd20	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd21	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd22	:	iic_sda_out <= SLAVE_ADDR[3];//器件地址
										6'd23	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd24	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd25	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd26	:	iic_sda_out <= SLAVE_ADDR[2];//器件地址
										6'd27	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd28	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd29	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd30	:	iic_sda_out <= SLAVE_ADDR[1];//器件地址
										6'd31	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd32	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd33	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd34	:	iic_sda_out <= SLAVE_ADDR[0];//器件地址
										6'd35	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd36	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd37	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd38	:	iic_sda_out <= WRITE_FLAG;//写
										6'd39	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd40	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd41	:	iic_scl		<= 1'b0;//拉低数据线准备																		
										6'd42	:	begin//从机应答
														sda_dir <= 1'b0;//让数据线变为高阻态,主机读取数据线的电平,低电平为有效应答
													end
										6'd43	:	iic_scl	<= 1'b1;//拉高锁存数据	
										6'd44	:	begin
														iic_scl 	<= 1'b1;//拉高锁存数据	
														ACK		<=	iic_sda_in;
													end
										6'd45	:	begin
														iic_scl		<= 1'b0;//拉低数据线准备	
														dri_clk_cnt	<= 6'd0;//
														state_done1	<= 1'b1;
													end
										default : ;
									endcase
								end
			iic_addr16 : begin							
								case( dri_clk_cnt )
										6'd0	:	begin
														iic_scl		<= 1'b0;
														sda_dir 		<= 1'b1;
														iic_sda_out <= addr_buff[15];
													end
										6'd1	:	iic_scl		<= 1'b1;
										6'd2	:	iic_scl		<= 1'b1;
										6'd3	:	iic_scl		<= 1'b0;
										6'd4	:	iic_sda_out <= addr_buff[14];
										6'd5	:	iic_scl		<= 1'b1;
										6'd6	:	iic_scl		<= 1'b1;
										6'd7	:	iic_scl		<= 1'b0;
										6'd8	:	iic_sda_out <= addr_buff[13];
										6'd9	:	iic_scl		<= 1'b1;
										6'd10	:	iic_scl		<= 1'b1;
										6'd11	:	iic_scl		<= 1'b0;
										6'd12	:	iic_sda_out <= addr_buff[12];
										6'd13	:	iic_scl		<= 1'b1;
										6'd14	:	iic_scl		<= 1'b1;
										6'd15	:	iic_scl		<= 1'b0;
										6'd16	:	iic_sda_out <= addr_buff[11];
										6'd17	:	iic_scl		<= 1'b1;
										6'd18	:	iic_scl		<= 1'b1;
										6'd19	:	iic_scl		<= 1'b0;
										6'd20	:	iic_sda_out <= addr_buff[10];
										6'd21	:	iic_scl		<= 1'b1;
										6'd22	:	iic_scl		<= 1'b1;
										6'd23	:	iic_scl		<= 1'b0;
										6'd24	:	iic_sda_out <= addr_buff[9];
										6'd25	:	iic_scl		<= 1'b1;
										6'd26	:	iic_scl		<= 1'b1;
										6'd27	:	iic_scl		<= 1'b0;
										6'd28	:	iic_sda_out <= addr_buff[8];
										6'd29	:	iic_scl		<= 1'b1;
										6'd30	:	iic_scl 		<= 1'b1;									
										6'd31	:	iic_scl		<= 1'b0;
										6'd32	:	begin						//从机应答
														sda_dir 	<= 1'b0;
													end
										6'd33	:	iic_scl 		<= 1'b1;	
										6'd34	:	begin
														iic_scl 	<= 1'b1;
														ACK		<=	iic_sda_in;
													end
										6'd35	:	begin
														iic_scl 		<= 1'b0;	
														state_done2	<= 1'b1;//4
														dri_clk_cnt	<= 6'd0;//
													end
										default : ;
									endcase
							 end
			iic_addr8 : begin
								case( dri_clk_cnt )
										6'd0	:	begin
														iic_scl		<= 1'b0;
														sda_dir 		<= 1'b1;
														iic_sda_out <= addr_buff[7];
													end
										6'd1	:	iic_scl		<= 1'b1;
										6'd2	:	iic_scl		<= 1'b1;
										6'd3	:	iic_scl		<= 1'b0;
										6'd4	:	iic_sda_out <= addr_buff[6];
										6'd5	:	iic_scl		<= 1'b1;
										6'd6	:	iic_scl		<= 1'b1;
										6'd7	:	iic_scl		<= 1'b0;
										6'd8	:	iic_sda_out <= addr_buff[5];
										6'd9	:	iic_scl		<= 1'b1;
										6'd10	:	iic_scl		<= 1'b1;
										6'd11	:	iic_scl		<= 1'b0;
										6'd12	:	iic_sda_out <= addr_buff[4];
										6'd13	:	iic_scl		<= 1'b1;
										6'd14	:	iic_scl		<= 1'b1;
										6'd15	:	iic_scl		<= 1'b0;
										6'd16	:	iic_sda_out <= addr_buff[3];
										6'd17	:	iic_scl		<= 1'b1;
										6'd18	:	iic_scl		<= 1'b1;
										6'd19	:	iic_scl		<= 1'b0;
										6'd20	:	iic_sda_out <= addr_buff[2];
										6'd21	:	iic_scl		<= 1'b1;
										6'd22	:	iic_scl		<= 1'b1;
										6'd23	:	iic_scl		<= 1'b0;
										6'd24	:	iic_sda_out <= addr_buff[1];
										6'd25	:	iic_scl		<= 1'b1;
										6'd26	:	iic_scl		<= 1'b1;
										6'd27	:	iic_scl		<= 1'b0;
										6'd28	:	iic_sda_out <= addr_buff[0];
										6'd29	:	iic_scl		<= 1'b1;
										6'd30	:	iic_scl		<= 1'b1;								
										6'd31	:	iic_scl		<= 1'b0;
										6'd32	:	begin			//从机应答
														sda_dir 	<= 1'b0;
													end
										6'd33	:	iic_scl 		<= 1'b1;	
										6'd34	:	begin
														iic_scl 	<= 1'b1;	
														ACK		<= iic_sda_in;		
													end	
										6'd35	:	begin
														iic_scl 		<= 1'b0;	
														state_done3 <= 1'b1;//8
														dri_clk_cnt	<= 6'd0;//
													end
										default : ;
									endcase
							end
			iic_data_wr : begin	
								case( dri_clk_cnt )
										6'd0	:	begin
														iic_scl		<= 1'b0;
														sda_dir 		<= 1'b1;
														sda_dir 		<= 1'b1;
														iic_sda_out <= wdata_buff[7];
													end
										6'd1	:	iic_scl		<= 1'b1;
										6'd2	:	iic_scl		<= 1'b1;
										6'd3	:	iic_scl		<= 1'b0;
										6'd4	:	iic_sda_out <= wdata_buff[6];
										6'd5	:	iic_scl		<= 1'b1;
										6'd6	:	iic_scl		<= 1'b1;
										6'd7	:	iic_scl		<= 1'b0;
										6'd8	:	iic_sda_out <= wdata_buff[5];
										6'd9	:	iic_scl		<= 1'b1;
										6'd10	:	iic_scl		<= 1'b1;
										6'd11	:	iic_scl		<= 1'b0;
										6'd12	:	iic_sda_out <= wdata_buff[4];
										6'd13	:	iic_scl		<= 1'b1;
										6'd14	:	iic_scl		<= 1'b1;
										6'd15	:	iic_scl		<= 1'b0;
										6'd16	:	iic_sda_out <= wdata_buff[3];
										6'd17	:	iic_scl		<= 1'b1;
										6'd18	:	iic_scl		<= 1'b1;
										6'd19	:	iic_scl		<= 1'b0;
										6'd20	:	iic_sda_out <= wdata_buff[2];
										6'd21	:	iic_scl		<= 1'b1;
										6'd22	:	iic_scl		<= 1'b1;
										6'd23	:	iic_scl		<= 1'b0;
										6'd24	:	iic_sda_out <= wdata_buff[1];
										6'd25	:	iic_scl		<= 1'b1;
										6'd26	:	iic_scl		<= 1'b1;
										6'd27	:	iic_scl		<= 1'b0;
										6'd28	:	iic_sda_out <= wdata_buff[0];
										6'd29	:	iic_scl		<= 1'b1;
										6'd30	:	iic_scl		<= 1'b1;								
										6'd31	:	iic_scl		<= 1'b0;
										6'd32	:	begin			//从机应答
														sda_dir 	<= 1'b0;
													end
										6'd33	:	begin
														iic_scl 	<= 1'b1;
														ACK		<= iic_sda_in;
													end
										6'd34	:	iic_scl 		<= 1'b1;		
										6'd35	:	begin
														iic_scl 		<= 1'b0;	
														state_done4	<= 1'b1;//16
														dri_clk_cnt	<= 6'd0;//
													end
										default : ;
									endcase								 
							  end
			iic_addr_rd : begin
									case( dri_clk_cnt )
										6'd0	:	begin
														iic_scl 		<= 1'b0;	//拉低数据线准备
														sda_dir 		<= 1'b1;	//输出
														iic_sda_out <= 1'b1;	//数据线拉低,发送起始信号
													end
										6'd1	:	begin
														iic_scl 		<= 1'b1;	//拉低数据线准备
														sda_dir 		<= 1'b1;	//输出
														iic_sda_out <= 1'b1;	//数据线拉低,发送起始信号
													end
										
										6'd2	:	iic_sda_out	<= 1'b1;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd3	:	iic_sda_out	<= 1'b1;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd4	:	iic_sda_out	<= 1'b1;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd5	:	iic_sda_out	<= 1'b1;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd6	:	iic_sda_out	<= 1'b1;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd7	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd8	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd9	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd10	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd11	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										6'd12	:	iic_sda_out	<= 1'b0;		//SDA低电平时间持续要大于4us时SCL才能拉低
										
										6'd13	:	iic_scl		<= 1'b0;		//拉低数据线准备
										6'd14	:	iic_sda_out <= SLAVE_ADDR[6];//器件地址
										6'd15	:	iic_scl		<= 1'b1;	//拉高锁存数据
										6'd16	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd17	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd18	:	iic_sda_out <= SLAVE_ADDR[5];//器件地址
										6'd19	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd20	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd21	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd22	:	iic_sda_out <= SLAVE_ADDR[4];//器件地址
										6'd23	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd24	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd25	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd26	:	iic_sda_out <= SLAVE_ADDR[3];//器件地址
										6'd27	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd28	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd29	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd30	:	iic_sda_out <= SLAVE_ADDR[2];//器件地址
										6'd31	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd32	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd33	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd34	:	iic_sda_out <= SLAVE_ADDR[1];//器件地址
										6'd35	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd36	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd37	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd38	:	iic_sda_out <= SLAVE_ADDR[0];//器件地址
										6'd39	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd40	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd41	:	iic_scl		<= 1'b0;//拉低数据线准备
										6'd42	:	iic_sda_out <= READ_FLAG;//写
										6'd43	:	iic_scl		<= 1'b1;//拉高锁存数据
										6'd44	:	iic_scl		<= 1'b1;//拉高锁存数据
										
										6'd45	:	iic_scl		<= 1'b0;//拉低数据线准备																		
										6'd46	:	begin//从机应答
														sda_dir <= 1'b0;//让数据线变为高阻态,主机读取数据线的电平,低电平为有效应答
													end
										6'd47	:	iic_scl	<= 1'b1;//拉高锁存数据	
										6'd48	:	begin
														iic_scl 	<= 1'b1;//拉高锁存数据	
														ACK		<=	iic_sda_in;
													end
										6'd49	:	begin
														iic_scl		<= 1'b0;//拉低数据线准备	
														dri_clk_cnt	<= 6'd0;//
														state_done5	<= 1'b1;
													end
										default : ;
									endcase
							end
			iic_data_rd : begin
									case( dri_clk_cnt )
										6'd0	:	begin
														iic_scl		<= 1'b0;
														sda_dir 		<= 1'b0;
													end
										6'd1	:	iic_scl			<= 1'b1;
										6'd2	:	rdata_buff[7]	<= iic_sda_in;
										6'd3	:	iic_scl			<= 1'b0;
										6'd4	:	iic_scl			<= 1'b0;
										6'd5	:	iic_scl			<= 1'b1;
										6'd6	:	rdata_buff[6]	<= iic_sda_in;
										6'd7	:	iic_scl			<= 1'b0;
										6'd8	:	iic_scl			<= 1'b0;
										6'd9	:	iic_scl			<= 1'b1;
										6'd10	:	rdata_buff[5]	<= iic_sda_in;
										6'd11	:	iic_scl			<= 1'b0;
										6'd12	:	iic_scl			<= 1'b0;
										6'd13	:	iic_scl			<= 1'b1;
										6'd14	:	rdata_buff[4]	<= iic_sda_in;
										6'd15	:	iic_scl			<= 1'b0;
										6'd16	:	iic_scl			<= 1'b0;
										6'd17	:	iic_scl			<= 1'b1;
										6'd18	:	rdata_buff[3]	<= iic_sda_in;
										6'd19	:	iic_scl			<= 1'b0;
										6'd20	:	iic_scl			<= 1'b0;
										6'd21	:	iic_scl			<= 1'b1;
										6'd22	:	rdata_buff[2]	<= iic_sda_in;
										6'd23	:	iic_scl			<= 1'b0;
										6'd24	:	iic_scl			<= 1'b0;
										6'd25	:	iic_scl			<= 1'b1;
										6'd26	:	rdata_buff[1]	<= iic_sda_in;
										6'd27	:	iic_scl			<= 1'b0;
										6'd28	:	iic_scl			<= 1'b0;
										6'd29	:	iic_scl 			<= 1'b1;
										6'd30	:	rdata_buff[0]	<= iic_sda_in;								
										6'd31	:	iic_scl			<= 1'b0;
										6'd32	:	begin			
														sda_dir 		<= 1'b1;//非应答
														iic_sda_out	<= 1'b1;
													end
										6'd33	:	iic_scl 	<= 1'b1;	
										6'd34	:	iic_scl 	<= 1'b1;		
										6'd35	:	begin
														iic_scl 		<= 1'b0;	
														state_done6	<= 1'b1;//64
														dri_clk_cnt	<= 6'd0;//
														read_data	<= rdata_buff;
													end
										default : ;
									endcase				
								end
			iic_stop : begin													//结束IIC操作
								sda_dir 		<= 1'b1;
							 case( dri_clk_cnt )
								6'd0 	:	begin									//从机应答
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;
											end
								6'd1	:	 iic_scl 		<= 1'b1;	
								6'd2	:	 iic_sda_out	<= 1'b0;				
								6'd3	:	 iic_sda_out	<= 1'b0;	
								6'd4	:	 iic_sda_out	<= 1'b0;	
								6'd5	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd6	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd7	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd8	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd9	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd10	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b0;	
											end
								6'd11	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd12	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd13	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd14	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd15	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd16	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd17	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd18	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd19	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd20	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd21	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd22	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd23	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd24	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd25	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd26	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd27	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd28	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd29	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd30	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd31	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd32	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd33	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd34	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;	
											end
								6'd35	:	begin
												iic_scl 		<= 1'b1;
												sda_dir 		<= 1'b1;
												iic_sda_out	<= 1'b1;
												dri_clk_cnt	<= 6'd0;//
												iic_done		<= 1'b1;
												state_done7 <= 1'b1;//128	
											end
										default : ;
							 endcase
						  end
		endcase
	end
end
endmodule里插入代码片
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个简单的FPGA代码,用于通过IIC总线读写24LC04B芯片: ```verilog module IIC_24LC04B( input wire clk, input wire reset, input wire start, input wire stop, input wire [7:0] addr, input wire [7:0] data_in, output reg [7:0] data_out, output reg ack ); reg [7:0] state; reg [7:0] count; reg [7:0] reg_addr; reg [7:0] reg_data; reg sda; reg scl; wire sda_i; wire sda_oen; wire sda_en; wire scl_en; assign sda_en = (state == 1 || state == 3 || state == 5 || state == 7 || state == 9); assign scl_en = (state == 2 || state == 4 || state == 6 || state == 8 || state == 10); // IIC总线状态定义 localparam IDLE = 0; localparam START = 1; localparam ADDR = 2; localparam ADDR_ACK = 3; localparam DATA_IN = 4; localparam DATA_IN_ACK = 5; localparam DATA_OUT = 6; localparam DATA_OUT_ACK = 7; localparam STOP = 8; always @(posedge clk) begin if (reset) begin state <= IDLE; count <= 0; sda <= 1; scl <= 1; reg_addr <= 0; reg_data <= 0; ack <= 0; end else begin case (state) IDLE: begin if (start) begin sda <= 0; scl <= 0; state <= START; end end START: begin sda <= 1; scl <= 1; state <= ADDR; end ADDR: begin sda <= addr[count]; state <= ADDR_ACK; end ADDR_ACK: begin sda <= 1; state <= (ack == 0) ? DATA_IN : DATA_OUT; end DATA_IN: begin sda <= 1; state <= DATA_IN_ACK; end DATA_IN_ACK: begin sda <= 1; if (ack == 0) begin state <= STOP; data_out <= reg_data; end else begin state <= DATA_OUT; end end DATA_OUT: begin sda <= (reg_data[count] == 1); state <= DATA_OUT_ACK; end DATA_OUT_ACK: begin sda <= 1; if (ack == 0) begin state <= (count == 7) ? STOP : DATA_OUT; count <= count + 1; end end STOP: begin sda <= 0; scl <= 0; state <= IDLE; end endcase end end // IIC总线信号同步 assign sda_i = sda; assign sda_oen = (sda_en) ? 1 : 0; SB_IO #( .PIN_TYPE(6'b 101001), .PULLUP(1'b 1), .NEG_TRIGGER(1'b 1), .IO_STANDARD("SB_LVCMOS") ) iic_sda ( .PACKAGE_PIN(sda_i), .OUTPUT_ENABLE(sda_oen), .D_OUT_0(sda), .D_IN_0(sda) ); SB_IO #( .PIN_TYPE(6'b 101001), .PULLUP(1'b 1), .NEG_TRIGGER(1'b 1), .IO_STANDARD("SB_LVCMOS") ) iic_scl ( .PACKAGE_PIN(scl), .OUTPUT_ENABLE(scl_en), .D_OUT_0(scl), .D_IN_0(scl) ); // 读写24LC04B芯片 always @(posedge clk) begin if (reset) begin ack <= 0; reg_addr <= 0; reg_data <= 0; end else if (start) begin reg_addr <= addr; count <= 0; reg_data <= data_in; end else if (state == DATA_OUT_ACK) begin if (count == 7) begin ack <= 1; end else begin reg_addr <= reg_addr + 1; end count <= count + 1; reg_data <= data_in; end else if (state == DATA_IN_ACK) begin ack <= 1; end end endmodule ``` 使用方法: 1. 将代码保存为 IIC_24LC04B.v 文件。 2. 根据您的FPGA型号和开发板,修改 iic_sda 和 iic_scl 的属性。 3. 实例化 IIC_24LC04B 模块,并将 clk, reset, start, stop, addr, data_in, data_out 和 ack 连接到您的FPGA设计中。 4. 在您的FPGA设计中使用该模块实现对24LC04B芯片的读写操作。 注意事项: 1. 该代码只是一个参考,具体实现需要根据您的FPGA型号和开发板进行修改。 2. 在使用该代码时,请仔细阅读24LC04B芯片的数据手册,以确保正确使用IIC总线进行读写操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值