FPGA笔记 I2C协议

FPGA学习笔记:协议-I2C

文章目录

  • FPGA学习笔记:协议-I2C
    • 一、I2C协议介绍
    • 二、I2C通信过程
      • 2.1 起始位与停止位
      • 2.2 数据传输及应答期
        • 2.2.1 数据传输
        • 2.2.2 应答期
    • 三、数据内容
      • 3.1 器件地址
      • 3.2 寄存器地址(字地址)
        • 3.2.1 单字节的存储单元地址
        • 3.2.2 双字节的存储单元地址
    • 四、I2C写
      • 4.1 单字节写
      • 4.2 连续写
    • 五、I2C读
      • 5.1 单字节读
      • 5.2 连续读
    • `源码:`
    • 参考资料

一、I2C协议介绍

​ I2C即Inter-Integrated Circuit(集成电路总线),它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。

​ I2C是指一个能够支持多个设备的总线,其中包含了一条双向串行数据线SDA,一条串行时钟线SCL。每个连接到主线的设备都有独立的地址,主机通过相应的地址来访问相应的设备。主机通过发送设备(器件)地址来寻找到从机,然后通过相应指令对从机进行读操作或写操作。

​ I2C数据传输的速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s,各种被控器件均并联在总线上,通过器件地址(SLAVE_ADDR,具体可查看器件手册)识别。

FPGA笔记 I2C协议_第1张图片

二、I2C通信过程

2.1 起始位与停止位

​ 在I2C器件与主机通信之前,串行时钟线SCL和串行数据线SDA线由于上拉而处于高电平状态,这表示I2C总线目前处于空闲状态。

​ 如果主机想要开始传输数据时,就会产生一个起始位信号,而从机检测到起始信号后,将准备接收数据,待数据传输结束后主机又要产生一个停止位信号,通知从机数据传输结束了。在起始位信号发送之前,总线是处于空闲状态的;而在起始位信号之后到停止位信号发送之前的这段时间,主机可以向从机写数据也可以读取其输出的数据;最后产生停止位信号,总线回归至空闲状态。

起始位 : SCL为高电平时,SDA电平从高到低变化

停止位 : SCL为高电平时,SDA电平从低到高变化

FPGA笔记 I2C协议_第2张图片

2.2 数据传输及应答期

​ 整个I2C通信过程中,前8个时钟周期为数据传输过程,第8个周期末到第9个周期末是应答期。

​ 在整个过程中,SDA在数据传输过程中受传送器件控制,而在应答期中受接收器件控制,因此SDA总线是三态门inout)。

2.2.1 数据传输

​ 当SCL为低电平时,SDA进行变化

​ 当SCL为高电平时,SDA数据锁存

FPGA笔记 I2C协议_第3张图片

2.2.2 应答期

​ 应答期是第9个时钟周期,指第8个周期末到第9个周期末:

​ 1.第8个周期末,主机(传送器件)释放SDA以让从机(接收器件)应答。

​ 2.第9个周期中,从机(接收器件)控制SDA拉低以相应主机(传送器件),其中SCL为高电平时,如果SDA没有被检测到低电平,则视为非应答,表明此次数据传输失败。

​ 3.第9个周期末,从机(接收器件)释放SDA以让主机(传送器件)继续传输数据,如果主机发送停止位信号,则传输结束。

FPGA笔记 I2C协议_第4张图片

三、数据内容

3.1 器件地址

​ 每个I2C器件都有一个器件地址,有的2C器件的器件地址是固定的(出厂时设置好了),例如OV7670的器件地址为固定的0x42,而还有的I2C器件的器件地址是由一个固定部分和一个可编程的部分构成,例如常见的E2PROM存储器,其中就留有3个控制的引脚,这三个引脚可以联接到GNDVCC来设置不同的编程地址,达到了I2C总线上挂载多个E2PROM器件而增加了系统E2PROM容量的效果。

​ 因此,主机第一步不是向从机发送地址,而是向总线上发送地址,所有的从机接收到主机发送的地址进行比较,若从机匹配上就会向总线发送一个响应信号;主机收到响应信号后,才开始向总线发送数据,这样就达成了与从机的通信。如果主机没收到响应信号,也就代表此次寻址失败。

​ 进行数据传输时,主机发送起始位信号后,按照从高到低的位序发送器件地址,一般为7bit。

​ 第8bit位为读写控制位R/W,该位为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作。

​ 一般器件地址格式如下图所示:

FPGA笔记 I2C协议_第5张图片

3.2 寄存器地址(字地址)

​ 一般来说,每个支持I2C协议的器件,内部总会有一些可供读写的寄存器或者存储器,例如E2PROM存储器,内部是一系列顺序编址的内存单元。因此,我们还要向I2C器件内的存储单元(寄存器或者存储器)进行读写操作时,就需要先制定存储单元的地址,然后才能够向该地址写入数据或者读取其内容。

