需要注意以下几点:
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