FPGA入门——EEPROM读写例程(二 代码)

一、代码功能

1.写一个数据(0x12)到EEPROM(AT24C04)的地址0, 再读出地址0的内容。
2.实例化 iic_com 模块和 chipscope 的两个模块 chipscope_icon 和 chipscope_ila。使用
50Mhz 时钟做为 chipscope 的采样时钟。采样的信号为从 EEPROM 读出的数据 RdData。

二、代码

1.顶层代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
module eeprom_test(
		input	CLK_50MHz,
		input RSTn,
		output [3:0]LED,
		output SCL,
		inout	 SDA
    );

wire [7:0] RdData;	//读数据
wire Done_Sig;	//iic_com块中通信结束信号

reg [3:0] i;
reg [3:0] rLED;

reg [7:0] rAddr;
reg [7:0] rData;
reg [1:0] isStart;

assign LED = rLED;

/*****************************************/
/*			EEPROM write and read			  */
/*****************************************/

always@(posedge CLK_50MHz or negedge RSTn)
	if(!RSTn)
		begin
			i <= 4'd0;
			rAddr <= 8'd0;
			rData <= 8'd0;
			isStart <= 2'b00;
			rLED <= 4'b0000;
		end
	else
		case(i)
		0:		//eeprom write 0x12 to addr 0
			if(Done_Sig)begin isStart <= 2'b00;i <= i + 1'b1;end			//在iic_com模块中isDone需要开始复位
			else begin isStart <= 2'b01; rData <= 8'h12; rAddr <= 8'd0;end	//isStart == 01 写开始
		
		1:		//eeprom read addr 0
			if(Done_Sig)begin isStart <= 2'b00; i <= i + 1'b1;end
			else begin isStart <= 2'b10; rAddr <= 8'd0; end		//isStart ==10  读开始
			
		2:	
			begin rLED <= RdData[3:0];end
		endcase

/**********************************/
//					通信程序				//
/*********************************/

iic_com inst_iic_com(
		.CLK(CLK_50MHz),
		.RSTn(RSTn),
		.Start_Sig(isStart),
		.Addr_Sig(rAddr),
		.WrData(rData),
		.RdData(RdData),
		.Done_Sig(Done_Sig),
		.SCL(SCL),
		.SDA(SDA)
);


wire [35:0]	CONTROL0;
wire [255:0] TRIG0;

icon_debug inst_icon_debug(
    .CONTROL0(CONTROL0)
); /* synthesis syn_black_box syn_noprune=1 */;

chipscoepe_ila inst_chipscope_ila(
    .CONTROL(CONTROL0),
    .CLK(CLK_50MHz),
    .TRIG0(TRIG0)
); /* synthesis syn_black_box syn_noprune=1 */;


assign TRIG0[7:0] = RdData;

endmodule

程序先从case0(写0x12数据到eeprom中地址0)开始。当Done_Sig为0时(在iic_com模块中isDone开始复位后), isStart <= 2’b01——Start_Sig[0]为1,选择写操作; rData <= 8’h12——写入数据为0x12; rAddr <= 8’d0——写入地址为0。
写完后,iic_com中isDone置1,即Done_Sig=1,执行i=i+1;跳转到case1。
case1同case0,只是只有读地址0操作。

二、IIC 的通信程序

1.代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    20:54:09 07/22/2019 
// Design Name: 
// Module Name:    iic_com 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module iic_com(
			input CLK,
			input	RSTn,
			
			input	[1:0]	Start_Sig,	//选择读或写操作
			input	[7:0]	Addr_Sig,	//eeprom 字的地址
			input [7:0]	WrData,		//写入字的内容
			output[7:0]	RdData,		//输出读字节的内容到chipscope
			output	Done_Sig,		//iic通信结束完成信号
			
			output	SCL,
			output	SDA
    );
	 
	 
parameter	F100K =	9'd500;	//100KHz的时钟分频系数

reg [4:0] i;    //流程
reg [4:0] Go;	//总体流程		
reg [8:0] C1;	//计数器
reg [7:0] rData;	//读数据寄存器
reg rSCL;		
reg rSDA;		
reg isAck;	 
reg isDone;	 	//完成信号寄存器
reg isOut;	 	//输入输出寄存器,SDA为1输出,为0输入
	 
assign Done_Sig = isDone;
assign RdData = rData;	 
assign SCL = rSCL;
assign SDA = isOut ? rSDA:1'bz;	//蓝线表示高阻态,说明sda口此时作为输入口

