资讯详情

FPGA实现IIC协议(二)之IIC总线的FPGA实现(单次读写驱动)

IIC协议系列博文: 上一篇文章是对的IIC总线详细介绍,了解IIC总线的阅读和写作方法。本文基于编写FPGA的IIC驱动模块,模拟和验证模块。

先回顾一下IIC总线单次读写时序。 单写时序如下: 在这里插入图片描述 单读时序如下:

总结一下单次写作顺序的过程(假设机器响应正确,如果响应不正确或不正确,跳转到初始状态重新开始写作):

1.发送起始信号,开始一次传输 2.发送器件地址 低电平(表示写)等待从机正确响应 3.发送寄存器地址(16或8位地址),等待从机正确响应 4.将8位数据发送到从机,等待从机正确响应 5.发送停止信号,结束本次传输

单读时序的过程(假设机器响应正确,如果响应不正确或不响应,跳转到初始状态重新开始读操作):

1.发送起始信号,一次写传输开始 2.发送器件地址 低电平(表示写作,写作操作为虚写,真正的目的是指向需要读取的地址),等待从机器中正确回应 3.发送寄存器地址(16或8位地址),等待从机正确响应 4.再次发送起始信号,开始读取和传输 5.发送器件地址 高电平(表示读取,这个读取操作才是真正的读取操作),等待从机正确回应 6.接收从机在总线上发送的8位数据,向从机发送非响应信号,表示无需再次接收数据 7.发送停止信号,结束本次传输

本文的目的是设计一个IIC驱动模块包括单读和单写,与16位寄存器和8位寄存器兼容。根据次数和上述时序图,可以绘制IIC单读写驱动状态机如下:

上图状态机将单字节写作操作与随机读取操作相结合,可以实用 现 I2C 设备单次写作和单次随机读取的状态跳转。需要注意的是状态ACK1、ACK2、ACK3、ACK4、ACK当主机没有正确接收到从机的响应信号时,5仍跳回初始状态。

说明各种状态、状态跳转条件和输出(假设主机是FPGA):

1.上电后 IDLE(初始状态)主机在收到有效的单字节数据读写信号后跳转到START1(起始状态)主机在该状态下发送起始信号; 然后跳转 SEND_D_ADDR_W(发送器件地址状态 写标志),在此状态下,主机发送控制指令,控制指令高7位为设备地址,最低位为读写控制字,写入0,表示执行写作操作;然后跳到 ACK(响应状态)。 3、在 ACK1状态下,根据从机寄存器地址字节数跳转。 3.1.当主机正确接收响应信号时,从机寄存器为16位 , 状态跳转到SEND_R_ADDR_H(发送高字节地址状态) 8 位写入从机,然后状态跳转到 ACK2(响应状态);主机正确接收响应信号后,跳转 SEND_R_ADDR_L(发送低字节地址状态); 3.2.当主机正确接收响应信号,从机寄存器为8位时,状态机状态机直接跳转 SEND_R_ADDR_L(发送低字节地址状态);将寄存器地址的低8位写入机器,跳转到 ACK3(应答 状态)。 4、在 ACK3状态下,根据读写使信号跳转不同状态(判断此操作是写作操作还是读写操作,1–读;0–写)。 4.1当主机正确接收从机响应信号,读写使能信号较低时,状态跳转WR_DATA(写数据状态); 4.1.1在WR_DATA状态, 在从机上写入单字节数据后,跳转到 ACK(应答状态); 4.1.2在 ACK4状态下,当主机正确接收的响应信号时,跳转到 STOP(停止状态); 4.2.当主机正确接收从机响应信号,读写使能信号高时, 状态跳转到 START_2(起始状态); 4.2.1START_2(起始状态);主机再次发送起始信号,状态跳转 SEND_D_ADDR_R(发送器件地址状态 读标志); 4.2.2主机再次发送控制字节,高 7 位置设备地址不变,读写控制位写入1,表示读写操作,控制字节写入后,状态机跳转 ACK5(响应状态); 4.2.3当主机正确接收响应信号时,状态跳转 RD_DATA(读数据状态); 4.2.4在 RD_DATA(读取数据状态)状态,主机在总线上读取数据8次,读取数据后跳转到 NACK(无应答状态),

在这种状态下,将一个时钟的高电平写入从机,表示数据读取完成,然后状态机跳转到 STOP(停止状态)。 5、在 STOP(停止状态)状态,FPGA 向 EEPROM 发送停止信号,一次单字节数据读/写操作完成,随后状态机跳回 IDLE(初始状态)等待下一个单字节数据读写开始信号。

