I2C之知(四)--I2C总线的7bit从机地址 数据传输时序的详细过程

时钟拉伸(Clock stretching)
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
从字节级来看,一个设备可能在快速模式下接受数据,但是需要更多的时间来存储接收到的字节或者准备将要传输的另一个字节.从机可以以一种握手的处理方式在接受和应答字节后将SCL线拉低来强制使得主机进入wait状态直到从机准备好下一个字节的传输.
从位级来看,I2C总线上的设备可以通过增长每一个时钟的低周期来降低总线时钟.所以每个主机可以适应这个设备的内部操作速率.
在Hs模式,握手处理只能用在字节级别.

从机地址和R/W bit
下图是数据传输的格式:

在开始条件(S)后,发送从机地址.地址是7bit,后面的第8bit是数据的读写bit,0表示写,1表示读.具体的看下图:

数据传输被主机产生的终止条件(P)终止.然而,主机也可以无需先产生终止条件,产生一个重复的开始条件(Sr)和寻址另一个从机,
可能的数据传输格式如下:
主-发送器传到从-接收器.传输方向不变.从-接收器应答每一个字节.如下:

在第一个字节后主机从从机读数据.第一个应答后,主-发送器变为主-接收器而从-接收器变为从-发送器.第一个应答仍然是由从机产生的.主机产生余下的应答.主机在产生终止条件之前要发送一个NA.如下:

复合模式.在传输过程中改变方向,开始条件和从机地址都要重复,而读写bit要取反.如果主-接收器发送一个重复的开始条件,那么它在这之前要发送一个NA.

