iic接口介绍

最近遇到一个BUG,跟IIC通讯有关,因此借这个机会总结一下IIC总线协议缓存

1.引脚接口介绍ide

1.A0,A1,A2为24LC64的片选信号,IIC总线最多能够挂载8个IIC接口器件,经过对A0,A1,A2寻址,能够实现对不一样的EEPROM操做spa

2.WP为读写使能信号,当WP悬空或者接地,EEPROM可读可写,当WP接电源,EEPROM只能读不能写。由于咱们要对EEPROM写,因此这里WP信号悬空code

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

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

2.接口时序input

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

   2.1 写时序it

        

  写操做步骤event

  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语句(仿真用的)屏蔽掉就能够直接用了

相关文章
相关标签/搜索