SCCB接口时序的实现
SCCB接口时序与IIC协议基本一致,是其简化版本,且SCCB协议兼容IIC协议。
基本时序介绍
最常用的时序是3线控制数据传输的方式,基本时序图如下。
具体的控制信号,则是先发送起始信号,再发送数据,最后发送停止信号。
其中,3相数据传输又是最常用的方式。
起始信号为scl有效时,sda拉低。
停止信号为scl有效时,sda拉高。
正确的有效数据是scl为高电平时,sda保持不变,从而正确地读取数据,正好在时钟的上升沿读取有效数据。
3相数据发送,即先发送器件地址,再发送子地址,最后发送需要写入的数据。
时序分析
根据上面的时序图,主要分为两部分来介绍整个时序实现的过程。
控制标志位产生
主要产生4个标志位,用于控制下面主体时序的是生成。
scl的上升沿scl_pos_flag、下降沿scl_neg_flag以及sda的上升沿sda_pos_flag和下降沿sda_neg_flag四个控制信号,在主体的时序实现上起到很重要的作用。
主体时序分析
有个上图实现的4个控制信号,就可以很方便输出起始信号、停止信号以及数据信号。如下图所示。
起始信号是在w_start_flag==1的范围内实现的,停止信号是在w_stop_flag==1的范围内实现的。数据信号是在phase_flag==1的范围内实现的。
时序测试
编写完成后,将其放入modelsim中进行测试。
波形如上图所示,起始和停止信号在上图中已标出。
然后,使用ila抓取时序,查看波形信号。
下图中是w_end_flag的信号。
还有w_start_flag的控制信号。
加入正确的控制信号后,再次进行测试。
OV5640摄像头控制
按照相应的寄存器写入控制寄存器,就可以获得正确的数据输出了。
寄存器配置
首先是信号的连接,看看下面的连接图。
PCLK、cmos_data[9] ~ cmos_data[2]以及VSYNC和HREF作为输入信号,RESETB、PWDN和XCLK作为输出信号,主要的控制信号还有SDA和SCL信号,非常重要的信号。
控制时序如下图所示。
步骤则如下。
步骤 1:
ResetB 拉低,复位 OV5640。 PWDN 引脚拉高。
步骤 2:
DOVDD 和 AVDD 上电。这两路最好同时上电,如果不能同时上电,那么必须使 DOVDD
上电在先,AVDD 上电在后。
步骤 3:
等 AVDD 稳定 5 毫秒后,拉低 PWDN。
步骤 4:
PWDN 置低 1 毫秒后, 拉高 ResetB。
步骤 5:
20 毫秒后, 初始化 OV5640 的 SCCB 寄存器设置。寄存器设置可参考第四章的 OV5640 软
件应用指南。
关于SCCB的寄存器控制,查看具体的寄存器配置。
在OV5640_camera_module_software_application_notes_1.3_Sonix.pdf文件中包含1080p的配置寄存器的控制指令,下面只要发送相应的寄存器即可。
当然,对于某些寄存器的数值,必须了解数值所代表的含义,才方便进行针对性的修改。
ov5640能够支持的分辨率有1080P,Pclk最大为192MHz。
在ov5640的数据手册中,还存在如下表格。
关于具体的寄存器配置,查看正点原子2_领航者ZYNQ之嵌入式开发指南_V1.2.1.pdf关于ov5640的教程。
主要的控制寄存器,我们需要了解。
- 软件复位及电源复位。
-
输出引脚使能控制
-
输出格式控制
默认设置的是0x30,即输出格式为YUV422,输出顺序为{b[4:0],g[5:3]},{g[2:0],r[4:0]},需要将YUV格式的数据转换为RGB格式的数据。由于对于YUV422格式不熟悉,这里修改为0x61,即输出格式为RGB565,输出顺序为{r[4:0],g[5:3]},{g[2:0],g[4:0]}。
重点来了,输出图像的参数设置。看下面的图像就一目了然,具体的配置都设置好了。
提取有效数据
再看看具体的时序,基本所有的数据就能提取出来了。最终的控制时序如下图所示。
根据时序图,编写相应的控制代码。
这个比imx222简单多了,直接输出相应的数据和控制信号。
其读写控制和普通的SCCB控制还是有些区别的,主要在于其寄存器地址为16bit,而不是常见的8位,故变成了4相位数据写入。如下图所示。
常用的三相位phase数据控制,phase 1写入器件地址,phase 2写寄存器地址,phase3写入器件地址。
ov5640的SCCB理论上的写入顺序是一致的,不过器件寄存器地址变化了16bit,故变成了4 phase数据写入,理论上还是3 phase数据写入,参考正点原子领航者 ZYNQ 之嵌入式开发指南.pdf。
上图中的 ID ADDRESS 是由 7 位器件地址和 1 位读写控制位构成( 0: 写 1: 读), OV5640 的器件地
址为 7’h3c,所以在写传输协议中, ID Address( W) = 8’h78(器件地址左移 1 位,低位补 0)
故Verilog代码需要修改为4 phase数据写入。
关于ov5640输出的时钟和数据信号的同步时序,如下图所示。
输出的时钟PCLK的时钟的上升沿刚好与输出的有效数据D同步,能够采集到有效的图像数据。
ILA调试
将输入输出信号提取出来放在ILA中测试,发现输入数据是正确的,输出信号没有正确的提取,增加相应的控制信号,查看控制信号是否正确。
发现行计数的最大值,hcnt不涉及的时序不符合,如上图所示。
参考以下博客,应该是寄存器关于2592和1944的部分没有配置正确。参考如下的链接OV5640摄像头时序实测与datasheet对不上求指导,很有可能是寄存器没有配置成功。
重新配置寄存器,问题解决了嘛。
如上图所示,获得了正确的数据。
注意事项
-
关于axi-lite的控制
PS为主控端,控制axi-lite接口读写数据。
一般来说,PS为Master接口,PL为Slave接口,如下图所示。
Master数据写入Slave
PS端的主控制器写入数据到PL端的SCCB控制器,则SCCB控制器的寄存器获取主控制器发送的数据,如下面的代码所示。
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin slv_reg0 <= 0; slv_reg1 <= 0; end else begin if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Slave register 0 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 2'h1: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Slave register 1 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end default : begin slv_reg0 <= slv_reg0; slv_reg1 <= slv_reg1; end endcase end end end
具体来说,Master端写入数据是在W_DATA中传输,Slave端接口来自Master端的数据,数据都是在W_DATA中传输,接收的数据保存在相应的寄存器slv_reg0和slv_reg1,位于Slave的PL端想要使用这个数据的话,输出就行了。
相应的控制条件应该这么写。
assign write_data = slv_reg0; assign write_flag = slv_reg1;
Master读取Slave数据
对于Master端读取slave数据,则是在RD_DATA中传输,即Master读取Slave端的数据,Slave将要发送给Master端的数据存入寄存器slv_reg0和slv_reg1,再把寄存器中的数据存入reg_data中,最后reg_data_out中的数据放入共同的数据线RD_DATA,具体可见如下的代码。
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid; always @(*) begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0 : reg_data_out <= slv_reg0; 2'h1 : reg_data_out <= slv_reg1; default : reg_data_out <= 0; endcase end // Output register or memory read data always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_rdata <= 0; end else begin if (slv_reg_rden) begin axi_rdata <= reg_data_out; // register read data end end end
相应的控制条件可以直接赋值给reg_data_out,就可以获得正确的结果。
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid; always @(*) begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0 : reg_data_out <= slv_reg0; 2'h1 : reg_data_out <= slv_reg1; 2'h2 : reg_data_out <= write_end;//slv_reg2; 2'h3 : reg_data_out <= slv_reg3; default : reg_data_out <= 0; endcase end