zoukankan      html  css  js  c++  java
  • 基于FPGA的IIC驱动设计

    一、IIC基本概念

        IIC 总线(I2C busInter-IC bus)是一个双向的两线连续总线,提供集成电路(ICs)之间的通信线路。IIC总线是一种串行扩展技术,最早由Philips公司推出,广泛应用于电视,录像机和音频设备,IIC 线的意思是完成集成电路或功能单元之间信息交换的规范或协议Philips 公司推出的 IIC 总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展。 如图1所示:

    图1

    二、主机和从机的概念

        主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。

     

    三、传输速率

        IIC 总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/sIIC总线上的主设备与从设备之间以字节(8)为单位进行双向的数据传输,IIC协议为半双工协议。 

    四、器件地址的概念

       每个 IIC 器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比如常见的 IIC 接口的 EEPROM 存储器,留有 3 个控制地 址的引脚,由用户自己在硬件设计时确定)。

     

    五、总体时序简介

    图2

        在IIC器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原 因处于高电平状态,此时IIC总线处于空闲状态。如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数 据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生 是在SCL为高电平时, SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。 IiC 整体时序如图2所示,起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传 输状态,主机可以向从机写数据, 也可以读取从机输出的数据,数据的传输由双向数据线( SDA) 完成。停止信号产生后,总线再次处于空闲状态。

        由于IIC总线协议启动和停止信号都是在SCL高电平期间发生跳变的(当不发送或接收数据时,SCL一直处于高电平),这就决定了我们其他数据的改变只能发生在SCL低电平期间,在SCL为高电平期间,数据必须保持稳定。即在SCL低电平时改变数据,在SCL高电平时采集数据。

    六、三态电路设计

        主机(FPGA)给从机发送信号时,如传输启动信号,停止信号,数据位时,sda为输出;从机给主机发送信号,如ACK,读取数据时,sda充当输入,如图3所示;

     

     图3


    七、单字节写时序

    图4 单字节写时序

        由时序图可以看出,如果要向EEPROM 24C64中写入1字节,那么必须经过以下步骤。

            (1)发送启动信号;

            (2)发送器件地址(CONTROL BYTE);

            (3)接收并检测EEPROM发送过来的应答信号(ACK);

            (4)发送高字节地址位(ADDRESS HIGH BYTE);

            (5)接收并检测EEPROM发送过来的应答信号(ACK);

            (6)发送低字节地址位(ADDRESS LOW BYTE);

            (7)接收并检测EEPROM发送过来的应答信号(ACK);

            (8)发送8bit有效数据(DATA);

            (9)接收并检测EEPROM发送过来的应答信号(ACK);

            (10)发送停止信号(STOP);

    八、单字节读时序

    图5 单字节读时序

     

        由时序图可以看出,如果要向EEPROM 24C64中读出1字节,那么必须经过以下步骤。

            (1)发送启动信号;

            (2)发送器件地址1010_0000(CONTROL BYTE);

            (3)接收并检测EEPROM发送过来的应答信号(ACK);

            (4)发送高字节地址位(ADDRESS HIGH BYTE);

            (5)接收并检测EEPROM发送过来的应答信号(ACK);

            (6)发送低字节地址位(ADDRESS LOW BYTE);

            (7)接收并检测EEPROM发送过来的应答信号(ACK);

            (8)发送启动信号;

            (9)发送器件地址1010_0001(CONTROL BYTE);

            (10)接收并检测EEPROM发送过来的应答信号(ACK);

            (11)读取1字节的数据(DATA);

            (12)发送NO_ACK信号;

            (13)发送停止信号(STOP);

     

    九、时序设计

       (1)启动信号:在SCL为高电平期间,sda出现从高到低的跳变沿,代表启动信号;

       (2)器件地址:EEPROM 24C64 的写时序时的器件地址为:8'b1010_0000,读时序时的器件地址为:8'b1010_0001;

       (3)高、低位的字地址:由于24C64有64bit的存储空间,所以我们需要13位的地址位宽才能寻址所有的存储空间,由于IIC协议规定只能以字节形式写入,所以必须将13位的地址扩展为16位的地址,分为高8位和低8位,多出来的前三位填充任意数据即可。

       (4)停止信号:在SCL为高电平期间,sda出现从低到高的跳变沿,代表停止信号;

       (5)应答信号:应答信号是由数据接收方发出的,当SCL为高电平期间,如果监测到SDA为低电平,则说明有应答信号;

       (6)非应答信号:非应答信号也是由数据接收方发出的,当SCL为高电平时,如果SDA为高电平,则说明有非应答信号;

    九、代码设计(代码还未上板验证)

       功能描述:按键读写EEPROM 24C64

      1 module iic_test(
      2   clk,
      3   rst_n,
      4   
      5   scl,
      6   sda,
      7   
      8   key_rd,
      9   key_wr,
     10   
     11   //data_in,
     12   data_out
     13 );
     14 
     15 input clk;
     16 input rst_n;
     17 
     18 input key_rd;
     19 input key_wr;
     20 //input[7:0] data_in;
     21 
     22 output scl;
     23 output reg[7:0] data_out;
     24 
     25 inout sda;
     26 
     27 wire flag;
     28 reg sda_buffer;
     29 reg scl_r;
     30 
     31 reg[11:0] current_state,next_state;
     32 
     33 parameter W_IDLE              = 12'd0;
     34 parameter W_START            = 12'd1;
     35 parameter W_DEVICE_ADD     = 12'd2;
     36 parameter W_ACK1              = 12'd3;
     37 parameter W_WORD_H_ADD     = 12'd4;
     38 parameter W_ACK2           = 12'd5;
     39 parameter W_WORD_L_ADD     = 12'd6;
     40 parameter W_ACK3           = 12'd7;
     41 parameter W_DATA           = 12'd8;
     42 parameter W_ACK4           = 12'd9;
     43 parameter W_STOP           = 12'd10;
     44 
     45 parameter R_START            = 12'd11;
     46 parameter R_DEVICE_ADD      = 12'd12;
     47 parameter R_ACK1              = 12'd13;
     48 parameter R_DATA             = 12'd14;
     49 parameter R_NO_ACK            = 12'd15;
     50 parameter R_STOP             = 12'd16;
     51 
     52 reg[1:0] en;
     53 always@(posedge clk or negedge rst_n)  // en信号设计
     54   if(!rst_n)
     55     en <= 2'b00;
     56   else begin
     57     if(!key_wr)
     58        en <= 2'b01;
     59      else begin
     60        if(!key_rd)
     61           en <= 2'b10;
     62      end
     63   end
     64 
     65 parameter SYS_CLOCK = 50_000_000;
     66 parameter SCL_CLOCK = 400_000;
     67 localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK;
     68 
     69 reg[11:0] cnt;
     70 always@(posedge clk or negedge rst_n) begin
     71   if(!rst_n)
     72     cnt <= 12'd0;
     73   else if(cnt == SCL_CNT_M - 1)
     74     cnt <= 12'd0;
     75   else
     76     cnt <= cnt + 1'b1;
     77 end
     78 
     79 always@(posedge clk or negedge rst_n) begin
     80   if(!rst_n)
     81     scl_r <= 1'b1;
     82   else if(cnt == SCL_CNT_M >> 1)
     83     scl_r <= 1'b0;
     84   else if(cnt == 12'd0)
     85     scl_r <= 1'b1;
     86   else
     87     scl_r <= scl_r;
     88 end
     89 
     90 wire scl_l;
     91 wire scl_h;
     92 assign scl_h = (cnt == SCL_CNT_M >> 2) ? 1'b1 : 1'b0;  // scl 高电平的中间
     93 
     94 assign scl_l = (cnt == (SCL_CNT_M >> 1) + (SCL_CNT_M >> 2)) ? 1'b1 : 1'b0;  // scl低电平的中间
     95 
     96 reg[4:0] count;
     97 reg[7:0] memory;     
     98 always@(posedge clk or negedge rst_n)  // 同步时序,次态寄存器迁移到现态寄存器
     99   if(!rst_n)
    100     current_state <= W_IDLE;
    101   else if(scl_l)
    102     current_state <= next_state;
    103      
    104 always@(*) begin  // 状态转移条件判断
    105   next_state = W_IDLE;
    106     case(current_state)
    107        W_IDLE:begin
    108           if(en != 2'b00)
    109             next_state = W_START;
    110           else
    111             next_state = W_IDLE;
    112         end
    113         
    114         W_START:begin
    115           if(scl_l)
    116             next_state = W_DEVICE_ADD;
    117           else
    118             next_state = W_START;
    119         end
    120         
    121         W_DEVICE_ADD:begin
    122           if((count == 5'd8) && (scl_l))
    123             next_state = W_ACK1;
    124           else
    125             next_state = W_DEVICE_ADD;
    126         end
    127         
    128         W_ACK1:begin
    129           if(scl_l)
    130             next_state = W_WORD_H_ADD;
    131           else
    132             next_state = W_ACK1;
    133         end
    134         
    135         W_WORD_H_ADD:begin
    136           if((count == 5'd8) && (scl_l))
    137             next_state = W_ACK2;
    138           else
    139             next_state = W_WORD_H_ADD;
    140         end
    141         
    142         W_ACK2:begin
    143           if(scl_l)
    144             next_state = W_WORD_L_ADD;
    145           else
    146             next_state = W_ACK2;
    147         end
    148         
    149         W_WORD_L_ADD:begin
    150           if((count == 5'd8) && (scl_l))
    151             next_state = W_ACK3;
    152           else
    153             next_state = W_WORD_L_ADD;
    154         end
    155         
    156         W_ACK3:begin
    157           if(scl_l) begin
    158             if(en == 2'b01)
    159                next_state = W_DATA;
    160              else if(en == 2'b10)
    161                next_state = R_START;
    162           end
    163           else
    164             next_state = W_ACK3;   
    165         end
    166         
    167         W_DATA:begin
    168           if((count == 5'd8) && (scl_l))
    169             next_state = W_ACK4;
    170           else
    171             next_state = W_DATA;
    172         end
    173         
    174         W_ACK4:begin
    175           if(scl_l)
    176             next_state = W_STOP;
    177           else
    178             next_state = W_ACK4;
    179         end
    180         
    181         W_STOP:begin
    182           if(scl_l)
    183             next_state = W_IDLE;
    184           else
    185             next_state = W_STOP;
    186         end
    187         
    188         R_START:begin
    189           if(scl_l)
    190             next_state = R_DEVICE_ADD;
    191           else
    192             next_state = R_START;
    193         end
    194         
    195         R_DEVICE_ADD:begin
    196           if((count == 5'd8) && (scl_l))
    197             next_state = R_ACK1;
    198           else
    199             next_state = R_DEVICE_ADD;
    200         end
    201         
    202         R_ACK1:begin
    203           if(scl_l)
    204             next_state = R_DATA;
    205           else
    206             next_state = R_ACK1;
    207         end
    208         
    209         R_DATA:begin
    210           if((count == 5'd8) && (scl_l))
    211             next_state = R_NO_ACK;
    212           else
    213             next_state = R_DATA;
    214         end
    215         
    216         R_NO_ACK:begin
    217           if(scl_l)
    218             next_state = R_STOP;
    219           else
    220             next_state = R_NO_ACK;
    221         end
    222         
    223         R_STOP:begin
    224           if(scl_l)
    225             next_state = W_IDLE;
    226           else
    227             next_state = R_STOP;
    228         end
    229         
    230         default:next_state = W_IDLE;
    231     endcase
    232 end
    233 
    234 always@(posedge clk or negedge rst_n)  // 状态输出
    235   if(!rst_n) begin
    236      sda_buffer <= 1'b1;
    237      count <= 5'd0;
    238      //data_out <= 8'b00000000;
    239      memory <= 8'b00000000;
    240   end
    241   else if(scl_l) begin
    242     case(next_state)
    243        W_IDLE:begin
    244           sda_buffer <= 1'b1;
    245         end
    246         
    247         W_START:begin
    248           sda_buffer <= 1'b0;  //起始位
    249           count <= 5'd0;
    250           memory <= 8'b10100000;  // 写器件地址
    251         end
    252         
    253         W_DEVICE_ADD:begin
    254           count <= count + 1'b1;
    255           sda_buffer <= memory[3'd7 - count];
    256         end
    257         
    258         W_ACK1:begin
    259           count <= 5'd0;
    260           memory <= 8'b00000000;  // 高八位字地址
    261         end
    262         
    263         W_WORD_H_ADD:begin
    264           count <= count + 1'b1;
    265           sda_buffer <= memory[3'd7 - count];
    266         end
    267         
    268         W_ACK2:begin
    269           count <= 5'd0;
    270           memory <= 8'b01010011;  // 低八位的字地址
    271         end
    272         
    273         W_WORD_L_ADD:begin
    274           count <= count + 1'b1;
    275           sda_buffer <= memory[3'd7 - count];
    276         end
    277         
    278         W_ACK3:begin
    279           count <= 5'd0;
    280           memory <= 8'b11110000;  // 写数据
    281         end
    282        
    283        W_DATA:begin
    284           count <= count + 1'b1;
    285           sda_buffer <= memory[3'd7 - count];
    286        end
    287         
    288         W_ACK4:begin
    289           count <= 5'd0;
    290         end
    291         
    292         W_STOP:begin
    293           sda_buffer <= 1'b1;
    294         end
    295         
    296         R_START:begin
    297           sda_buffer <= 1'b0;
    298           memory <= 8'b10100001;  // 读器件地址
    299           count <= 5'd0;
    300         end
    301         
    302         R_DEVICE_ADD:begin
    303           count <= count + 1'b1;
    304           sda_buffer <= memory[3'd7 - count];
    305         end
    306         
    307         R_ACK1:begin
    308           count <= 5'd0;
    309         end
    310         
    311         R_DATA:begin
    312           count <= count + 1'b1;
    313           //data_out <= {data_out[6:0],sda};  //memory[5'd7 - count] <= sda;  // data_out[5'd7 - count] <= sda;    data_out[5'd7 - count] <= sda;
    314         end
    315         
    316         R_NO_ACK:begin
    317           sda_buffer <= 1'b1;
    318         end
    319         
    320         R_STOP:begin
    321           sda_buffer <= 1'b0;
    322         end
    323         
    324         default:begin
    325           count <= 5'd0;
    326           sda_buffer <= 1'b1;
    327           memory <= 8'b00000000;
    328         end
    329     endcase 
    330   end
    331   else begin
    332     sda_buffer <= sda_buffer;
    333      count <= count;
    334   end
    335   
    336 always@(posedge clk or negedge rst_n) 
    337   if(!rst_n)
    338     data_out <= 8'b00000000;
    339   else if(scl_h) begin
    340     case(next_state)
    341        R_DATA:data_out[3'd7 - count] <= sda;
    342         default:;
    343     endcase
    344   end
    345   else
    346     data_out <= data_out;
    347   
    348 assign scl = ((current_state >= W_DEVICE_ADD && current_state <= W_ACK4) || 
    349               (current_state >= R_DEVICE_ADD && current_state <= R_NO_ACK)) ? scl_r : 1'b1;
    350   
    351 assign flag =((current_state == W_ACK1)||
    352              (current_state == W_ACK2) || 
    353                  (current_state == W_ACK3) || 
    354                  (current_state == W_ACK4) ||
    355                  (current_state == R_ACK1) ||
    356                  (current_state == R_DATA) 
    357                  
    358                  ) ? 1'b0 : 1'b1;
    359                  
    360 assign sda = flag ? sda_buffer : 1'bz;
    361   
    362 endmodule
    View Code

        测试代码

     1 `timescale 1s/1ps
     2 module iic_test_tb;
     3   reg clk;
     4   reg rst_n;
     5   
     6   reg key_rd;  // 低电平有效
     7   reg key_wr;  // 低电平有效
     8   
     9   wire[7:0] data_out;
    10   wire scl;
    11   wire sda;
    12   
    13 iic_test u0(
    14   .clk(clk),
    15   .rst_n(rst_n),
    16   
    17   .scl(scl),
    18   .sda(sda),
    19   
    20   .key_wr(key_wr),
    21   .key_rd(key_rd),
    22   .data_out(data_out)
    23 );
    24 
    25 initial
    26   clk = 1'b0;
    27   always #10 clk = ~clk;
    28   
    29 initial
    30   begin
    31     rst_n = 1'b0;
    32      key_rd = 1'b1;
    33      key_wr = 1'b1;
    34      #1000;
    35      rst_n = 1'b1;
    36      
    37      #800;
    38      #8000 key_wr = 1'b0;
    39      #40000;
    40      key_wr = 1'b1;
    41      
    42      #1000000;
    43      key_rd = 0;
    44      #40000;
    45      key_rd = 1'b1;
    46      
    47      #1000000;
    48      $stop;
    49   end
    50 endmodule
    51 
    52      
    View Code

        仿真结果

        (1)写时序

     

     

       写时序存在的一些问题,在停止信号上没有完全符合在SCL的高电平时,sda由0变为1。解决办法是在停止信号之前,再加一个状态,在这个状态中,令scl处于低电平的中间时,sda_buffer为0,这样在停止信号时,就可以由0变1.

       (2)读时序

     

     读时序也存在一些问题,停止信号出现了和写时序时同样的问题,没有完全符合预期,数据接收部分全为高阻状态。

      注:

        (1)文字叙述方面参考了小梅哥,正点原子,至芯科技的相关文章;

        (2)代码设计参考了小梅哥,CrazyBingo的设计思路;

    下面的代码为至芯科技的源码(最初的代码无法仿真成功,下面是我做了小小修改的代码)

      1 // Time : 2020.03.19 22:59
      2 // Describe : iic_driver
      3 module iic_driver(
      4   clk,
      5   rst_n,
      6   
      7   iic_scl,
      8   iic_sda,
      9   
     10   key_wr,
     11   key_rd,
     12   
     13   data_in,
     14   data_out
     15 );
     16 
     17 input clk;
     18 input rst_n;
     19 
     20 input key_rd;
     21 input key_wr;
     22 
     23 input[7:0] data_in;
     24 
     25 inout iic_sda;
     26 
     27 output reg iic_scl;
     28 output reg[7:0] data_out;
     29 
     30 reg[7:0] sda_buffer;
     31 reg flag;
     32 
     33 assign iic_sda = (flag) ? sda_buffer : 1'bz;
     34 
     35 reg[7:0] count;  
     36 reg clk_sys;
     37 
     38 always@(posedge clk or negedge rst_n)  // 分频为800KHz的时钟
     39   if(!rst_n) begin
     40     clk_sys <= 1'b0;
     41      count <= 8'd0;
     42   end
     43   else begin
     44     if(count < 31)
     45        count <= count + 1'b1;
     46      else begin
     47        count <= 8'd0;
     48         clk_sys <= ~clk_sys;
     49      end
     50   end
     51 
     52 reg[5:0] state;  // 状态寄存器
     53 
     54 always@(posedge clk_sys or negedge rst_n)  // iic_scl设计
     55   if(!rst_n)
     56     iic_scl <= 1'b1;
     57   else begin
     58     if(state > 0)  // 当总线忙的时候,iic_scl为近400kHz的时钟
     59        iic_scl <= ~iic_scl;
     60      else 
     61        iic_scl <= 1'b1;  // 空闲时为高电平
     62   end
     63   
     64 reg[1:0] en;
     65 always@(posedge clk or negedge rst_n)  // en信号设计
     66   if(!rst_n)
     67     en <= 2'b00;
     68   else begin
     69     if(!key_wr)
     70        en <= 2'b01;
     71      else begin
     72        if(!key_rd)
     73           en <= 2'b10;
     74      end
     75   end
     76   
     77 reg[3:0] cnt; // 发送或接收数据的个数
     78 reg[1:0] temp;  // 读/写使能中间寄存器
     79 reg[7:0] memory;  // 发送或接收数据的中间寄存器
     80 
     81 always@(posedge clk_sys or negedge rst_n)
     82   if(!rst_n) begin
     83      data_out <= 8'd0;
     84      flag <= 1'b1;        //复位时,系统获得总线的控制权,作为输出端口
     85      sda_buffer <= 1'b1;  //向 iic_scl 发送高电平
     86      state <= 0;
     87      temp <= 2'b0;
     88   end
     89   else
     90     case(state)
     91        0:begin
     92           if(iic_scl) begin
     93             if(en != temp) begin  // 有按键按下
     94                sda_buffer <= 1'b0;  // 发送启动信号
     95                 state <= 1;
     96                 temp <= en;
     97                 cnt <= 0;
     98                 memory <= 8'b10100000;
     99              end
    100              else
    101                state <= 0;   
    102           end
    103           else
    104             state <= 0; 
    105         end
    106         
    107         1:begin
    108           if((iic_scl == 0) && (cnt < 8)) begin  // 发送8位控制字,器件地址
    109             sda_buffer <= memory[7];
    110              cnt <= cnt + 1'b1;
    111              memory <= {memory[6:0],memory[7]};
    112              state <= 1;
    113           end
    114           else begin
    115             if((iic_scl == 0) && (cnt == 8)) begin
    116                cnt <= 0;
    117                 flag <= 0;  // 释放总线控制权
    118                 state <= 2;
    119              end
    120              else
    121                state <= 1;
    122           end
    123         end
    124         
    125         2:begin
    126           if(!iic_scl) begin  // 检测应答信号
    127             state <= 3;
    128              memory <= 8'd0;  // 高字节地址
    129           end
    130           else
    131             state <= 2;
    132         end
    133         
    134         3:begin  // 发送高字节地址
    135           if((iic_scl == 0) && (cnt < 8 )) begin
    136             flag <= 1'b1;  // 系统获得总线控制权
    137              sda_buffer <= memory[7];
    138              cnt <= cnt + 1'b1;
    139              memory ={memory[6:0],memory[7]};
    140              state <= 3;
    141           end
    142           else begin
    143             if((iic_scl == 0) && (cnt == 8)) begin
    144                cnt <= 0;
    145                 flag <= 0;  // 释放总线控制权
    146                 state <= 4;
    147              end
    148              else
    149                state <= 3;
    150           end
    151         end
    152         
    153         4:begin
    154           if(!iic_scl) begin  // 检测应答信号
    155             state <= 5;
    156              memory <= 8'b00110101;  // 低字节地址
    157           end
    158           else begin
    159             state <= 4;
    160           end
    161         end
    162         
    163         5:begin
    164           if((iic_scl == 0) && (cnt < 8)) begin  //发送低字节地址
    165             flag <= 1'b1;  // 获得总线控制权
    166              sda_buffer <= memory[7];
    167              cnt <= cnt + 1'b1;
    168              memory = {memory[6:0],memory[7]};
    169              state <= 5; 
    170           end
    171           else begin
    172             if((iic_scl == 0) && (cnt == 8)) begin
    173                cnt <= 0;
    174                 flag <= 0;  // 释放总线控制权
    175                 state <= 6;
    176              end
    177              else 
    178                state <= 5;
    179           end
    180         end
    181         
    182         6:begin
    183           if(!iic_scl) begin  // 检测应答信号
    184             if(temp == 2'b01) begin // 判断是否为写信号
    185                state <= 7;
    186                 memory = data_in[7:0];  // 发送数据
    187              end
    188              if(temp == 2'b10)  // 判断是否为读信号
    189                state <= 11;
    190           end
    191           else
    192             state <= 6;
    193         end
    194         
    195         7:begin
    196           if((iic_scl == 0) && (cnt < 8)) begin  // 发送数据
    197             flag <= 1'b1;  // 获得总线控制权
    198              sda_buffer <= memory[7];
    199              cnt <= cnt + 1'b1;
    200              memory = {memory[6:0],memory[7]};
    201              state <= 7;
    202           end
    203           else begin
    204             if((iic_scl == 0) && (cnt == 8)) begin
    205                cnt <= 0;
    206                 flag <= 0;  // 释放总线控制权
    207                 state <= 8;
    208              end
    209              else
    210                state <= 7;
    211           end
    212         end
    213         
    214         8:begin
    215           if(!iic_scl)  //检测应答信号
    216             state <= 9;
    217           else
    218             state <= 8;
    219         end
    220         
    221         9:begin
    222           if(iic_scl == 0) begin
    223             flag <= 1; //获得总线控制权
    224              sda_buffer <= 0;  //拉低IIC的数据线,为发送停止信号做准备
    225              state <= 10;
    226           end
    227           else
    228             state <= 9;
    229         end
    230         
    231         10:begin
    232           if(iic_scl == 1) begin
    233             sda_buffer <= 1;  // 发送停止信号
    234              state <= 0;
    235           end
    236           else
    237             state <= 10;
    238         end
    239 //--------------------------------------//        
    240         11:begin
    241           flag <= 1;  //获得总线控制权
    242           sda_buffer <= 1;  // 拉高IIC控制线(为发送启动信号做准备)
    243           state <= 12;
    244         end
    245         
    246         12:begin
    247           sda_buffer <= 0;  // 发送启动信号
    248           state <= 13;
    249           memory <= 8'b10100001; 
    250         end
    251         
    252         13:begin
    253           if((iic_scl == 0) && (cnt < 8)) begin  // 发送8位控制字
    254             flag <= 1;  // 获得总线控制权
    255              sda_buffer <= memory[7];
    256              cnt <= cnt + 1'b1;
    257              memory <= {memory[6:0],memory[7]};
    258              state <= 13;
    259           end
    260           else begin
    261             if((iic_scl == 0) && (cnt == 8)) begin
    262                cnt <= 0;
    263                 flag <= 0;  // 释放总线控制权
    264                 state <= 14;
    265              end
    266              else
    267                state <= 13; 
    268           end
    269         end
    270         
    271         14:begin
    272           if(!iic_scl)  // 检测应答信号
    273             state <= 15;
    274           else
    275             state <= 14;   
    276         end
    277         
    278         15:begin
    279           if((iic_scl == 1) && (cnt < 8)) begin  // 接收数据
    280             cnt <= cnt + 1'b1;
    281              memory <= {memory[6:0],iic_sda};
    282           end
    283           else begin
    284             if((iic_scl == 0) && (cnt == 8)) begin
    285                cnt <= 0;
    286                 flag <= 1;  // 获得总线控制权
    287                 state <= 16;
    288                 sda_buffer <= 1;  // 发送应答信号
    289              end
    290              else
    291                state <= 15;   
    292           end
    293         end
    294         
    295         16:begin
    296           data_out <= memory;  // 发送数据
    297           state <= 17;
    298         end
    299         
    300         17:begin
    301           if(iic_scl == 0) begin
    302             sda_buffer <= 0;  //拉低IIC的数据线,为发送停止信号做准备
    303              state <= 18;
    304           end
    305           else
    306             state <= 17;
    307         end
    308         
    309         18:begin  // 发送停止信号
    310           if(iic_scl == 1) begin
    311             sda_buffer <= 1;
    312              state <= 0;
    313           end
    314           else
    315             state <= 18; 
    316         end
    317         
    318         default: state <= 0;
    319      endcase
    320 
    321 endmodule
    322 
    323   
    324   
    325   
    326   
    327   
    328   
    329   
    330   
    331   
    332   
    333   
    334   
    335   
    336   
    337   
    338   
    339   
    View Code

    测试代码

     1 `timescale 1ns/1ps
     2 module iic_driver_tb;
     3   reg clk;
     4   reg rst_n;
     5   
     6   reg key_rd;
     7   reg key_wr;
     8   
     9   reg[7:0] data_in;
    10   
    11   wire iic_sda;
    12   
    13   wire iic_scl;
    14   wire[7:0] data_out;
    15   
    16 iic_driver u0(
    17   .clk(clk),
    18   .rst_n(rst_n),
    19   
    20   .iic_scl(iic_scl),
    21   .iic_sda(iic_sda),
    22   
    23   .key_wr(key_wr),
    24   .key_rd(key_rd),
    25   
    26   .data_in(data_in),
    27   .data_out(data_out)
    28 );
    29 
    30 initial
    31   clk = 0;
    32   always #10 clk = ~clk;
    33   
    34 initial
    35   begin
    36     rst_n = 1'b0;
    37      key_rd = 1'b1;
    38      key_wr = 1'b1;
    39      data_in = 1'b0;
    40      
    41      #1000;
    42      rst_n = 1'b1;
    43      
    44      #800;
    45      
    46      #8000 key_wr = 1'b0;
    47      data_in = 8'h23;
    48      #4000 key_wr = 1'b1;
    49      
    50      #1000000;
    51      key_rd = 1'b0;
    52      #40000;
    53      key_rd = 1'b1;
    54      
    55      #10000000;
    56      $stop;
    57   end
    58 endmodule
    View Code

    仿真结果:

        (1)写时序

         (2)读时序

     

  • 相关阅读:
    [转]MySQL日志——Undo | Redo
    linux查看系统的硬件信息
    Linux HDD information (SATA/SCSI/SAS/SSD)
    sysbench 0.5使用手册
    MYSQL数据丢失讨论
    innodb_flush_method理解
    快速从mysqldump文件中恢复一个表
    Python 交互模式中 Delete/Backspace 键乱码问题
    Django--源码安装
    greenplum-时间处理
  • 原文地址:https://www.cnblogs.com/571328401-/p/12639976.html
Copyright © 2011-2022 走看看