开发板使用的是EP4CE15F23C8,软件使用的是quartus 12.1 ,工程主要是对24c04进行读写
eeprom顶层
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: eeprom_top
// Function: write and read eeprom using I2C bus
//////////////////////////////////////////////////////////////////////////////////
module eeprom_top
(
input CLK_50M,
input RSTn,
output [3:0]LED,
output SCL,
inout SDA
);
wire [7:0] RdData;
wire Done_Sig;
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_50M 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:
if( Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else begin isStart <= 2'b01; rData <= 8'h12; rAddr <= 8'd0; end //eeprom write 0x12 to EEPROM addr 0
1:
if( Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else begin isStart <= 2'b10; rAddr <= 8'd0; end //eeprom read data from EEPROM addr 0
2:
begin rLED <= RdData[3:0]; end
endcase
/***************************/
//I2C通信程序//
/***************************/
iic_control U1
(
.CLK ( CLK_50M ),
.RSTn ( RSTn ),
.Start_Sig ( isStart ),
.Addr_Sig ( rAddr ),
.WrData ( rData ),
.RdData ( RdData ),
.Done_Sig ( Done_Sig ),
.SCL ( SCL ),
.SDA ( SDA )
);
endmodule
iic 控制模块
module iic_control
(
input CLK,
input RSTn,
input [1:0] Start_Sig, //read or write command
input [7:0] Addr_Sig, //eeprom words address
input [7:0] WrData, //eeprom write data
output [7:0] RdData, //eeprom read data
output Done_Sig, //eeprom read/write finish
output SCL,
inout SDA
);
parameter F100K = 9'd500; //100Khz的时钟分频系数
reg [4:0]i;
reg [4:0]Go;
reg [9:0]C1;
reg [7:0]rData;
reg rSCL;
reg rSDA;
reg isAck;
reg isDone;
reg isOut;
assign Done_Sig = isDone;
assign RdData = rData;
assign SCL = rSCL;
assign SDA = isOut ? rSDA : 1'bz; //SDA数据输出选择
//****************************************//
//* I2C读写处理程序 *//
//****************************************//
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] ) //I2C 数据写
case( i )
0: // iic Start
begin
isOut <= 1; //SDA端口输出
if( C1 == 0 ) rSCL <= 1'b1;
else if( C1 == 400 ) rSCL <= 1'b0; //SCL由高变低
if( C1 == 0 ) rSDA <= 1'b1;
else if( C1 == 200 ) rSDA <= 1'b0; //SDA先由高变低
if( C1 == F100K -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; Go <= i + 1'b1; end
2: // Wirte Word Addr
begin rData <= Addr_Sig; i <= 5'd7; Go <= i + 1'b1; end
3: // Write Data
begin rData <= WrData; i <= 5'd7; Go <= i + 1'b1; end
4: //iic Stop
begin
isOut <= 1'b1;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1; //SCL先由低变高
if( C1 == 0 ) rSDA <= 1'b0;
else if( C1 == 300 ) rSDA <= 1'b1; //SDA由低变高
if( C1 == F100K -1 ) begin C1 <= 9'd0; i <= i + 1'b1; end
else C1 <= C1 + 1'b1;
end
5:
begin isDone <= 1'b1; i <= i + 1'b1; end //写I2C 结束
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 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) rSCL <= 1'b0;
if( C1 == F100K -1 ) begin C1 <= 9'd0; i <= i + 1'b1; end
else C1 <= C1 + 1'b1;
end
15: // waiting for acknowledge
begin
isOut <= 1'b0; //SDA端口改为输入
if( C1 == 100 ) isAck <= SDA;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) 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] ) //I2C 数据读
case( i )
0: //iic Start
begin
isOut <= 1; //SDA端口输出
if( C1 == 0 ) rSCL <= 1'b1;
else if( C1 == 400 ) rSCL <= 1'b0; //SCL由高变低
if( C1 == 0 ) rSDA <= 1'b1;
else if( C1 == 200 ) rSDA <= 1'b0; //SDA先由高变低
if( C1 == F100K -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'd9; Go <= i + 1'b1; end
2: // Wirte Word Addr
begin rData <= Addr_Sig; i <= 5'd9; Go <= i + 1'b1; end
3: //iic Start again
begin
isOut <= 1'b1;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1;
else if( C1 == 500 ) rSCL <= 1'b0; //SCL后变低
if( C1 == 0 ) rSDA <= 1'b0;
else if( C1 == 100 ) rSDA <= 1'b1;
else if( C1 == 300 ) rSDA <= 1'b0; //SDA先变低
if( C1 == 600 -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'd19; Go <= i + 1'b1; end
6: //iic Stop
begin
isOut <= 1'b1;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1; //SCL先变高
if( C1 == 0 ) rSDA <= 1'b0;
else if( C1 == 300 ) rSDA <= 1'b1; //SDA后变高
if( C1 == F100K -1 ) begin C1 <= 9'd0; i <= i + 1'b1; end
else C1 <= C1 + 1'b1;
end
7: //写I2C 结束
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 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) 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 == 200 ) isAck <= SDA;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) 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 == 200 ) rData[26-i] <= SDA;
if( C1 == 0 ) rSCL <= 1'b0;
else if( C1 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) 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 == 100 ) rSCL <= 1'b1;
else if( C1 == 300 ) rSCL <= 1'b0;
if( C1 == F100K -1 ) begin C1 <= 9'd0; i <= Go; end
else C1 <= C1 + 1'b1;
end
endcase
endmodule
使用iic对eeprom进行读写,这个工程主要要注意iic协议的起始信号,停止信号,以及ack,并且要清楚如何使用三态门,将一个
IO口设置为双向口,程序部分要注意状态机里GO这个是如何跳转的
设置管脚
点击工具栏快捷按钮进行编译
使用Signaltap 观测端口信号
点击工具栏上的Open按钮,然后选择Signaltap Ⅱ Logic Analyzer File,点击OK
点击截图中的红色方框添加采样时钟
这里的CLK_50M来自顶层,点击List将CLK_50M添加进来
选择CLK_50M,点击 > 将CLK_50M添加进来
点击OK
双击空白添加想要观测的信号管脚
添加管脚SCL,点击List
这里因顶层例化的模块里有相同的管脚名所以都加进来,但我们只选外接的eeprom模块的这个管脚,点击 > 加入进来
点击OK
添加SDA管脚,输入管脚名SDA,点击List,在下面的管脚列表里选择SDA,再点击 > 添加进去
添加RdData,输入RdData名称,点击List,选择列表里的RdData,点击 > 添加进来
点击OK
SignalTap 就配置完了,按ctrl+s保存 SignalTap 文件为 stp1.stp
关闭SignalTap 界面在quartus里重新编译工程,双击者个Files里的step1.stp文件打开 SignalTap
连接USB Blaster 下载器,可以看到我们的开发板型号
点击 SOF Manager 最右边的按钮,并选择生成的 eeprom_test.sof 文件
点击Open添加进来
点击下载按钮下载进去
下载完成后,如下图所示
将SDA设置为下降沿触发
点击 Run Analysis 按钮开始运行一次 SignalTap 的数据采集
SignalTap 处于等待状态,按一下开发板的reset按键
触发IIC总线,采集到的波形如下图所示