IIC总线协议 与 EEPROM 的使用

需要注意以下几点:

1、wire类型不能设置初值

2、当在module后的括号中写了output之后 对应的变量名不能再定义为reg

3、使用Modelsim只能检查输出信号是否正确 不能仿真接收信号是否正确

4、注意 inout 类型的 port 的用法

inout  SDA;  //输出或输入数据信号
reg rSDA = 1'b1;
assign SDA = isOut ? rSDA : 1'bz; //输出时选择rSDA,输入时设为高阻态 

在输入状态下 应当使用SDA为其他变量赋值 不应使用rSDA

5、注意 在下面程序中 在 IIC 读数协议据中 第二次写设备地址最后一位是 1

二级文件

`timescale 1 ns/ 1 ps

//本module实现IIC总线的读与写的功能,读写都是一次读写,一次8bit数据
module IIC_test(
	clk,
	rst_n,
	read_or_write,
	Addr,  
	WrData,
	RdData,
	isDone,
	SCL,
	SDA
);

input clk;    //50MHz时钟信号
input rst_n;  //复位信号,0电平使能

input  [1:0] read_or_write; //选择读还是写,01表示写,10表示读
input  [7:0] Addr;          //读或者写的地址
input  [7:0] WrData;        //需要写入的数据
output [7:0] RdData;        //读出的数据
output isDone;              //读或者写操作是否完成,1表示完成

output SCL;  //输出时钟信号
inout  SDA;  //输出或输入数据信号

parameter freq_div = 9'd500; //时钟分频系数,500MHz经分频得到100KHz

reg [4:0] state = 5'd0; //表明当前的状态
reg [4:0] Go = 5'd0;    //表明跳转到哪个状态
reg [8:0] cnt = 9'd0;   //分频计数
reg [7:0] Data = 8'd0;  //当前操作的数据

reg rSCL = 1'b1; 
reg rSDA = 1'b1;  

reg isAck = 1'b1;   //应答信号,0表示接收正常
reg isDone = 1'b0;  //1表示操作完毕
reg isOut = 1'b1;   //1表示SDA为输出状态,0表示SDA为输入状态

assign RdData = Data;
assign SCL = rSCL;
assign SDA = isOut ? rSDA : 1'bz; //输出时选择rSDA,输入时设为高阻态 

always @(posedge clk or negedge rst_n)
begin
	if(~rst_n)  //复位
	begin
		state <= 5'd0;
		Go <= 5'd0;  
		cnt <= 9'd0; 
		Data <= 8'd0;
		rSCL <= 1'b1;
		rSDA <= 1'b1;
		isAck <= 1'b1; 
		isDone <= 1'b0;
		isOut <= 1'b1; 
	end
	else if(read_or_write[0])  //IIC写数据
	begin
		case(state)
		
		5'd0: //start开始状态
		begin
			isOut <= 1'b1;//设置SDA为输出状态
			
			if(cnt == 9'd0) rSDA <= 1'b1; //SDA先由高变低
			else if(cnt == 9'd200) rSDA <= 1'b0;
			else rSDA <= rSDA;
			
			if(cnt == 9'd0) rSCL <= 1'b1; //SCL再由高变低
			else if(cnt == 9'd400) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd1: //获取设备的地址,在开发板上EEPROM地址为0xA0
		begin
			Data <= {4'b1010, 3'b000, 1'b0}; //设备地址为A0
			state <= 5'd7;       //跳转到状态7
			Go <= state + 5'd1;  //在写入设备地址之后再跳转到状态2
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;		
		end
		
		5'd2: //获取数据的地址
		begin
			Data <= Addr;       //获取数据的地址
			state <= 5'd7;      //跳转到状态7
			Go <= state + 5'd1; //在写入数据地址之后再跳转到状态3
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;			
		end
		
		5'd3: //获取需要写入的数据
		begin
			Data <= WrData;     //获取需要写入的数据
			state <= 5'd7;      //跳转到状态7
			Go <= state + 5'd1; //在写入数据地址之后再跳转到状态3
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd4: //stop停止状态
		begin
			isOut <= 1'b1;//设置SDA为输出状态
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL先由低变高
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else rSCL <= rSCL;
			
			if(cnt == 9'd0) rSDA <= 1'b0; //SDA再由低变高
			else if(cnt == 9'd300) rSDA <= 1'b1;
			else rSDA <= rSDA;			
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd5: //状态5和6是发送一个操作完成信号,1表示操作完成
		begin
			isDone <= 1'b1;
			state <= state + 5'd1; //跳转到下一个状态]
			
			Go <= Go;  
			cnt <= cnt; 
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isAck <= isAck; 
			isOut <= isOut;
		end
		
		5'd6: //状态5和6是发送一个操作完成信号
		begin
			isDone <= 1'b0;
			state <= 5'd0; //回到初始状态
			
			Go <= Go;  
			cnt <= cnt; 
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isAck <= isAck; 
			isOut <= isOut;
		end
		
		5'd7, 5'd8, 5'd9, 5'd10, 5'd11, 5'd12, 5'd13, 5'd14: 
		//状态7到状态14发送数据,从高位开始发送,每个状态发送1bit
		begin
			isOut <= 1'b1;
			
			rSDA <= Data[5'd14-state]; //从高位开始发送
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0; 
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end		
		
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd15: //等待接收方返回应答ACK信号
		begin
			isOut <= 1'b0; //将SDA设为输入模式
			
			if(cnt == 9'd200) isAck <= SDA; //读取返回的应答ACK信号,特别注意这里是SDA!!!
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data; 
			isDone <= isDone;
		end
		
		5'd16: //处理应答信号
		begin
			if(isAck != 1'b0)
				state <= 5'd0; //未收到应答,跳转到初始状态
			else 
				state <= Go;   //收到应答,则跳转到其他相应的状态
			
			cnt <= cnt; 
			Go <= Go;
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isDone <= isDone;
			isAck <= isAck; 
			isOut <= isOut;
		end		
		
		endcase
	end
	else if(read_or_write[1])  //IIC读数据
	begin
		case(state)
		
		5'd0: //start起始状态
		begin
			isOut <= 1'b1;//设置SDA为输出状态
			
			if(cnt == 9'd0) rSDA <= 1'b1; //SDA先由高变低
			else if(cnt == 9'd200) rSDA <= 1'b0;
			else rSDA <= rSDA;
			
			if(cnt == 9'd0) rSCL <= 1'b1; //SCL再由高变低
			else if(cnt == 9'd400) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd1: //获取设备的地址,在开发板上EEPROM地址为0xA0
		begin
			Data <= {4'b1010, 3'b000, 1'b0}; //设备地址为A0
			state <= 5'd9;       //跳转到状态9
			Go <= state + 5'd1;  //在写入设备地址之后再跳转到状态2
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;		
		end
		
		5'd2: //获取数据的地址
		begin
			Data <= Addr;       //获取数据的地址
			state <= 5'd9;      //跳转到状态9
			Go <= state + 5'd1; //在写入数据地址之后再跳转到状态3
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;			
		end
		
		5'd3: //再一次start起始状态
		begin
			isOut <= 1'b1;//设置SDA为输出状态
			
			if(cnt == 9'd0) rSDA <= 1'b1; //SDA先由高变低
			else if(cnt == 9'd200) rSDA <= 1'b0;
			else rSDA <= rSDA;
			
			if(cnt == 9'd0) rSCL <= 1'b1; //SCL再由高变低
			else if(cnt == 9'd400) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd4: //再一次获取设备地址,在开发板上EEPROM地址为0xA0
		begin
			Data <= {4'b1010, 3'b000, 1'b1}; //设备地址为A1,特别注意这里最后一位是1 !!!!!!!!
			state <= 5'd9;       //跳转到状态9
			Go <= state + 5'd1;  //在写入设备地址之后再跳转到状态2
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;		
		end
		
		5'd5: //准备读取数据
		begin
			Data <= 8'd0;       //清空Data
			state <= 5'd19;     //跳转到状态19
			Go <= state + 5'd1; //读数据之后再跳转到状态6
			
			rSCL <= rSCL;
			rSDA <= rSDA;
			cnt <= cnt;
			isOut <= isOut;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd6: //stop停止状态
		begin
			isOut <= 1'b1;//设置SDA为输出状态
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL先由低变高
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else rSCL <= rSCL;
			
			if(cnt == 9'd0) rSDA <= 1'b0; //SDA再由低变高
			else if(cnt == 9'd300) rSDA <= 1'b1;
			else rSDA <= rSDA;			
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd7: //状态7和8是发送一个操作完成信号,1表示操作完成
		begin
			isDone <= 1'b1;
			state <= state + 5'd1; //跳转到下一个状态]
			
			Go <= Go;  
			cnt <= cnt; 
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isAck <= isAck; 
			isOut <= isOut;
		end
		
		5'd8: //状态5和6是发送一个操作完成信号
		begin
			isDone <= 1'b0;
			state <= 5'd0; //回到初始状态
			
			Go <= Go;  
			cnt <= cnt; 
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isAck <= isAck; 
			isOut <= isOut;
		end
		
		5'd9, 5'd10, 5'd11, 5'd12, 5'd13, 5'd14, 5'd15, 5'd16:
		//状态9到状态16据,从高位开始发送,每个状态发送1bit
		begin
			isOut <= 1'b1;
			
			rSDA <= Data[5'd16-state]; //从高位开始发送
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0; 
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end		
		
			Go <= Go;
			Data <= Data;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd17: //等待接收方返回应答ACK信号
		begin
			isOut <= 1'b0; //将SDA设为输入模式
			
			if(cnt == 9'd200) isAck <= SDA; //读取返回的应答ACK信号,特别注意这里是SDA!!!
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data; 
			isDone <= isDone;
		end
		
		5'd18: //处理应答信号
		begin
			if(isAck != 1'b0)
				state <= 5'd0; //未收到应答,跳转到初始状态
			else 
				state <= Go;   //收到应答,则跳转到其他相应的状态
			
			cnt <= cnt; 
			Go <= Go;
			Data <= Data;
			rSCL <= rSCL;
			rSDA <= rSDA;
			isDone <= isDone;
			isAck <= 1'b1; 
			isOut <= isOut;
		end
		
		5'd19, 5'd20, 5'd21, 5'd22, 5'd23, 5'd24, 5'd25, 5'd26:
		//接收数据,发送端从高位开始发送,主控器开始读取数据
		begin
			isOut <= 1'b0;
			
			if(cnt == 9'd200) Data[5'd26-state] <= SDA; //从高位开始发送,特别注意这里是SDA!!!
			else Data <= Data;
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0; 
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= state + 5'd1; //跳转到下一个状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end		
		
			Go <= Go;
			isAck <= isAck; 
			isDone <= isDone;
		end
		
		5'd27: //主控器在接收完毕后发送一个NACK信号
		begin
			isOut <= 1'b1; //将SDA设为输出模式
			
			rSDA <= 1'b1; //SDA输出高电平
			
			if(cnt == 9'd0) rSCL <= 1'b0; //SCL由低变高再由高变低
			else if(cnt == 9'd100) rSCL <= 1'b1;
			else if(cnt == 9'd300) rSCL <= 1'b0;
			else rSCL <= rSCL;
			
			if(cnt == freq_div - 9'd1)
			begin
				cnt <= 9'd0;
				state <= Go; //跳转到stop状态
			end
			else
			begin
				cnt <= cnt + 9'd1;
				state <= state;
			end
			
			Go <= Go;
			Data <= Data; 
			isDone <= isDone;
		end
		
		endcase
	end
	else
	begin
		state <= 5'd0;
		Go <= 5'd0;  
		cnt <= 9'd0; 
		Data <= 8'd0;
		rSCL <= 1'b1;
		rSDA <= 1'b1;
		isAck <= 1'b1; 
		isDone <= 1'b0;
		isOut <= 1'b1; 
	end
end

endmodule

一级文件:

`timescale 1 ns/ 1 ps

