一文带你迅速了解常用串行总线之IIC协议1

电子说

1.3w人已加入

描述

01

IIC基础知识

集成电路总线 (Inter-Intergrated Circuit),通常称作IICBUS,简称为IIC,是一种采用多主从结构的串行通信总线。

IIC由PHILIPS公司于1980年推出,利用该总线可实现多主机系统所需的裁决和高低速设备同步等功能。

IIC串行总线一般有两根信号线,分别是时钟线SCL和数据线SDA,所有IIC总线各设备的串行数据SDA接到总线SDA上,时钟线SLC接到总线SCL上。

双向串行数据SDA:输出电路用于向总线发送数据,输入电路用于接收总线上的数据;

双向时钟数据SCL:输出电路用于向总线发送时钟信号,输入电路用于检测总线时钟电平以决定下次时钟脉冲电平时间。

各设备与总线相连的输出端采用高阻态以避免总线信号的混乱,通常采用漏极开路输出或集电极开路输出。总线空闲时,各设备都是开漏输出,通常可采用上拉电阻使总线保持高电平,而任一设备输出低电平都将拉低相应的总线信号。

SDA

IIC总线上的设备可分为主设备和从设备两种:主设备有权利主动发起/结束一次通信;从设备只能被动响应。

所有连接在IIC总线上的设备都有各自唯一的地址(原地址位宽为7bits,改进后采用10bits位宽),每个设备都可以作为主设备或从设备,但是同一时刻只能有一个主设备控制。

如果总线上有多个设备同时启用总线,IIC通过检测和仲裁机制解决传输冲突。IIC允许连接的设备传输速率不同,多台设备之间时钟信号的同步过程称为同步化。

02

IIC传输协议

2.1 IIC协议模式

IIC协议为半双工模式,同一条数据线上完成读/写操作,不同操作时的数据帧格式可分为写操作、读操作、读写操作。

2.1.1 写操作数据帧格式

主机向从机写数据的数据帧格式如下:

SDA

白色为主机发送数据,灰色为从机发送数据。

  1. 主机发送开始信号S;
  2. 主机发送地址数据ADDR;
  3. 主机发送写信号0;
  4. 从机响应主机写信号ACK;
  5. 主机发送数据信息DATA;
  6. 从机响应主机发送数据ACK;
  7. 循环5 6步骤可完成主机向从机连续写数据过程;
  8. 主机发送结束信号P.

2.1.2 读操作数据帧格式

主机从从机读数据的数据帧格式如下:

SDA

白色为主机发送数据,灰色为从机发送数据。

  1. 主机发送开始信号S;
  2. 主机发送地址数据ADDR;
  3. 主机发送读信号1;
  4. 从机响应主机读信号ACK;
  5. 从机发送数据信息DATA;
  6. 主机响应从机发送数据ACK;
  7. 循环5 6步骤可完成主机向从机连续读数据过程;
  8. 主机发送结束信号P.

2.1.3 读写同时操作数据帧格式

主机先向从机写数据后从从机读数据的数据帧格式如下:

SDA

白色为主机发送数据,灰色为从机发送数据。

主机可以在完成写操作后不发送结束信号P,直接进行读操作。该过程主机不可变,而从机可以通过发送不同地址选择不同的从机。

2.2 IIC写时序

IIC协议写时序可分为单字节写时序和连续写时序:

2.2.1 单字节写时序

单字节地址写时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送单字节寄存器地址信息WORD ADDRESS;
  5. 从机响应主机发送寄存器地址ACK;
  6. 主机发送单字节数据信息DATA;
  7. 从机响应主机发送数据信息ACK;
  8. 主机发送结束信号P.

双字节地址写时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;
  5. 从机响应主机发送寄存器地址高字节ACK;
  6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;
  7. 从机响应主机发送寄存器地址低字节ACK;
  8. 主机发送单字节数据信息DATA;
  9. 从机响应主机发送数据信息ACK;
  10. 主机发送结束信号P.

