開發板使用的是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總線,採集到的波形如下圖所示