//*******************************************************************//
//*				IIC读写处理程序													***//
//*******************************************************************//
always@(posedge CLK or negedge RSTn)
	if(!RSTn)begin
		i <= 5'd0;
		Go <= 5'd0;
		C1 <= 9'd0;
		rData <= 8'd0;
		rSCL <= 1'b1;
		rSDA <= 1'b1;
		isAck <= 1'b1;
		isDone <= 1'b0;		
		isOut <= 1'b1;
	end
	else if(Start_Sig[0])	//IIC数据写
		case(i)
			0:	//start
				begin
					isOut <= 1;	//SDA端口输出
				
					if(C1 == 0)
						rSCL <= 1'b1;
					else if(C1 == 200)
						rSCL <= 1'b0;	//SCL由高变低
					
					if(C1 == 0)
						rSDA <= 1'b1; 
					else if(C1 == 100)
						rSDA <= 1'b0;	//SDA先由高边低      
					
					if(C1 == 250 - 1) 
						begin 
							C1 <= 9'd0; i<= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
			
			1:	//write Device addr
				begin 
					rData <= {4'b1010, 3'b000, 1'b0}; 
					i <= 5'd7; 			//跳转到输出8个位
					Go <= i + 1'b1;	//input
				end
			2:	//write word addr
				begin
					rData <= Addr_Sig; //input
					i <= 5'd7;
					Go <= i + 1'b1;
				end
			3:	//write Data
				begin
					rData <= WrData ;	//WrData input
					i <= 5'd7;
					Go <= i + 1'b1;
				end
			4:	//stop
				begin
					isOut <= 1'b1;
					
					if(C1 == 0)
						rSCL <= 1'b0;
					else if(C1 == 50)
						rSCL <= 1'b1;		//scl由低变高
					
					if(C1 == 0)
						rSDA <=0;
					else if(C1 == 150)
						rSDA <=1;			//sda由低变高
						
					if(C1 == 250 - 1)
						begin
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
				
			5:	//写IIC结束
				begin
					isDone <= 1'b1;
					i <= i + 1'b1;
				end
				
			6: //
				begin 
					isDone <= 1'b0;
					i <= 5'd0;
				end
				
			7,8,9,10,11,12,13,14:	//发送Device addr/word addr/write data
				begin
					isOut <= 1'b1;
					rSDA <= rData[14-i];	//高位先发送
					
					if(C1 == 0)
						rSCL <= 1'b0;
					else if(C1 == 50)
						rSCL <= 1'b1;
					else if(C1 == 150)
						rSCL <= 1'b0;
					
					if(C1 == F100K - 1)	//F100K 分屏系数
						begin 
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
			
			15:		//waiting for acknowledge   来等Ack或者NAck
				begin
					isOut <= 1'b0;		//SDA端口改为输入
					if(C1 == 100) isAck <= SDA;
						
					if(C1 == 0)rSCL <= 1'b0;
					else if(C1 == 50)rSCL <=1'b1;
					else if(C1 == 150)rSCL <= 1'b0;
					
					if(C1 == F100K - 1)
						begin
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
						
			16:
				if(isAck != 0)i<= 5'd0;
				else i <= Go;
		endcase
	
	else if(Start_Sig[1])		//IIC数据读
		case(i)
			0:	//start
				begin
					isOut <= 1;	//SDA端口输出
					
					if(C1 == 0)rSCL <= 1'b1;
					else if(C1 == 200)rSCL <= 1'b0;	//SCL由高变低
						
					if(C1 == 0)rSDA <= 1'b1;
					else if(C1 ==100)rSDA <= 1'b0;	//SDA先由高变低	
					
					if(C1 == 250 -1)
						begin 
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
					
			1:	//write device addr
				begin 
					rData <= {4'b1010,3'b000,1'b0};	//note
					i <= 5'd9;		//跳转到9 输出8个位,相比于7多两种,16-9   //14-7
					Go <= i + 1'b1;
				end
			
			2:	//write word addr
				begin
					rData <= Addr_Sig;
					i <= 5'd9;
					Go <= i + 1'b1;
				end
			
			3:	//start again
				begin
					isOut <= 1'b1;
					
					if(C1 == 0)rSCL <= 1'b0;
					else if(C1 ==50)rSCL <= 1'b1;
					else if(C1 == 250)rSCL <= 1'b0;
					
					if(C1 == 0)rSDA <= 1'b0;
					else if(C1 == 50)rSDA <= 1'b1;
					else if(C1 == 150)rSDA <= 1'b0;
					
					if(C1 == 300 - 1)
						begin
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else
						C1 <= C1 + 1'b1;
				end
		
			4:	//write device addr(read)
				begin	
					rData <= {4'b1010,3'b000,1'b1};
					i <= 5'd9;
					Go <= i + 1'b1;
				end
				
			5:	//read data
				begin
					rData <= 8'd0;
					i <= 5'd9;
					Go <= i + 1'b1;
				end
				
			6:	//stop
				begin
					isOut <= 1'b1;
					if(C1 == 0)rSCL <= 1'b0;
					else if(C1 == 50)rSCL <= 1'b1;
					
					if(C1 == 0)rSDA <= 1'b0;
					else if(C1 == 150)rSDA <= 1'b1;
					
					if(C1 == 250 -1)begin C1 <= 9'd0; i <= i + 1'b1;end
					else C1 <= C1 + 1'b1;
				end
				
			7:	//写IIC结束
				begin
					isDone <= 1'b1;
					i <= i + 1'b1;
				end
				
			8:	
				begin 
					isDone <= 1'b0;
					i <=5'd0;
				end
			
			9,10,11,12,13,14,15,16:	//发送Device addr(write)/word addr/device addr(read)
				begin
					isOut <= 1'b1;
					rSDA <= rData[16-i];
					
					if(C1 == 0)rSCL <= 1'b0;
					else if(C1 == 50)rSCL <= 1'b1;
					else if(C1 == 150)rSCL <= 1'b0;
					
					if(C1 == F100K - 1)
						begin
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
			
			17:	//waiting for acknowledge 
				begin
					isOut <= 1'b0;	//SDA端口改为输入
					
					if(C1 == 100)isAck <= SDA;
					
					if(C1 == 0) rSCL <= 1'b0;
					else if(C1 == 50)rSCL <= 1'b1;
					else if(C1 == 150)rSCL <= 1'b0;
					
					if(C1 == F100K -1)
						begin
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
				
			18:
				if(isAck != 0)i<=5'd0;
				else i <= Go;
				
			19,20,21,22,23,24,25,26:	//read data
				begin
					isOut <= 1'b0;
					if(C1 == 100)rData[26-i] <= SDA;
					
					if(C1 == 0 )rSCL <= 1'b0;
					else if(C1 == 50)rSCL <= 1'b1;
					else if(C1 == 150)rSCL <= 1'b0;
					
					if(C1 == F100K - 1)
						begin 
							C1 <= 9'd0;
							i <= i + 1'b1;
						end
					else C1 <= C1 + 1'b1;
				end
			
			27:	//no acknowledge
				begin
					isOut <= 1'b1;
					
					if(C1 == 0)rSCL <= 1'b0;
					else if(C1 == 50)rSCL <= 1'b1;
					else if(C1 == 150)rSCL <= 1'b0;
					
					if(C1 == F100K - 1)
						begin
							C1 <= 9'd0;
							i <= Go;
						end
					else C1 <= C1 + 1'b1;
				end
		
		endcase
endmodule

2 .IIC读写程序解读

先需要复位(RSTn==0),isDone=0;顶层模块读取到Done_Sig=01,iic_com模块中Start_Sig[0]=1,开始IIC写操作。

1.写单个储存字节

FPGA入门——EEPROM读写例程(二 代码)_第1张图片FPGA入门——EEPROM读写例程(二 代码)_第2张图片
FPGA入门——EEPROM读写例程(二 代码)_第3张图片

0:写起始信号
由IIC通信时序,SCL保持高电平,SDA由高变低(拉低)。发送开始信号。
1:写设备地址
设备地址为1010_0000,写完地址跳转到 7。
A2,A1为0(可以扩展at24c04),A8根据AT24C04D数据手册 为字地址的最高位(0x12 ——0001_0010)。bit0为0时写操作。
FPGA入门——EEPROM读写例程(二 代码)_第4张图片
7(7~14):发送设备地址,字地址,写的数据
SDA选择输出,高位先发送。每个F100K(500)发送一个位。
15:等待ACK/NACK
SDA选择输入,当isACK为0,答应有效。等待500跳转16.
16:当isAck为0时,跳转到2(i=GO,此时GO为2);isAck为1 ,i=0;
2:写字地址,再调到7,7-16循环,和1相同。
3:写数据,再跳转到7,7-16循环,和1相同。
4:停止位。
5:写IIC结束,输出isDone=1;
6:isDone =0,i=0;

2.读单个储存字节

FPGA入门——EEPROM读写例程(二 代码)_第5张图片
0:start 起始信号
1:写设备地址,设备地址为1010_0000,写完地址跳转到 9。
9(9~16):写设备地址,字地址, 写的数据。
17:等待ACK/NACK
18:当isAck为0时,跳转到2(i=GO,此时GO为2);isAck为1 ,i=0;
2:写 字 地址,再跳转到9,重复9~18;
3:start 起始信号
4:读设备地址
1010_0001,再跳转到9,重复9~18;
5:读 字的数据
初始rData=0,i=19;
19~26:读数据
SDA为输入,
27:等待ACK/NACK

(程序来自黑金 AX545/516 开发板 Verilog 教程)

你可能感兴趣的:(FPGA入门——EEPROM读写例程(二 代码))