模塊特點
- 利用狀態機完成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
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/240563.html
標籤:其他