注意:
1.复合模式可以在比如控制串行内存器时用到.在第一个数据字节时一定要写内存器内部的地址.开开始条件和从地址重复后,数据就开始传输了.
2.自动增加或减少之前访问的内存位置都由设备的设计者决定.
3.每一个字节后面都跟着一个应答位,在图中用A或非A来表示.
4.兼容I2C总线的设备在接收到开始条件或重复开始条件时都一定要重启它们的总线逻辑,即使开始条件都不是正确的格式,它们都期望发送从机地址.
5.开始条件后立马跟着一个终止条件是不合法的格式.很多设备在设计时考虑了这一点,可以处理.
6.连接到总线上的每个设备都由唯一的地址来确定.通常是简单的主从关系,但可能存在多个一样的从机可以同时接收和响应,比如说组播.这里是以NXP的PCA9546A作为例子说明.(PCA9546A是NXP半导体生产的一款基于I2C总线控制的4通道双向多路复用器和开关。使用PCA9546A可以将一路SCL/SDA输入扩展为4路SCL/SDA输出,在对内部控制寄存器进行相应配置后,可同时选择一路或多路下行I2C总线与上行I2C总线通信。)
 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个基本的Verilog代码示例,用于I2C总线的读取和写入。这里假设I2C总线的SCL时钟频率为100 kHz,可以根据需要进行修改。 ``` module i2c_master ( input wire i_clk, // 时钟 input wire i_reset, // 复位信号 inout wire io_sda, // 数据线 output wire o_scl // 时钟线 ); // 定义I2C总线状态 localparam S_IDLE = 3'b000; // 空闲状态 localparam S_START = 3'b001; // 起始状态 localparam S_ADDR = 3'b010; // 地址状态 localparam S_READ = 3'b011; // 读取状态 localparam S_WRITE = 3'b100; // 写入状态 localparam S_STOP = 3'b101; // 停止状态 // 定义I2C总线数据传输方向 localparam D_WRITE = 1'b0; // 写入 localparam D_READ = 1'b1; // 读取 // 定义I2C总线地址 localparam I2C_ADDRESS = 7'h50; // I2C设备地址 // 定义I2C总线时钟周期 localparam T_HALF = 5; // 半个时钟周期,用于时序控制 // 定义I2C总线控制信号 reg [2:0] state; // 状态 reg [7:0] data_out; // 输出数据 reg [7:0] data_in; // 输入数据 reg [6:0] address; // 地址 reg rw; // 读/写 reg busy; // 忙标志位 reg done; // 完成标志位 // 状态转移 always @ (posedge i_clk) begin if (i_reset) begin state <= S_IDLE; address <= 0; data_out <= 0; data_in <= 0; rw <= D_WRITE; busy <= 0; done <= 0; end else begin case (state) S_IDLE: begin // 空闲状态 o_scl <= 1; io_sda <= 1; if (!done) begin state <= S_START; end end S_START: begin // 起始状态 o_scl <= 1; io_sda <= 0; state <= S_ADDR; end S_ADDR: begin // 地址状态 o_scl <= 1; if (busy) begin state <= S_READ; end else begin io_sda <= 1; if (rw == D_WRITE) begin // 写入 data_out <= address; state <= S_WRITE; end else begin // 读取 data_out <= (I2C_ADDRESS << 1) | D_READ; state <= S_READ; end end end S_READ: begin // 读取状态 o_scl <= 1; if (io_sda == 0 ### 回答2: 以下是一个用于实现I2C总线读写的完整Verilog代码: ```verilog module i2c ( input wire clk, input wire resetn, input wire scl, inout wire sda, input wire read_write, input wire [7:0] slave_address, inout wire [7:0] data_in, output wire [7:0] data_out, inout wire ack ); reg [7:0] data_reg; reg [7:0] control_reg; reg data_valid; reg ack_reg; wire read_mode; wire write_mode; wire start_condition; wire stop_condition; wire address_received; wire data_received; wire data_requested; // I2C状态机定义 parameter IDLE = 0; parameter START_BIT = 1; parameter ADDRESS_BIT = 2; parameter ACK_BIT = 3; parameter DATA_BIT = 4; parameter SEND_ACK = 5; parameter READ_DATA = 6; parameter STOP_BIT = 7; reg [2:0] state_reg; assign read_mode = ~read_write; assign write_mode = read_write; assign start_condition = (~sda & scl); assign stop_condition = (sda & scl); assign address_received = (state_reg == ADDRESS_BIT); assign data_received = (state_reg == DATA_BIT); assign data_requested = (state_reg == SEND_ACK || state_reg == READ_DATA); // I2C状态机逻辑 always @(posedge clk or negedge resetn) begin if (~resetn) begin state_reg <= IDLE; data_valid <= 0; ack_reg <= 0; end else begin case (state_reg) IDLE: if (start_condition) state_reg <= START_BIT; START_BIT: if (scl) state_reg <= ADDRESS_BIT; ADDRESS_BIT: if (~scl) state_reg <= ACK_BIT; ACK_BIT: if (scl) state_reg <= DATA_BIT; DATA_BIT: if (~scl) state_reg <= SEND_ACK; SEND_ACK: if (scl) state_reg <= READ_DATA; READ_DATA: if (~scl) state_reg <= STOP_BIT; STOP_BIT: if (stop_condition) state_reg <= IDLE; endcase end end // 数据处理逻辑 always @(posedge clk or negedge resetn) begin if (~resetn) begin ack_reg <= 0; data_valid <= 0; data_reg <= 0; end else begin case (state_reg) IDLE: if (start_condition) ack_reg <= 0; ADDRESS_BIT: if (~scl) ack_reg <= 1; DATA_BIT: if (~scl) ack_reg <= 1; END_DATA_BIT: if (~scl) ack_reg <= 1; READ_DATA: if (~scl) data_valid <= 1; else if (scl) data_valid <= 0; STOP_BIT: if (stop_condition) ack_reg <= 0; endcase if (address_received || data_received) data_valid <= 1; else if (address_received || data_requested) data_valid <= 0; if (data_valid) data_reg <= data_in; end end // I2C总线物理层逻辑 always @(posedge clk or negedge resetn) begin if (~resetn) begin sda <= 1; ack <= 1; end else begin case (state_reg) START_BIT: sda <= 0; ADDRESS_BIT: sda <= slave_address[0]; DATA_BIT: sda <= data_reg[0]; READ_DATA: sda <= data_out[0]; STOP_BIT: sda <= 1; endcase if ((state_reg == ACK_BIT && ack_reg) || (state_reg == SEND_ACK && !ack_reg)) ack <= 1; else ack <= 0; end end endmodule ``` 此Verilog代码实现了I2C总线的读写操作。主要模块包括I2C状态机逻辑模块、数据处理逻辑模块和I2C总线物理层逻辑模块。它定义了I2C总线的各个状态(如IDLE、START_BIT、ADDRESS_BIT等等)和相应的状态转换逻辑。该代码还处理了数据的读写、ACK的发送、中断条件的检测等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值