協議——SCCB與IIC的區別

  SCCB(Serial Camera Control Bus,串列攝影機控制匯流排)是由OV(OmniVision的簡稱)公司定義和發展的三線式串列匯流排,該匯流排控制著攝影機大部分的功能,包括影像數據格式、解析度以及影像處理參數等。結構框圖如下所示:

  OV公司為了減少感測器引腳的封裝,現在SCCB匯流排大多採用兩線式介面匯流排。OV7725使用的是兩線式介面匯流排,該介面匯流排包括SIO_C串列時鐘輸入線和SIO_D串列雙向數據線,分別相當於IIC協議的SCL訊號線和SDA訊號線。SIO_C的最小時間為10us,即最大頻率為100K。一般來說,100K-400K之間都可以。

  由此可見,SCCB就是改編版的IIC,完全可以按照IIC來理解,下面仔細講解SCCB的時序以及和IIC的不同之處。

一、SCCB起始和結束(與IIC完全一致)

起始:SIO_C為高時,SIO_D由高拉低。

  停止:SIO_C為高時,SIO_D由低拉高

二、SCCB寫(與IIC完全一致)

  ID Address(W)裡面就已經包括進了IIC中的「讀寫控制位」,所以沒有額外寫出。

  即:start + phase_1 + phase_2 + phase_3 + stop

  「X」的意思是「don't care」,該位是由從機發出應答訊號來響應主機表示當前ID Address、Sub-address和Write Data是否傳輸完成,但是從機有可能不發出應答訊號,因此主機(此處指FPGA)可不用判斷此處是否有應答,直接默認當前傳輸完成即可。「X」即IIC中的ACK應答位。

三、SCCB讀

  數據手冊中的SCCB讀只寫了上圖的Phase3和Phase4,實際上它是和Phase1和Phase2聯繫在一起的。SCCB不支援連續讀,Phase4的主機應答位必須為NA(no ack),即為1,所以SCCB讀其實就專指單次讀,和IIC單次讀幾乎一樣。

  區別就一點:在IIC讀傳輸協議中,寫完暫存器地址後會有restart即重複開始的操作;而SCCB讀傳輸協議中沒有重複開始的概念,在寫完暫存器地址後,需發起匯流排停止訊號。

  即:start_1 + phase_1 + phase_2 + stop_1 + start_2 + phase_3 + phase_4 + stop_2

四、SCCB和IIC的區別

  1.SCCB的應答位稱為X,表示「don't care」,而IIC應答位稱為ACK。

  2.SCCB只能單次讀,而IIC除了單次讀還支援連續讀。

  3.SCCB讀操作中間有stop,而IIC讀操作中間可以有stop也可以不需要stop,具體表現如下

SCCB讀:start_1 + phase_1 + phase_2 + stop_1 + start_2 + phase_3 + phase_4 + stop_2   IIC讀:start_1 + phase_1 + phase_2 +        + start_2 + phase_3 + phase_4 + stop_2

除去上面三點,SCCB和IIC再無區別,因此如果只需要配置暫存器(只用到寫),可以直接拿IIC的時序來當做SCCB用,如果需要讀,讀操作中間必須有一個stop。

五、SCCB控制器Verilog程式碼


