需要注意以下幾點:
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