关于iic的时序,之前已经讲过,这里纠正一个小错误就是inout类型的变量不可以用reg型,只能用wire型,之前在alter板子上reg可以也不知道为啥,反正vivado不行。由于博主很懒,就把之前写的代码照搬过来了,修改下,就可以在vivado上用了。
关于代码不做介绍,通过实验验证了我们的程序在连续写或读的时候都不会存在问题,有想修改的朋友直接照搬即可,然后自己修改测试程序就是,读和写单字节都封装的比较好可以不做修改。直接用就行了。也欢迎大家可以一起讨论,下面给出代码。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/09/17 10:29:21
// Design Name:
// Module Name: iic
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic(
clk,
rst,
choose,
device,
address,
data,
rdata,
slk,
sda_a,
isdone
);
inout wire sda_a;
input clk;
input rst;
input [1:0]choose; //选择读、写
input [7:0]device;
input [7:0]address;
input [7:0]data;
output reg [7:0]rdata;
output reg slk;
reg sda;
output reg isdone;
assign sda_a=sda;
parameter r100K = 9'd500;
reg [5:0]i;
reg [15:0]clkcount;
reg isask;
always@(posedge clk or negedge rst)
if(!rst)
begin
rdata<=8'b0;
slk<=1'b1;
sda<=1'b1;
i<=6'd0;
clkcount<=16'd0;
isask<=1'b0;
isdone<=1'b0;
end
else if(choose[0]) //代表开始写一个数的地址
begin
case(i)
0:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b1;
else if(clkcount==16'd350)
slk<=1'b0;
if(clkcount==16'd0)
sda<=1'b1;
else if(clkcount==16'd250)
sda<=1'b0;
end
//写设备地址
1,2,3,4,5,6,7,8:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=device[8-i];
end
//释放总线设为高阻态
9:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
sda<=1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
10: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
//写字节地址
11,12,13,14,15,16,17,18:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=address[18-i];
end
//释放总线设为高阻态
19:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
sda<=1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
20: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
21,22,23,24,25,26,27,28: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=data[28-i];
end
//释放总线设为高阻态
29:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
sda<=1'b0;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
30: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
31:begin
if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd200)
slk<=1'b1;
if(clkcount==16'd0)
sda<=1'b0;
else if(clkcount==16'd300)
sda<=1'b1;
end
32: begin i<=i+1'b1;
isdone<=1'b1;
end
33: begin i<=6'd0;
isdone<=1'b0;
end
endcase
end
else if(choose[1])
begin
case(i)
0: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b1;
else if(clkcount==16'd350)
slk<=1'b0;
if(clkcount==16'd0)
sda<=1'b1;
else if(clkcount==16'd250)
sda<=1'b0;
end
//写设备地址
1,2,3,4,5,6,7,8:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=device[8-i];
end
//释放总线设为高阻态
9:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
sda<=1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
10: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
//写字节地址
11,12,13,14,15,16,17,18:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=address[18-i];
end
//释放总线设为高阻态
19:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
sda<=1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
20: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
//再来一次起始信号
21: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b1;
else if(clkcount==16'd350)
slk<=1'b0;
if(clkcount==16'd0)
sda<=1'b1;
else if(clkcount==16'd250)
sda<=1'b0;
end
22,23,24,25,26,27,28: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=device[29-i];
end
29: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
//最后一位表示读
sda<=1'b1;
end
//释放总线设为高阻态
30:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
//总线重新让主机掌握
//sda<=1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'bz;
if(clkcount==16'd200)
isask<=sda;
end
31: if(isask!=0)
i<=6'd0;
else
i<=i+1'b1;
//开始读数据
32,33,34,35,36,37,38,39: begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
if(clkcount==16'd200)
rdata[39-i]<=sda;
sda<=1'bz;
end
//释放总线设为高阻态
40:begin if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd100)
slk<=1'b1;
else if(clkcount==16'd300)
slk<=1'b0;
sda<=1'b0;
end
41: begin
if(clkcount==16'd499)
begin
clkcount<=16'd0;
i<=i+1'b1;
end
else
clkcount<=clkcount+1'b1;
if(clkcount==16'd0)
slk<=1'b0;
else if(clkcount==16'd200)
slk<=1'b1;
if(clkcount==16'd0)
sda<=1'b0;
else if(clkcount==16'd300)
sda<=1'b1;
end
42: begin i<=i+1'b1;
isdone<=1'b1;
end
43: begin i<=6'd0;
isdone<=1'b0;
end
endcase
end
endmodule
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/09/17 10:35:01
// Design Name:
// Module Name: iic_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic_test(
clk,
rst,
led,
slk,
sda_a
);
input clk;
input rst;
output reg [3:0]led;
output slk;
inout sda_a;
wire isdone;
wire [7:0]rdata;
iic(
.clk(clk),
.rst(rst),
.choose(choose),
.device(device),
.address(address),
.data(data),
.rdata(rdata),
.slk(slk),
.sda_a(sda_a),
.isdone(isdone)
);
reg [1:0]choose;
reg [2:0]i;
reg [7:0]device;
reg [7:0]address;
reg [7:0]data;
reg [27:0]count;
always@(posedge clk or negedge rst)
if(!rst)
begin
choose<=2'b00;
i<=3'b0;
device<=8'b0;
address<=8'b0;
data<=8'b0;
led<=4'd0;
count<=28'd0;
end
else
case(i)
0: if(isdone)
begin
choose<=2'b00;
i<=i+1'b1;
end
else
begin
choose<=2'b01;
device<=8'b10100000;
address<=8'b00000000;
data<=8'h4e;
end
1: if(isdone)
begin
choose<=2'b00;
i<=i+1'b1;
end
else
begin
choose<=2'b01;
device<=8'b10100000;
address<=8'b00000001;
data<=8'h3a;
end
2: if(isdone)
begin
choose<=2'b00;
i<=i+1'b1;
end
else
begin
choose<=2'b10;
device<=8'b10100000;
address<=8'b00000000;
//data<=8'h2a;
end
3:
if(count==28'd50000000)
begin
count<=28'd0;
i<=i+1'b1;
end
else
begin
led<=~rdata[3:0];
count<=count+1'b1;
end
4:
if(count==28'd50000000)
begin
count<=28'd0;
i<=i+1'b1;
end
else
begin
led<=~rdata[7:4];
count<=count+1'b1;
end
5: if(isdone)
begin
choose<=2'b00;
i<=i+1'b1;
end
else
begin
choose<=2'b10;
device<=8'b10100000;
address<=8'b00000001;
//data<=8'h2a;
end
6:
if(count==28'd50000000)
begin
count<=28'd0;
i<=i+1'b1;
end
else
begin
led<=~rdata[3:0];
count<=count+1'b1;
end
7:
begin
led<=~rdata[7:4];
end
endcase
//assign led=rdata[3:0];
endmodule
验证很简单,通过led指示灯变化来判断数据是否对。上电测试结果正确。