FPGA实现SCCB协议和IIC协议

在CMOS摄像头sensor里面,发送初始化命令的接口叫SSBC,其实就是为了规避IIC的专利权,当然现在没有这个问题了。

SCCB协议实现(因为类似iic 所以下面的信号名全按iic起的)
SCCB有两根信号线或者三根线

  1. 时钟线iic_clk 位宽 1bit
  2. 数据线iic_data 位宽 1bit
  3. 片选信号SIO_E 位宽 1bit(只有一个设备的话可以没有他)
    强调:
  4. 时钟线始终掌控在主机手里,从机根据时钟信号配合工作。
  5. iic_data是一个三态门,双向数据线,既可以由主机控制,也可以由从机控制

状态:
空闲状态 iic_clk= 1; iic_data = 1;
开始标志 在iic_clk 为高电平的时候iic_data 产生一个下降沿
结束标志 在iic_clk位高电平的时候,iic_data产生一个上升沿 释放掉对总线的控制权。

类比uart学习:总共分为两大阶段

第一大阶段
空闲状态
产生开始标志
写从机设备ID 从机在高电平中间抓取iic_data 的数据。一次写八个时钟周期具体为7位ID地址+1位读写控制R/!W 此时的读写控制必须为写不管你是想要读还是写 就是这么不讲理 所以第八位为0
第九个时钟周期 放弃对iic_data 的控制 从机控制iic_data产生一个低电平的ACK (即下文的don’t care)
写寄存器地址 同写从机设备ID(但是全是地址,没有读写控制位)
第九个时钟周期 同上
先总结一个下:ID地址(7位ID地址+1位读写控制+don’tcare) + 要写的寄存器地址(8位寄存器地址+don’t care)
此时算是确定了设备对象和要写的寄存器地址
在此插播一段SCCB和IIC的区别。
因为SCCB没有重复起始的概念,因此在SCCB的读周期中,当主机发送完片内寄存器地址后,必须发送总线停止条件。不然在发送读命令时,从机将不能产care响应信号。即每一个传输都要有开始和结束来释放总线 (start +sotp),这也是SCCB与I2C不同的一个地方。而I2C只需要产生一个start就可以了。
重复起始的概念:在主控器控制总线期间完成了一次数据通信(发送或接收)之后,如果想继续占用总线再进行一次数据通信(发送或接收),而又不释放总线,就需要利用重启动Sr信号时序。重启动信号Sr既作为前一次数据传输的结束,又作为后一次数据传输的开始。利用重启动信号的优点是,在前后两次通信之间主控器不需要释放总线,这样就不会丢失总线的控制权,即不让其他主器件节点抢占总线。
第二大阶段
接下来才是读写的不同:
假设是写:

  1. 则只需要在向里面数据就行,就像写ID和写地址一样简单。同时对方必然会给你发一个ACK信号,这就是个握手信号,少不了的。
  2. 写完后产生结束标志
    但是读操作这个调皮孩子就麻烦很多了
    假设是读:
  3. 产生一个结束标志 释放掉对总线的控制权。
  4. 产生一个开始标志 在申请到总线的控制权。
  5. 向里面再写一次设备ID+1位读写控制位 此时读写控制为读,也就是1。当你写完设备ID+读写控制位后,从机就翻身独立把歌唱,拿到了数据线的控制权(ack时,也是从机掌控数据线的控制权,但是毕竟太短,不够过瘾)
  6. 拿到控制权后,从机会保在的iic_clk每个高电平时候iic_data线上的数据稳定有效。
  7. 当从机给主机传输了8bit数据后,主机给从机发送一个NA_ACK 。该响应为高电平。
  8. 产生一个stop标志
    这就算一次读操作完成了。

当然了理想很丰满,现实很骨感。写操作很快就实现,读用了4天的时间,期间又是查手册看时序,又是用chipscrop抓信号,时序图都符合,但是一直读到的是全零。身心俱疲,实在懒得改了,就先这样吧。
代码奉上,有人要是看出来哪有问题,记得告诉我。

