• I2C通信


    项目之前研究了I2C通信协议的实现,完成FPGA对视频解码芯片SAA7111A的初始化配置,设计实现了I2C主机对从机(SAA7111A)32个寄存器的写操作,因此只简单实现了I2C的写时序。

    这次重新梳理学习了I2C协议,借助黑金开发板设计I2C主机控制器完成对EEPROM(24LC02)的读写操作,设计单字节的写时序和随机读时序。通过按键将数据先入EEPROM,再通过按键选择将数据显示在数码管上进行验证。

    1. 时序介绍

    主要的时序如下所示:

    image

    数据线SDA在空闲状态时为高电平,在SCL高电平时拉低SDA表示开始,在SCL低电平时拉高SDA表示结束。数据在SCL低电平时变化,8位数据,高位在前,低位在后。一个数据字节后,接收器需要产生一个低电平,即拉低SDA,表示接收正确。

    写时序:

    image 

    读时序:

    image

    其中,应答位一般由接收器产生,在读时序时主机接收数据一般不产生应答位(NO ACK),除了在连续读模式下,一个数据读完需要拉低SDA产生应答位。

    2. 串行时钟线(SCL)

    首先要确定SCL时钟,根据系统时钟利用计数器完成SCL的100KHz的设置,这里SCL作为输出信号,因此为输出单向口。

    image

     1         //分频部分
     2 reg[2:0] cnt;    // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
     3 reg[8:0] cnt_delay;    //500循环计数,产生iic所需要的时钟
     4 reg scl_r;        //时钟脉冲寄存器
     5 
     6 always @ (posedge clk or negedge rst_n)
     7     if(!rst_n) cnt_delay <= 9'd0;
     8     else if(cnt_delay == 9'd499) cnt_delay <= 9'd0;    //计数到10us为scl的周期,即100KHz
     9     else cnt_delay <= cnt_delay+1'b1;    //时钟计数
    10 
    11 always @ (posedge clk or negedge rst_n) begin
    12     if(!rst_n) cnt <= 3'd5;
    13     else begin
    14         case (cnt_delay)
    15             9'd124:    cnt <= 3'd1;    //cnt=1:scl高电平中间,用于数据采样
    16             9'd249:    cnt <= 3'd2;    //cnt=2:scl下降沿
    17             9'd374:    cnt <= 3'd3;    //cnt=3:scl低电平中间,用于数据变化
    18             9'd499:    cnt <= 3'd0;    //cnt=0:scl上升沿
    19             default: cnt <= 3'd5;
    20             endcase
    21         end
    22 end
    23 
    24 
    25 `define SCL_POS        (cnt==3'd0)        //cnt=0:scl上升沿
    26 `define SCL_HIG        (cnt==3'd1)        //cnt=1:scl高电平中间,用于数据采样
    27 `define SCL_NEG        (cnt==3'd2)        //cnt=2:scl下降沿
    28 `define SCL_LOW        (cnt==3'd3)        //cnt=3:scl低电平中间,用于数据变化
    29 
    30 
    31 always @ (posedge clk or negedge rst_n)
    32     if(!rst_n) scl_r <= 1'b0;
    33     else if(cnt==3'd0) scl_r <= 1'b1;    //scl信号上升沿
    34        else if(cnt==3'd2) scl_r <= 1'b0;    //scl信号下降沿
    35 
    36 assign scl = scl_r;    //产生iic所需要的时钟
    分频产生SCL

    根据计数器的计数结果获得SCL的上升沿、高电平中间时刻、下降沿和低电平中间时刻。四个信号作为系统时钟的使能信号,保持信号的同步,完成发送和接收。

    3. 串行数据线(SDA)

    串行数据线是双向口,作为输出口时,完成开始信号、结束信号、从机地址、字节地址和写数据的输出;作为输入口时,完成从机应答位和读数据的输入。因此需要实现一个三态口控制:

    assign sda = sda_link ? sda_r:1'bz;

    本实验设计了一段式的状态机控制串行数据口的输入和输出,涉及单字节写时序和随机读时序。

    image

    image

    由时序可知,前两次数据字节操作一样,可共享代码。在第3个数据字节处理时,写时序进行之前同样的操作即可,最后产生停止位;读时序时先发送从机地址读操作命令字节(最后一位为1),然后SDA口设置为输入口读取数据,最后FPGA无需产生应答位而产生停止位即可。返回IDLE状态前,产生清零标志以清零上次按键结果。

    写一个字节:

     1             IDLE:    begin
     2                     sda_link <= 1'b1;            //数据线sda为output
     3                     sda_r <= 1'b1;
     4                     if(!sw1_r || !sw2_r) begin    //SW1,SW2键有一个被按下            
     5                         db_r <= `DEVICE_WRITE;    //送器件地址(写操作) //写读控制字节
     6                         cstate <= START1;        
     7                         end
     8                     else cstate <= IDLE;    //没有任何键被按下
     9                 end
    10             START1:if(`SCL_HIG) begin        //scl为高电平期间
    11                         sda_link <= 1'b1;    //数据线sda为output
    12                         sda_r <= 1'b0;        //拉低数据线sda,产生起始位信号
    13                         cstate <= ADD1;
    14                         num <= 4'd0;        //num计数清零
    15                         end
    16                      else cstate <= START1; //等待scl高电平中间位置到来
    17 
    18             ADD1:    if(`SCL_LOW) begin
    19                             if(num == 4'd8) begin    
    20                                     num <= 4'd0;            //num计数清零
    21 //                                    sda_r <= 1'b1;
    22                                     sda_link <= 1'b0;        //sda置为高阻态(input)
    23                                     cstate <= ACK1;
    24                                 end
    25                             else begin
    26                                     cstate <= ADD1;
    27                                     num <= num+1'b1;
    28                                     case (num)
    29                                         4'd0: sda_r <= db_r[7];
    30                                         4'd1: sda_r <= db_r[6];
    31                                         4'd2: sda_r <= db_r[5];
    32                                         4'd3: sda_r <= db_r[4];
    33                                         4'd4: sda_r <= db_r[3];
    34                                         4'd5: sda_r <= db_r[2];
    35                                         4'd6: sda_r <= db_r[1];
    36                                         4'd7: sda_r <= db_r[0];
    37                                         default: ;
    38                                         endcase
    39                             //        sda_r <= db_r[4'd7-num];    //送器件地址,从高位开始
    40                                 end
    41                         end
    42             //        else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};    //器件地址左移1bit
    43                     else cstate <= ADD1;
    44 
    45             ACK1:    
    46 //                    if(/*!sda*/`SCL_NEG) begin    //注:24C01/02/04/08/16器件可以不考虑应答位
    47                     if(`SCL_HIG && !sda) begin   // SCL_HIG高电平时sda稳定,可以考虑`SCL_HIG && !sda和!sda效果一样
    48                             cstate <= ADD2;    //从机响应信号
    49                             db_r <= `BYTE_ADDR;    // 存储器读写地址        
    50                         end
    51                     else cstate <= ACK1;        //等待从机响应
    View Code

    读一个字节:

     1 //*********读操作起始位,先拉高SDA,再拉低SDA    ******************************//            
     2             START2:if(`SCL_LOW) begin   //等待应答位高电平过去,检测一下个SCL的低电平!!!!
     3                         sda_link <= 1'b1;    //sda作为output
     4                         sda_r <= 1'b1;        //拉高数据线sda
     5                         cstate <= READ;
     6                         end 
     7                     else cstate <= START2;
     8             
     9             READ: if(`SCL_HIG) begin    //scl为高电平中间
    10                         sda_r <= 1'b0;        //拉低数据线sda,产生起始位信号
    11                         cstate <= ADD3;
    12                 end    
    13             
    14             ADD3:    //送读控制字节
    15                     if(`SCL_LOW) begin
    16                             if(num==4'd8) begin    
    17                                     num <= 4'd0;            //num计数清零
    18 //                                    sda_r <= 1'b1;
    19                                     sda_link <= 1'b0;        //sda置为高阻态(input)
    20                                     cstate <= ACK3;
    21                                 end
    22                             else begin
    23                                     num <= num+1'b1;
    24                                     case (num)
    25                                         4'd0: sda_r <= db_r[7];
    26                                         4'd1: sda_r <= db_r[6];
    27                                         4'd2: sda_r <= db_r[5];
    28                                         4'd3: sda_r <= db_r[4];
    29                                         4'd4: sda_r <= db_r[3];
    30                                         4'd5: sda_r <= db_r[2];
    31                                         4'd6: sda_r <= db_r[1];
    32                                         4'd7: sda_r <= db_r[0];
    33                                         default: ;
    34                                         endcase                                    
    35                                 //    sda_r <= db_r[4'd7-num];    //送EEPROM地址(高bit开始)        
    36                                     cstate <= ADD3;                    
    37                                 end
    38                         end
    39                 //    else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};    //器件地址左移1bit
    40                     else cstate <= ADD3;        
    41                     
    42             ACK3:    begin
    43 //                    if(/*!sda*/`SCL_NEG) begin
    44                     if(`SCL_HIG && !sda) begin
    45                             cstate <= wait_L;    //从机响应信号
    46 //                            sda_link <= 1'b0;
    47                         end
    48                     else cstate <= ACK3;         //等待从机响应
    49                 end
    50                 
    51             wait_L: if(/*`SCL_NEG*/`SCL_LOW) cstate <= DATA; //等待应答位高电平过去,检测一下个SCL的低电平!!!!
    52             
    53             DATA:    if(!sw2_r) begin      //读操作
    54                             if((`SCL_LOW) && (num==4'd8)) begin
    55                                 num <= 4'd0;            //num计数清零
    56                                 cstate <= NO_ACK;
    57                             end
    58                             else if(`SCL_HIG && (num<=4'd7) ) begin    
    59                                     num <= num+1'b1;    
    60                                     case (num)
    61                                         4'd0: read_data[7] <= sda;
    62                                         4'd1: read_data[6] <= sda;  
    63                                         4'd2: read_data[5] <= sda; 
    64                                         4'd3: read_data[4] <= sda; 
    65                                         4'd4: read_data[3] <= sda; 
    66                                         4'd5: read_data[2] <= sda; 
    67                                         4'd6: read_data[1] <= sda; 
    68                                         4'd7: read_data[0] <= sda; 
    69                                         default: ;
    70                                     endcase                                                                        
    71                     //                read_data[4'd7-num] <= sda;    //读数据(高bit开始)
    72                                    cstate <= DATA;
    73                 //                else if(`SCL_NEG) read_data <= {read_data[6:0],read_data[7]};    //数据循环右移
    74                             end
    75                             else cstate <= DATA;
    76                     end    
    View Code
  • 相关阅读:
    URL解析模式(伪静态)
    PHP各环境下的伪静态配置
    亚马逊-购书(电子)
    前端路由-JS实现
    SpringBoot 2.3.0.RELEASE版本后自定义404页面,SpringBoot 404错误兼容Ajax请求
    不设置DIV宽度水平居中,div不设置宽度居中
    js 保留两位小数,Js四舍五入,JavaScript Math四舍五入
    Laravel 自定义公共函数的引入
    EF Core3.1 CodeFirst动态自动添加表和字段的描述信息
    Android 高德地图API INVALID_USER_SCODE 错误
  • 原文地址:https://www.cnblogs.com/aikimi7/p/3905252.html
Copyright © 2020-2023  润新知