​ 存储单元地址的字节大小一般为一个或两个字节,其受I2C器件的影响,例如同为E2PROM存储器,AT24C02的地址长度仅用了一位字节,而AT24C04的地址长度为9位bit(其中他占用了器件地址的A0位),而AT24C64的地址长度为两个字节(器件地址中的A2,A1,A0位不够用了,才增加一个字节),这是因为AT24C64的存储单元数目超过了一个字节和器件地址中的三位bit(A2,A1,A0位)所表示的最大值(2^8 * 2^3 == 2K),所以需要用两个字节来表示。

3.2.1 单字节的存储单元地址

FPGA笔记 I2C协议_第6张图片

3.2.2 双字节的存储单元地址

​ 以下为AT24C64的字地址示意图:

FPGA笔记 I2C协议_第7张图片

四、I2C写

​ 主机在发送存储单元地址后,从机会在应答后将内部的存储单元地址指向主机所发的单元。如果读写控制R/W位为0即写命令,从机就处于等待接收数据的状态。此时,主机就开始写数据了。写数据分为单字节写(对于EEPROM而言,称为字节写)和连续写(对于EEPROM而言,称为页写)。

​ 不管单字节写和连续写,都可概括为: start+[器件地址,写命令]+字地址+数据+stop

4.1 单字节写

​ 发送完一字节数据后发送结束信号。

FPGA笔记 I2C协议_第8张图片

4.2 连续写

​ 发送完一字节数据后继续发送下一字节数据,最后发送的是结束信号。

FPGA笔记 I2C协议_第9张图片

五、I2C读

​ 主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“1”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。读数据分为当前地址读单字节读连续读

​ 不管是单字节读还是连续读,都可以概括为:start +[器件地址,写命令(0)] + 字地址 + start + [器件地址,读命令(1)] + 接收从机的数据 + 主机非应答(1) + stop

​ 为什么会先进行没有发送数据的单次写操作,然后再进行读操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次Dummy Write也就是虚写操作,之所以称为虚写,是因为我们并不是真的要写数据,而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以按当前地址读的方式读数据了,所以读数据可以理解为:没有发送数据的单次写操作 + 当前地址的读操作

5.1 单字节读

​ 读取完一字节数据后,主机发送非应答信号。

FPGA笔记 I2C协议_第10张图片

5.2 连续读

​ 读取完一字节数据后主机发送应答信号,继续读取下一字节数据,最后主机发送非应答信号。

FPGA笔记 I2C协议_第11张图片

源码:

module	i2c_test(
	input 			 clk,
    input 			 rst_n,
	output	reg		 i2c_scl,
    inout			 i2c_sda
);

//sda三态门
module iic_test(
       //System Clock
       input          clk,
       input          rst_n,
       //Wr/Rd signal
       input     [1:0] Rd_Wr_Sig,
       input     [7:0] Word_Addr,
       input     [6:0] Device_Addr,
       //Wr/Rd Data
       input     [7:0] Wr_Data,
       output    [7:0] Rd_Data,
       output          Done_Sig,
       //I2c  Two Line
       output         Scl,
       inout          Sda
);

assign Rd_Data = Rd_Data_r;
assign Done_Sig = Done;
//F100K时钟产生
parameter F100K = 9'd500;