`timescale 1ns/1ns
module iic_control(
    input               sys_clk         , //sys_clk = 20KHz
    input               rst_n           ,
    //config_reg interface
    input               w_data_en       ,
    input               r_ov_en         ,
    input       [ 7:0]  config_addr     ,
    input       [ 7:0]  config_data     ,
    output  reg         config_end      ,
    output  reg         r_end           ,
    output  reg [ 7:0]  r_data          ,
    //ov7670
    output  reg         iic_clk         ,   //10KHz
 inout               iic_data        
);

    parameter       IDLE         =  4'd0        ;
    parameter       START        =  4'd1        ;  
    parameter       ID_ADDR      =  4'd2        ;
    parameter       ACK_ID       =  4'd3        ;
    parameter       REG_ADDR     =  4'd4        ;
    parameter       ACK_AD       =  4'd5        ;
    parameter       W_DATA       =  4'd6        ;
    parameter       ACK_W        =  4'd7        ;
    parameter       R_STOP       =  4'd8        ;
    parameter       R_START      =  4'd9        ;
    parameter       R_ID_ADDR    =  4'd10       ;
    parameter       ACK_RI       =  4'd11       ;
    parameter       R_DATA       =  4'd12       ;
    parameter       NA_ACK       =  4'd13       ;
    parameter       STOP         =  4'd14       ;


    reg     [ 7:0]      id_addr_w   = 8'h42;
    reg     [ 7:0]      id_addr_r   = 8'h43;
    (*KEEP = "TRUE"*)
    reg     [ 3:0]      state       ;
  (*KEEP = "TRUE"*)
    reg     [ 3:0]      tx_cnt      ;
    reg                 wen         ;
    reg                 ren         ;
    reg                 iic_data_r  ;
    reg     [ 7:0]      r_data_r    ;

    //(*KEEP = "TRUE"*)
    //reg                 iic_in_d    ;

    always@(posedge sys_clk or negedge rst_n)begin
        if(!rst_n)
            state   <=  IDLE    ;
        else case(state)
            IDLE        :   if(w_data_en || r_ov_en)
                                state   <=  START       ;
            START       :   state   <=  ID_ADDR         ;

            ID_ADDR     :   if(tx_cnt == 15 )
                                state   <=  ACK_ID      ;
            ACK_ID      :   if(iic_clk && !iic_data)  //iic_clk 在低电平的时候且从机响应
                                state   <=  REG_ADDR    ;
            REG_ADDR    :   if(tx_cnt == 15)
                                state   <=  ACK_AD      ;               
            ACK_AD      :   if(iic_clk && !iic_data)
                                if(wen)
                                    state   <=  W_DATA  ;
                                else if(ren)
                                    state   <=  R_STOP  ;
            //分支一 写
            W_DATA      :   if(tx_cnt == 15)
                               state   <=  ACK_W        ;
            ACK_W       :   if(iic_clk  && !iic_data)
                                state   <=  STOP        ;
            //分支二 读
            R_STOP      :   if(iic_clk )
                                state   <=  R_START     ;
            R_START     :   state   <=  R_ID_ADDR   ;
            R_ID_ADDR   :   if(tx_cnt == 15)
                                state   <=  ACK_RI      ;
            ACK_RI      :   if(iic_clk && !iic_data)
                                state   <=  R_DATA      ;
            R_DATA      :   if(tx_cnt == 15)
                                state <=  NA_ACK        ;
            NA_ACK      :   if(iic_clk )
                                state   <=  STOP        ;

            STOP        :   if(iic_clk && iic_data)
                                state   <=  IDLE        ;
            default     :   state   <=  IDLE            ;
        endcase
    end


    always@(posedge sys_clk or  negedge rst_n)begin
        if(!rst_n)  
            tx_cnt  <=  'd0;
        else    case (state)
            IDLE,START  :   tx_cnt  <=  'd0;
            //发送设备ID数据的计数
            ID_ADDR     :   tx_cnt  <=  tx_cnt  + 1'b1;
            ACK_ID      :   tx_cnt  <=  'd0;
            //寄存器地址计数
            REG_ADDR    :   tx_cnt  <=  tx_cnt  + 1'b1;
            ACK_AD      :   tx_cnt  <=  'd0;
            //写数据计数
            W_DATA      :   tx_cnt  <=  tx_cnt  + 1'b1;
            ACK_W       :   tx_cnt  <=  'd0;
            //读寄存器地址计数
            R_ID_ADDR   :   tx_cnt  <=  tx_cnt  + 1'b1;
            ACK_RI      :   tx_cnt  <=  'd0;
            R_DATA      :   tx_cnt  <=  tx_cnt  + 1'b1;
            NA_ACK      :   tx_cnt  <=  'd0;
            //停止
            STOP        :   tx_cnt  <=  'd0;
            default     :   tx_cnt  <=  'd0;
        endcase
    end

    always@(posedge sys_clk or negedge  rst_n)begin
        if(!rst_n)
            iic_clk <=  1'd1;
        else if(state == IDLE || state == R_STOP  || state == STOP)
            iic_clk <=  1'd1;
        else
            iic_clk <=  ~iic_clk;
    end

    always@(posedge sys_clk or negedge  rst_n)begin
        if(!rst_n)
            wen <=  1'd1;
        else if(state == IDLE)
            wen <=  w_data_en;
        else if(state == STOP)
            wen <= 1'd0;
    end

    always@(posedge sys_clk or negedge  rst_n)begin
        if(!rst_n)
            ren <=  1'd1;
        else if(state == IDLE)
            ren <=  r_ov_en;
        else if(state == STOP)
            ren <= 1'd0;
    end


    always@(negedge sys_clk or negedge  rst_n)begin
        if(!rst_n)
            iic_data_r    <=  1'd1;
        else case(state)
            IDLE        :   iic_data_r  <=  1'd1;
            START       :   iic_data_r  <=  1'd0;
            ID_ADDR     :   iic_data_r  <=  id_addr_w   [7  - tx_cnt[3:1]];
            ACK_ID      :   iic_data_r  <=  1'dz;
            REG_ADDR    :   iic_data_r  <=  config_addr [7  - tx_cnt[3:1]];
            ACK_AD      :   iic_data_r  <=  1'dz;

            W_DATA      :   iic_data_r  <=  config_data [7  - tx_cnt[3:1]];
            ACK_W       :   iic_data_r  <=  1'dz;
            R_STOP      :   if(iic_clk)      iic_data_r    <=  1'd1;
                            else             iic_data_r    <=  1'd0;
            R_START     :   iic_data_r  <=  1'd0;
            R_ID_ADDR   :   iic_data_r  <=  id_addr_r   [7  - tx_cnt[3:1]];
            ACK_RI      :   iic_data_r  <=  1'dz;
            R_DATA      :   iic_data_r  <=  1'dz;
            NA_ACK      :   iic_data_r  <=  1'd1;
            STOP        :   if(iic_clk)      iic_data_r    <=  1'd1;
                            else             iic_data_r    <=  1'd0;
            default     :   iic_data_r  <=  1'd1;
        endcase
    end

    always@(negedge sys_clk or negedge rst_n)begin
        if(!rst_n)
            r_data_r  <= 'hff;
        else if(state == R_DATA && iic_clk)
            r_data_r  <= {r_data_r[6:0],iic_data};
    end


    always@(posedge sys_clk or negedge rst_n)begin
        if(!rst_n)
            r_data  <= 'h00;
        else if(ren && state == STOP)
            r_data  <= r_data_r;
    end

    assign  iic_data = iic_data_r;

    always@(posedge sys_clk or negedge rst_n)begin
        if(!rst_n)
            config_end  <= 'd0;
        else if( wen && state == STOP)
            config_end  <= 1'd1;
        else 
            config_end  <= 1'd0;
    end

    always@(posedge sys_clk or negedge rst_n)begin
        if(!rst_n)
            r_end  <= 'd0;
        else if(ren && state == STOP)
            r_end  <= 1'd1;
        else
            r_end  <= 1'd0;
    end
endmodule
编辑 重设标签(回车键确认) 标为违禁 关闭 合并 删除

提问于 2019-04-28 08:39:26 +0800

这个帖子被标记为一个社区wiki

这个帖子是一个wiki(维基). 任何一个积分 >500的人都可以完善它