//基于IIC总线的EEPROM读写程序
module EEPROM_test(
	clk,     //50MHz时钟信号
	rst_n,   //复位信号,0电平使能
	led,     //LED灯指示信号
	SCL,     //IIC时钟线
	SDA      //IIC数据线
);

input  clk, rst_n;
output [3:0] led;
output SCL;
inout  SDA;

reg [31:0] cnt = 32'd0;
reg  [7:0] state = 8'b0;
reg  [3:0] led = 4'd0;

reg  [7:0] w_Data = 8'd0;         //需要写入EEPROM的数据
reg  [1:0] read_or_write = 2'b00; //控制读还是写,01为写,10为读,00为无操作,不可置为11
reg  [7:0] Addr = 8'd0;           //读或写操作的数据地址

wire isDone;        //操作完成指示信号,1表示操作完成
wire [7:0] r_Data;  //读到到数据

always @(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
		cnt <= 32'd0;
		state <= 8'd0;
	end
	else 
	begin
		if(cnt >= 32'd0 && cnt < 32'd49999999)
		begin
			cnt <= cnt + 32'd1;
			state <= state;
		end
		else if(cnt == 32'd49999999)
		begin
			cnt <= 32'd0;
			if(state == 8'd31) state <= 8'd16;
			else state <= state + 8'd1;
		end
		else
		begin
			cnt <= 32'd0;
			state <= 8'd0;
		end
	end
end

always @(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
		led <= 4'd0;
		w_Data <= 8'd0;
		read_or_write <= 2'b00;
		Addr <= 8'd0;
	end
	else
	begin
		case(state)
		
		8'd0,8'd1,8'd2,8'd3,8'd4,8'd5,8'd6,8'd7,8'd8,8'd9,8'd10,
		8'd11,8'd12,8'd13,8'd14,8'd15:
		begin
			if(isDone == 1'b1) 
			begin
				read_or_write <= 2'b00;
				Addr <= Addr;
				w_Data <= w_Data;
				led <= led;
			end
			else
			begin
				read_or_write <= 2'b01;  //写入数据
				Addr <= state;
				w_Data <= state;
				led <= led;
			end
		end
		
		8'd16,8'd17,8'd18,8'd19,8'd20,8'd21,8'd22,8'd23,8'd24,8'd25,8'd26,
		8'd27,8'd28,8'd29,8'd30,8'd31:
		begin
			if(isDone == 1'b1) 
			begin
				read_or_write <= 2'b00;
				Addr <= Addr;
				w_Data <= w_Data;
				led <= r_Data[3:0];
			end
			else
			begin
				read_or_write <= 2'b10; //读出数据
				Addr <= state - 8'd16;
				w_Data <= w_Data;
				led <= led;
			end
		end
		
		default:
		begin
			led <= 4'd0;
			w_Data <= 8'd0;
			read_or_write <= 2'b00;
			Addr <= 8'd0;
		end
		
		endcase
	end
end

IIC_test #(
	.freq_div        (9'd500)
)
IIC_test_inst(
	.clk             (clk),
	.rst_n           (rst_n),
	.read_or_write   (read_or_write),
	.Addr            (Addr),  
	.WrData          (w_Data),
	.RdData          (r_Data),
	.isDone          (isDone),
	.SCL             (SCL),
	.SDA             (SDA)
);

endmodule
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IIC总线EEPROM存储器应用程序的源码设计需要根据具体的芯片型号和软件平台来确定。下面提供一个参考的示例,可以帮助您了解如何设计基于IIC总线EEPROM存储器应用程序。 示例代码: ```c #include <Wire.h> #define EEPROM_ADDRESS 0x50 // EEPROM芯片的I2C地址,可根据具体型号修改 #define EEPROM_SIZE 256 // EEPROM芯片的容量,可根据具体型号修改 void setup() { Wire.begin(); // 初始化I2C总线 } void loop() { // 写入数据到EEPROM int address = 0; // 写入数据的地址 byte data = 0x12; // 写入的数据 writeEEPROM(address, data); delay(1000); // 从EEPROM读取数据 byte readData = readEEPROM(address); Serial.println(readData); delay(1000); } // 写入数据到EEPROM void writeEEPROM(int address, byte data) { Wire.beginTransmission(EEPROM_ADDRESS); Wire.write((int)(address >> 8)); // EEPROM地址高8位 Wire.write((int)(address & 0xFF)); // EEPROM地址低8位 Wire.write(data); // 写入数据 Wire.endTransmission(); delay(5); // 等待EEPROM完成写入 } // 从EEPROM读取数据 byte readEEPROM(int address) { byte readData = 0xFF; Wire.beginTransmission(EEPROM_ADDRESS); Wire.write((int)(address >> 8)); // EEPROM地址高8位 Wire.write((int)(address & 0xFF)); // EEPROM地址低8位 Wire.endTransmission(); Wire.requestFrom(EEPROM_ADDRESS, 1); // 从EEPROM读取1个字节 if (Wire.available()) { readData = Wire.read(); // 读取数据 } return readData; } ``` 以上示例代码使用了Arduino的Wire库,通过I2C总线EEPROM芯片进行通信。其中,`writeEEPROM()`函数用来向EEPROM写入数据,`readEEPROM()`函数用来从EEPROM读取数据。在`setup()`函数中初始化I2C总线,`loop()`函数中循环执行写入和读取操作。 需要注意的是,具体的EEPROM芯片型号和I2C地址可能会有所不同,需要根据具体型号进行修改。另外,EEPROM的容量也需要根据具体型号进行修改,以上代码示例中假设EEPROM的容量为256字节。 希望以上示例代码能够对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值