上电之后要做的是通过 SCCB 协议对摄像头的寄存器进行配置,在之前的博客中详细介绍过 SCCB 协议的基本原理以及和 IIC 协议的区别。经过半年后,在正点原子的 SCCB 配置寄存器的基础上,我进行了小小的改动,使得代码更加简洁易懂。
一、SCCB协议编写
SCCB协议的介绍见之前的博客《协议——SCCB与IIC的区别》,此处不再赘述。之前的 SCCB 协议需要在端口进行 8 位地址和16位地址的选择,这次我把选择开关删了,改为内部代码判断,端口更加简洁了。
1、端口例化
//SCCB寄存器配置 //--------------------------------------------------- sccb_ov5640_cfg u_sccb_ov5640_cfg ( .clk (sccb_dri_clk ), .rst_n (rst_n ), .sccb_vld (sccb_vld ), //SCCB配置有效信号 .sccb_done (sccb_done ), //SCCB配置一次结束 .sccb_en (sccb_en ), .sccb_data (sccb_data ), .sccb_cfg_done (sccb_cfg_done ) //SCCB配置全部结束 ); //SCCB时序驱动 //--------------------------------------------------- sccb #( .DEVICE_ID (8\'h78 ), //器件ID .CLK (26\'d24_000_000 ), //24Mhz .SCL (18\'d250_000 ) //250Khz ) u_sccb ( .clk (cmos_xclk ), .rst_n (rst_n ), //SCCB input ------------------------------------ .sccb_en (sccb_en ), .sccb_addr (sccb_data[23:8] ), .sccb_data (sccb_data[7:0] ), //SCCB output ----------------------------------- .sccb_done (sccb_done ), .sccb_scl (cmos_scl ), .sccb_sda (cmos_sda ), //dri_clk --------------------------------------- .sccb_dri_clk (sccb_dri_clk ) //1Mhz );
OV7670 和 OV7725 的写 ID 都是 8\'h42,而 OV5640 的写 ID 为 8\'h78,这些都可以在各自的 datasheet 中查到,没什么说的。
SCCB协议的 SCL 要求不高于400Khz,我们设置为 250Khz,通过例化模块的参数传递可以直接更改这些信息,对于 sccb 代码内部不必再更改,方便移植。
sccb_start信号为开始配置标志,在上一讲博客中设计得到。sccb_en、sccb_data、sccb_done、sccb_dri_clk 都是和 sccb_cfg 模块进行交互的信号,其中 sccb_dri_clk 是 sccb_cfg 模块的时钟驱动信号。而 cmos_scl 和 cmos_sda 则是要输出到端口的信号。sccb_cfg_done 信号表示所有寄存器都配置完成,这个信号可以给之后的图像获取模块 cmos_capture 使用。
2、sccb 代码
//************************************************************************** // *** 名称 : sccb.v // *** 作者 : xianyu_FPGA // *** 博客 : https://www.cnblogs.com/xianyufpga/ // *** 日期 : 2019-08-10 // *** 描述 : SCCB控制器,只支持写 //************************************************************************** module sccb //========================< 参数 >========================================== #( parameter DEVICE_ID = 8\'b01010000 , //器件ID parameter CLK = 26\'d50_000_000 , //本模块的时钟频率 parameter SCL = 18\'d250_000 //输出的SCL时钟频率 ) //========================< 端口 >========================================== ( input wire clk , //时钟 input wire rst_n , //复位,低电平有效 //SCCB input ---------------------------------------- input wire sccb_en , //SCCB触发信号 input wire [15:0] sccb_addr , //SCCB器件内地址 input wire [ 7:0] sccb_data , //SCCB要写的数据 //SCCB output --------------------------------------- output reg sccb_done , //SCCB一次操作完成 output reg sccb_scl , //SCCB的SCL时钟信号 inout wire sccb_sda , //SCCB的SDA数据信号 //dri_clk ------------------------------------------- output reg sccb_dri_clk //驱动SCCB操作的驱动时钟,1Mhz ); //========================< 状态机参数 >==================================== localparam IDLE = 6\'b00_0001 ; //空闲状态 localparam DEVICE = 6\'b00_0010 ; //写器件地址 localparam ADDR_16 = 6\'b00_0100 ; //写字地址高8位 localparam ADDR_8 = 6\'b00_1000 ; //写字地址低8位 localparam DATA = 6\'b01_0000 ; //写数据 localparam STOP = 6\'b10_0000 ; //结束 //========================< 信号 >========================================== reg sda_dir ; //SCCB数据(SDA)方向控制 reg sda_out ; //SDA输出信号 reg state_done ; //状态结束 reg [ 6:0] cnt ; //计数 reg [ 7:0] state_c ; //状态机当前状态 reg [ 7:0] state_n ; //状态机下一状态 reg [15:0] sccb_addr_t ; //地址寄存 reg [ 7:0] sccb_data_t ; //数据寄存 reg [ 9:0] clk_cnt ; //分频时钟计数 wire [ 8:0] clk_divide ; //模块驱动时钟的分频系数 //========================================================================== //== SDA数据输出或高阻 //========================================================================== assign sccb_sda = sda_dir ? sda_out : 1\'bz; //========================================================================== //== 生成SCL的4倍时钟来驱动后面SCCB的操作,生成1Mhz的sccb_dri_clk //========================================================================== assign clk_divide = (CLK/SCL) >> 3; //>>3即除以8 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin sccb_dri_clk <= 1\'b1; clk_cnt <= 10\'d0; end else if(clk_cnt == clk_divide - 1\'d1) begin clk_cnt <= 10\'d0; sccb_dri_clk <= ~sccb_dri_clk; end else clk_cnt <= clk_cnt + 1\'b1; end //========================================================================== //== 状态机 //========================================================================== always @(posedge sccb_dri_clk or negedge rst_n) begin if(!rst_n) state_c <= IDLE; else state_c <= state_n; end always @(*) begin case(state_c) IDLE: begin //空闲状态 if(sccb_en) state_n = DEVICE; else state_n = IDLE; end DEVICE: begin //写器件ID if(state_done) begin if(sccb_addr[15:8]!=0) state_n = ADDR_16; else if(sccb_addr[15:8]==0) state_n = ADDR_8 ; end else state_n = DEVICE; end ADDR_16: begin //写地址高8位 if(state_done) state_n = ADDR_8; else state_n = ADDR_16; end ADDR_8: begin //写地址低8位 if(state_done) state_n = DATA; else state_n = ADDR_8; end DATA: begin //写数据 if(state_done) state_n = STOP; else state_n = DATA; end STOP: begin //结束 if(state_done) state_n = IDLE; else state_n = STOP ; end default:state_n= IDLE; endcase end //========================================================================== //== 设计各路信号 //========================================================================== always @(posedge sccb_dri_clk or negedge rst_n) begin if(!rst_n) begin sccb_scl <= 1\'b1; sda_out <= 1\'b1; sda_dir <= 1\'b1; sccb_done <= 1\'b0; cnt <= 1\'b0; state_done <= 1\'b0; sccb_addr_t <= 1\'b0; sccb_data_t <= 1\'b0; end else begin state_done <= 1\'b0; cnt <= cnt + 1\'b1; case(state_c) //--------------------------------------------------- 空闲状态 IDLE: begin sccb_scl <= 1\'b1; sda_out <= 1\'b1; sda_dir <= 1\'b1; sccb_done <= 1\'b0; cnt <= 7\'b0; if(sccb_en) begin sccb_addr_t <= sccb_addr; sccb_data_t <= sccb_data; end end //--------------------------------------------------- 写器件ID DEVICE: begin case(cnt) 7\'d1 : sda_out <= 1\'b0; 7\'d3 : sccb_scl <= 1\'b0; 7\'d4 : sda_out <= DEVICE_ID[7]; 7\'d5 : sccb_scl <= 1\'b1; 7\'d7 : sccb_scl <= 1\'b0; 7\'d8 : sda_out <= DEVICE_ID[6]; 7\'d9 : sccb_scl <= 1\'b1; 7\'d11: sccb_scl <= 1\'b0; 7\'d12: sda_out <= DEVICE_ID[5]; 7\'d13: sccb_scl <= 1\'b1; 7\'d15: sccb_scl <= 1\'b0; 7\'d16: sda_out <= DEVICE_ID[4]; 7\'d17: sccb_scl <= 1\'b1; 7\'d19: sccb_scl <= 1\'b0; 7\'d20: sda_out <= DEVICE_ID[3]; 7\'d21: sccb_scl <= 1\'b1; 7\'d23: sccb_scl <= 1\'b0; 7\'d24: sda_out <= DEVICE_ID[2]; 7\'d25: sccb_scl <= 1\'b1; 7\'d27: sccb_scl <= 1\'b0; 7\'d28: sda_out <= DEVICE_ID[1]; 7\'d29: sccb_scl <= 1\'b1; 7\'d31: sccb_scl <= 1\'b0; 7\'d32: sda_out <= DEVICE_ID[0]; 7\'d33: sccb_scl <= 1\'b1; 7\'d35: sccb_scl <= 1\'b0; 7\'d36: begin sda_dir <= 1\'b0; //从机应答 sda_out <= 1\'b1; end 7\'d37: sccb_scl <= 1\'b1; 7\'d38: state_done <= 1\'b1; //状态结束 7\'d39: begin sccb_scl <= 1\'b0; cnt <= 1\'b0; end default:; endcase end //--------------------------------------------------- 写字地址高8位 ADDR_16: begin case(cnt) 7\'d0 : begin sda_dir <= 1\'b1 ; sda_out <= sccb_addr_t[15]; end 7\'d1 : sccb_scl <= 1\'b1; 7\'d3 : sccb_scl <= 1\'b0; 7\'d4 : sda_out <= sccb_addr_t[14]; 7\'d5 : sccb_scl <= 1\'b1; 7\'d7 : sccb_scl <= 1\'b0; 7\'d8 : sda_out <= sccb_addr_t[13]; 7\'d9 : sccb_scl <= 1\'b1; 7\'d11: sccb_scl <= 1\'b0; 7\'d12: sda_out <= sccb_addr_t[12]; 7\'d13: sccb_scl <= 1\'b1; 7\'d15: sccb_scl <= 1\'b0; 7\'d16: sda_out <= sccb_addr_t[11]; 7\'d17: sccb_scl <= 1\'b1; 7\'d19: sccb_scl <= 1\'b0; 7\'d20: sda_out <= sccb_addr_t[10]; 7\'d21: sccb_scl <= 1\'b1; 7\'d23: sccb_scl <= 1\'b0; 7\'d24: sda_out <= sccb_addr_t[9]; 7\'d25: sccb_scl <= 1\'b1; 7\'d27: sccb_scl <= 1\'b0; 7\'d28: sda_out <= sccb_addr_t[8]; 7\'d29: sccb_scl <= 1\'b1; 7\'d31: sccb_scl <= 1\'b0; 7\'d32: begin sda_dir <= 1\'b0; //从机应答 sda_out <= 1\'b1; end 7\'d33: sccb_scl <= 1\'b1; 7\'d34: state_done <= 1\'b1; //状态结束 7\'d35: begin sccb_scl <= 1\'b0; cnt <= 1\'b0; end default:; endcase end //--------------------------------------------------- 写字地址低8位 ADDR_8: begin case(cnt) 7\'d0: begin sda_dir <= 1\'b1 ; sda_out <= sccb_addr_t[7]; end 7\'d1 : sccb_scl <= 1\'b1; 7\'d3 : sccb_scl <= 1\'b0; 7\'d4 : sda_out <= sccb_addr_t[6]; 7\'d5 : sccb_scl <= 1\'b1; 7\'d7 : sccb_scl <= 1\'b0; 7\'d8 : sda_out <= sccb_addr_t[5]; 7\'d9 : sccb_scl <= 1\'b1; 7\'d11: sccb_scl <= 1\'b0; 7\'d12: sda_out <= sccb_addr_t[4]; 7\'d13: sccb_scl <= 1\'b1; 7\'d15: sccb_scl <= 1\'b0; 7\'d16: sda_out <= sccb_addr_t[3]; 7\'d17: sccb_scl <= 1\'b1; 7\'d19: sccb_scl <= 1\'b0; 7\'d20: sda_out <= sccb_addr_t[2]; 7\'d21: sccb_scl <= 1\'b1; 7\'d23: sccb_scl <= 1\'b0; 7\'d24: sda_out <= sccb_addr_t[1]; 7\'d25: sccb_scl <= 1\'b1; 7\'d27: sccb_scl <= 1\'b0; 7\'d28: sda_out <= sccb_addr_t[0]; 7\'d29: sccb_scl <= 1\'b1; 7\'d31: sccb_scl <= 1\'b0; 7\'d32: begin sda_dir <= 1\'b0; //从机应答 sda_out <= 1\'b1; end 7\'d33: sccb_scl <= 1\'b1; 7\'d34: state_done <= 1\'b1; //状态结束 7\'d35: begin sccb_scl <= 1\'b0; cnt <= 1\'b0; end default:; endcase end //--------------------------------------------------- 写数据 DATA: begin case(cnt) 7\'d0: begin sda_out <= sccb_data_t[7]; sda_dir <= 1\'b1; end 7\'d1 : sccb_scl <= 1\'b1; 7\'d3 : sccb_scl <= 1\'b0; 7\'d4 : sda_out <= sccb_data_t[6]; 7\'d5 : sccb_scl <= 1\'b1; 7\'d7 : sccb_scl <= 1\'b0; 7\'d8 : sda_out <= sccb_data_t[5]; 7\'d9 : sccb_scl <= 1\'b1; 7\'d11: sccb_scl <= 1\'b0; 7\'d12: sda_out <= sccb_data_t[4]; 7\'d13: sccb_scl <= 1\'b1; 7\'d15: sccb_scl <= 1\'b0; 7\'d16: sda_out <= sccb_data_t[3]; 7\'d17: sccb_scl <= 1\'b1; 7\'d19: sccb_scl <= 1\'b0; 7\'d20: sda_out <= sccb_data_t[2]; 7\'d21: sccb_scl <= 1\'b1; 7\'d23: sccb_scl <= 1\'b0; 7\'d24: sda_out <= sccb_data_t[1]; 7\'d25: sccb_scl <= 1\'b1; 7\'d27: sccb_scl <= 1\'b0; 7\'d28: sda_out <= sccb_data_t[0]; 7\'d29: sccb_scl <= 1\'b1; 7\'d31: sccb_scl <= 1\'b0; 7\'d32: begin sda_dir <= 1\'b0; //从机应答 sda_out <= 1\'b1; end 7\'d33: sccb_scl <= 1\'b1; 7\'d34: state_done <= 1\'b1; //状态结束 7\'d35: begin sccb_scl <= 1\'b0; cnt <= 1\'b0; end default:; endcase end //--------------------------------------------------- 结束 STOP: begin case(cnt) 7\'d0: begin sda_dir <= 1\'b1; sda_out <= 1\'b0; end 7\'d1 : sccb_scl <= 1\'b1; 7\'d3 : sda_out <= 1\'b1; 7\'d15: state_done <= 1\'b1; //状态结束 7\'d16: begin cnt <= 1\'b0; sccb_done <= 1\'b1; //sccb配置完成 end default:; endcase end endcase end end endmodule
3、SCCB协议配置寄存器代码
module sccb_ov5640_cfg //========================< 参数 >========================================== #( parameter REG_NUM = 240 , //寄存器个数 parameter CMOS_H_PIXEL = 24\'d1024 , //CMOS水平方向像素个数 parameter CMOS_V_PIXEL = 24\'d768 , //CMOS垂直方向像素个数 parameter TOTAL_H_PIXEL = CMOS_H_PIXEL+13\'d1216 , //水平总像素大小 parameter TOTAL_V_PIXEL = CMOS_V_PIXEL+13\'d504 //垂直总像素大小 ) //========================< 端口 >========================================== ( input wire clk , //时钟,1Mhz input wire rst_n , //复位,低电平有效 input wire sccb_vld , //SCCB配置有效信号 input wire sccb_done , //SCCB寄存器配置完成信号 output reg sccb_en , //SCCB触发执行信号 output reg [23:0] sccb_data , //SCCB要配置的地址与数据(高8位地址,低8位数据) output reg sccb_cfg_done //SCCB全部寄存器配置完成信号 ); //========================< 信号 >========================================== reg sccb_vld_r ; wire sccb_start ; reg [7:0] reg_cnt ; //寄存器配置个数计数器 //========================================================================== //== sccb_vld上升沿检测 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) sccb_vld_r <= 1\'b0; else sccb_vld_r <= sccb_vld; end assign sccb_start = sccb_vld && ~sccb_vld_r; //========================================================================== //== sccb触发执行信号 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) sccb_en <= 1\'b0; else if(sccb_start) //开始配置寄存器 sccb_en <= 1\'b1; else if(sccb_done && reg_cnt < REG_NUM) //上一个配置完后立马配置下一个 sccb_en <= 1\'b1; else sccb_en <= 1\'b0; end //========================================================================== //== 寄存器配置个数计数 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) reg_cnt <= 8\'d0; else if(sccb_en) reg_cnt <= reg_cnt + 8\'b1; end //========================================================================== //== 所有寄存器全部配置完成信号 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) sccb_cfg_done <= 1\'b0; else if((reg_cnt == REG_NUM) && sccb_done) sccb_cfg_done <= 1\'b1; end //========================================================================== //== 配置寄存器地址与数据,Xclk=24Mhz //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) sccb_data <= 24\'b0; else begin case(reg_cnt) ...... default: endcase end end endmodule
上述代码是改自正点原子的FPGA教程,我将软件复位后的延时删了,因为在前面我们已经进行了硬件复位,所以完全没必要再进行寄存器软件复位,而且就算进行软件复位,不需要再延时也是OK的。
ov7725、ov7670和ov5640在这部分也是几乎一样的,只不过是寄存器和寄存器的个数那需要改一下,相关注意事项请看下面的详细介绍。
二、OV7670寄存器配置
1 //************************************************************************** 2 // *** 名称 : sccb_ov7670_cfg.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : SCCB配置OV7670寄存器 7 //************************************************************************** 8 9 module sccb_ov7670_cfg 10 //========================< 参数 >========================================== 11 #( 12 parameter REG_NUM = 123 //寄存器个数 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input wire clk , //时钟,1Mhz 17 input wire rst_n , //复位,低电平有效 18 input wire sccb_vld , //SCCB配置有效信号 19 input wire sccb_done , //SCCB寄存器配置完成信号 20 output reg sccb_en , //SCCB触发执行信号 21 output reg [15:0] sccb_data , //SCCB要配置的地址与数据(高8位地址,低8位数据) 22 output reg sccb_cfg_done //SCCB全部寄存器配置完成信号 23 ); 24 //========================< 信号 >========================================== 25 reg sccb_vld_r ; 26 wire sccb_start ; 27 reg [6:0] reg_cnt ; //寄存器配置个数计数器 28 //========================================================================== 29 //== sccb_vld上升沿检测 30 //========================================================================== 31 always @(posedge clk or negedge rst_n) begin 32 if(!rst_n) 33 sccb_vld_r <= 1\'b0; 34 else 35 sccb_vld_r <= sccb_vld; 36 end 37 38 assign sccb_start = sccb_vld && ~sccb_vld_r; 39 //========================================================================== 40 //== sccb触发执行信号 41 //========================================================================== 42 always @(posedge clk or negedge rst_n) begin 43 if(!rst_n) 44 sccb_en <= 1\'b0; 45 else if(sccb_start) //开始配置寄存器 46 sccb_en <= 1\'b1; 47 else if(sccb_done && reg_cnt < REG_NUM) //上一个配置完后立马配置下一个 48 sccb_en <= 1\'b1; 49 else 50 sccb_en <= 1\'b0; 51 end 52 //========================================================================== 53 //== 寄存器配置个数计数 54 //========================================================================== 55 always @(posedge clk or negedge rst_n) begin 56 if(!rst_n) 57 reg_cnt <= 7\'d0; 58 else if(sccb_en) 59 reg_cnt <= reg_cnt + 7\'b1; 60 end 61 //========================================================================== 62 //== 所有寄存器全部配置完成信号 63 //========================================================================== 64 always @(posedge clk or negedge rst_n) begin 65 if(!rst_n) 66 sccb_cfg_done <= 1\'b0; 67 else if((reg_cnt == REG_NUM) && sccb_done) 68 sccb_cfg_done <= 1\'b1; 69 end 70 //========================================================================== 71 //== 配置寄存器地址与数据 72 //========================================================================== 73 always @(posedge clk or negedge rst_n) begin 74 if(!rst_n) 75 sccb_data <= 16\'b0; 76 else begin 77 case(reg_cnt) //30fps,XCLK=24Mhz,PCLK=24Mhz,ID=0x42 78 //像素格式 ---------------------------------------------------------------------------------- 79 7\'d0 : sccb_data <= 16\'h12_04; //COM7 寄存器复位:否,图像选择:00_YUV,04_RGB 80 7\'d1 : sccb_data <= 16\'h40_d0; //COM15 RGB565,范围00-FF(YUV为80,,范围为01-FE) 81 // YUYV YVYU UYVY VYUY 82 7\'d2 : sccb_data <= 16\'h3a_04; //TSLB 与COM13配合,bit[3]:0 0 1 1 83 7\'d3 : sccb_data <= 16\'h3d_88; //COM13 与TSLB 配合,bit[3]: 0 1 0 1 84 //帧率控制(Xclk=24Mhz) ---------------------------------------------------------------------- 85 //fps 30 15 25 14.3 86 //pclk 24 12 24 12 87 7\'d4 : sccb_data <= 16\'h11_80; //CLKRC 80 00 80 00 88 7\'d5 : sccb_data <= 16\'h6b_0a; //DBLV 0a 0a 0a 0a 89 7\'d6 : sccb_data <= 16\'h2a_00; //EXHCH 00 00 00 00 90 7\'d7 : sccb_data <= 16\'h2b_00; //EXHCL 00 00 00 00 91 7\'d8 : sccb_data <= 16\'h92_00; //DM_LNL 00 00 66 1a 92 7\'d9 : sccb_data <= 16\'h93_00; //DM_LNH 00 00 00 00 93 7\'d10 : sccb_data <= 16\'h3b_0a; //COM11 0a 0a 0a 0a 94 //镜像控制 ---------------------------------------------------------------------------------- 95 7\'d17 : sccb_data <= 16\'h1e_01; //镜像翻转 正常模式01 垂直翻转11 96 // 水平翻转21 水平垂直翻转31 97 //测试图案 ---------------------------------------------------------------------------------- 98 // 正常 彩条 细纹 渐变彩条 99 7\'d18 : sccb_data <= 16\'h70_00; //测试图案 00 00 80 80 100 7\'d19 : sccb_data <= 16\'h71_01; //测试图案 01 81 01 81 101 //行场时序(默认值) -------------------------------------------------------------------------- 102 7\'d20 : sccb_data <= 16\'h17_11; //HSTART 行频开始高8位 103 7\'d21 : sccb_data <= 16\'h18_61; //HSTOP 行频结束高8位 104 7\'d22 : sccb_data <= 16\'h32_80; //HREF bit[5:3]行频开始低3位,bit[2:0]行频结束低3位 105 7\'d23 : sccb_data <= 16\'h19_03; //VSTART 场频开始高8位 106 7\'d24 : sccb_data <= 16\'h1a_7b; //VSTOP 场频结束高8位 107 7\'d25 : sccb_data <= 16\'h03_00; //VREF bit[3:2]场频开始低2位,bit[1:0]场频结束低2位 108 //其他 -------------------------------------------------------------------------------------- 109 7\'d26 : sccb_data <= 16\'h3e_00; //COM14 PCLK分频 110 7\'d27 : sccb_data <= 16\'h73_00; //SCALING DSP缩放时钟分频,与COM14需一致,选择不分频 111 7\'d28 : sccb_data <= 16\'h0c_00; 112 7\'d29 : sccb_data <= 16\'h7a_20; 113 7\'d30 : sccb_data <= 16\'h7b_1c; 114 7\'d31 : sccb_data <= 16\'h7c_28; 115 7\'d32 : sccb_data <= 16\'h7d_3c; 116 7\'d33 : sccb_data <= 16\'h7e_55; 117 7\'d34 : sccb_data <= 16\'h7f_68; 118 7\'d35 : sccb_data <= 16\'h80_76; 119 7\'d36 : sccb_data <= 16\'h81_80; 120 7\'d37 : sccb_data <= 16\'h82_88; 121 7\'d38 : sccb_data <= 16\'h83_8f; 122 7\'d39 : sccb_data <= 16\'h84_96; 123 7\'d40 : sccb_data <= 16\'h85_a3; 124 7\'d41 : sccb_data <= 16\'h86_af; 125 7\'d42 : sccb_data <= 16\'h87_c4; 126 7\'d43 : sccb_data <= 16\'h88_d7; 127 7\'d44 : sccb_data <= 16\'h89_e8; 128 7\'d45 : sccb_data <= 16\'h00_00; 129 7\'d46 : sccb_data <= 16\'h10_00; 130 7\'d47 : sccb_data <= 16\'h0d_00; 131 7\'d48 : sccb_data <= 16\'h14_28; 132 7\'d49 : sccb_data <= 16\'h24_75; 133 7\'d50 : sccb_data <= 16\'h25_63; 134 7\'d51 : sccb_data <= 16\'h26_A5; 135 7\'d52 : sccb_data <= 16\'h9f_78; 136 7\'d53 : sccb_data <= 16\'ha0_68; 137 7\'d54 : sccb_data <= 16\'ha1_03; 138 7\'d55 : sccb_data <= 16\'ha6_df; 139 7\'d56 : sccb_data <= 16\'ha7_df; 140 7\'d57 : sccb_data <= 16\'ha8_f0; 141 7\'d58 : sccb_data <= 16\'ha9_90; 142 7\'d59 : sccb_data <= 16\'haa_94; 143 7\'d60 : sccb_data <= 16\'h13_ef; 144 7\'d61 : sccb_data <= 16\'h0e_61; 145 7\'d62 : sccb_data <= 16\'h72_11; 146 7\'d63 : sccb_data <= 16\'h16_02; 147 7\'d64 : sccb_data <= 16\'h0f_4b; 148 7\'d65 : sccb_data <= 16\'h21_02; 149 7\'d66 : sccb_data <= 16\'h22_91; 150 7\'d67 : sccb_data <= 16\'h29_07; 151 7\'d68 : sccb_data <= 16\'h33_0b; 152 7\'d69 : sccb_data <= 16\'h35_0b; 153 7\'d70 : sccb_data <= 16\'h37_1d; 154 7\'d71 : sccb_data <= 16\'h38_71; 155 7\'d72 : sccb_data <= 16\'h39_2a; 156 7\'d73 : sccb_data <= 16\'h3c_78; 157 7\'d74 : sccb_data <= 16\'h4d_40; 158 7\'d75 : sccb_data <= 16\'h4e_20; 159 7\'d76 : sccb_data <= 16\'h69_00; 160 7\'d77 : sccb_data <= 16\'ha2_02; 161 7\'d78 : sccb_data <= 16\'h74_19; 162 7\'d79 : sccb_data <= 16\'h8d_4f; 163 7\'d80 : sccb_data <= 16\'h8e_00; 164 7\'d81 : sccb_data <= 16\'h8f_00; 165 7\'d82 : sccb_data <= 16\'h90_00; 166 7\'d83 : sccb_data <= 16\'h91_00; 167 7\'d84 : sccb_data <= 16\'h96_00; 168 7\'d85 : sccb_data <= 16\'h9a_80; 169 7\'d86 : sccb_data <= 16\'hb0_84; 170 7\'d87 : sccb_data <= 16\'hb1_0c; 171 7\'d88 : sccb_data <= 16\'hb2_0e; 172 7\'d89 : sccb_data <= 16\'hb3_82; 173 7\'d90 : sccb_data <= 16\'hb8_0a; 174 7\'d91 : sccb_data <= 16\'h43_14; 175 7\'d92 : sccb_data <= 16\'h44_f0; 176 7\'d93 : sccb_data <= 16\'h45_34; 177 7\'d94 : sccb_data <= 16\'h46_58; 178 7\'d95 : sccb_data <= 16\'h47_28; 179 7\'d96 : sccb_data <= 16\'h48_3a; 180 7\'d97 : sccb_data <= 16\'h59_88; 181 7\'d98 : sccb_data <= 16\'h5a_88; 182 7\'d99 : sccb_data <= 16\'h5b_44; 183 7\'d100 : sccb_data <= 16\'h5c_67; 184 7\'d101 : sccb_data <= 16\'h5d_49; 185 7\'d102 : sccb_data <= 16\'h5e_0e; 186 7\'d103 : sccb_data <= 16\'h64_04; 187 7\'d104 : sccb_data <= 16\'h65_20; 188 7\'d105 : sccb_data <= 16\'h66_05; 189 7\'d106 : sccb_data <= 16\'h94_04; 190 7\'d107 : sccb_data <= 16\'h95_08; 191 7\'d108 : sccb_data <= 16\'h6c_0a; 192 7\'d109 : sccb_data <= 16\'h6d_55; 193 7\'d110 : sccb_data <= 16\'h4f_80; 194 7\'d111 : sccb_data <= 16\'h50_80; 195 7\'d112 : sccb_data <= 16\'h51_00; 196 7\'d113 : sccb_data <= 16\'h52_22; 197 7\'d114 : sccb_data <= 16\'h53_5e; 198 7\'d115 : sccb_data <= 16\'h54_80; 199 7\'d116 : sccb_data <= 16\'h09_03; 200 7\'d117 : sccb_data <= 16\'h6e_11; 201 7\'d118 : sccb_data <= 16\'h6f_9f; 202 7\'d119 : sccb_data <= 16\'h55_00; 203 7\'d120 : sccb_data <= 16\'h56_40; 204 7\'d121 : sccb_data <= 16\'h57_80; 205 7\'d122 : sccb_data <= 16\'h15_00; 206 default: sccb_data <= 16\'h1c_7f; //MIDH 制造商ID 高8位 207 endcase 208 end 209 end 210 211 endmodule
我只是把重要的寄存器提前了,可以通过更改 pll 寄存器而改变帧率,也可以通过窗口相关的寄存器改变输出的分辨率,但有些麻烦,我后面会介绍一种更好的办法来获取任意分辨率,以期适应我们的屏幕。
这里输出的分辨率为 640x480,Pclk 为 24Mhz,fps 是我通过测量得到的,那如何进行理论值计算呢?
datasheet 中有这样一张图,我们来算一下。一帧图像时间 = (2 x tPclk)x 510 x 784 = (2 x 510 x 784) / Pclk hz,一帧图像时间的倒数即为帧率 fps,因此帧率 fps = Pclk /(2 x 510 x 784),Pclk为24000000hz,因此计算得到 fps = 24000000 /(2 x 510 x 784)≈ 30.012。计算结果和实际测量值一致。
三、OV7725寄存器配置
1 //************************************************************************** 2 // *** 名称 : sccb_cfg.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : SCCB配置ov7725寄存器 7 //************************************************************************** 8 9 module sccb_ov7725_cfg 10 //========================< 参数 >========================================== 11 #( 12 parameter REG_NUM = 70 //寄存器个数 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input wire clk , //时钟,1Mhz 17 input wire rst_n , //复位,低电平有效 18 input wire sccb_vld , //SCCB配置有效信号 19 input wire sccb_done , //SCCB寄存器配置完成信号 20 output reg sccb_en , //SCCB触发执行信号 21 output reg [15:0] sccb_data , //SCCB要配置的地址与数据(高8位地址,低8位数据) 22 output reg sccb_cfg_done //SCCB全部寄存器配置完成信号 23 ); 24 //========================< 信号 >========================================== 25 reg sccb_vld_r ; 26 wire sccb_start ; 27 reg [6:0] reg_cnt ; //寄存器配置个数计数器 28 //========================================================================== 29 //== sccb_vld上升沿检测 30 //========================================================================== 31 always @(posedge clk or negedge rst_n) begin 32 if(!rst_n) 33 sccb_vld_r <= 1\'b0; 34 else 35 sccb_vld_r <= sccb_vld; 36 end 37 38 assign sccb_start = sccb_vld && ~sccb_vld_r; 39 //========================================================================== 40 //== sccb触发执行信号 41 //========================================================================== 42 always @(posedge clk or negedge rst_n) begin 43 if(!rst_n) 44 sccb_en <= 1\'b0; 45 else if(sccb_start) //开始配置寄存器 46 sccb_en <= 1\'b1; 47 else if(sccb_done && reg_cnt < REG_NUM) //上一个配置完后立马配置下一个 48 sccb_en <= 1\'b1; 49 else 50 sccb_en <= 1\'b0; 51 end 52 //========================================================================== 53 //== 寄存器配置个数计数 54 //========================================================================== 55 always @(posedge clk or negedge rst_n) begin 56 if(!rst_n) 57 reg_cnt <= 7\'d0; 58 else if(sccb_en) 59 reg_cnt <= reg_cnt + 7\'b1; 60 end 61 //========================================================================== 62 //== 所有寄存器全部配置完成信号 63 //========================================================================== 64 always @(posedge clk or negedge rst_n) begin 65 if(!rst_n) 66 sccb_cfg_done <= 1\'b0; 67 else if((reg_cnt == REG_NUM) && sccb_done) 68 sccb_cfg_done <= 1\'b1; 69 end 70 //========================================================================== 71 //== 配置寄存器地址与数据 72 //========================================================================== 73 always @(posedge clk or negedge rst_n) begin 74 if(!rst_n) 75 sccb_data <= 16\'b0; 76 else begin 77 case(reg_cnt) //30fps,XCLK=24Mhz,PCLK=24Mhz,ID=0x42 78 //基本设置 ---------------------------------------------------------------------------- 79 7\'d0 : sccb_data <= {8\'h12, 8\'h06}; //COM7 复位选择:否;格式:VGA_RGB565 80 7\'d1 : sccb_data <= {8\'h0c, 8\'h10}; //COM3 10正常模式; 90垂直翻转 81 // 50水平翻转; d0水平垂直翻转 82 //Bit[0] 0正常模式; 1彩条测试 83 //时序参数 ---------------------------------------------------------------------------- 84 7\'d2 : sccb_data <= {8\'h17, 8\'h22}; //HSTART VGA:8\'h22; QVGA:8\'h3f 85 7\'d3 : sccb_data <= {8\'h18, 8\'ha4}; //HSIZE VGA:8\'ha4; QVGA:8\'h50 86 7\'d4 : sccb_data <= {8\'h19, 8\'h07}; //VSTART VGA:8\'h07; QVGA:8\'h03 87 7\'d5 : sccb_data <= {8\'h1a, 8\'hf0}; //VSIZE VGA:8\'hf0; QVGA:8\'h78 88 7\'d6 : sccb_data <= {8\'h29, 8\'ha0}; //HOutSize VGA:8\'hA0; QVGA:8\'hF0 89 7\'d7 : sccb_data <= {8\'h2c, 8\'hf0}; //VOutSize VGA:8\'hF0; QVGA:8\'h78 90 //帧率PLL(Xclk=24Mhz) --------------------------------------------------------------- 91 //fps/pclk 30/24 15/12 25/24 14.3/12 92 7\'d8 : sccb_data <= {8\'h11, 8\'h01}; // 01 03 01 03 93 7\'d9 : sccb_data <= {8\'h0d, 8\'h41}; // 41 41 41 41 94 7\'d10 : sccb_data <= {8\'h2a, 8\'h00}; // 00 00 00 00 95 7\'d11 : sccb_data <= {8\'h33, 8\'h00}; // 00 00 66 1a 96 7\'d12 : sccb_data <= {8\'h34, 8\'h00}; // 00 00 00 00 97 7\'d13 : sccb_data <= {8\'h2d, 8\'h00}; // 00 00 00 00 98 7\'d14 : sccb_data <= {8\'h2e, 8\'h00}; // 00 00 00 00 99 7\'d15 : sccb_data <= {8\'h0e, 8\'h65}; // 65 65 65 65 100 7\'d16 : sccb_data <= {8\'h2b, 8\'h00}; //60hz/50hz 默认00/60hz,有灯光干扰,fps如上 101 // 改为9e/50hz,无灯光干扰,fps会变 102 //DSP 控制 ---------------------------------------------------------------------------- 103 7\'d17 : sccb_data <= {8\'h42, 8\'h7f}; //TGT_B 黑电平校准蓝色通道目标值 104 7\'d18 : sccb_data <= {8\'h4d, 8\'h09}; //FixGain 模拟增益放大器 105 7\'d19 : sccb_data <= {8\'h63, 8\'hf0}; //AWB_Ctrl0 自动白平衡控制字节0 106 7\'d20 : sccb_data <= {8\'h64, 8\'hff}; //DSP_Ctrl1 DSP控制字节1 107 7\'d21 : sccb_data <= {8\'h65, 8\'h00}; //DSP_Ctrl2 DSP控制字节2 108 7\'d22 : sccb_data <= {8\'h66, 8\'h00}; //DSP_Ctrl3 DSP控制字节3 109 7\'d23 : sccb_data <= {8\'h67, 8\'h00}; //DSP_Ctrl4 DSP控制字节4 110 //AGC AEC AWB ------------------------------------------------------------------------ 111 7\'d24 : sccb_data <= {8\'h13, 8\'hff}; //COM8 自动增益/白平衡/曝光 112 7\'d25 : sccb_data <= {8\'h0f, 8\'hc5}; //COM6 113 7\'d26 : sccb_data <= {8\'h14, 8\'h11}; 114 7\'d27 : sccb_data <= {8\'h22, 8\'h98}; 115 7\'d28 : sccb_data <= {8\'h23, 8\'h03}; 116 7\'d29 : sccb_data <= {8\'h24, 8\'h40}; 117 7\'d30 : sccb_data <= {8\'h25, 8\'h30}; 118 7\'d31 : sccb_data <= {8\'h26, 8\'ha1}; 119 7\'d32 : sccb_data <= {8\'h6b, 8\'haa}; 120 7\'d33 : sccb_data <= {8\'h13, 8\'hff}; 121 //参数调整 --------------------------------------------------------------------------- 122 7\'d34 : sccb_data <= {8\'h90, 8\'h0a}; //EDGE1 边缘增强控制1 123 7\'d35 : sccb_data <= {8\'h91, 8\'h01}; //DNSOff 阈值下限 124 7\'d36 : sccb_data <= {8\'h92, 8\'h01}; //EDGE2 锐度(边缘增强)强度上限 125 7\'d37 : sccb_data <= {8\'h93, 8\'h01}; //EDGE3 锐度(边缘增强)强度下限 126 7\'d38 : sccb_data <= {8\'h94, 8\'h5f}; //MTX1 矩阵系数1 127 7\'d39 : sccb_data <= {8\'h95, 8\'h53}; //MTX2 矩阵系数2 128 7\'d40 : sccb_data <= {8\'h96, 8\'h11}; //MTX3 矩阵系数3 129 7\'d41 : sccb_data <= {8\'h97, 8\'h1a}; //MTX4 矩阵系数4 130 7\'d42 : sccb_data <= {8\'h98, 8\'h3d}; //MTX5 矩阵系数5 131 7\'d43 : sccb_data <= {8\'h99, 8\'h5a}; //MTX6 矩阵系数6 132 7\'d44 : sccb_data <= {8\'h9a, 8\'h1e}; //MTX_Ctrl 矩阵控制 133 7\'d45 : sccb_data <= {8\'h9b, 8\'h3f}; //BRIGHT 亮度 134 7\'d46 : sccb_data <= {8\'h9c, 8\'h25}; //CNST 对比度 135 7\'d47 : sccb_data <= {8\'h9e, 8\'h81}; //UV/ADJ0 紫外线调控 136 7\'d48 : sccb_data <= {8\'ha6, 8\'h06}; //SDE 特殊数字效果控制 137 7\'d49 : sccb_data <= {8\'ha7, 8\'h65}; //USAT "U"饱和增益 138 7\'d50 : sccb_data <= {8\'ha8, 8\'h65}; //VSAT "V"饱和增益 139 7\'d51 : sccb_data <= {8\'ha9, 8\'h80}; //HUECOS cos值 140 7\'d52 : sccb_data <= {8\'haa, 8\'h80}; //HUESIN sin值 141 //伽马控制 ------------------------------------------------------------------------- 142 7\'d53 : sccb_data <= {8\'h7e, 8\'h0c}; 143 7\'d54 : sccb_data <= {8\'h7f, 8\'h16}; 144 7\'d55 : sccb_data <= {8\'h80, 8\'h2a}; 145 7\'d56 : sccb_data <= {8\'h81, 8\'h4e}; 146 7\'d57 : sccb_data <= {8\'h82, 8\'h61}; 147 7\'d58 : sccb_data <= {8\'h83, 8\'h6f}; 148 7\'d59 : sccb_data <= {8\'h84, 8\'h7b}; 149 7\'d60 : sccb_data <= {8\'h85, 8\'h86}; 150 7\'d61 : sccb_data <= {8\'h86, 8\'h8e}; 151 7\'d62 : sccb_data <= {8\'h87, 8\'h97}; 152 7\'d63 : sccb_data <= {8\'h88, 8\'ha4}; 153 7\'d64 : sccb_data <= {8\'h89, 8\'haf}; 154 7\'d65 : sccb_data <= {8\'h8a, 8\'hc5}; 155 7\'d66 : sccb_data <= {8\'h8b, 8\'hd7}; 156 7\'d67 : sccb_data <= {8\'h8c, 8\'he8}; 157 7\'d68 : sccb_data <= {8\'h8d, 8\'h20}; 158 //others -------------------------------------------------------------------------- 159 7\'d69 : sccb_data <= {8\'h3d, 8\'h03}; //模拟过程的直流偏移 160 endcase 161 end 162 end 163 164 endmodule
和 OV7670 差不多,注意一下第 13 个寄存器 8\'h2b,根据韩彬的书《FPGA设计技巧与案例开发详解》介绍,假设我们原本寄存器配置的是 640x480@30fps,此时8\'h2b的值为00,大陆的白炽灯频率为50hz,会导致图像效果出现条纹干扰。当把 8\'h2b寄存器的值改为 9e 时,就可以避免该条纹干扰,但帧率却会由 30fps 变成 25fps。
四、OV5640寄存器配置
1 //************************************************************************** 2 // *** 名称 : sccb_ov5640_cfg.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : SCCB配置ov5640寄存器 7 //************************************************************************** 8 9 module sccb_ov5640_cfg 10 //========================< 参数 >========================================== 11 #( 12 parameter REG_NUM = 240 , //寄存器个数 13 parameter CMOS_H_PIXEL = 24\'d1024 , //CMOS水平方向像素个数 14 parameter CMOS_V_PIXEL = 24\'d768 , //CMOS垂直方向像素个数 15 parameter TOTAL_H_PIXEL = CMOS_H_PIXEL+13\'d1216 , //水平总像素大小 16 parameter TOTAL_V_PIXEL = CMOS_V_PIXEL+13\'d504 //垂直总像素大小 17 ) 18 //========================< 端口 >========================================== 19 ( 20 input wire clk , //时钟,1Mhz 21 input wire rst_n , //复位,低电平有效 22 input wire sccb_vld , //SCCB配置有效信号 23 input wire sccb_done , //SCCB寄存器配置完成信号 24 output reg sccb_en , //SCCB触发执行信号 25 output reg [23:0] sccb_data , //SCCB要配置的地址与数据(高8位地址,低8位数据) 26 output reg sccb_cfg_done //SCCB全部寄存器配置完成信号 27 ); 28 //========================< 信号 >========================================== 29 reg sccb_vld_r ; 30 wire sccb_start ; 31 reg [7:0] reg_cnt ; //寄存器配置个数计数器 32 //========================================================================== 33 //== sccb_vld上升沿检测 34 //========================================================================== 35 always @(posedge clk or negedge rst_n) begin 36 if(!rst_n) 37 sccb_vld_r <= 1\'b0; 38 else 39 sccb_vld_r <= sccb_vld; 40 end 41 42 assign sccb_start = sccb_vld && ~sccb_vld_r; 43 //========================================================================== 44 //== sccb触发执行信号 45 //========================================================================== 46 always @(posedge clk or negedge rst_n) begin 47 if(!rst_n) 48 sccb_en <= 1\'b0; 49 else if(sccb_start) //开始配置寄存器 50 sccb_en <= 1\'b1; 51 else if(sccb_done && reg_cnt < REG_NUM) //上一个配置完后立马配置下一个 52 sccb_en <= 1\'b1; 53 else 54 sccb_en <= 1\'b0; 55 end 56 //========================================================================== 57 //== 寄存器配置个数计数 58 //========================================================================== 59 always @(posedge clk or negedge rst_n) begin 60 if(!rst_n) 61 reg_cnt <= 8\'d0; 62 else if(sccb_en) 63 reg_cnt <= reg_cnt + 8\'b1; 64 end 65 //========================================================================== 66 //== 所有寄存器全部配置完成信号 67 //========================================================================== 68 always @(posedge clk or negedge rst_n) begin 69 if(!rst_n) 70 sccb_cfg_done <= 1\'b0; 71 else if((reg_cnt == REG_NUM) && sccb_done) 72 sccb_cfg_done <= 1\'b1; 73 end 74 //========================================================================== 75 //== 配置寄存器地址与数据,Xclk=24Mhz 76 //========================================================================== 77 always @(posedge clk or negedge rst_n) begin 78 if(!rst_n) 79 sccb_data <= 24\'b0; 80 else begin 81 case(reg_cnt) //Xclk=24Mhz FPS=30fps Pclk=24Mhz(不解) 82 //基本配置 ------------------------------------------------------------------------------------ 83 8\'d0 : sccb_data <= {16\'h3008,8\'h02}; //复位休眠 Bit[7]:复位 Bit[6]:休眠 84 8\'d1 : sccb_data <= {16\'h300e,8\'h58}; //DVP 使能 DVP enable 85 8\'d2 : sccb_data <= {16\'h4300,8\'h61}; //格式控制 RGB565 86 8\'d3 : sccb_data <= {16\'h503d,8\'h00}; //测试图案 00正常 80彩条 81混乱 82棋盘 87 //PLL(11_23为20fps) --------------------------------------------------------------------------- 88 8\'d4 : sccb_data <= {16\'h3035,8\'h21}; //PLL分频 11/1x 21/2x 32/3x 89 8\'d5 : sccb_data <= {16\'h3036,8\'h69}; //PLL倍频 23/1÷ 46/2÷ 69/3÷ 90 8\'d6 : sccb_data <= {16\'h3037,8\'h03}; //PLL分频 bit[4]:0/1 bypass/÷2 91 //ISP(VGA模式) -------------------------------------------------------------------------------- 92 8\'d7 : sccb_data <= {16\'h3800,8\'h00}; 93 8\'d8 : sccb_data <= {16\'h3801,8\'h00}; 94 8\'d9 : sccb_data <= {16\'h3802,8\'h00}; 95 8\'d10 : sccb_data <= {16\'h3803,8\'h04}; 96 8\'d11 : sccb_data <= {16\'h3804,8\'h0a}; 97 8\'d12 : sccb_data <= {16\'h3805,8\'h3f}; 98 8\'d13 : sccb_data <= {16\'h3806,8\'h07}; 99 8\'d14 : sccb_data <= {16\'h3807,8\'h9b}; 100 //输出窗口设置 -------------------------------------------------------------------------------- 101 8\'d15 : sccb_data <= {16\'h3808,{4\'d0,CMOS_H_PIXEL[11:8] }}; //水平像素点数高4位 102 8\'d16 : sccb_data <= {16\'h3809, CMOS_H_PIXEL[ 7:0] }; //水平像素点数低8位 103 8\'d17 : sccb_data <= {16\'h380a,{5\'d0,CMOS_V_PIXEL[10:8] }}; //垂直像素点数高3位 104 8\'d18 : sccb_data <= {16\'h380b, CMOS_V_PIXEL[ 7:0] }; //垂直像素点数低8位 105 8\'d19 : sccb_data <= {16\'h380c,{3\'d0,TOTAL_H_PIXEL[12:8] }}; //水平总像素大小高5位 106 8\'d20 : sccb_data <= {16\'h380d, TOTAL_H_PIXEL[ 7:0] }; //水平总像素大小低8位 107 8\'d21 : sccb_data <= {16\'h380e,{3\'d0,TOTAL_V_PIXEL[12:8] }}; //垂直总像素大小高5位 108 8\'d22 : sccb_data <= {16\'h380f, TOTAL_V_PIXEL[ 7:0] }; //垂直总像素大小低8位 109 //预缩放 -------------------------------------------------------------------------------------- 110 8\'d23 : sccb_data <= {16\'h3810,8\'h00}; //Timing Hoffset[11:8] 111 8\'d24 : sccb_data <= {16\'h3811,8\'h10}; //Timing Hoffset[7:0] 112 8\'d25 : sccb_data <= {16\'h3812,8\'h00}; //Timing Voffset[10:8] 113 8\'d26 : sccb_data <= {16\'h3813,8\'h06}; //Timing Voffset[7:0] 114 8\'d27 : sccb_data <= {16\'h3814,8\'h31}; //Timing X INC 115 8\'d28 : sccb_data <= {16\'h3815,8\'h31}; //Timing Y INC 116 8\'d29 : sccb_data <= {16\'h3820,8\'h40}; //上下翻转:40/46 117 8\'d30 : sccb_data <= {16\'h3821,8\'h07}; //左右翻转:01/07 118 //SCCB ---------------------------------------------------------------------------------------- 119 8\'d31 : sccb_data <= {16\'h3103,8\'h02}; //Bit[1]:1 PLL Clock 120 8\'d32 : sccb_data <= {16\'h3108,8\'h01}; //系统分频 121 //VCM ----------------------------------------------------------------------------------------- 122 8\'d33 : sccb_data <= {16\'h3600,8\'h08}; //VCM控制,用于自动聚焦 123 8\'d34 : sccb_data <= {16\'h3601,8\'h33}; //VCM控制,用于自动聚焦 124 //AEC/AGC ------------------------------------------------------------------------------------- 125 8\'d35 : sccb_data <= {16\'h3a02,8\'h17}; //60Hz max exposure 126 8\'d36 : sccb_data <= {16\'h3a03,8\'h10}; //60Hz max exposure 127 8\'d37 : sccb_data <= {16\'h3a0f,8\'h30}; //AEC控制;stable range in high 128 8\'d38 : sccb_data <= {16\'h3a10,8\'h28}; //AEC控制;stable range in low 129 8\'d39 : sccb_data <= {16\'h3a11,8\'h60}; //AEC控制; fast zone high 130 8\'d40 : sccb_data <= {16\'h3a13,8\'h43}; //AEC(自动曝光控制) 131 8\'d41 : sccb_data <= {16\'h3a14,8\'h17}; //50Hz max exposure 132 8\'d42 : sccb_data <= {16\'h3a15,8\'h10}; //50Hz max exposure 133 8\'d43 : sccb_data <= {16\'h3a18,8\'h00}; //AEC 增益上限 134 8\'d44 : sccb_data <= {16\'h3a19,8\'hf8}; //AEC 增益上限 135 8\'d45 : sccb_data <= {16\'h3a1b,8\'h30}; //AEC控制;stable range out high 136 8\'d46 : sccb_data <= {16\'h3a1e,8\'h26}; //AEC控制;stable range out low 137 8\'d47 : sccb_data <= {16\'h3a1f,8\'h14}; //AEC控制; fast zone low 138 //5060Hz -------------------------------------------------------------------------------------- 139 8\'d48 : sccb_data <= {16\'h3c01,8\'h34}; 140 8\'d49 : sccb_data <= {16\'h3c04,8\'h28}; 141 8\'d50 : sccb_data <= {16\'h3c05,8\'h98}; 142 8\'d51 : sccb_data <= {16\'h3c06,8\'h00}; //light meter 1 阈值[15:8] 143 8\'d52 : sccb_data <= {16\'h3c07,8\'h08}; //light meter 1 阈值[7:0] 144 8\'d53 : sccb_data <= {16\'h3c08,8\'h00}; //light meter 2 阈值[15:8] 145 8\'d54 : sccb_data <= {16\'h3c09,8\'h1c}; //light meter 2 阈值[7:0] 146 8\'d55 : sccb_data <= {16\'h3c0a,8\'h9c}; //sample number[15:8] 147 8\'d56 : sccb_data <= {16\'h3c0b,8\'h40}; //sample number[7:0] 148 //BLC ----------------------------------------------------------------------------------------- 149 8\'d57 : sccb_data <= {16\'h4001,8\'h02}; //BLC(黑电平校准)补偿起始行号 150 8\'d58 : sccb_data <= {16\'h4004,8\'h02}; //BLC(背光) 2 lines 151 8\'d59 : sccb_data <= {16\'h4005,8\'h1a}; //BLC(黑电平校准)补偿始终更新 152 //ISP ----------------------------------------------------------------------------------------- 153 8\'d60 : sccb_data <= {16\'h5000,8\'ha7}; //ISP 控制 154 8\'d61 : sccb_data <= {16\'h5001,8\'ha3}; //ISP 控制 155 8\'d62 : sccb_data <= {16\'h501d,8\'h40}; //ISP 控制 156 8\'d63 : sccb_data <= {16\'h501f,8\'h01}; //ISP RGB 157 //LENC(镜头校正)控制 16\'h5800~16\'h583d -------------------------------------------------------- 158 8\'d64 : sccb_data <= {16\'h5800,8\'h23}; 159 8\'d65 : sccb_data <= {16\'h5801,8\'h14}; 160 8\'d66 : sccb_data <= {16\'h5802,8\'h0f}; 161 8\'d67 : sccb_data <= {16\'h5803,8\'h0f}; 162 8\'d68 : sccb_data <= {16\'h5804,8\'h12}; 163 8\'d69 : sccb_data <= {16\'h5805,8\'h26}; 164 8\'d70 : sccb_data <= {16\'h5806,8\'h0c}; 165 8\'d71 : sccb_data <= {16\'h5807,8\'h08}; 166 8\'d72 : sccb_data <= {16\'h5808,8\'h05}; 167 8\'d73 : sccb_data <= {16\'h5809,8\'h05}; 168 8\'d74 : sccb_data <= {16\'h580a,8\'h08}; 169 8\'d75 : sccb_data <= {16\'h580b,8\'h0d}; 170 8\'d76 : sccb_data <= {16\'h580c,8\'h08}; 171 8\'d77 : sccb_data <= {16\'h580d,8\'h03}; 172 8\'d78 : sccb_data <= {16\'h580e,8\'h00}; 173 8\'d79 : sccb_data <= {16\'h580f,8\'h00}; 174 8\'d80 : sccb_data <= {16\'h5810,8\'h03}; 175 8\'d81 : sccb_data <= {16\'h5811,8\'h09}; 176 8\'d82 : sccb_data <= {16\'h5812,8\'h07}; 177 8\'d83 : sccb_data <= {16\'h5813,8\'h03}; 178 8\'d84 : sccb_data <= {16\'h5814,8\'h00}; 179 8\'d85 : sccb_data <= {16\'h5815,8\'h01}; 180 8\'d86 : sccb_data <= {16\'h5816,8\'h03}; 181 8\'d87 : sccb_data <= {16\'h5817,8\'h08}; 182 8\'d88 : sccb_data <= {16\'h5818,8\'h0d}; 183 8\'d89 : sccb_data <= {16\'h5819,8\'h08}; 184 8\'d90 : sccb_data <= {16\'h581a,8\'h05}; 185 8\'d91 : sccb_data <= {16\'h581b,8\'h06}; 186 8\'d92 : sccb_data <= {16\'h581c,8\'h08}; 187 8\'d93 : sccb_data <= {16\'h581d,8\'h0e}; 188 8\'d94 : sccb_data <= {16\'h581e,8\'h29}; 189 8\'d95 : sccb_data <= {16\'h581f,8\'h17}; 190 8\'d96 : sccb_data <= {16\'h5820,8\'h11}; 191 8\'d97 : sccb_data <= {16\'h5821,8\'h11}; 192 8\'d98 : sccb_data <= {16\'h5822,8\'h15}; 193 8\'d99 : sccb_data <= {16\'h5823,8\'h28}; 194 8\'d100: sccb_data <= {16\'h5824,8\'h46}; 195 8\'d101: sccb_data <= {16\'h5825,8\'h26}; 196 8\'d102: sccb_data <= {16\'h5826,8\'h08}; 197 8\'d103: sccb_data <= {16\'h5827,8\'h26}; 198 8\'d104: sccb_data <= {16\'h5828,8\'h64}; 199 8\'d105: sccb_data <= {16\'h5829,8\'h26}; 200 8\'d106: sccb_data <= {16\'h582a,8\'h24}; 201 8\'d107: sccb_data <= {16\'h582b,8\'h22}; 202 8\'d108: sccb_data <= {16\'h582c,8\'h24}; 203 8\'d109: sccb_data <= {16\'h582d,8\'h24}; 204 8\'d110: sccb_data <= {16\'h582e,8\'h06}; 205 8\'d111: sccb_data <= {16\'h582f,8\'h22}; 206 8\'d112: sccb_data <= {16\'h5830,8\'h40}; 207 8\'d113: sccb_data <= {16\'h5831,8\'h42}; 208 8\'d114: sccb_data <= {16\'h5832,8\'h24}; 209 8\'d115: sccb_data <= {16\'h5833,8\'h26}; 210 8\'d116: sccb_data <= {16\'h5834,8\'h24}; 211 8\'d117: sccb_data <= {16\'h5835,8\'h22}; 212 8\'d118: sccb_data <= {16\'h5836,8\'h22}; 213 8\'d119: sccb_data <= {16\'h5837,8\'h26}; 214 8\'d120: sccb_data <= {16\'h5838,8\'h44}; 215 8\'d121: sccb_data <= {16\'h5839,8\'h24}; 216 8\'d122: sccb_data <= {16\'h583a,8\'h26}; 217 8\'d123: sccb_data <= {16\'h583b,8\'h28}; 218 8\'d124: sccb_data <= {16\'h583c,8\'h42}; 219 8\'d125: sccb_data <= {16\'h583d,8\'hce}; 220 //AWB(自动白平衡控制) 16\'h5180~16\'h519e ------------------------------------------------------- 221 8\'d126: sccb_data <= {16\'h5180,8\'hff}; 222 8\'d127: sccb_data <= {16\'h5181,8\'hf2}; 223 8\'d128: sccb_data <= {16\'h5182,8\'h00}; 224 8\'d129: sccb_data <= {16\'h5183,8\'h14}; 225 8\'d130: sccb_data <= {16\'h5184,8\'h25}; 226 8\'d131: sccb_data <= {16\'h5185,8\'h24}; 227 8\'d132: sccb_data <= {16\'h5186,8\'h09}; 228 8\'d133: sccb_data <= {16\'h5187,8\'h09}; 229 8\'d134: sccb_data <= {16\'h5188,8\'h09}; 230 8\'d135: sccb_data <= {16\'h5189,8\'h75}; 231 8\'d136: sccb_data <= {16\'h518a,8\'h54}; 232 8\'d137: sccb_data <= {16\'h518b,8\'he0}; 233 8\'d138: sccb_data <= {16\'h518c,8\'hb2}; 234 8\'d139: sccb_data <= {16\'h518d,8\'h42}; 235 8\'d140: sccb_data <= {16\'h518e,8\'h3d}; 236 8\'d141: sccb_data <= {16\'h518f,8\'h56}; 237 8\'d142: sccb_data <= {16\'h5190,8\'h46}; 238 8\'d143: sccb_data <= {16\'h5191,8\'hf8}; 239 8\'d144: sccb_data <= {16\'h5192,8\'h04}; 240 8\'d145: sccb_data <= {16\'h5193,8\'h70}; 241 8\'d146: sccb_data <= {16\'h5194,8\'hf0}; 242 8\'d147: sccb_data <= {16\'h5195,8\'hf0}; 243 8\'d148: sccb_data <= {16\'h5196,8\'h03}; 244 8\'d149: sccb_data <= {16\'h5197,8\'h01}; 245 8\'d150: sccb_data <= {16\'h5198,8\'h04}; 246 8\'d151: sccb_data <= {16\'h5199,8\'h12}; 247 8\'d152: sccb_data <= {16\'h519a,8\'h04}; 248 8\'d153: sccb_data <= {16\'h519b,8\'h00}; 249 8\'d154: sccb_data <= {16\'h519c,8\'h06}; 250 8\'d155: sccb_data <= {16\'h519d,8\'h82}; 251 8\'d156: sccb_data <= {16\'h519e,8\'h38}; 252 //Gamma(伽马)控制 16\'h5480~16\'h5490 ----------------------------------------------------------- 253 8\'d157: sccb_data <= {16\'h5480,8\'h01}; 254 8\'d158: sccb_data <= {16\'h5481,8\'h08}; 255 8\'d159: sccb_data <= {16\'h5482,8\'h14}; 256 8\'d160: sccb_data <= {16\'h5483,8\'h28}; 257 8\'d161: sccb_data <= {16\'h5484,8\'h51}; 258 8\'d162: sccb_data <= {16\'h5485,8\'h65}; 259 8\'d163: sccb_data <= {16\'h5486,8\'h71}; 260 8\'d164: sccb_data <= {16\'h5487,8\'h7d}; 261 8\'d165: sccb_data <= {16\'h5488,8\'h87}; 262 8\'d166: sccb_data <= {16\'h5489,8\'h91}; 263 8\'d167: sccb_data <= {16\'h548a,8\'h9a}; 264 8\'d168: sccb_data <= {16\'h548b,8\'haa}; 265 8\'d169: sccb_data <= {16\'h548c,8\'hb8}; 266 8\'d170: sccb_data <= {16\'h548d,8\'hcd}; 267 8\'d171: sccb_data <= {16\'h548e,8\'hdd}; 268 8\'d172: sccb_data <= {16\'h548f,8\'hea}; 269 8\'d173: sccb_data <= {16\'h5490,8\'h1d}; 270 //CMX(彩色矩阵控制) 16\'h5381~16\'h538b --------------------------------------------------------- 271 8\'d174: sccb_data <= {16\'h5381,8\'h1e}; 272 8\'d175: sccb_data <= {16\'h5382,8\'h5b}; 273 8\'d176: sccb_data <= {16\'h5383,8\'h08}; 274 8\'d177: sccb_data <= {16\'h5384,8\'h0a}; 275 8\'d178: sccb_data <= {16\'h5385,8\'h7e}; 276 8\'d179: sccb_data <= {16\'h5386,8\'h88}; 277 8\'d180: sccb_data <= {16\'h5387,8\'h7c}; 278 8\'d181: sccb_data <= {16\'h5388,8\'h6c}; 279 8\'d182: sccb_data <= {16\'h5389,8\'h10}; 280 8\'d183: sccb_data <= {16\'h538a,8\'h01}; 281 8\'d184: sccb_data <= {16\'h538b,8\'h98}; 282 //SDE(特殊数码效果)控制 16\'h5580~16\'h558b ----------------------------------------------------- 283 8\'d185: sccb_data <= {16\'h5580,8\'h06}; 284 8\'d186: sccb_data <= {16\'h5583,8\'h40}; 285 8\'d187: sccb_data <= {16\'h5584,8\'h10}; 286 8\'d188: sccb_data <= {16\'h5589,8\'h10}; 287 8\'d189: sccb_data <= {16\'h558a,8\'h00}; 288 8\'d190: sccb_data <= {16\'h558b,8\'hf8}; 289 //CIP(颜色插值)控制 (16\'h5300~16\'h530c) ------------------------------------------------------- 290 8\'d191: sccb_data <= {16\'h5300,8\'h08}; 291 8\'d192: sccb_data <= {16\'h5301,8\'h30}; 292 8\'d193: sccb_data <= {16\'h5302,8\'h10}; 293 8\'d194: sccb_data <= {16\'h5303,8\'h00}; 294 8\'d195: sccb_data <= {16\'h5304,8\'h08}; 295 8\'d196: sccb_data <= {16\'h5305,8\'h30}; 296 8\'d197: sccb_data <= {16\'h5306,8\'h08}; 297 8\'d198: sccb_data <= {16\'h5307,8\'h16}; 298 8\'d199: sccb_data <= {16\'h5309,8\'h08}; 299 8\'d200: sccb_data <= {16\'h530a,8\'h30}; 300 8\'d201: sccb_data <= {16\'h530b,8\'h04}; 301 8\'d202: sccb_data <= {16\'h530c,8\'h06}; 302 //测试闪光灯功能 ------------------------------------------------------------------------------ 303 8\'d203: sccb_data <= {16\'h3000,8\'h00}; //系统块复位控制 304 8\'d204: sccb_data <= {16\'h3004,8\'hff}; //时钟使能控制 305 8\'d205: sccb_data <= {16\'h3017,8\'hff}; //I/O控制[3:0] 00:input ff:output 306 8\'d206: sccb_data <= {16\'h3018,8\'hff}; //I/O控制[7:2] 00:input ff:output 307 8\'d207: sccb_data <= {16\'h3016,8\'h02}; 308 8\'d208: sccb_data <= {16\'h301c,8\'h02}; 309 8\'d209: sccb_data <= {16\'h3019,8\'h02}; //打开闪光灯 310 8\'d210: sccb_data <= {16\'h3019,8\'h00}; //关闭闪光灯 311 //others -------------------------------------------------------------------------------------- 312 8\'d211: sccb_data <= {16\'h3612,8\'h29}; 313 8\'d212: sccb_data <= {16\'h3618,8\'h00}; 314 8\'d213: sccb_data <= {16\'h3620,8\'h52}; 315 8\'d214: sccb_data <= {16\'h3621,8\'he0}; 316 8\'d215: sccb_data <= {16\'h3622,8\'h01}; 317 8\'d216: sccb_data <= {16\'h302d,8\'h60}; //系统控制 318 8\'d217: sccb_data <= {16\'h3630,8\'h36}; 319 8\'d218: sccb_data <= {16\'h3631,8\'h0e}; 320 8\'d219: sccb_data <= {16\'h3632,8\'he2}; 321 8\'d220: sccb_data <= {16\'h3633,8\'h12}; 322 8\'d221: sccb_data <= {16\'h3634,8\'h40}; 323 8\'d222: sccb_data <= {16\'h3635,8\'h13}; 324 8\'d223: sccb_data <= {16\'h3636,8\'h03}; 325 8\'d224: sccb_data <= {16\'h3703,8\'h5a}; 326 8\'d225: sccb_data <= {16\'h3704,8\'ha0}; 327 8\'d226: sccb_data <= {16\'h3705,8\'h1a}; 328 8\'d227: sccb_data <= {16\'h3708,8\'h64}; 329 8\'d228: sccb_data <= {16\'h3709,8\'h52}; 330 8\'d229: sccb_data <= {16\'h370b,8\'h60}; 331 8\'d230: sccb_data <= {16\'h370c,8\'h03}; 332 8\'d231: sccb_data <= {16\'h3715,8\'h78}; 333 8\'d232: sccb_data <= {16\'h3717,8\'h01}; 334 8\'d233: sccb_data <= {16\'h371b,8\'h20}; 335 8\'d234: sccb_data <= {16\'h3731,8\'h12}; 336 8\'d235: sccb_data <= {16\'h3901,8\'h0a}; 337 8\'d236: sccb_data <= {16\'h3905,8\'h02}; 338 8\'d237: sccb_data <= {16\'h3906,8\'h10}; 339 8\'d238: sccb_data <= {16\'h3b07,8\'h0a}; //帧曝光模式 340 8\'d239: sccb_data <= {16\'h4407,8\'h04}; //量化标度 341 default:sccb_data <= {16\'h300a,8\'h00}; //器件ID高8位 342 endcase 343 end 344 end 345 346 347 348 endmodule
ov5640的寄存器配置如上所示,可以配出 1024x768@30fps 的图像,该寄存器配置表同样来自正点原子的改编。注意一下,OV5640的寄存器地址是 16 位的,加上数据,sccb_data 的值为 24 位,这点和OV7670、OV7725不一样。
1、窗口输出的疑惑
其窗口输出分三个部分:
(1)ISP 输入窗口设置( ISP input size):0x3800~0x3807
(2)预缩放窗口设置( pre-scaling size) :0X3810~0X3821
(3)输出窗口设置( data output size) :0X3808~0X380B
按照很多开发板提供的寄存器参考表确实能得到图像,但是很奇怪。首先 ISP 部分的寄存器。尽管输出分辨率很大(例如1024x768),但是很多人的ISP寄存器部分都是参照的 VGA(640x480)模式下的值,最后竟然也能输出。其次是预缩放窗口的寄存器,这个没有深究,仅仅试了下上下翻转和左右翻转。最后是输出窗口的寄存器,正点原子的还加上了0x380C~0x380F,而且和0x3808~0x380B是配合的。如果不改0x380C~0x380F,那还没什么问题,当我试着更改这些值时,有时会不出图像,有时图像会缩小或放大。
2、Pclk的疑惑
OV7670 和 OV7725 的 Pclk 很容易通过 PLL 的调配得到,但是 OV5640 却不一样。首先,OV5640 的 PLL 有三个,如下所示:
8\'d4 : sccb_data <= {16\'h3035,8\'h21}; //PLL分频 11/1÷ 21/2÷ 32/3÷ 8\'d5 : sccb_data <= {16\'h3036,8\'h69}; //PLL倍频 23/1x 46/2x 69/3x 8\'d6 : sccb_data <= {16\'h3037,8\'h03}; //PLL分频 bit[4]:0/1 bypass/÷2
先说寄存器 16\'h3037寄存器,该寄存器默认值是03(也有很多人设为13),bit[4]的意思是旁路 PLL(即不管)或 2 分频,而 bit[3:0] 在 datasheet 中也有分频的意义,可却没有说明怎么回事。我这里设置 3037=03,即旁路pll,以下的说明都以这个为前提。
然后是16\'h3035寄存器,该pll寄存器比较简单,其[7:4]的值代表着分频系数。
最后是寄存器16\'h3036,这个寄存器的值就奇怪了,很多人写的是69,我研究发现 69 应该是 x3 的意思,相应的 23 是 x1,46 是 x2,当 3035 为 11 而 3036 为 23 时,fps为20,以此为基准就可以对这两个寄存器配置不同的值达到想要的帧率了,如下是我实验测得的不同配置下的 fps 值。(如何测量 fps 值在下一讲会整理)
虽然 3036 寄存器和 3037 寄存器没有完全搞懂,但是用最傻瓜的办法还是得到了一些规律,至少可以得到一些想要的 fps 的值了。但是 Pclk 为多少呢?完全没办法知道!首先是这些测量结果和《ov5640寄存器参考列表》等PDF的值有出入,可能是设置窗口的数据和那些手册不完全一致。其次按道理说,fps 增大那么 Pclk 也会增大,但是我测量 Pclk 的结果却很诡异,根本不是那么回事。关于 Pclk 的相关资料很少,我实在搞不懂,如果有人知道,烦请指教一二。
多说一句,OV7725 和 OV5640 的成像效果都很好,而 OV7670 的效果则非常差,我试了很多人的配置表,都是一样的效果,非常差劲。而且很奇怪的是,OV7670 的图像效果是旋转了 90 度的,非常别扭,我查看寄存器列表也没有发现改变旋转的寄存器配置。所以说,如果嫌 OV5640 贵,那可以用 OV7725,千万别为了便宜去买 OV7670,这个摄像头就是个垃圾,谁用谁恶心!
至此,我们完成了 SCCB 的配置,下一讲的内容是 cmos_capture_rgb565,即 DVP 窗口输出的设置。
参考资料:
[1]正点原子FPGA教程
[2]小梅哥《OV5640图像采集从原理到应用》
[3]开源骚客《SDRAM那些事儿》
[4]韩彬, 于潇宇, 张雷鸣. FPGA设计技巧与案例开发详解[M]. 电子工业出版社, 2014.