SCCB(Serial Camera Control Bus,串行摄像头控制总线)是由OV(OmniVision的简称)公司定义和发展的三线式串行总线,该总线控制着摄像头大部分的功能,包括图像数据格式、分辨率以及图像处理参数等。结构框图如下所示:

协议——SCCB与IIC的区别_d3

协议——SCCB与IIC的区别_I2C_02

  OV公司为了减少传感器引脚的封装,现在SCCB总线大多采用两线式接口总线。OV7725使用的是两线式接口总线,该接口总线包括SIO_C串行时钟输入线和SIO_D串行双向数据线,分别相当于IIC协议的SCL信号线和SDA信号线。SIO_C的最小时间为10us,即最大频率为100K。一般来说,100K-400K之间都可以。


协议——SCCB与IIC的区别_d3_03

   由此可见,SCCB就是改编版的IIC,完全可以按照IIC来理解,下面仔细讲解SCCB的时序以及和IIC的不同之处。


一、SCCB起始和结束(与IIC完全一致)

协议——SCCB与IIC的区别_d3_04

  起始:SIO_C为高时,SIO_D由高拉低。

  停止:SIO_C为高时,SIO_D由低拉高


二、SCCB写(与IIC完全一致)

协议——SCCB与IIC的区别_I2C_05

  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与IIC的区别_SCCB_06

  数据手册中的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代码

协议——SCCB与IIC的区别_状态机_07

1 //**************************************************************************
2 // *** 名称 : sccb.v
3 // *** 作者 : xianyu_FPGA
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

协议——SCCB与IIC的区别_状态机_07

参考资料:

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

[2]正点原子FPGA教程

[3]开源骚客.SDRAM那些事儿

--------------------------------------------------------------------------------------

作者:​​咸鱼FPGA​

本文版权归作者所有,如需请保留此段声明。