2.2.2 连续写时序

单字节地址写时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送单字节寄存器地址信息WORD ADDRESS;
  5. 从机响应主机发送寄存器地址ACK;
  6. 主机发送单字节数据信息DATA;
  7. 从机响应主机发送数据信息ACK;
  8. 循环6 7步骤可以连续向从机写数据DATA(D+n);
  9. 主机发送结束信号P.

双字节地址写时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;
  5. 从机响应主机发送寄存器地址高字节ACK;
  6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;
  7. 从机响应主机发送寄存器地址低字节ACK;
  8. 主机发送单字节数据信息DATA;
  9. 从机响应主机发送数据信息ACK;
  10. 循环8 9步骤可以连续向从机写数据DATA(D+n);
  11. 主机发送结束信号P.

2.3 IIC读时序

IIC协议读时序可分为单字节读时序和连续读时序:

2.3.1 单字节读时序

单字节地址读时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送单字节寄存器地址信息WORD ADDRESS;
  5. 从机响应主机发送寄存器地址ACK;
  6. 主机发送开始信号S;
  7. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;
  8. 从机响应主机控制字节ACK;
  9. 从机发送单字节数据信息DATA;
  10. 主机响应从机发送数据信息NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);
  11. 主机发送结束信号P.

双字节地址读时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;
  5. 从机响应主机发送寄存器地址高字节ACK;
  6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;
  7. 从机响应主机发送寄存器地址低字节ACK;
  8. 主机发送开始信号S;
  9. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;
  10. 从机响应主机控制字节ACK;
  11. 从机发送单字节数据信息DATA;
  12. 主机响应从机发送数据信息NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);
  13. 主机发送结束信号P.

2.3.2 连续读时序

单字节地址读时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送单字节寄存器地址信息WORD ADDRESS;
  5. 从机响应主机发送寄存器地址ACK;
  6. 主机发送开始信号S;
  7. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;
  8. 从机响应主机控制字节ACK;
  9. 从机发送单字节数据信息DATA;
  10. 主机响应从机发送数据信息ACK;
  11. 循环9 10步骤可完成主机向从机连续读数据过程,读取最后一个字节数据时主机应响应NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);
  12. 主机发送结束信号P.

双字节地址读时序过程:

SDA

  1. 主机发送开始信号S;
  2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;
  3. 从机响应主机控制字节ACK;
  4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;
  5. 从机响应主机发送寄存器地址高字节ACK;
  6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;
  7. 从机响应主机发送寄存器地址低字节ACK;
  8. 主机发送开始信号S;
  9. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;
  10. 从机响应主机控制字节ACK;
  11. 从机发送单字节数据信息DATA;
  12. 主机响应从机发送数据信息ACK;
  13. 循环11 12步骤可完成主机向从机连续读数据过程,读取最后一个字节数据时主机应响应NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);
  14. 主机发送结束信号P.

03

IIC代码实现

3.1 IIC目标实现功能

设计一个IIC模块,具体要求如下:

设计一个IIC协议提供给主设备,通过查找表实现对从设备寄存器进行配置。按查找表顺序lut_index依次对设备地址为lut_dev_addr的从设备寄存器进行配置,为lut_reg_addr配置寄存器数据lut_reg_data,同时将传输异常和传输结束信号引出以对传输过程进行监控。模块的定义如下:

module i2c(

input rst, //复位信号

input clk, //时钟信号

input[15:0] clk_div_cnt, //时钟计数器

input i2c_addr_2byte, //双字节地址

output reg[9:0] lut_index, //查找表顺序号

input[7:0] lut_dev_addr, //从设备地址

input[15:0] lut_reg_addr, //寄存器地址

input[7:0] lut_reg_data, //寄存器数据

output reg error, //传输异常信号

output done, //传输结束信号

inout i2c_scl, //IIC时钟信号

inout i2c_sda //IIC数据信号

);

