• 【FPGA】verilog实现的i2c接口控制


     i2c协议规范:

     一、时钟

    首先第一步是产生fast-mode的400khz的scl速率,假设方波高低电平各占一半,即1.25us,理论上不满足规范上scl低周期1.3us,但是绝大多数器件都支持稍微超过400khz的速率。

    我们仍打算产生一个规范内的速率。输入时钟clk=20mhz,计数12+1次后翻转,即可产生一个周期为13x2x50ns=1.3us的方波clk_800,这个周期满足规范。

    也可以改变计数方式,产生一个高低占比不一样的波形,可以适当提高速率,同时也满足规范。

    //    +clk2------------------------------------------
    //    1) 20mhz -> clk2 -> SCL-400khz  clk2==2xSCL
    //    2) cnt = 20m/800k = 25 /2 = 12 cnt[3:0]
    //    -clk2------------------------------------------
    always @(posedge clk or negedge rst_n)
        if(rst_n==0)
            begin
            cnt_clk <= 0;
            clk_800k <= 0;
            end
        else if(cnt_clk==12)
            begin
            cnt_clk <= 0;
            clk_800k <= ~clk_800k;// scl@falling sda@rising
            end
        else
            cnt_clk <= cnt_clk +1;

     二、我们的规划是,scl在clk_800k的下降沿翻转,而sda在clk_800k的上升沿变化,这样就能保证在scl的高电平周期,sda是绝对稳定的,同时也能方便地产生start/stop条件。

    根据上面clk的计数,可知scl的周期是1.3us x2=2.6us,速率约是384khz,没到400k。

    //    +iic------------------------------------------
    //    1) 
    //    -iic------------------------------------------
    always @(negedge clk_800k or negedge rst_n)// negedge
        if(rst_n==0)
            iic_scl <= 0;
        else
            iic_scl <= ~iic_scl;// 400k

     三、接下来就是采用状态机控制产生协议要求的时序波形。在clk_800k的rising沿动作。

    IIC_start:检测到scl的高,将sda拉低,产生start条件。占用了1/4个scl周期,0.65us,完全满足start的建立条件;

    IIC_data_setup:这一节拍正好是scl的低周期,不需要判断,在sda上送出数据的MSB;

    IIC_data_hold:scl的高周期,sda必须保持不变。循环传出8个bit,完成一个字节后跳到下一状态;

    IIC_ack_setup:先要释放sda,让从器件去拉低响应;

    IIC_ack_hold:判断响应;如果不响应,或者我这里要求传完3个byte的数据,跳到下一状态;

    IIC_stop_setup:这一步主要是把sda拉低,准备做stop;

    IIC_stop_hold:在scl高周期,sda拉高,产生stop条件;结束。

    只要第一步的start条件切入准了,后面按照节拍来,就不需要反复判断scl的高低了。

    //    +iic------------------------------------------
    //    1)
    //    -iic------------------------------------------
    always @(posedge clk_800k or negedge rst_n)
        if(rst_n==0)
            begin
            NS_iic <= IIC_idle;
            iic_sda <= 1;
            finish_iic <= 0;
            end
        else
            case(NS_iic)
                IIC_idle:
                    begin
                    if(start_iic==1)
                        NS_iic <= IIC_start;
                    else
                        NS_iic <= IIC_idle;
                    end
                IIC_start:
                    begin
                    if(iic_scl==1)
                        begin
                        NS_iic <= IIC_data_setup;
                        iic_sda <= 0;// start condition
                        data_iic <= bram_dout[23:0];
                        cnt_byte <= 2;
                        cnt_bit <= 0;// 0->7
                        end
                //    else 
                //        NS_iic <= IIC_start;
                    end
                IIC_data_setup:// data setup
                    begin
                    NS_iic <= IIC_data_hold;
                    iic_sda <= data_iic[23];
                    cnt_bit <= cnt_bit -1;
                    end
                IIC_data_hold:// data hold
                    begin
                    if(cnt_bit==0) NS_iic <= IIC_ack_setup;
                    else NS_iic <= IIC_data_setup;
                    data_iic <= {data_iic[22:0],1'b0};
                    end
                IIC_ack_setup:// ack prepare
                    begin
                    NS_iic <= IIC_ack_hold;
                    iic_sda <= 1'bz;
                    end
                IIC_ack_hold:// ack response
                    begin
                    if(iic_sda==1||cnt_byte==0)
                        NS_iic <= IIC_stop_setup;
                    else
                        NS_iic <= IIC_data_setup;
                    cnt_byte <= cnt_byte -1;
                    end
                IIC_stop_setup:
                    begin
                    NS_iic <= IIC_stop_hold;
                    iic_sda <= 0;
                    finish_iic <= 1;
                    end
                IIC_stop_hold:
                    begin
                    NS_iic <= IIC_idle;
                    iic_sda <= 1;
                    finish_iic <= 0;
                    end
                default:
                    begin
                    NS_iic <= IIC_idle;
                    end
            endcase

    每次发送3字节的数据,addr+reg+data。cnt_byte是计数3个字节的cnt_bit是计数每个字节内的bit位。蓝色是相应位。

     start条件。发送的数据是AA,即10101010,跟计数位相对应。

     stop条件。

    //

  • 相关阅读:
    vue代码调试
    在vscode中无法使用yarn
    js滚动条计算公式
    chrome插件制作-高级篇
    网站js注入实现自动输入账号密码
    String、StringBuffer和StringBuilder有什么区别?
    待重写
    http协议
    待重写
    java内存加载机制
  • 原文地址:https://www.cnblogs.com/kevinchase/p/7411494.html
Copyright © 2020-2023  润新知