模块特点
- 利用状态机完成iic的驱动,将重复的状态进行合并,使代码更简单;
- 由于iic不能连续完成读写操作,在STOP状态后又加了一个DELAY (延迟)状态,延迟了5ms,可以保证程序不会卡死;
- 为体现iic的节能特点,使用了三态门,在空闲状态下,sda和scl线处于释放状态;
- 为了使一次通讯稳定运行,在通讯刚开始时对数据进行寄存,而且在通讯过程中不能发生变化;
iic协议读/写数据步骤
iic协议读数据时序图
iic协议写数据时序图
iic协议通讯驱动模块
下面展示一些 内联代码片
。
module i2c_driver(
input wire clk,
input wire rst_n,
input wire read,
input wire write,
input wire [7:0] dataw, //要写的数据
input wire [6:0] add, //从机地址
input wire [7:0] add_reg,//寄存器地址
inout wire sda,
output wire scl,
output reg done,
output reg [7:0] datar //接收到的数据
);
//sda线的三态门,空闲时释放
reg sda_sel;
reg sda_buf;
assign sda = sda_sel ? sda_buf : 1'bz ;
//scl线的三态门,空闲时释放
reg scl_sel;
reg scl_buf;
assign scl = scl_sel ? scl_buf : 1'bz ;
//assign scl_sel = !done ? 1'b1 : 1'b0;
reg [7:0] dataw_r;
reg [6:0] add_r;
reg [7:0] add_reg_r;
//数据缓存,只有空闲时可以缓存数据
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
dataw_r <=0;
add_r <=0;
add_reg_r <=0;
end
else if(( write || read ) && done==1)
begin
dataw_r <= dataw ;
add_r <= add ;
add_reg_r <= add_reg;
end
else
begin
dataw_r <= dataw_r ;
add_r <= add_r ;
add_reg_r <= add_reg_r;
end
localparam T_4_1 = 125 - 1 ; // 1/4周期
localparam T_4_2 = 250 - 1 ; // 2/4周期
localparam T_4_3 = 375 - 1 ; // 3/4周期
localparam T_4_4 = 500 - 1 ; // 4/4周期
reg [10:0] cnt;
always@(posedge clk or negedge rst_n)
if(rst_n==0)
cnt<=0;
else if((cnt==T_4_4) || (done==1)) //执行读写时计数,空闲时不计数
cnt<=0;
else
cnt<=cnt+1;
//建立scl线,执行读写时开始为1,建立100k时钟线
always@(posedge clk or negedge rst_n)
if(rst_n==0)
scl_buf<=1;
else if(done==1)
scl_buf<=1;
else if(cnt==T_4_2 || cnt==T_4_4)
scl_buf <= ~scl_buf;
else
scl_buf <= scl_buf;
localparam FSM_IDLE =0; //空闲状态
localparam FSM_START =1; //发送start位
localparam FSM_DATAW =2; //发送数据(从机地址+读/写,寄存器地址,数据)
localparam FSM_ACK =3; //接收ack
localparam FSM_RSTART =4; //发送restart位
localparam FSM_DATAR =5; //接收数据
localparam FSM_NACK =6; //发送nack
localparam FSM_STOP =7; //发送停止位
localparam FSM_DELAY =8; //延迟大约5ms
reg [3:0] cstate;
reg read_flag;
reg write_flag;
reg [7:0] datas;
reg [2:0] cs_flag;
reg [8:0] num;
reg flag;
always@(posedge clk or negedge rst_n)
if(rst_n==0)
begin
cstate<=FSM_IDLE;
read_flag<=0;
write_flag<=0;
cs_flag<=0;
flag<=0;
sda_sel<=0;
sda_buf<=1;
num<=0;
datas<=0;
scl_sel<=0;
done<=1;
end
else
case(cstate)
FSM_IDLE : begin
sda_sel<=0;
scl_sel<=0;
if(read)
begin
cstate<=FSM_START;
read_flag<=1;
end
else if(write)
begin
cstate<=FSM_START;
write_flag<=1;
end
else
cstate<=FSM_IDLE;
end
FSM_START : begin
scl_sel<=1;
done<=0;
if(cnt==T_4_1)
begin
sda_sel <= 1;
sda_buf <= 0;
datas<={add_r,1'b0};
cstate<=FSM_DATAW;
cs_flag<=1;
end
end
FSM_DATAW : begin
if(cnt==T_4_3)
begin
if(num<=7)
begin
sda_sel <= 1;
sda_buf <= datas[7-num];
num <= num+1;
end
else
begin
cstate<=FSM_ACK;
sda_sel <= 0;
num<=0;
end
end
end
FSM_ACK : begin
if(cnt==T_4_1)
if(sda==0)
begin
if(cs_flag==1)
begin
cstate<=FSM_DATAW;
datas<=add_reg_r;
cs_flag<=2;
end
else if(cs_flag==2)
begin
if(read_flag==1)
begin
cstate<=FSM_RSTART;
read_flag<=0;
end
else if(write_flag==1)
begin
cstate<=FSM_DATAW;
write_flag<=0;
datas<=dataw_r;
cs_flag<=3;
end
end
else if(cs_flag==3)
cstate<=FSM_STOP;
else if(cs_flag==4)
cstate<=FSM_DATAR;
end
end
FSM_RSTART : begin
if(cnt==T_4_1)
begin
sda_sel<=1;
sda_buf<=0;
cstate<=FSM_DATAW;
datas<={add_r,1'b1};
cs_flag<=4;
end
end
FSM_DATAR : begin
if( cnt==T_4_1 )
begin
if(num<=7)
begin
datar[7-num]<=sda;
num<=num+1;
end
else
begin
cstate<=FSM_NACK;
num<=0;
end
end
end
FSM_NACK : begin
if( cnt == T_4_3 )
begin
sda_sel<=1;
sda_buf<=1;
cstate<=FSM_STOP;
end
end
FSM_STOP : begin
if(cnt == T_4_3)
begin
sda_sel<=1;
sda_buf<=0;
flag <= 1;
end
else if(cnt == T_4_1 && flag == 1)
begin
sda_buf<=0;
flag <= 0;
cstate<=FSM_DELAY;
end
end
FSM_DELAY : begin
sda_sel<=0;
scl_sel<=0;
if(cnt == T_4_4 && num == 500)
begin
num<=0;
cstate<=FSM_IDLE;
done<=1;
end
else if(cnt == T_4_4)
num<=num+1;
end
default : begin
cstate<=FSM_IDLE;
read_flag<=0;
write_flag<=0;
cs_flag<=0;
flag<=0;
sda_sel<=0;
sda_buf<=1;
num<=0;
datas<=0;
scl_sel<=0;
done<=1;
end
endcase
endmodule