協議——SCCB與IIC的區別
- 2020 年 4 月 2 日
- 筆記
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那些事兒