Verilog编写的IIC驱动的整体框图和输入输出信号如下: 在SCL要求高电平设备SDA上述数据保持稳定;在SCL允许使用低电平设备SDA数据变化。我们知道驱动模块对IIC读写总线上的数据肯定需要一个驱动时钟。从上图可以看出,驱动时钟是SCL当频率为4倍时,操作最方便。如下图所示:

根据上述状态机的描述和整体设计,编写驱动模块并不难Verilog代码(详细说明): 这里主要讲几点需要注意和大致思路:

1.首先,三段状态机不能运行,需要结合上述状态转移图来理解(FPGA状态机(一段、二段、三段)(Moore)和米勒型(Mealy)) 2、SDA数据线是双向接口,需要处理双向接口(如何规范使用双向接口)(inout)信号?) 3.需要一个寄存器来标记写入(或读取)数据的数量 4、驱动时钟i2c_clk是SCL的4倍频,声明一个计数器来进行分频,这个计数器同时还能很方便的找到SCL中间高低电平(数据最稳定时)

module  i2c_drive #(     parameter   DEVICE_ADDR     =   7'b1010_000     ,   //i2c从机地址     parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   ///系统时钟频率     parameter   I2C_FREQ        =   18'd250_000         //i2c时钟频率,250k ) ( ///系统接口     input          sys_clk     ,      ///输入系统时钟,50MHz     input          sys_rst_n   ,      ///输入复位信号,低电平有效 //I2C时序控制接口   input    i2c_rw  ,    //读写使能信号-1:读写     input          i2c_start   ,      //i2c开始信号     input          i2c_num     ,      //i2c字节地址字节数-1:16位,0:8位     input  [15:
      
       0
       ] i2c_addr 
       , 
       //i2c字节地址 input 
       [
       7
       :
       0
       ] i2c_data_w 
       , 
       //写入i2c数据 output reg i2c_clk 
       , 
       //i2c驱动时钟 output reg i2c_end 
       , 
       //i2c一次读/写操作完成 output reg 
       [
       7
       :
       0
       ] i2c_data_r 
       , 
       //i2c读取数据 
       //I2C物理接口  output reg scl 
       , 
       //输出至i2c设备的串行时钟信号scl inout wire sda 
       //输出至i2c设备的串行数据信号sda 
       )
       ; 
       //状态机定义  localparam IDLE 
       = 
       4'd0
       , 
       //初始化状态 START1 
       = 
       4'd1
       , 
       //发送开始信号状态1 SEND_D_ADDR_W 
       = 
       4'd2
       , 
       //设备地址写入状态 + 控制写 ACK1 
       = 
       4'd3
       , 
       //等待从机响应信号1 SEND_R_ADDR_H 
       = 
       4'd4
       , 
       //发送寄存器地址高8位 ACK2 
       = 
       4'd5
       , 
       //等待从机响应信号2 SEND_R_ADDR_L 
       = 
       4'd6
       , 
       //发送寄存器地址低8位 ACK3 
       = 
       4'd7
       , 
       //等待从机响应信号3 WR_DATA 
       = 
       4'd08
       , 
       //写数据状态 ACK4 
       = 
       4'd09
       , 
       //应答状态4 START2 
       = 
       4'd10
       , 
       //发送开始信号状态12 SEND_D_ADDR_R 
       = 
       4'd11
       , 
       //设备地址写入状态 + 控制读 ACK5 
       = 
       4'd12
       , 
       //应答状态5 RD_DATA 
       = 
       4'd13
       , 
       //读数据状态 NACK 
       = 
       4'd14
       , 
       //非应答状态 STOP 
       = 
       4'd15
       ; 
       //结束状态 
       //根据系统频率及IIC驱动频率计算分频系数  localparam CLK_DIVIDE 
       = SYS_CLK_FREQ 
       / I2C_FREQ 
       >> 
       2'd3
       ; 
       //reg定义  reg 
       [
       9
       :
       0
       ] clk_cnt 
       ; 
       //分频时钟计数器,最大计数1023  reg 
       [
       3
       :
       0
       ] cur_state 
       ; 
       //状态机现态  reg 
       [
       3
       :
       0
       ] next_state 
       ; 
       //状态机次态  reg i2c_clk_cnt_en 
       ; 
       //驱动时钟计数使能 reg 
       [
       1
       :
       0
       ] i2c_clk_cnt 
       ; 
       //驱动计数时钟,方便在SCL的高电平中间采集数据;和在SCL的低电平中间变化数据 reg sda_out 
       ; 
       //IIC总线三态输出 reg sda_en 
       ; 
       //IIC总线三态门使能 reg 
       [
       2
       :
       0
       ] bit_cnt 
       ; 
       //接收数据个数计数器 reg ack_flag 
       ; 
       //应答信号标志 reg 
       [
       7
       :
       0
       ] i2c_data_r_temp 
       ; 
       //读取数据寄存器,暂存读到的数据 
       //wire定义  wire sda_in 
       ; 
       //IIC总线三态输入 wire 
       [
       7
       :
       0
       ] addr_r 
       ; 
       //器件地址+读控制位 wire 
       [
       7
       :
       0
       ] addr_w 
       ; 
       //器件地址+写控制位 assign addr_r 
       = 
       { 
        DEVICE_ADDR
       ,
       1'b1
       }
       ; 
       //器件地址+读控制位 assign addr_w 
       = 
       { 
        DEVICE_ADDR
       ,
       1'b0
       }
       ; 
       //器件地址+写控制位 
       //双向口处理 assign sda_in 
       = sda
       ; assign sda 
       = sda_en 
       ? sda_out 
       : 
       1'bz
       ; 
       //scl4分频时钟=IIC驱动时钟i2c_clk,方便操作对采集数据及变化数据操作 always@
       (posedge sys_clk or negedge sys_rst_n
       )begin 
       if
       (
       ~sys_rst_n
       )begin i2c_clk 
       <= 
       1'b0
       ; clk_cnt 
       <= 
       10'd0
       ; end 
       else 
       if
       (clk_cnt 
       == CLK_DIVIDE 
       - 
       1'b1
       )begin i2c_clk 
       <= 
       ~i2c_clk
       ; clk_cnt 
       <= 
       10'd0
       ; end 
       else begin i2c_clk 
       <= i2c_clk
       ; clk_cnt 
       <= clk_cnt 
       + 
       1'd1
       ; end end 
       //i2c_clk计数器使能 always@
       (posedge i2c_clk or negedge sys_rst_n
       )begin 
       if
       (
       !sys_rst_n
       ) i2c_clk_cnt_en 
       <= 
       1'b0
       ; 
       //只有在发送完了结束信号或者没有接收到IIC开始传输信号的初始状态下才不停对i2c_clk计数器复位(使能为0) 
       else 
       if 
       (
       (cur_state 
       == STOP 
       && i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 2'd3
       )
       ||
       (cur_state 
       == IDLE 
       && 
       !i2c_start 
       )
       ) i2c_clk_cnt_en 
       <= 
       1'b0
       ; 
       else 
       if
       (i2c_start
       ) i2c_clk_cnt_en 
       <= 
       1'b1
       ; 
       //接收到开始信号,代表一次传输开始,计数器开始计数 
       else i2c_clk_cnt_en 
       <= i2c_clk_cnt_en
       ; 
       //其他时候保持不变 end 
       //i2c_clk_cnt计数器 always@
       (posedge i2c_clk or negedge sys_rst_n
       )begin 
       if
       (
       !sys_rst_n
       ) i2c_clk_cnt 
       <= 
       2'd0
       ; 
       else 
       if
       (i2c_clk_cnt_en
       ) i2c_clk_cnt 
       <= i2c_clk_cnt 
       + 
       1'd1
       ; 
       //使能信号有效,计数器开始计数 
       else i2c_clk_cnt 
       <= 
       2'd0
       ; 
       //使能信号无效,计数器清零 end 
       //三段式状态机第一段 always@
       (posedge i2c_clk or negedge sys_rst_n
       )begin 
       if
       (
       ~sys_rst_n
       ) cur_state 
       <= IDLE
       ; 
       else cur_state 
       <= next_state
       ; end 
       //三段式状态机第二段 always@
       (
       *
       )begin next_state 
       = IDLE
       ; 
       case
       (cur_state
       ) IDLE
       : 
       if
       (i2c_start
       ) next_state 
       = START1
       ; 
       //接收到开始信号,跳转到发送起始信号状态 
       else next_state 
       = IDLE
       ; START1
       : 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) 
       //i2c_clk 计数到最大值3,跳转到发送器件地址+写标志位状态 next_state 
       = SEND_D_ADDR_W
       ; 
       else next_state 
       = START1
       ; SEND_D_ADDR_W
       : 
       if
       (i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 3'd7
       ) 
       //发送了8位地址后跳转到从机响应状态 next_state 
       = ACK1
       ; 
       else next_state 
       = SEND_D_ADDR_W
       ; ACK1
       : 
       if
       (ack_flag 
       && i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //响应标志有效 
       //根据地址状态位判断是16位地址还是8位地址,从而跳转到不同状态 
       if
       (i2c_num
       ) 
       //16位地址 next_state 
       = SEND_R_ADDR_H
       ; 
       //跳转到寄存器高8位地址发送状态 
       else 
       //8位地址 next_state 
       = SEND_R_ADDR_L
       ; 
       //跳转到寄存器低8位地址发送状态 end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) 
       //响应无效或者响应不及时则跳转回初始状态 next_state 
       = IDLE
       ; 
       else next_state 
       = ACK1
       ; SEND_R_ADDR_H
       : 
       if
       (i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 3'd7
       ) 
       //发送了寄存器高8位地址后跳转到从机响应状态 next_state 
       = ACK2
       ; 
       else next_state 
       = SEND_R_ADDR_H
       ; ACK2
       : 
       if
       (ack_flag 
       && i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = SEND_R_ADDR_L
       ; 
       //响应标志有效则跳转到寄存器低8位地址发送状态 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) 
       //响应无效或者响应不及时则跳转回初始状态 next_state 
       = IDLE
       ; 
       else next_state 
       = ACK2
       ; SEND_R_ADDR_L
       : 
       if
       (i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 3'd7
       ) 
       //发送了寄存器低8位地址后跳转到从机响应状态 next_state 
       = ACK3
       ; 
       else next_state 
       = SEND_R_ADDR_L
       ; ACK3
       : 
       if
       (ack_flag 
       && i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //响应标志有效  
       if
       (i2c_rw
       ) 
       //读状态 next_state 
       = START2
       ; 
       //跳转到第二次发送起始信号 
       else 
       //写状态 next_state 
       = WR_DATA
       ; 
       //跳转到写数据状态 end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = IDLE
       ; 
       //响应无效或者响应不及时则跳转回初始状态 
       else next_state 
       = ACK3
       ; START2
       : 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = SEND_D_ADDR_R
       ; 
       //第二次发送起始信号后跳转到发送器件地址+读标志位状态 
       else next_state 
       = START2
       ; SEND_D_ADDR_R
       : 
       if
       (i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 3'd7
       ) 
       //发送完了8位地址后跳转到从机响应状态 next_state 
       = ACK5
       ; 
       else next_state 
       = SEND_D_ADDR_R
       ; ACK5
       : 
       if
       (ack_flag 
       && i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = RD_DATA
       ; 
       //响应标志有效则跳转到读数据状态 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = IDLE
       ; 
       //响应无效或者响应不及时则跳转回初始状态 
       else next_state 
       = ACK5
       ; RD_DATA
       : 
       if
       (i2c_clk_cnt 
       == 
       2
       'd3 && bit_cnt == 3'd7
       ) 
       //接收完了8位数据后跳转到主机发送非响应状态 next_state 
       = NACK
       ; 
       else next_state 
       = RD_DATA
       ; NACK
       : 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = STOP
       ; 
       //发送完了非响应信号后跳转到发送结束信号状态 
       else next_state 
       = NACK
       ; WR_DATA
       : 
       if
       (bit_cnt 
       == 
       3
       'd7 && i2c_clk_cnt == 2'd3
       ) next_state 
       = ACK4
       ; 
       //写完了8位数据后跳转到从机响应状态 
       else next_state 
       = WR_DATA
       ; ACK4
       : 
       if
       (ack_flag 
       && i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = STOP
       ; 
       //响应标志有效则跳转到发送结束信号状态 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       ) next_state 
       = IDLE
       ; 
       //响应无效或者响应不及时则跳转回初始状态 
       else next_state 
       = ACK4
       ; STOP
       : 
       if
       (bit_cnt 
       == 
       2
       'd3 && i2c_clk_cnt == 2'd3
       ) 
       //结束信号发送完毕(这里还预留了2个周期)跳转到初始状态,等待下一次传输开始信号 next_state 
       = IDLE
       ; 
       else next_state 
       = STOP
       ; 
       default
       :next_state 
       = IDLE
       ; endcase end 
       //三段式状态机第三段 always@
       (posedge i2c_clk or negedge sys_rst_n
       )begin 
       if
       (
       ~sys_rst_n
       )begin 
       //初始状态 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; bit_cnt 
       <= 
       3'd0
       ; i2c_end 
       <= 
       1'b0
       ; i2c_data_r 
       <= 
       8'd0
       ; i2c_data_r_temp 
       <= 
       8'd0
       ; end 
       else begin i2c_end 
       <= 
       1'b0
       ; 
       case
       (cur_state
       ) IDLE
       :begin sda_en 
       <= 
       1'b1
       ; 
       //控制总线 sda_out 
       <= 
       1'b1
       ; 
       //拉高总线 end START1
       :begin 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //发送完了开始信号 
       if
       (addr_w
       [
       7
       ]
       )begin 
       //如果器件地址的最高位为1则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //如果器件地址的最高位为0则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end 
       else begin 
       //还没发送完开始信号则保持低电平 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end SEND_D_ADDR_W
       :begin 
       if
       (bit_cnt 
       == 
       3'd7
       )begin 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //发送了8个数据(器件地址+写标志位) bit_cnt 
       <= 
       3'd0
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b0
       ; 
       //释放总线 end end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //发送完了一个数据 bit_cnt 
       <= bit_cnt 
       + 
       1'd1
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b1
       ; 
       //控制总线 sda_out 
       <= addr_w
       [
       6
       -bit_cnt
       ]
       ; 
       //总线依次串行输出地址 end end ACK1
       :begin 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       if
       (i2c_num
       )begin 
       //如果器件地址为16位 
       if
       (i2c_addr
       [
       15
       ]
       )begin 
       //如果器件地址的16位为1则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //如果器件地址的16位为0则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end 
       else begin 
       //如果器件地址为8位 
       if
       (i2c_addr
       [
       7
       ]
       )begin 
       //如果器件地址的8位为1则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //如果器件地址的8位为0则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end end end SEND_R_ADDR_H
       :begin 
       if
       (bit_cnt 
       == 
       3'd7
       )begin 
       //8个数据发送完了 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= 
       3'd0
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b0
       ; 
       //释放总线 end end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= bit_cnt 
       + 
       1'd1
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b1
       ; 
       //控制总线 sda_out 
       <= i2c_addr
       [
       14
       -bit_cnt
       ]
       ;
       //总线依次串行输出地址 end end ACK2
       :begin 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       if
       (i2c_addr
       [
       7
       ]
       )begin 
       //下一个要发送数据的首个数据为高则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //下一个要发送数据的首个数据为低则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end end SEND_R_ADDR_L
       :begin 
       if
       (bit_cnt 
       == 
       3'd7
       )begin 
       //8个数据发送完了 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= 
       3'd0
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b0
       ; 
       //释放总线 end end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= bit_cnt 
       + 
       1'd1
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b1
       ; 
       //控制总线 sda_out 
       <= i2c_addr
       [
       6
       -bit_cnt
       ]
       ; 
       //总线依次串行输出地址 end end ACK3
       :begin 
       if
       (
       !i2c_rw
       )begin 
       //是写操作 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       if
       (i2c_data_w
       [
       7
       ]
       )begin 
       //下一个要发送数据的首个数据为高则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //下一个要发送数据的首个数据为低则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end end 
       else begin 
       //是读操作 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       //提前拉高总线进入再次发送起始信号状态 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end end START2
       :begin 
       if
       (i2c_clk_cnt 
       == 
       2'd1
       )begin 
       //拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       if
       (addr_r
       [
       7
       ]
       )begin 
       //下一个要发送数据的首个数据为高则提前拉高总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b1
       ; end 
       else begin 
       //下一个要发送数据的首个数据为低则提前拉低总线 sda_en 
       <= 
       1'b1
       ; sda_out 
       <= 
       1'b0
       ; end end end SEND_D_ADDR_R
       :begin 
       if
       (bit_cnt 
       == 
       3'd7
       )begin 
       //8个数据发送完了 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= 
       3'd0
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b0
       ; 
       //释放总线 end end 
       else 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin bit_cnt 
       <= bit_cnt 
       + 
       1'd1
       ; 
       //发送数据计数器清零 sda_en 
       <= 
       1'b1
       ; 
       //控制总线 sda_out 
       <= addr_r
       [
       6
       -bit_cnt
       ]
       ; 
       //总线依次串行输出地址 end end ACK5
       : sda_en 
       <= 
       1'b0
       ; 
       //下一个状态是接收数据,所以释放总线 RD_DATA
       : 
       if
       (i2c_clk_cnt 
       == 
       2'd3
       )begin 
       if
       (bit_cnt 
       == 
       3'd7
       )begin 
       //接收了8个数据 bit_cnt 
       <= 
       3'd0
       ; 
       //发送数据计数

标签: d5d电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台