• 基于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)读时序

     

  • 相关阅读:
    Generate Parentheses
    Length of Last Word
    Maximum Subarray
    Count and Say
    二分搜索算法
    Search Insert Position
    Implement strStr()
    Remove Element
    Remove Duplicates from Sorted Array
    Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/571328401-/p/12639976.html
Copyright © 2020-2023  润新知