• iic接口介绍


    最近遇到一个BUG,跟IIC通信有关,所以借这个机会总结一下IIC总线协议

    1.引脚接口介绍

    1.A0,A1,A2为24LC64的片选信号,IIC总线最多可以挂载8个IIC接口器件,通过对A0,A1,A2寻址,可以实现对不同的EEPROM操作

    2.WP为读写使能信号,当WP悬空或者接地,EEPROM可读可写,当WP接电源,EEPROM只能读不能写。因为我们要对EEPROM写,所以这里WP信号悬空

    3.SCL为时钟信号线,最高频率400Khz

    4.SDA为数据线,双向线(inout),当为in时,数据通过SDA写到EEPROM。为out时,eeprom读出来的数据通过SDA传到外面

    2.接口时序

       IIC读写时序分为随机读写和页读写,这里只研究随机读写

       2.1 写时序

            

      写操作步骤

      1.发送启动信号

      2.发送控制字写(1010_A0A1A2_0 )

      3.EEPROM发送应答信号ACK

      4.发送高字节写地址

      5.EEPROM发送应答信号ACK

      6.发送低字节写地址

      7.EEPROM发送应答信号ACK

      8.发送8bit写数据

      9.EEPROM发送应答信号ACK

      10.发送停止信号

      2.2 读时序

     读操作信号  

     1.发送启动信号

      2.发送控制字写(1010_A0A1A2_0)

      3.EEPROM发送应答信号ACK

      4.发送高字节读地址

      5.EEPROM发送应答信号ACK

      6.发送低字节读地址

      7.EEPROM发送应答信号ACK

      8.发送启动信号

      9.发送控制字读(1010_A0A1A2_1)

      10.EEPROM发送应答信号ACK

      11.读取一个8bit数据

      12..EEPROM发送NO ACK信号

      13.发送停止信号

    3.操作步骤解析

      3.1启动信号

            

           SCL 保持高电平期间 ,如果 SDA 出现由高到低的跳变,代表启动信号

       3.2控制字

            1010_A0A1A2X,

          1.1010为EEPROM信号标识,为一组固定的序列

          2.A0A1A2为片选信号,由于只有一个flash,所以A0A1A2在这里全为0

          3.最后一个bit X,为0时代表写,为1时代表读。

      3.3地址

           24LC64表示有64Kbit的存储空间,需要13位地址线寻址。但是IIC是以字节的实行操作的,所以需要13位地址线扩展成16位,高3位随意填0或者1,习惯填0

      3.4应答信号与非应答信号

           应答信号和非应答信号都是由数据接收方(EEPROM)发出的,当SCL为高电平时候,如果检测到SDA为低电平,说明有应答信号。如果检测到SDA为高电平,说明有非应答信号。所以在应答时钟周期的时候,我们要释放SDA信号线,让EEPROM通过SDA发送一个低电平或者高电平过来。

      3.5停止信号

             

            SCL 保持高电平期间 ,如果 SDA 出现由低到高的跳变,代表停止信号

      3.6 数据传输

            由于IIC总线协议的启动和停止信号都是在SCL高电平期间发生跳变,这就决定了其数据只能在SCL低电平期间发生改变,不然会被当做启动或者停止信号处理。在SCL为高电平期间,数据必须保持稳定。即在SCL低电平的时候改变数据,高电平的时候采集数据

       4关键代码解析

         4.1状态机设置

     

         4.2 sda信号线控制

                由于sda是inout型,读写都是有这根线控制。所以我们要有一个信号,来指示sda信号线什么时候写,什么时候是读。

    当link_sda信号为1的时候,指示sda信号写。这时候我们把需要写的数据一个bit一个bit的赋给中间变量sda_buf信号,该信号经过sda信号线把数据写进flash

    当link_sda信号为0的时候,指示sda信号读。

    完整代码如下

    module iic_control(
      input  wire       sclk,
      input  wire       reset,
      input  wire       key_wr,
      input  wire       key_rd,
      
      output reg        scl,
      inout  wire       sda,
      output wire[7:0]  dataout,
      output reg        led
    );
    
      parameter    IDLE           =  14'b00_0000_0000_0000,
                   start1         =  14'b00_0000_0000_0001,
                   control_byte1  =  14'b00_0000_0000_0010,
                   ack1           =  14'b00_0000_0000_0100,
                   high_addr_byte =  14'b00_0000_0000_1000,
                   ack2           =  14'b00_0000_0001_0000,
                   low_addr_byte  =  14'b00_0000_0010_0000,
                   ack3           =  14'b00_0000_0100_0000,
                   start2         =  14'b00_0000_1000_0000,
                   control_byte2  =  14'b00_0001_0000_0000,
                   ack4           =  14'b00_0010_0000_0000,
                   transfer_data  =  14'b00_0100_0000_0000,
                   ack5           =  14'b00_1000_0000_0000,
                   no_ack         =  14'b01_0000_0000_0000,
                   stop           =  14'b10_0000_0000_0000;
                   
      reg[13:0]         state;   
      
      reg[6:0]          cnt;         //分频计数
      reg               link_sda;    //总线开关
      reg               sda_buf;     //总线数据缓存器
      reg               wr;          //写使能
      reg               rd;          //读使能
      
      reg[7:0]          data;
      reg[3:0]          cnt_num;
      reg[7:0]          result;
      
      assign sda=(link_sda)?sda_buf:1'hz;
      assign dataout = result;
      
      always@(posedge sclk or negedge reset)
        if(!reset)
          cnt <= 7'd0;
        else if(cnt==7'd124)
          cnt <= 7'd0;
        else 
          cnt <= cnt + 1'b1;     
          
       always@(posedge sclk or negedge reset)
         if(!reset)
           scl <= 1'b0;
         else if(cnt==7'd30)
           scl <= 1'b1;
         else if(cnt==8'd93)
           scl <= 1'b0;
      
      always@(posedge sclk or negedge reset)
        if(!reset==1)
          begin
            state    <= IDLE;
            link_sda <= 0;
            sda_buf  <= 0;
            data     <= 0;
            cnt_num  <= 4'd0;
            result   <= 8'd0;
            led      <= 1;
          end
        else case(state)
          IDLE: 
            begin
              if(!key_wr)
                wr <= 1;
              if(!key_rd)
                rd <= 1;
              if((wr==1)||(rd==1)&&(!scl==1))
                begin
                  state    <= start1;
                  link_sda <= 1;
                  sda_buf  <= 1;
                  data     <= 8'b10100000;  //写控制字准备
                end 
            end
          start1:
            begin
              if((scl==1)&&(cnt==7'd61))
                begin
                  state    <= control_byte1;
                  link_sda <= 1;
                  sda_buf  <= 0;
                end 
            end
          control_byte1:
            begin
              if((cnt_num<4'd8)&&(cnt==7'd124))
                begin                           
                  cnt_num  <= cnt_num + 1;
                  sda_buf  <= data[7];
                  data     <= {data[6:0],data[7]};
                end
              else if((cnt_num==4'd8)&&(cnt==7'd124))
                begin        
                  state    <= ack1; 
                  link_sda <= 0;
                  cnt_num  <= 0;
                end
            end 
          ack1:
            begin
    //          if((scl==1)&&(cnt==7'd61)&&(sda==1'b0))
    //            begin
    //              state    <= high_addr_byte;
    //              data     <= 8'b00000000;  //高字节地址准备
    //              link_sda <= 1;
    //            end
              if((scl==1)&&(cnt==7'd61)) 
                begin
                  state    <= high_addr_byte; 
                  data     <= 8'b00000000;  //高字节地址准备
    //              link_sda <= 1;
                end
            end
          high_addr_byte:
            begin
              if(cnt==7'd124)
                begin
                  link_sda <= 1;
                end
              if((cnt_num<4'd8)&&(cnt==7'd124))
                begin 
                  cnt_num  <= cnt_num + 1;  
                  sda_buf  <= data[7];
                  data     <= {data[6:0],data[7]};
                end
              else if((cnt_num==4'd8)&&(cnt==7'd124))
                begin
                  state    <= ack2;
                  link_sda <= 0;
                  cnt_num  <= 0;
                end
            end
          ack2:
            begin
    //          if((scl==1)&&(cnt==7'd61)&&(sda==1'b0))
    //            begin
    //              state    <= low_addr_byte;
    //              data     <= 8'b00000000;  //低字节地址准备
    //              link_sda <= 1;
    //            end
             if((scl==1)&&(cnt==7'd61))
               begin
                 state    <= low_addr_byte;
                 data     <= 8'b00000000;  //低字节地址准备
    //             link_sda <= 1;
               end 
             end
          low_addr_byte:
            begin
              if(cnt==7'd124)
                begin
                  link_sda <= 1;
                end
              if((cnt_num<4'd8)&&(cnt==7'd124))
                begin
                  cnt_num  <= cnt_num + 1;
                  sda_buf  <= data[7];
                  data     <= {data[6:0],data[7]};
                end
              else if((cnt_num==4'd8)&&(cnt==7'd124))
                begin
                  state    <= ack3;
                  link_sda <= 0;
                  cnt_num  <= 0;
                end 
            end
          ack3:
            begin
    //          if((scl==1)&&(cnt==7'd61)&&(sda==1'b0))
    //            begin
    //              link_sda <= 1;          
    //              if(wr==1)
    //                begin
    //                  state    <= transfer_data;
    //                  data     <= 8'b10101010;//准备想要写入的数据
    //                end               
    //              if(rd==1)
    //                begin
    //                  state    <= start2;
    //                  sda_buf  <= 1;//准备再次发启动信号
    //                end
    //            end 
              if((scl==1)&&(cnt==7'd61))
                begin
    //              link_sda <= 1;
                  if(wr==1)
                    begin
                      state    <= transfer_data;
                      data     <= 8'b10101010;//准备想要写入的数据
                    end
                  if(rd==1)
                    begin
                      state    <= start2;
                      sda_buf  <= 1;//准备再次发启动信号
                    end 
                end             
            end
          start2:
            begin
              if(cnt==7'd124)
                begin
                  link_sda <= 1;
                end
              if((scl==1)&&(cnt==7'd61))
                begin
                  state    <= control_byte2;              
                  sda_buf  <= 0;
                  data     <= 8'b10100001;  //读控制字准备
                end 
            end    
          control_byte2:
            begin
              if((cnt_num<4'd8)&&(cnt==7'd124))
                begin                           
                  cnt_num  <= cnt_num + 1;
                  sda_buf  <= data[7];
                  data     <= {data[6:0],data[7]};
                end
              else if((cnt_num==4'd8)&&(cnt==7'd124))
                begin
                  state    <= ack4;
                  link_sda <= 0;
                  cnt_num  <= 0;
                end
            end
          ack4:
            begin
    //          if((scl==1)&&(cnt==7'd61)&&(sda==1'b0))
    //            begin
    //              state    <= transfer_data;
    //              data     <= 8'b00000000;  //低字节地址准备
    //              link_sda <= 0;
    //            end 
              if((scl==1)&&(cnt==7'd61))
                begin
                  state    <= transfer_data;
                  data     <= 8'b00000000;  //低字节地址准备
    //              link_sda <= 0;
                end
            end
          transfer_data:
            begin
              if(wr==1)
                begin
                  if(cnt==7'd124)
                    begin
                      link_sda <= 1;
                    end
                  if((cnt_num<4'd8)&&(cnt==7'd124))
                    begin                           
                      cnt_num  <= cnt_num + 1;
                      sda_buf  <= data[7];
                      data     <= {data[6:0],data[7]};
                    end
                  else if((cnt_num==4'd8)&&(cnt==7'd124))
                    begin
                      state    <= ack5;
                      link_sda <= 0;
                      cnt_num  <= 0;
                      wr       <= 0;
                      led      <= 0;
                    end
                end 
              if(rd==1) 
                begin
                  if(cnt==7'd124)
                    begin
                      link_sda <= 0;
                    end
                  if((cnt_num<4'd8)&&(cnt==7'd124))
                    begin                           
                      cnt_num  <= cnt_num + 1;
                      result   <= {result[6:0],sda};         
                    end
                  else if((cnt_num==4'd8)&&(cnt==7'd124))
                    begin
                      state    <= no_ack;
                      link_sda <= 1;
                      cnt_num  <= 0;
                      sda_buf  <= 1;
                      rd       <= 0;
                    end
                end
            end
          ack5:
            begin
    //          if((scl==1)&&(cnt==7'd61)&&(sda==1'b0))
    //            begin
    //              state    <= stop;
    //                link_sda <= 1;             
    //            end
              if((scl==1)&&(cnt==7'd61))
                begin
                  state    <= stop; 
    //              link_sda <= 1;
                end
            end
          no_ack:
            begin
              if(cnt==7'd124)
                begin
                  state    <= stop;
                  link_sda <= 1; 
                  sda_buf  <= 0;
                end
            end
          stop:
            begin
              if(cnt==7'd124)
                begin
                  link_sda <= 1;
                end
              if((scl==1)&&(cnt==7'd61))
                begin
                  sda_buf  <= 1;
                  state    <= IDLE; 
                end
            end
          default:state<=0;
        endcase  
              
    
    endmodule
    View Code

      说明:由于仿真中没有嵌入EEPROM仿真模型,因此,无法给出ACK应答信号,没有应答信号,状态机就没办法继续向下跳转。所以为了完成仿真,就在代码中屏蔽了所有的ACK检测。在仿真中,只要看本该出现ACK信号的时候,sda信号是不是蓝色高组态。如果是高组态就表示仿真没有问题。在实际工程中,只需要把代码中屏蔽掉的if语句放开,把对应的if语句(仿真用的)屏蔽掉就可以直接用了

  • 相关阅读:
    EXT2
    Ext.DomQuery

    spring.net+nhibernate
    网址
    一句代码就解决一个开发中常见的小bug....
    Swift 已经正式开源了! 你了解到了吗?
    [知识点随笔] UIView--transform 属性
    懒加载(延迟加载)之后,在使用数据过程中容易出现的bug
    java设计模式
  • 原文地址:https://www.cnblogs.com/xingjian92/p/9021502.html
Copyright © 2020-2023  润新知