IIC(Inter-Integrated Circuit,集成电路总线)是一种广泛使用的同步、半双工、多主从串行通信协议,由飞利浦(现恩智浦)公司开发,主要用于短距离板级设备间通信。以下是IIC协议的详细解析:
核心特点:
物理层结构:
IIC通信由起始信号、地址帧、数据帧、应答信号(ACK/NACK)、停止信号组成。
0xD0
(0x68 << 1 | 0)。11110
,后接2位高位地址和读写位;第二字节为剩余8位地址。以主设备向从设备(地址0x50)写入数据0xAA
为例:
0xA0
(0x50 << 1 | 0,写操作)。0xAA
。时钟同步:
仲裁机制:
重复起始条件(Repeated Start):
模式 | 速率 | 应用场景 |
---|---|---|
标准模式(Standard) | 100 kbps | 通用低速设备(如EEPROM) |
快速模式(Fast) | 400 kbps | 传感器、实时时钟 |
高速模式(High-Speed) | 3.4 Mbps | 高速数据传输 |
///代码说明/
///1.此代码仅通过modelsim仿真,无实际上板调试///
///2.此代码可能存在与芯片时序不匹配,如需使用请根据芯片数据手册时序图调整///
module i2c_driver (
input wire clk , // 系统时钟(如50MHz)
input wire rst , // 异步复位(高电平有效)
// I2C物理接口
output reg i2c_scl , // I2C时钟线
inout wire i2c_sda , // I2C数据线
// 用户控制接口
input wire start , // 启动传输信号(上升沿触发)
input wire [7:0] restart_num , // 重发起始信号位置设置,读数据时需要在发送地址后重新发送start信号
input wire [7:0] wr_num , // IIC待发送数据字节个数
input wire [7:0] rd_num , // IIC接收字节个数
input wire wr_en , // IIC待发送数据写入使能
input wire [7:0] wr_data , // IIC待发送数据
input wire rd_en , // IIC接收数据fifo读取
output wire [7:0] rd_data , // IIC接收数据
output wire rd_data_ready , // IIC接收数据准备完成
output wire i2c_busy // IIC忙状态
);
parameter CLK_DIV = 500 ; // 50MHz / (100kHz * 2) = 250 → 实际SCL为50MHz/(2*CLK_DIV)=50kHz
parameter START_DIV = 200 ; // 50MHz / (100kHz * 2) = 250 → 实际SCL为50MHz/(2*CLK_DIV)=50kHz
parameter IDLE = 8'b00000000 ; ///空闲状态
parameter START = 8'b00000001 ; ///起始位发送
parameter SEND_DATA = 8'b00000010 ; ///发送数据
parameter SD_ACK = 8'b00000100 ; ///发送数据等待ACK
parameter READ_DATA = 8'b00001000 ; ///接收数据
parameter RD_ACK = 8'b00010000 ; ///接收数据返回ACK
parameter STOP = 8'b00100000 ; ///发送停止位
parameter RESTART_DLY = 8'b01000000 ; ///重新发送起始位延时
reg [7:0] cur_state ;
reg [7:0] next_state ;
reg start_d ;
reg start_d1 ;
wire start_p ; //IIC流程开始信号
reg start_done ; //IIC起始位发送完成
reg send_done ; //IIC单字节数据发送完成
reg ack_sdone0 ; //IIC发送数据接收ACK正常,发送字节未完成
reg ack_sdone1 ; //IIC发送数据接收ACK正常,发送字节完成
reg ack_sdone2 ; //IIC发送数据接收ACK正常,需重新发送起始位
reg ack_sdone3 ; //IIC发送数据接收ACK正常,发送字节完成,无读取数据操作
reg read_done ; //IIC单字节数据接收完成
reg ack_rdone0 ; //IIC单字节数据接收未达到目标字节数,同时返回ACK完毕
reg ack_rdone1 ; //IIC单字节数据接收达到目标字节数,同时返回ACK完毕
reg stop_done ; //IIC发送接收字节完毕后发送停止位完成
reg [7:0] send_bit_cnt ; //发送bit数据计数
reg [7:0] send_byte_cnt ; //发送字节数据计数
reg [7:0] read_bit_cnt ; //接收bit数据计数
reg [7:0] read_byte_cnt ; //接收字节数据计数
reg rd_sdata ; //读取发送数据使能
wire [8:0] send_data ; //发送字节数据
reg wr_rdata ; //写入接收数据使能
reg [7:0] read_data ; //接收字节数据
reg sda_out ; //SDA管脚IO状态,0:输入;1:输出
reg sda_reg ; //SDA输出值
wire clk_middle ; //分频中间态
reg [31:0] clk_cnt ; //分频计数
reg [31:0] start_cnt ; //起始位保持时间计数
reg restart_done ; //重新发送起始位等待完成
send_data_fifo u_send_data_fifo(//发送数据fifo,需使用First_Word Fall_Through模式
.Data (wr_data ), //input [7:0] Data
.Clk (clk ), //input Clk
.WrEn (wr_en ), //input WrEn
.RdEn (rd_sdata ), //input RdEn
.Reset (rst ), //input Reset
.Q (send_data[7:0] ), //output [7:0] Q
.Empty ( ), //output Empty
.Full ( ) //output Full
);
assign send_data[8] = 1'b0 ;
assign start_p = ~start_d1 && start_d ;
assign i2c_busy = (cur_state == IDLE) ? 1'b0 : 1'b1 ;
assign clk_middle = (clk_cnt == CLK_DIV/2) ? 1'b1 : 1'b0 ;
assign i2c_sda = sda_out ? sda_reg : 1'bz ;
always @(posedge clk or posedge rst) begin
if(rst) begin
start_d <= 1'b0 ;
start_d1<= 1'b0 ;
end
else begin
start_d <= start ;
start_d1<= start_d;
end
end
/主状态机
always @(posedge clk or posedge rst) begin
if (rst)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @(*) begin
if(rst)
next_state <= IDLE ;
else begin
case (cur_state)
IDLE :
next_state <= start_p ? START : IDLE ;
START :
next_state <= start_done ? SEND_DATA : START ;
SEND_DATA :
next_state <= send_done ? SD_ACK : SEND_DATA ;
SD_ACK :
next_state <= ack_sdone0 ? SEND_DATA :
ack_sdone1 ? READ_DATA :
ack_sdone2 ? RESTART_DLY :
ack_sdone3 ? STOP : SD_ACK ;
READ_DATA :
next_state <= read_done ? RD_ACK : READ_DATA ;
RD_ACK :
next_state <= ack_rdone0 ? READ_DATA :
ack_rdone1 ? STOP : RD_ACK ;
STOP :
next_state <= stop_done ? IDLE : STOP ;
RESTART_DLY :
next_state <= restart_done ? START : RESTART_DLY ;
default :
next_state <= IDLE ;
endcase
end
end
重新发送起始位延时计数
always @(posedge clk or posedge rst) begin
if (rst)
start_cnt <= 32'd0;
else if(cur_state == RESTART_DLY)
start_cnt <= start_cnt + 1'b1;
else
start_cnt <= 32'd0;
end
重新发送起始位延时完成
always @(posedge clk or posedge rst) begin
if (rst)
restart_done <= 1'b0;
else if(start_cnt == (CLK_DIV - 1'b1))
restart_done <= 1'b1;
else
restart_done <= 1'b0;
end
起始位发送完成
always @(posedge clk or posedge rst) begin
if (rst)
start_done <= 1'b0;
else if((clk_cnt == (CLK_DIV - 1'b1))&&(cur_state == START))
start_done <= 1'b1;
else
start_done <= 1'b0;
end
分频处理
always @(posedge clk or posedge rst) begin
if (rst)
clk_cnt <= 32'd0;
else if(cur_state == IDLE)
clk_cnt <= 32'd0;
else if(clk_cnt < (CLK_DIV - 1'b1))
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 32'd0;
end
时钟处理
always @(posedge clk or posedge rst) begin
if (rst)
i2c_scl <= 1'b1;
else if((cur_state == IDLE)||(cur_state == RESTART_DLY))
i2c_scl <= 1'b1;
else if(clk_cnt == (CLK_DIV - 1'b1))
i2c_scl <= ~ i2c_scl;
else
i2c_scl <= i2c_scl;
end
SDA方向处理
always @(posedge clk or posedge rst) begin
if (rst)
sda_out <= 1'b1;
else if(cur_state == SD_ACK)
sda_out <= 1'b0;
else
sda_out <= 1'b1;
end
SDA输出数据处理
always @(posedge clk or posedge rst) begin
if (rst)
sda_reg <= 1'b1;
else if(cur_state == START)
sda_reg <= 1'b0;
else if(cur_state == SEND_DATA)
sda_reg <= send_data[send_bit_cnt];
else if((cur_state == RD_ACK)&&(read_byte_cnt (CLK_DIV/2)) && i2c_scl)
sda_reg <= 1'b0;
else
sda_reg <= 1'b1;
end
SDA输出bit计数
always @(posedge clk or posedge rst) begin
if (rst)
send_bit_cnt <= 8'd8;
else if((cur_state == SEND_DATA) && ~i2c_scl && clk_middle )
send_bit_cnt <= send_bit_cnt - 1'b1 ;
else if((cur_state == SEND_DATA) &&(next_state == SD_ACK))
send_bit_cnt <= 8'd8;
else
send_bit_cnt <= send_bit_cnt;
end
SDA输出字节完成
always @(posedge clk or posedge rst) begin
if (rst)
send_done <= 1'b0;
else if((cur_state == SEND_DATA) && i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (send_bit_cnt == 8'd0))
send_done <= 1'b1 ;
else
send_done <= 1'b0;
end
SDA输出byte计数
always @(posedge clk or posedge rst) begin
if (rst)
send_byte_cnt <= 8'd0;
else if((cur_state == SEND_DATA) &&(next_state == SD_ACK) )
send_byte_cnt <= send_byte_cnt + 1'b1 ;
else if(cur_state == STOP)
send_byte_cnt <= 8'd0;
else
send_byte_cnt <= send_byte_cnt;
end
SDA输出数据切换使能
always @(posedge clk or posedge rst) begin
if (rst)
rd_sdata <= 1'b0 ;
else if((cur_state == SEND_DATA) &&(next_state == SD_ACK) )
rd_sdata <= 1'b1 ;
else
rd_sdata <= 1'b0 ;
end
SDA输入bit计数
always @(posedge clk or posedge rst) begin
if (rst)
read_bit_cnt <= 8'd0;
else if((cur_state == READ_DATA) && i2c_scl && clk_middle )
read_bit_cnt <= read_bit_cnt + 1'b1 ;
else if((cur_state == READ_DATA) &&(next_state == RD_ACK))
read_bit_cnt <= 8'd0;
else
read_bit_cnt <= read_bit_cnt;
end
SDA输入bit计数
always @(posedge clk or posedge rst) begin
if (rst)
read_byte_cnt <= 8'd0;
else if((cur_state == READ_DATA)&&(next_state == RD_ACK))
read_byte_cnt <= read_byte_cnt + 8'd1;
else if(cur_state == STOP)
read_byte_cnt <= 8'd0;
else
read_byte_cnt <= read_byte_cnt;
end
SDA输出bit计数
always @(posedge clk or posedge rst) begin
if (rst)
read_done <= 1'b0;
else if((cur_state == READ_DATA) && ~i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (read_bit_cnt == 8'd8))
read_done <= 1'b1 ;
else
read_done <= 1'b0;
end
SDA输入数据处理
always @(posedge clk or posedge rst) begin
if (rst)
read_data <= 8'h0;
else if((cur_state == READ_DATA)&& i2c_scl && clk_middle)
read_data <= {read_data[6:0],i2c_sda};
else
read_data <= read_data;
end
SDA输入数据写入使能
always @(posedge clk or posedge rst) begin
if (rst)
wr_rdata <= 1'b0 ;
else if((cur_state == READ_DATA) &&(next_state == RD_ACK) )
wr_rdata <= 1'b1 ;
else
wr_rdata <= 1'b0 ;
end
sack输入数据处理
reg ack_signle;
always @(posedge clk or posedge rst) begin//ACK信号提取
if (rst)
ack_signle <= 1'b0;
else if((cur_state == SD_ACK)&& i2c_scl && clk_middle && ~i2c_sda)
ack_signle <= 1'b1;
else if(cur_state != SD_ACK)
ack_signle <= 1'b0;
else
ack_signle <= ack_signle;
end
always @(posedge clk or posedge rst) begin//当前发送字节数小于设定字节数,同时不等于重新发送起始位字节数
if (rst)
ack_sdone0 <= 1'b0;
else if((cur_state == SD_ACK)&& ack_signle && i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (send_byte_cnt < wr_num) && (send_byte_cnt != restart_num))
ack_sdone0 <= 1'b1;
else
ack_sdone0 <= 1'b0;
end
always @(posedge clk or posedge rst) begin//当前发送字节数等于设定字节数
if (rst)
ack_sdone1 <= 1'b0;
else if((cur_state == SD_ACK)&& ack_signle && i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (send_byte_cnt == wr_num) && (rd_num != 8'd0))
ack_sdone1 <= 1'b1;
else
ack_sdone1 <= 1'b0;
end
always @(posedge clk or posedge rst) begin//当前发送字节数等于重新发送起始位字节数
if (rst)
ack_sdone2 <= 1'b0;
else if((cur_state == SD_ACK)&& ack_signle && i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (send_byte_cnt == restart_num))
ack_sdone2 <= 1'b1;
else
ack_sdone2 <= 1'b0;
end
always @(posedge clk or posedge rst) begin//当前发送字节数等于设定字节数,同时无读取操作
if (rst)
ack_sdone3 <= 1'b0;
else if((cur_state == SD_ACK)&& ack_signle && i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (send_byte_cnt == wr_num) && (rd_num == 8'd0))
ack_sdone3 <= 1'b1;
else
ack_sdone3 <= 1'b0;
end
rack输入数据处理
always @(posedge clk or posedge rst) begin//当前接收字节数小于设置值
if (rst)
ack_rdone0 <= 1'b0;
else if((cur_state == RD_ACK)&& i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (read_byte_cnt < rd_num))
ack_rdone0 <= 1'b1;
else
ack_rdone0 <= 1'b0;
end
always @(posedge clk or posedge rst) begin//当前接收字节数等于设置值
if (rst)
ack_rdone1 <= 1'b0;
else if((cur_state == RD_ACK)&& i2c_scl && (clk_cnt == (CLK_DIV - 1'b1)) && (read_byte_cnt == rd_num))
ack_rdone1 <= 1'b1;
else
ack_rdone1 <= 1'b0;
end
///STOP状态处理///
always @(posedge clk or posedge rst) begin//当前接收字节数等于设置值
if (rst)
stop_done <= 1'b0;
else if((cur_state == STOP) && (clk_cnt == (CLK_DIV -1'b1)) && i2c_scl)
stop_done <= 1'b1;
else
stop_done <= 1'b0;
end
read_data_fifo u_read_data_fifo(//接收数据fifo
.Data (read_data ), //input [7:0] Data
.Clk (clk ), //input Clk
.WrEn (wr_rdata ), //input WrEn
.RdEn (rd_en ), //input RdEn
.Reset (rst ), //input Reset
.Q (rd_data ), //output [7:0] Q
.Empty (rd_data_ready ), //output Empty
.Full ( ) //output Full
);
endmodule