I2C(Inter-Integrated Circuit)中文名称“集成电路总线”,是一种串行同步半双工总线。
I2C总线是由Philips公司开发的一种简单,双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传达信息,一根是数据线(SDA),一根是时钟线(SCL)。
I2C总线可实现多主机,多从机之间的互联,具有低功耗,抗干扰,电源电压范围宽,工作温度范围广的特点。其通讯速率为:标准模式0-100kbit/s,快速模式0-400kbit/s,和高速模式0-3.4Mbit/s。可连接很多设备,总电容在400pF。
I2C结构:1, 设备之间互联只需要两根线(SDA,SCL)
2, I2C总线支持任何IC生产过程(NMOS CMOS双极性)
3, SDA双向数据线
4, SCL双向时钟线
5, 每个设备有且只有一个设备地址
6, 设备分为主机(master)和从机,主机允许输出时钟信号
7, 主机发送信号时,发送时钟和数据,当主机发送结束后,数据线高阻,主机继续发送时钟,从机发送数据给主机
I2C硬件连接:1, I2C只使用两条漏极开路(Open Drain)SDA及SCL并利用电阻将电平上拉。
2, VDD通常为+5V或者是+3.3V
3, 逻辑0 = 低电平
4, 逻辑1 = 高点平
I2C总线协议:
写格式:1,主机发start位
2,主机发从机地址和写命令
3,从机回ack
4,主机发寄存器地址
5,从机回ack
6,主机发数据
7,从机回ack
8,主机发stop位
读格式:1,主机发atart位
2,主机发从机地址和写命令
3,从机回ack
4,主机发寄存器地址
5,从机回ack
6,主机发restart位
7,主机发从机地址和读命令
8,从机回ack
9,从机回数据
10,主机发nack
11,主机发stop位
I2C驱动模块:
`define IIC_FREQ100K 500 //频率100k
`define IIC_FREQ400K 125 //频率400k
module i2c_driver #(
parameter IIC_CNTMAX = `IIC_FREQ100K
)
(
input clk,
input rst_n,
input wire [1:0] cmd, //2'b00-无操作 2'b01-写操作 2'b10-读操作
input wire [6:0] device_addr, //从机地址
input wire [7:0] reg_addr, //寄存器地址
input wire [7:0] wr_data, //写入的数据
output reg [7:0] rd_data, //读出的数据
output reg done, //结束脉冲
output reg err, //报错信号
output reg scl, //时钟线
inout sda //数据线
);
reg sda_sel;
reg sda_reg;
assign sda = sda_sel ? sda_reg : 1'bz;
localparam IIC_CNTMAX_4_1 = IIC_CNTMAX/4-1,
IIC_CNTMAX_4_2 = IIC_CNTMAX/2-1,
IIC_CNTMAX_4_3 = (IIC_CNTMAX/4)*3-1,
IIC_CNTMAX_4_4 = IIC_CNTMAX-1;
reg [25:0] cnt; //时钟计数器
reg [25:0] delay_cnt; //延迟计数器
reg ack; //寄存一个从机回的ack信号
reg [7:0] data_reg; //定义一个八位的数据寄存器
reg [3:0] num; //定义个计数num
reg [3:0] state;
reg [3:0] back_state;
localparam FSM_START = 0, //主机发start位
FSM_DEVICE_ADDR_WR_RD = 1, //主机发从机地址和写命令
FSM_ACK = 2, //从机回ack
FSM_WR_REG_ADDR = 3, //主机发寄存器地址
FSM_WR_DATA = 4, //主机发数据
FSM_RESTART = 5, //主机发restart位
FSM_RD_DATA = 6, //从机回数据
FSM_NACK = 7, //主机发nack
FSM_STOP = 8, //主机发stop位
FSM_DELAY = 9; //延迟20us
reg [1:0] scl_state; //定义个时钟线状态
always @(posedge clk or negedge rst_n)
if (!rst_n)
cnt <= 0;
else if (state == FSM_DELAY)
cnt <= 0;
else if (cmd == 2'b01 || cmd == 2'b10)
begin
if (cnt == IIC_CNTMAX_4_4)
cnt <= 0;
else cnt <= cnt + 1;
end
else cnt <= 0;
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
scl <= 1;
scl_state <= 0;
end
else if (cmd == 2'b01 || cmd == 2'b10)
case (scl_state)
0 : if (cnt == 0)
scl <= 1;
else if (cnt == IIC_CNTMAX_4_3)
scl <= 0;
else if (cnt == IIC_CNTMAX_4_4)
scl_state <= 1;
else scl_state <= 0;
1 : if ((back_state == FSM_STOP && state == FSM_ACK && cnt == IIC_CNTMAX_4_4) || (state == FSM_NACK && cnt == IIC_CNTMAX_4_4))
scl_state <= 2;
else if (cnt == 0)
scl <= 0;
else if (cnt == IIC_CNTMAX_4_1)
scl <= 1;
else if (cnt == IIC_CNTMAX_4_3)
scl <= 0;
else scl_state <= 1;
2 : if (cnt == 0)
scl <= 0;
else if (cnt == IIC_CNTMAX_4_1)
scl <= 1;
else if (cnt == IIC_CNTMAX_4_4)
scl_state <= 3;
else scl_state <= 2;
3 : if (delay_cnt == 999)
scl_state <= 0;
else scl_state <= 3;
default : begin scl <= 1; scl_state <= 0; end
endcase
else begin
scl <= 1;
scl_state <= 0;
end
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
state <= FSM_START;
back_state <= 0;
rd_data <= 0;
done <= 0;
sda_sel <= 1;
sda_reg <= 1;
delay_cnt <= 0;
ack <= 1;
data_reg <= 0;
num <= 0;
err <= 0;
end
else if (cmd == 2'b01 || cmd == 2'b10)
case (state)
FSM_START : begin
done <= 0;
sda_sel <= 1;
data_reg <= {device_addr,1'b0};
back_state <= FSM_WR_REG_ADDR;
if (cnt == 0)
sda_reg <= 1;
else if (cnt == IIC_CNTMAX_4_2)
sda_reg <= 0;
if (cnt == IIC_CNTMAX_4_4)
state <= FSM_DEVICE_ADDR_WR_RD;
else state <= FSM_START;
end
FSM_DEVICE_ADDR_WR_RD : begin
sda_sel <= 1;
if (cnt == 0)
sda_reg <= data_reg[7-num];
if (cnt == IIC_CNTMAX_4_4 && num == 7)
begin
num <= 0;
state <= FSM_ACK;
end
else if (cnt == IIC_CNTMAX_4_4)
num <= num + 1;
else state <= FSM_DEVICE_ADDR_WR_RD;
end
FSM_ACK : begin
sda_sel <= 0;
sda_reg <= 1;
if (cnt == IIC_CNTMAX_4_2)
ack <= sda;
if (cnt == IIC_CNTMAX_4_4)
state <= back_state;
else state <= FSM_ACK;
end
FSM_WR_REG_ADDR : begin
if (!ack)
begin
sda_sel <= 1;
if (cmd == 2'b01)
back_state <= FSM_WR_DATA;
else if (cmd == 2'b10)
back_state <= FSM_RESTART;
if (cnt == 0)
sda_reg <= reg_addr[7-num];
if (cnt == IIC_CNTMAX_4_4 && num == 7)
begin
num <= 0;
state <= FSM_ACK;
end
else if (cnt == IIC_CNTMAX_4_4)
num <= num + 1;
else state <= FSM_WR_REG_ADDR;
end
else begin
err <= 1;
state <= FSM_START;
end
end
FSM_WR_DATA : begin
if (!ack)
begin
sda_sel <= 1;
back_state <= FSM_STOP;
if (cnt == 0)
sda_reg <= wr_data[7-num];
if (cnt == IIC_CNTMAX_4_4 && num == 7)
begin
num <= 0;
state <= FSM_ACK;
end
else if (cnt == IIC_CNTMAX_4_4)
num <= num + 1;
else state <= FSM_WR_DATA;
end
else begin
err <= 1;
state <= FSM_START;
end
end
FSM_RESTART : begin
if (!ack)
begin
sda_sel <= 1;
data_reg <= {device_addr,1'b1};
back_state <= FSM_RD_DATA;
if (cnt == 0)
sda_reg <= 1;
else if (cnt == IIC_CNTMAX_4_2)
sda_reg <= 0;
if (cnt == IIC_CNTMAX_4_4)
state <= FSM_DEVICE_ADDR_WR_RD;
else state <= FSM_RESTART;
end
else begin
err <= 1;
state <= FSM_START;
end
end
FSM_RD_DATA : begin
if (!ack)
begin
sda_sel <= 0;
if (cnt == IIC_CNTMAX_4_2)
rd_data[7-num] <= sda;
if (cnt == IIC_CNTMAX_4_4 && num == 7)
begin
num <= 0;
state <= FSM_NACK;
end
else if (cnt == IIC_CNTMAX_4_4)
num <= num + 1;
else state <= FSM_RD_DATA;
end
else begin
err <= 1;
state <= FSM_START;
end
end
FSM_NACK : begin
sda_sel <= 1;
sda_reg <= 1;
if (cnt == IIC_CNTMAX_4_4)
state <= FSM_STOP;
else state <= FSM_NACK;
end
FSM_STOP : begin
sda_sel <= 1;
if (cnt == 0)
sda_reg <= 0;
else if (cnt == IIC_CNTMAX_4_2)
sda_reg <= 1;
if (cnt == IIC_CNTMAX_4_4)
state <= FSM_DELAY;
else state <= FSM_STOP;
end
FSM_DELAY : if (delay_cnt == 999) //延迟20us
begin
delay_cnt <= 0;
done <= 1;
state <= FSM_START;
end
else delay_cnt <= delay_cnt + 1;
default : state <= FSM_START;
endcase
else
begin
state <= FSM_START;
back_state <= 0;
rd_data <= rd_data;
done <= 0;
sda_sel <= 1;
sda_reg <= 1;
delay_cnt <= 0;
ack <= 1;
data_reg <= 0;
num <= 0;
err <= 0;
end
endmodule