1 //**************************************************************************    2 // *** 名稱 : sccb.v    3 // *** 作者 : xianyu_FPGA    4 // *** 部落格 : https://www.cnblogs.com/xianyufpga/    5 // *** 日期 : 2019-08-10    6 // *** 描述 : SCCB控制器,只支援寫    7 //**************************************************************************    8    9 module sccb   10 //========================< 參數 >==========================================   11 #(   12 parameter DEVICE_ID         = 8'b01010000           , //器件ID   13 parameter CLK               = 26'd50_000_000        , //本模組的時鐘頻率   14 parameter SCL               = 18'd250_000             //輸出的SCL時鐘頻率   15 )   16 //========================< 埠 >==========================================   17 (   18 input                       clk                     , //時鐘   19 input                       rst_n                   , //複位,低電平有效   20 //SCCB control --------------------------------------   21 input                       sccb_en                 , //SCCB觸發訊號   22 input                       addr16_en               , //16位地址使能   23 input                       addr8_en                , //8位地址使能   24 //SCCB input ----------------------------------------   25 input        [15:0]         sccb_addr               , //SCCB器件內地址   26 input        [ 7:0]         sccb_data               , //SCCB要寫的數據   27 //SCCB output ---------------------------------------   28 output  reg                 sccb_done               , //SCCB一次操作完成   29 output  reg                 sccb_scl                , //SCCB的SCL時鐘訊號   30 inout                       sccb_sda                , //SCCB的SDA數據訊號   31 //dri_clk -------------------------------------------   32 output  reg                 sccb_dri_clk              //驅動SCCB操作的驅動時鐘,1Mhz   33 );   34 //========================< 狀態機參數 >====================================   35 localparam  IDLE            = 6'b00_0001            ; //空閑狀態   36 localparam  DEVICE          = 6'b00_0010            ; //寫器件地址   37 localparam  ADDR_16         = 6'b00_0100            ; //寫字地址高8位   38 localparam  ADDR_8          = 6'b00_1000            ; //寫字地址低8位   39 localparam  DATA            = 6'b01_0000            ; //寫數據   40 localparam  STOP            = 6'b10_0000            ; //結束   41 //========================< 訊號 >==========================================   42 reg                         sda_dir                 ; //SCCB數據(SDA)方向控制   43 reg                         sda_out                 ; //SDA輸出訊號   44 reg                         state_done              ; //狀態結束   45 reg    [ 6:0]               cnt                     ; //計數   46 reg    [ 7:0]               state_c                 ; //狀態機當前狀態   47 reg    [ 7:0]               state_n                 ; //狀態機下一狀態   48 reg    [15:0]               sccb_addr_t             ; //地址寄存   49 reg    [ 7:0]               sccb_data_t             ; //數據寄存   50 reg    [ 9:0]               clk_cnt                 ; //分頻時鐘計數   51 wire   [ 8:0]               clk_divide              ; //模組驅動時鐘的分頻係數   52   53 //==========================================================================   54 //==    sda控制   55 //==========================================================================   56 assign  sccb_sda = sda_dir ?  sda_out : 1'bz;         //SDA數據輸出或高阻   57   58 //==========================================================================   59 //==     生成SCL的4倍時鐘來驅動後面SCCB的操作,生成1Mhz的sccb_dri_clk   60 //==========================================================================   61 assign  clk_divide = (CLK/SCL) >> 3;                  // >>3即除以8   62   63 always @(posedge clk or negedge rst_n) begin   64     if(!rst_n) begin   65         sccb_dri_clk <=  1'b1;   66         clk_cnt <= 10'd0;   67     end   68     else if(clk_cnt == clk_divide - 1'd1) begin   69         clk_cnt <= 10'd0;   70         sccb_dri_clk <= ~sccb_dri_clk;   71     end   72     else   73         clk_cnt <= clk_cnt + 1'b1;   74 end   75   76 //==========================================================================   77 //==    狀態機   78 //==========================================================================   79 always @(posedge sccb_dri_clk or negedge rst_n) begin   80     if(!rst_n)   81         state_c <= IDLE;   82     else   83         state_c <= state_n;   84 end   85   86 always @(*) begin   87     case(state_c)   88         IDLE: begin                             //空閑狀態   89            if(sccb_en)   90                state_n = DEVICE;   91            else   92                state_n = IDLE;   93         end   94         DEVICE: begin                           //寫器件ID   95             if(state_done) begin   96                 if(addr16_en)   97                    state_n = ADDR_16;   98                 else if(addr8_en)   99                    state_n = ADDR_8 ;  100             end  101             else  102                 state_n = DEVICE;  103         end  104         ADDR_16: begin                          //寫地址高8位  105             if(state_done)  106                 state_n = ADDR_8;  107             else  108                 state_n = ADDR_16;  109         end  110         ADDR_8: begin                           //寫地址低8位  111             if(state_done)  112                 state_n = DATA;  113             else  114                 state_n = ADDR_8;  115         end  116         DATA: begin                             //寫數據  117             if(state_done)  118                 state_n = STOP;  119             else  120                 state_n = DATA;  121         end  122         STOP: begin                             //結束  123             if(state_done)  124                 state_n = IDLE;  125             else  126                 state_n = STOP ;  127         end  128         default:state_n= IDLE;  129     endcase  130 end  131  132 //==========================================================================  133 //==    設計各路訊號  134 //==========================================================================  135 always @(posedge sccb_dri_clk or negedge rst_n) begin  136     if(!rst_n) begin  137         sccb_scl    <= 1'b1;  138         sda_out     <= 1'b1;  139         sda_dir     <= 1'b1;  140         sccb_done   <= 1'b0;  141         cnt         <= 1'b0;  142         state_done  <= 1'b0;  143         sccb_addr_t <= 1'b0;  144         sccb_data_t <= 1'b0;  145     end  146     else begin  147         state_done  <= 1'b0 ;  148         cnt         <= cnt + 1'b1 ;  149         case(state_c)  150             //--------------------------------------------------- 空閑狀態  151             IDLE: begin  152                     sccb_scl  <= 1'b1;  153                     sda_out   <= 1'b1;  154                     sda_dir   <= 1'b1;  155                     sccb_done <= 1'b0;  156                     cnt       <= 7'b0;  157                     if(sccb_en) begin  158                         sccb_addr_t <= sccb_addr;  159                         sccb_data_t <= sccb_data;  160                     end  161             end  162             //--------------------------------------------------- 寫器件ID  163             DEVICE: begin  164                 case(cnt)  165                     7'd1 : sda_out  <= 1'b0;  166                     7'd3 : sccb_scl <= 1'b0;  167                     7'd4 : sda_out  <= DEVICE_ID[7];  168                     7'd5 : sccb_scl <= 1'b1;  169                     7'd7 : sccb_scl <= 1'b0;  170                     7'd8 : sda_out  <= DEVICE_ID[6];  171                     7'd9 : sccb_scl <= 1'b1;  172                     7'd11: sccb_scl <= 1'b0;  173                     7'd12: sda_out  <= DEVICE_ID[5];  174                     7'd13: sccb_scl <= 1'b1;  175                     7'd15: sccb_scl <= 1'b0;  176                     7'd16: sda_out  <= DEVICE_ID[4];  177                     7'd17: sccb_scl <= 1'b1;  178                     7'd19: sccb_scl <= 1'b0;  179                     7'd20: sda_out  <= DEVICE_ID[3];  180                     7'd21: sccb_scl <= 1'b1;  181                     7'd23: sccb_scl <= 1'b0;  182                     7'd24: sda_out  <= DEVICE_ID[2];  183                     7'd25: sccb_scl <= 1'b1;  184                     7'd27: sccb_scl <= 1'b0;  185                     7'd28: sda_out  <= DEVICE_ID[1];  186                     7'd29: sccb_scl <= 1'b1;  187                     7'd31: sccb_scl <= 1'b0;  188                     7'd32: sda_out  <= DEVICE_ID[0];  189                     7'd33: sccb_scl <= 1'b1;  190                     7'd35: sccb_scl <= 1'b0;  191                     7'd36: begin  192                             sda_dir <= 1'b0;    //從機應答  193                             sda_out <= 1'b1;  194                     end  195                     7'd37: sccb_scl <= 1'b1;  196                     7'd38: state_done <= 1'b1;  //狀態結束  197                     7'd39: begin  198                             sccb_scl <= 1'b0;  199                             cnt <= 1'b0;  200                     end  201                     default :  ;  202                 endcase  203             end  204             //--------------------------------------------------- 寫字地址高8位  205             ADDR_16: begin  206                 case(cnt)  207                     7'd0 : begin  208                             sda_dir <= 1'b1 ;  209                             sda_out <= sccb_addr_t[15];  210                     end  211                     7'd1 : sccb_scl <= 1'b1;  212                     7'd3 : sccb_scl <= 1'b0;  213                     7'd4 : sda_out  <= sccb_addr_t[14];  214                     7'd5 : sccb_scl <= 1'b1;  215                     7'd7 : sccb_scl <= 1'b0;  216                     7'd8 : sda_out  <= sccb_addr_t[13];  217                     7'd9 : sccb_scl <= 1'b1;  218                     7'd11: sccb_scl <= 1'b0;  219                     7'd12: sda_out  <= sccb_addr_t[12];  220                     7'd13: sccb_scl <= 1'b1;  221                     7'd15: sccb_scl <= 1'b0;  222                     7'd16: sda_out  <= sccb_addr_t[11];  223                     7'd17: sccb_scl <= 1'b1;  224                     7'd19: sccb_scl <= 1'b0;  225                     7'd20: sda_out  <= sccb_addr_t[10];  226                     7'd21: sccb_scl <= 1'b1;  227                     7'd23: sccb_scl <= 1'b0;  228                     7'd24: sda_out  <= sccb_addr_t[9];  229                     7'd25: sccb_scl <= 1'b1;  230                     7'd27: sccb_scl <= 1'b0;  231                     7'd28: sda_out  <= sccb_addr_t[8];  232                     7'd29: sccb_scl <= 1'b1;  233                     7'd31: sccb_scl <= 1'b0;  234                     7'd32: begin  235                            sda_dir  <= 1'b0;    //從機應答  236                            sda_out  <= 1'b1;  237                     end  238                     7'd33: sccb_scl <= 1'b1;  239                     7'd34: state_done <= 1'b1;  //狀態結束  240                     7'd35: begin  241                            sccb_scl <= 1'b0;  242                            cnt <= 1'b0;  243                     end  244                     default :  ;  245                 endcase  246             end  247             //--------------------------------------------------- 寫字地址低8位  248             ADDR_8: begin  249                 case(cnt)  250                     7'd0: begin  251                             sda_dir <= 1'b1 ;  252                             sda_out <= sccb_addr_t[7];  253                     end  254                     7'd1 : sccb_scl <= 1'b1;  255                     7'd3 : sccb_scl <= 1'b0;  256                     7'd4 : sda_out  <= sccb_addr_t[6];  257                     7'd5 : sccb_scl <= 1'b1;  258                     7'd7 : sccb_scl <= 1'b0;  259                     7'd8 : sda_out  <= sccb_addr_t[5];  260                     7'd9 : sccb_scl <= 1'b1;  261                     7'd11: sccb_scl <= 1'b0;  262                     7'd12: sda_out  <= sccb_addr_t[4];  263                     7'd13: sccb_scl <= 1'b1;  264                     7'd15: sccb_scl <= 1'b0;  265                     7'd16: sda_out  <= sccb_addr_t[3];  266                     7'd17: sccb_scl <= 1'b1;  267                     7'd19: sccb_scl <= 1'b0;  268                     7'd20: sda_out  <= sccb_addr_t[2];  269                     7'd21: sccb_scl <= 1'b1;  270                     7'd23: sccb_scl <= 1'b0;  271                     7'd24: sda_out  <= sccb_addr_t[1];  272                     7'd25: sccb_scl <= 1'b1;  273                     7'd27: sccb_scl <= 1'b0;  274                     7'd28: sda_out  <= sccb_addr_t[0];  275                     7'd29: sccb_scl <= 1'b1;  276                     7'd31: sccb_scl <= 1'b0;  277                     7'd32: begin  278                            sda_dir  <= 1'b0;    //從機應答  279                            sda_out  <= 1'b1;  280                     end  281                     7'd33: sccb_scl <= 1'b1;  282                     7'd34: state_done <= 1'b1;  //狀態結束  283                     7'd35: begin  284                            sccb_scl <= 1'b0;  285                            cnt <= 1'b0;  286                     end  287                     default :  ;  288                 endcase  289             end  290             //--------------------------------------------------- 寫數據  291             DATA: begin  292                 case(cnt)  293                     7'd0: begin  294                             sda_out <= sccb_data_t[7];  295                             sda_dir <= 1'b1;  296                     end  297                     7'd1 : sccb_scl <= 1'b1;  298                     7'd3 : sccb_scl <= 1'b0;  299                     7'd4 : sda_out  <= sccb_data_t[6];  300                     7'd5 : sccb_scl <= 1'b1;  301                     7'd7 : sccb_scl <= 1'b0;  302                     7'd8 : sda_out  <= sccb_data_t[5];  303                     7'd9 : sccb_scl <= 1'b1;  304                     7'd11: sccb_scl <= 1'b0;  305                     7'd12: sda_out  <= sccb_data_t[4];  306                     7'd13: sccb_scl <= 1'b1;  307                     7'd15: sccb_scl <= 1'b0;  308                     7'd16: sda_out  <= sccb_data_t[3];  309                     7'd17: sccb_scl <= 1'b1;  310                     7'd19: sccb_scl <= 1'b0;  311                     7'd20: sda_out  <= sccb_data_t[2];  312                     7'd21: sccb_scl <= 1'b1;  313                     7'd23: sccb_scl <= 1'b0;  314                     7'd24: sda_out  <= sccb_data_t[1];  315                     7'd25: sccb_scl <= 1'b1;  316                     7'd27: sccb_scl <= 1'b0;  317                     7'd28: sda_out  <= sccb_data_t[0];  318                     7'd29: sccb_scl <= 1'b1;  319                     7'd31: sccb_scl <= 1'b0;  320                     7'd32: begin  321                            sda_dir  <= 1'b0;    //從機應答  322                            sda_out  <= 1'b1;  323                     end  324                     7'd33: sccb_scl <= 1'b1;  325                     7'd34: state_done <= 1'b1;  //狀態結束  326                     7'd35: begin  327                            sccb_scl  <= 1'b0;  328                            cnt  <= 1'b0;  329                     end  330                     default  :  ;  331                 endcase  332             end  333             //--------------------------------------------------- 結束  334             STOP: begin  335                 case(cnt)  336                     7'd0: begin  337                            sda_dir  <= 1'b1;  338                            sda_out  <= 1'b0;  339                     end  340                     7'd1 : sccb_scl <= 1'b1;  341                     7'd3 : sda_out  <= 1'b1;  342                     7'd15: state_done <= 1'b1;  //狀態結束  343                     7'd16: begin  344                            cnt <= 1'b0;  345                            sccb_done <= 1'b1;   //sccb配置完成  346                     end  347                     default  : ;  348                 endcase  349             end  350         endcase  351     end  352 end  353  354  355  356 endmodule

參考資料:

[1]OmniVision Serial Camera Control Bus (SCCB) Functional Specification

[2]正點原子FPGA教程

[3]開源騷客.SDRAM那些事兒