在FPGA中使用Verilog实现I2C通信

来源:互联网 发布:广州网络宽带资费标准 编辑:程序博客网 时间:2024/06/08 12:03

按照I2C标准的官方时序

FPGA之I2C的实现
可以看出时序看起来很简单,不过它严格的按照时序要求来传送数据,马虎不得的,特别是起始和停止的条件,起始必须要时钟线SCL为高电平时数据线SDA拉低;而停止时必须要时钟线SCL为高电平时数据线SDA拉高;中间的数据的每一位传送都是必须要求在时钟线SCL为高定平时完成;

Verilog HDL程序采用基于状态机的时序设计实现,I2C速度为100KHz,本人开发板的晶振20Mhz。代码有点长,就截取状态机部分好了

`define DEVICE_WRITE 8'b1010_1010 //the data;
reg[7:0] db_r;  


parameter  IDLE  = 4'd0;
parameter  START1  = 4'd1;
parameter  DATA  = 4'd2;
parameter  ACK1  = 4'd3;
parameter  STOP1  = 4'd11;
parameter  STOP2  = 4'd12;

reg[3:0] cstate; 
reg sda_r;  
reg sda_link;  
reg[3:0] num; 


always @ (posedge clk or negedge rst_n) begin
 if(!rst_n) begin
   cstate <= IDLE;
   sda_r <= 1'b1;  
   sda_link <= 1'b0;  //input
   num <= 4'd0;
  end
 else   
  case (cstate)
   IDLE: begin
      sda_link <= 1'b1;   //output
      sda_r <= 1'b1; 
      db_r <= `DEVICE_WRITE; 
      cstate <= START1;  
     end
   START1: begin
     if(`SCL_HIG) begin  
      sda_r <= 1'b0;  
      cstate <= DATA;
      num <= 4'd0;  
      end
     else cstate <= START1;
    end
   DATA: begin
     if(`SCL_LOW) begin
       if(num == 4'd8) begin 
         num <= 4'd0;   
         sda_r <= 1'b1;
         sda_link <= 1'b0;  //(input)
         cstate <= ACK1;
        end
       else begin
         cstate <= DATA;
         num <= num+1'b1;
         case (num)
          4'd0: sda_r <= db_r[7];
          4'd1: sda_r <= db_r[6];
          4'd2: sda_r <= db_r[5];
          4'd3: sda_r <= db_r[4];
          4'd4: sda_r <= db_r[3];
          4'd5: sda_r <= db_r[2];
          4'd6: sda_r <= db_r[1];
          4'd7: sda_r <= db_r[0];
          default: ;
         endcase
        end
      end
     else cstate <= DATA;
    end
   ACK1: begin
     if(`SCL_NEG) begin 
       cstate <= STOP1;   
      end
     else cstate <= ACK1;  
    end
   STOP1: begin
     if(`SCL_LOW) begin
       sda_link <= 1'b1;
       sda_r <= 1'b0;
       cstate <= STOP1;
      end
     else if(`SCL_HIG) begin
       sda_r <= 1'b1; 
       cstate <= STOP2;
      end
     else cstate <= STOP1;
    end
   STOP2: begin
     if(`SCL_LOW) sda_r <= 1'b1;
     else if(cnt_20ms==20'hffff0) cstate <= IDLE;
     else cstate <= STOP2;
    end
   default: cstate <= IDLE;
   endcase
end

assign sda = sda_link ? sda_r:1'bz;

经过综合,给激励源并仿真后截图

FPGA之I2C的实现

可以看出,时序完全正确,还要注意的就是传完8位数据之后读取的响应位ACK;

0 0
原创粉丝点击