3.2 Verilog代码

1. 顶层模块 (i2c):

module i2c(

input rst,

input clk,

input[15:0] clk_div_cnt,

input i2c_addr_2byte,

output reg[9:0] lut_index,

input[7:0] lut_dev_addr,

input[15:0] lut_reg_addr,

input[7:0] lut_reg_data,

output reg error,

output done,

inout i2c_scl,

inout i2c_sda

);

wire scl_pad_i;

wire scl_pad_o;

wire scl_padoen_o;

wire sda_pad_i;

wire sda_pad_o;

wire sda_padoen_o;

assign sda_pad_i = i2c_sda;

assign i2c_sda = ~sda_padoen_o ? sda_pad_o : 1'bz;

assign scl_pad_i = i2c_scl;

assign i2c_scl = ~scl_padoen_o ? scl_pad_o : 1'bz;

reg i2c_read_req;

wire i2c_read_req_ack;

reg i2c_write_req;

wire i2c_write_req_ack;

wire[7:0] i2c_slave_dev_addr;

wire[15:0] i2c_slave_reg_addr;

wire[7:0] i2c_write_data;

wire[7:0] i2c_read_data;

wire err;

reg[2:0] state;

localparam S_IDLE = 0;

localparam S_WR_I2C_CHECK = 1;

localparam S_WR_I2C = 2;

localparam S_WR_I2C_DONE = 3;

assign done = (state == S_WR_I2C_DONE);

assign i2c_slave_dev_addr = lut_dev_addr;

assign i2c_slave_reg_addr = lut_reg_addr;

assign i2c_write_data = lut_reg_data;

//cascatrix carson

always@(posedge clk or posedge rst)

begin

if(rst)

begin

state <= S_IDLE;

error <= 1'b0;

lut_index <= 8'd0;

end

else

case(state)

S_IDLE:

begin

    state <= S_WR_I2C_CHECK;

    error <= 1'b0;

    lut_index <= 8'd0;

end

S_WR_I2C_CHECK:

begin

    if(i2c_slave_dev_addr != 8'hff)

    begin

        i2c_write_req <= 1'b1;

        state <= S_WR_I2C;

    end

    else

    begin

        state <= S_WR_I2C_DONE;

    end

end

S_WR_I2C:

begin

    if(i2c_write_req_ack)

    begin

        error <= err ? 1'b1 : error; 

        lut_index <= lut_index + 8'd1;

        i2c_write_req <= 1'b0;

        state <= S_WR_I2C_CHECK;

    end

end

S_WR_I2C_DONE:

begin

    state <= S_WR_I2C_DONE;

end

default:

    state <= S_IDLE;

endcase

end

i2c_ctrl i2c_ctrl

(

.rst(rst),

.clk(clk),

.clk_div_cnt(clk_div_cnt),

// I2C signals

// i2c clock line

.scl_pad_i(scl_pad_i), // SCL-line input

.scl_pad_o(scl_pad_o), // SCL-line output (always 1'b0)

.scl_padoen_o(scl_padoen_o), // SCL-line output enable (active low)

// i2c data line

.sda_pad_i(sda_pad_i), // SDA-line input

.sda_pad_o(sda_pad_o), // SDA-line output (always 1'b0)

.sda_padoen_o(sda_padoen_o), // SDA-line output enable (active low)

.i2c_read_req(i2c_read_req),

.i2c_addr_2byte(i2c_addr_2byte),

.i2c_read_req_ack(i2c_read_req_ack),

.i2c_write_req(i2c_write_req),

.i2c_write_req_ack(i2c_write_req_ack),

.i2c_slave_dev_addr(i2c_slave_dev_addr),

.i2c_slave_reg_addr(i2c_slave_reg_addr),

.i2c_write_data(i2c_write_data),

.i2c_read_data(i2c_read_data),

.error(err)

);

endmodule

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分