//I2C双总线
reg Scl_r;
reg Sda_Dir;
reg Sda_r;
assign Scl = Scl_r;
assign Sda = (Sda_Dir)? Sda_r : 1'bz;
//I2C数据传输过程
reg [7:0] Wr_Data_r;
reg [7:0] Rd_Data_r;
reg [4:0] Step;
reg [4:0] Step_Next;
reg [9:0] Count_SCL;
reg       Ack_r;
reg       Done;
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)begin
        Wr_Data_r <= 8'd0;
        Rd_Data_r <= 8'd0;
        Step <= 5'd0;
        Step_Next <= 5'd0;
        Ack_r <= 1'b1;
        Done <= 1'b0;
        Count_SCL <= 9'd0;
        Scl_r <= 1'b1;
        Sda_r <= 1'b1;
        Sda_Dir <= 1'b1;
    end
    else if(Rd_Wr_Sig[0])begin  //写数据
        case(Step)
            
            0:begin     //Start
                Sda_Dir <= 1;                      //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b1;
                else if(Count_SCL == 400)   Scl_r <= 1'b0;
                
                if(Count_SCL == 0)          Sda_r <= 1'b1;
                else if(Count_SCL == 200)   Sda_r <= 1'b0;
            
                if(Count_SCL == F100K-1)begin   //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            1:begin     //Device_Wr
                Wr_Data_r <= {Device_Addr , 1'b0};  //Device Write
                Step <= 5'd7;                       //Turn to Send Bit
                Step_Next <= Step + 1'b1;
            end
            2:begin     //Word
                Wr_Data_r <= Word_Addr;             //Word  Addr
                Step <= 5'd7;                       //Turn to Send Bit
                Step_Next <= Step + 1'b1;
            end
            3:begin     //Data
                Wr_Data_r <= Wr_Data;               //Write  Data
                Step <= 5'd7;                       //Turn to Send Bit
                Step_Next <= Step + 1'b1;  
            end
            4:begin     //Stop
                Sda_Dir <= 1;                      //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                
                if(Count_SCL == 0)          Sda_r <= 1'b0;
                else if(Count_SCL == 300)   Sda_r <= 1'b1;
                
                if(Count_SCL == F100K-1)begin   //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            5:begin     //Done
                Done <= 1'b1;
                Step <= Step + 1'b1;
            end
            6:begin     //return
                Done <= 1'b0;
                Step <= 5'd0;
            end
            //Send Bit
            7,8,9,10,11,12,13,14:begin      
                Sda_Dir <= 1;                      //Sda Output
                Sda_r <= Wr_Data_r[5'd14 - Step];       //High Bit First
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;
                
                if(Count_SCL == F100K-1)begin //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            
            15:begin             //Receive Ack 
                Sda_Dir <= 0;                       //Sda Input
                
                if(Count_SCL == 100)        Ack_r <= Sda;
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;

                if(Count_SCL == F100K-1)begin  //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            16:begin            //Ack Check
                if(Ack_r != 1'b0)   Step <= 5'd0;
                else                Step <= Step_Next;
            end
            
        endcase
      end
      else if(Rd_Wr_Sig[1])begin    //读数据
        case(Step)
            0:begin     //Start
                Sda_Dir <= 1;                      //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b1;
                else if(Count_SCL == 400)   Scl_r <= 1'b0;

                if(Count_SCL == 0)          Sda_r <= 1'b1;
                else if(Count_SCL == 200)   Sda_r <= 1'b0;
               
                if(Count_SCL == F100K-1)begin   //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            1:begin     //Device_Wr
                Wr_Data_r <= {Device_Addr, 1'b0}; //Device Addr(Write)
                Step <= 5'd9;
                Step_Next <= Step + 1'b1;
            end
            2:begin     //Word
                Wr_Data_r <= Word_Addr; //Words Addr
                Step <= 5'd9;
                Step_Next <= Step + 1'b1;  
            end
            3:begin     //Start again
                Sda_Dir <= 1'b1;            //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 500)   Scl_r <= 1'b0;
                    
                if(Count_SCL == 0)          Sda_r <= 1'b0;
                else if(Count_SCL == 100)   Sda_r <= 1'b1;
                else if(Count_SCL == 300)   Sda_r <= 1'b0;
                
                if(Count_SCL == 600 - 1)begin
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else
                    Count_SCL <= Count_SCL + 1'b1;
            end
            4:begin     //Device_Rd
                Wr_Data_r <= {Device_Addr, 1'b1}; //Device Addr(Read)
                Step <= 5'd9;
                Step_Next <= Step + 1'b1;  
            end
            5:begin     //Read Data
                Rd_Data_r <= 8'd0;
                Step <= 5'd19;
                Step_Next <= Step + 1'b1;
            end
            6:begin     //Stop
                Sda_Dir <= 1;                      //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                
                if(Count_SCL == 0)          Sda_r <= 1'b0;
                else if(Count_SCL == 300)   Sda_r <= 1'b1;

                
                if(Count_SCL == F100K-1)begin   //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end 
            7:begin     //Done
               Done <= 1'b1;
               Step <= Step + 1'b1;
            end        
            8:begin     //return
               Done <= 1'b0;
               Step <= 5'b0;
            end
            //Send Bit
            9,10,11,12,13,14,15,16:begin      
                Sda_Dir <= 1;                      //Sda Output
                Sda_r <= Wr_Data_r[5'd16 - Step];       //High Bit First
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;
                
                if(Count_SCL == F100K-1)begin //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            17:begin             //Receive Ack 
                Sda_Dir <= 0;                       //Sda Input
                if(Count_SCL == 200)    Ack_r <= Sda;
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;
                
                if(Count_SCL == F100K-1)begin  //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            18:begin            //Ack Check
                if(Ack_r != 1'b0)   Step <= 5'd0;
                else                Step <= Step_Next;
                   
            end
            //Read Data
            19,20,21,22,23,24,25,26:begin      
                Sda_Dir <= 0;                      //Sda Input
                
                if(Count_SCL == 200)        Rd_Data_r[5'd26 - Step] <= Sda;       //High Bit First
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;
                
                if(Count_SCL == F100K-1)begin //100K
                    Count_SCL <= 9'd0;
                    Step <= Step + 1'b1;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end
            27:begin                //Send NoAck
                Sda_Dir <= 1;                       //Sda Output
                
                if(Count_SCL == 0)          Scl_r <= 1'b0;
                else if(Count_SCL == 100)   Scl_r <= 1'b1;
                else if(Count_SCL == 300)   Scl_r <= 1'b0;
                
                if(Count_SCL == F100K-1)begin  //100K
                    Count_SCL <= 9'd0;
                    Step <= Step_Next;
                end
                else                                //++
                    Count_SCL <= Count_SCL + 1'b1;
            end

        endcase
    end 
end
endmodule   


endmodule

参考资料

1.维基百科-I²C

2.协议——IIC - 咸鱼FPGA - 博客园

3.黑金Altera开发板Verilog实例教程

4.I²C_NXP数据手册

你可能感兴趣的:(FPGA,fpga)