赞
踩
有的摄像头输出像素数据是八位,有的是十位。使用时注意甄别
1.最大支持2592x1944像素输出
2.支持8~10位RGB或者RAW输出
3.输入时钟6~27MHZ
4.不同像素的输出速度
像素 | 刷新率 |
---|---|
QSXGA (2592 x 1944) | 15FPS |
1080p(1920 x 1080) | 30FPS |
1280 x 960 | 45FPS |
720p(1280 x 720) | 60FPS |
VGA(640 x 480) | 90FPS |
QVGA(320 x 240) | 120FPS |
DOVDD和AVDD不需要代码控制
在使用摄像头时只需要控制PWDN和RESET即可,其余引脚均未开放。
PWDN在t2时刻拉低,RESETB在t3时刻拉高,拉高后t4时间可以开始用SCCB线传输数据。
/* 摄像头上电初始化过程 上电时序 初始:pwdn = 1,rst_n = 0;done = 0; 6ms后:pwdn = 0,rst_n = 0; done = 0; 2ms后:pwdn = 0,rst_n = 1; done = 0; 21ms后:pwdn = 0,rst_n = 1; done = 1; */ module power_ctrl( input wire clk , //50MHZ时钟 input wire rst_n , //复位信号 output wire ov5640_pwdn , //ov5640的pwdn信号线,初始为高,6ms后拉低 output wire ov5640_rst_n, //ov5640的rst_n复位信号线,低电平有效 output wire power_done //上电完成标志位,完成后一直为高 ); localparam DELAY_6MS = 30_0000 ; localparam DELAY_2MS = 10_0000 ; localparam DELAY_21MS = 105_0000 ; reg [18:0] cnt_6ms ; reg [16:0] cnt_2ms ; reg [20:0] cnt_21ms ; always @(posedge clk) begin if (rst_n == 1'b0) begin // reset cnt_6ms <= 'd0; end else if (ov5640_pwdn == 1'b1) begin cnt_6ms <= cnt_6ms + 1'b1; end end always @(posedge clk) begin if (rst_n == 1'b0) begin // reset cnt_2ms <= 'd0; end else if (ov5640_rst_n == 1'b0 && ov5640_pwdn == 1'b0) begin cnt_2ms <= cnt_2ms + 1'b1; end end always @(posedge clk) begin if (rst_n == 1'b0) begin // reset cnt_21ms <= 'd0; end else if (ov5640_rst_n == 1'b1 & power_done == 1'b0) begin cnt_21ms <= cnt_21ms + 1'b1; end end assign ov5640_pwdn = (cnt_6ms >= DELAY_6MS) ? 1'b0 : 1'b1; //初始为高,6ms后置低 assign ov5640_rst_n = (cnt_2ms >= DELAY_2MS) ? 1'b1 : 1'b0; //初始为低,pwdn拉低后2ms置高 assign power_done = (cnt_21ms >= DELAY_21MS) ? 1'b1 : 1'b0; //初始为低,上电完成后置高 endmodule
ID Address(W) = 7’h78(低位补0后),在ov5640众多寄存器中,有些寄存器时可改写的,有些是只读的,只有可改写的才能正确输入。
关于SCCB协议可以参考其他文章,这里不详细介绍。在本模块中只用到了SCCB写功能。对SCCB协议精简后构建了以下模块
module SCCB_WR #( parameter CLK_FREQ = 26'd50_000_000, //模块输入的时钟频率 parameter SCCB_FREQ = 18'd250_000 //IIC_SCL的时钟频率 ) ( input wire clk , //系统时钟 input wire rst_n , //复位信号 input wire sccb_exec , //sccb协议传输开始 input wire bit_ctrl , //地址位控制 input wire [15:0] sccb_addr , //寄存器地址 input wire [ 7:0] sccb_data_wr , //写数据 input wire [ 6:0] SLAVE_ADDR , //从机地址 output reg sccb_done , //sccb协议传输完成 output reg sccb_clk , //sccb模块的工作时钟 output reg sio_c , //sccb协议传输时钟 inout wire sio_d //sccb协议数据线 ); parameter CLK_DIVIDE_MAX = (CLK_FREQ / SCCB_FREQ) >> (1'b1 + 2'd2) - 1'b1; //(SCCB协议的四分频计数最大值) parameter st_idle = 6'b00_0001 ; //初始状态 parameter st_addr_wr = 6'b00_0010 ; //设备地址写 parameter st_addr_16 = 6'b00_0100 ; //寄存器地址高八位写入 parameter st_addr_8 = 6'b00_1000 ; //寄存器地址低八位写入 parameter st_data_wr = 6'b01_0000 ; //写数据传输 parameter st_stop = 6'b10_0000 ; //一次通讯结束 reg [ 5:0] cur_state ; //状态机当前状态 reg [ 5:0] next_state ; //状态机下一状态 reg st_done ; //状态完成(数据发送完成) reg [ 8:0] clk_divide ; //模块驱动时钟的分频系数 reg [ 7:0] cnt ; //sccb_clk 计数 reg bit_ctrl_reg ; //地址位控制寄存 reg [ 7:0] sccb_data_wr_reg ; //写数据寄存 reg [15:0] sccb_addr_reg ; //寄存器地址寄存 reg [ 7:0] SLAVE_ADDR_reg ; //从机设备地址寄存 reg sio_d_dir ; //sio输入输出控制 高输出,低输入 reg sio_d_out ; //sio_d输出信号 wire sio_d_in ; //sio_d输入信号 parameter CLK_DIVIDE = (CLK_FREQ / SCCB_FREQ) >> (1'b1 + 2'd2) ; //(SCCB协议的四分频) //三态们输出 assign sio_d = (sio_d_dir == 1'b1) ? sio_d_out : 'dz; assign sio_d_in = sio_d; //模块驱动时钟计数器 always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset clk_divide <= 'd0; end else if (clk_divide == CLK_DIVIDE_MAX) begin clk_divide <= 'd0; end else begin clk_divide <= clk_divide + 1'b1; end end //模块驱动时钟 always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset sccb_clk <= 1'b0; end else if(clk_divide == CLK_DIVIDE_MAX) begin sccb_clk <= ~sccb_clk; end end //三段式状态机,同步时序描述状态转移 always @(posedge sccb_clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset cur_state <= st_idle; end else begin cur_state <= next_state; end end //组合逻辑判断状态转移条件 always @(*) begin next_state = st_idle; case(cur_state) st_idle : begin //初始状态,当传输开始时状态跳转 if(sccb_exec == 1'b1)begin next_state = st_addr_wr; end else begin next_state = st_idle; end end st_addr_wr : begin //发送设备地址加读 if(st_done == 1'b1)begin if (bit_ctrl == 1'b1)begin next_state = st_addr_16; end else begin next_state = st_addr_8; end end else begin next_state = st_addr_wr; end end st_addr_16 : begin //发送寄存器地址高八位 if(st_done == 1'b1)begin next_state = st_addr_8; end else begin next_state = st_addr_16; end end st_addr_8 : begin //发送寄存器地址低八位 if(st_done == 1'b1)begin next_state = st_data_wr; end else begin next_state = st_addr_8; //未完成,保持 end end st_data_wr : begin if(st_done == 1'b1)begin next_state = st_stop; end else begin next_state = st_data_wr; end end st_stop : begin if(st_done == 1'b1)begin next_state = st_idle; end else begin next_state = st_stop; end end default : next_state = st_idle; endcase end //时序电路描述状态输出 always @(posedge sccb_clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset cnt <= 'd0 ; //传输寄存器 st_done <= 'd0 ; //传输完成标志位 sio_c <= 'd1 ; //sio时钟线 sio_d_out <= 'd1 ; //sio_d输出 sio_d_dir <= 'd1 ; //sio_d输入输出判断 bit_ctrl_reg <= bit_ctrl ; //寄存器地址位寄存 sccb_addr_reg <= sccb_addr ; //寄存器地址寄存 sccb_data_wr_reg <= sccb_data_wr ; //写数据寄存 SLAVE_ADDR_reg <= SLAVE_ADDR ; //从机地址寄存 end else begin st_done <= 1'b0; cnt <= cnt + 1'b1; case(cur_state) st_idle : begin cnt <= 'd0 ; //传输寄存器 st_done <= 'd0 ; //传输完成标志位 sio_c <= 'd1 ; //sio时钟线,默认高电平 sio_d_out <= 'd1 ; //sio_d输出,默认高电平 sio_d_dir <= 'd1 ; //sio_d输入输出判断 sccb_done <= 'd0; if(sccb_exec == 1'b1)begin bit_ctrl_reg <= bit_ctrl ; //寄存器地址位寄存 sccb_addr_reg <= sccb_addr ; //寄存器地址寄存 sccb_data_wr_reg <= sccb_data_wr ; //写数据寄存 SLAVE_ADDR_reg <= SLAVE_ADDR ; //从机地址寄存 end end st_addr_wr : begin //发送起始信号设备地址加写标志 cnt <= cnt + 1'b1; case(cnt) 1 : sio_d_out <= 1'b0; 3 :sio_c <= 1'b0; 4 :sio_d_out <= SLAVE_ADDR_reg[7]; 5 :sio_c <= 1'b1; 7 :sio_c <= 1'b0; 8 :sio_d_out <= SLAVE_ADDR_reg[6]; 9 :sio_c <= 1'b1; 11:sio_c <= 1'b0; 12:sio_d_out <= SLAVE_ADDR_reg[5]; 13:sio_c <= 1'b1; 15:sio_c <= 1'b0; 16:sio_d_out <= SLAVE_ADDR_reg[4]; 17:sio_c <= 1'b1; 19:sio_c <= 1'b0; 20:sio_d_out <= SLAVE_ADDR_reg[3]; 21:sio_c <= 1'b1; 23:sio_c <= 1'b0; 24:sio_d_out <= SLAVE_ADDR_reg[2]; 25:sio_c <= 1'b1; 27:sio_c <= 1'b0; 28:sio_d_out <= SLAVE_ADDR_reg[1]; 29:sio_c <= 1'b1; 31:sio_c <= 1'b0; 32:sio_d_out <= SLAVE_ADDR_reg[0]; 33:sio_c <= 1'b1; 35:sio_c <= 1'b0; 36: begin sio_d_dir <= 1'b0; sio_d_out <= 1'b1; end 37:sio_c <= 1'b1; 38: begin //sccb的应答标志位不在乎 st_done <= 1'b1; //if(sccb_d_in == 1'b1) end 39: begin sio_c <= 1'b0; cnt <= 'd0; end default : ; endcase end st_addr_16 : begin cnt <= cnt + 1'b1; case(cnt) 0 : begin sio_d_out <= sccb_addr_reg[15]; sio_d_dir <= 1'b1; end 1 :sio_c <= 1'b1; 3 :sio_c <= 1'b0; 4 :sio_d_out <= sccb_addr_reg[14]; 5 :sio_c <= 1'b1; 7 :sio_c <= 1'b0; 8 :sio_d_out <= sccb_addr_reg[13]; 9 :sio_c <= 1'b1; 11:sio_c <= 1'b0; 12:sio_d_out <= sccb_addr_reg[12]; 13:sio_c <= 1'b1; 15:sio_c <= 1'b0; 16:sio_d_out <= sccb_addr_reg[11]; 17:sio_c <= 1'b1; 19:sio_c <= 1'b0; 20:sio_d_out <= sccb_addr_reg[10]; 21:sio_c <= 1'b1; 23:sio_c <= 1'b0; 24:sio_d_out <= sccb_addr_reg[9]; 25:sio_c <= 1'b1; 27:sio_c <= 1'b0; 28:sio_d_out <= sccb_addr_reg[8]; 29:sio_c <= 1'b1; 31:sio_c <= 1'b0; 32:begin sio_d_dir <= 1'b0; sio_d_out <= 1'b1; end 33:sio_c <= 1'b1; 34:st_done <= 1'b1; 35: begin sio_c <= 1'b0; cnt <= 'd0; end default : ; endcase end st_addr_8 : begin cnt <= cnt + 1'b1; case(cnt) 0 : begin sio_d_out <= sccb_addr_reg[7]; sio_d_dir <= 1'b1; end 1 :sio_c <= 1'b1; 3 :sio_c <= 1'b0; 4 :sio_d_out <= sccb_addr_reg[6]; 5 :sio_c <= 1'b1; 7 :sio_c <= 1'b0; 8 :sio_d_out <= sccb_addr_reg[5]; 9 :sio_c <= 1'b1; 11:sio_c <= 1'b0; 12:sio_d_out <= sccb_addr_reg[4]; 13:sio_c <= 1'b1; 15:sio_c <= 1'b0; 16:sio_d_out <= sccb_addr_reg[3]; 17:sio_c <= 1'b1; 19:sio_c <= 1'b0; 20:sio_d_out <= sccb_addr_reg[2]; 21:sio_c <= 1'b1; 23:sio_c <= 1'b0; 24:sio_d_out <= sccb_addr_reg[1]; 25:sio_c <= 1'b1; 27:sio_c <= 1'b0; 28:sio_d_out <= sccb_addr_reg[0]; 29:sio_c <= 1'b1; 31:sio_c <= 1'b0; 32:begin sio_d_dir <= 1'b0; sio_d_out <= 1'b1; end 33:sio_c <= 1'b1; 34:st_done <= 1'b1; 35: begin sio_c <= 1'b0; cnt <= 'd0; end default : ; endcase end st_data_wr : begin cnt <= cnt + 1'b1; case(cnt) 0 :begin sio_d_out <= sccb_data_wr_reg[7]; sio_d_dir <= 1'b1; end 1 :sio_c <= 1'b1; 3 :sio_c <= 1'b0; 4 :sio_d_out <= sccb_data_wr_reg[6]; 5 :sio_c <= 1'b1; 7 :sio_c <= 1'b0; 8 :sio_d_out <= sccb_data_wr_reg[5]; 9 :sio_c <= 1'b1; 11:sio_c <= 1'b0; 12:sio_d_out <= sccb_data_wr_reg[4]; 13:sio_c <= 1'b1; 15:sio_c <= 1'b0; 16:sio_d_out <= sccb_data_wr_reg[3]; 17:sio_c <= 1'b1; 19:sio_c <= 1'b0; 20:sio_d_out <= sccb_data_wr_reg[2]; 21:sio_c <= 1'b1; 23:sio_c <= 1'b0; 24:sio_d_out <= sccb_data_wr_reg[1]; 25:sio_c <= 1'b1; 27:sio_c <= 1'b0; 28:sio_d_out <= sccb_data_wr_reg[0]; 29:sio_c <= 1'b1; 31:sio_c <= 1'b0; 32:begin sio_d_dir <= 1'b0; sio_d_out <= 1'b1; end 33:sio_c <= 1'b1; 34:st_done <= 1'b1; 35: begin sio_c <= 1'b0; cnt <= 'd0; end default : ; endcase end st_stop : begin cnt <= cnt + 1'b1; case(cnt) 0 : begin sio_d_out <= 1'b0; sio_d_dir <= 1'b1; end 1 :sio_c <= 1'b1; 4 :sio_d_out <= 1'b1; 14: begin sccb_done <= 1'b1; st_done <= 1'b1; end 15: cnt <= 'd0; default : ; endcase end endcase end end endmodule
OV5640内部有许多的变量需要配置,如输出格式,像素大小等。
该模块内部有一块rom,用来存储寄存器的地址和参数,当摄像头上电完成时开始通过SCCB模块向ov5640内部寄存器写入参数。
ov5640关键参数和地址
开窗:摄像头物理像素工作区域:水平0-2591,竖直0-1943,地址0x3800到0x3807。两个地址标志一个值。
平移:将开窗平移,在不移动摄像头的前提下改变拍摄位置。地址0x3910到0x3813。这里只的数据代表的是偏移量。偏移量的大小值是基于 ISP 输入窗口的起始地址的增量。
输出窗口的大小:最终输出的像素大小。地址0x3808到0x380B。
module sccb_ov5640_cfg //========================< 参数 >========================================== #( parameter REG_NUM = 240 , //寄存器个数 parameter CMOS_H_PIXEL = 12'd1024 , //CMOS水平方向像素个数 parameter CMOS_V_PIXEL = 12'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要配置的地址与数据(高16位地址,低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) //Xclk=24Mhz FPS=30fps Pclk=24Mhz(不解) //基本配置 ------------------------------------------------------------------------------------ 8'd0 : sccb_data <= {16'h3008,8'h02}; //复位休眠 Bit[7]:复位 Bit[6]:休眠 8'd1 : sccb_data <= {16'h300e,8'h58}; //DVP 使能 DVP enable 8'd2 : sccb_data <= {16'h4300,8'h61}; //格式控制 RGB565 8'd3 : sccb_data <= {16'h503d,8'h00}; //测试图案 00正常 80彩条 81混乱 82棋盘 //PLL(11_23为20fps) --------------------------------------------------------------------------- 8'd4 : sccb_data <= {16'h3035,8'h21}; //PLL分频 11/1x 21/2x 32/3x 8'd5 : sccb_data <= {16'h3036,8'h69}; //PLL倍频 23/1÷ 46/2÷ 69/3÷ 8'd6 : sccb_data <= {16'h3037,8'h03}; //PLL分频 bit[4]:0/1 bypass/÷2 //ISP(VGA模式) -------------------------------------------------------------------------------- 8'd7 : sccb_data <= {16'h3800,8'h00}; 8'd8 : sccb_data <= {16'h3801,8'h00}; 8'd9 : sccb_data <= {16'h3802,8'h00}; 8'd10 : sccb_data <= {16'h3803,8'h04}; 8'd11 : sccb_data <= {16'h3804,8'h0a}; 8'd12 : sccb_data <= {16'h3805,8'h3f}; 8'd13 : sccb_data <= {16'h3806,8'h07}; 8'd14 : sccb_data <= {16'h3807,8'h9b}; //输出窗口设置 -------------------------------------------------------------------------------- 8'd15 : sccb_data <= {16'h3808,{4'd0,CMOS_H_PIXEL[11:8] }}; //水平像素点数高4位 8'd16 : sccb_data <= {16'h3809, CMOS_H_PIXEL[ 7:0] }; //水平像素点数低8位 8'd17 : sccb_data <= {16'h380a,{5'd0,CMOS_V_PIXEL[10:8] }}; //垂直像素点数高3位 8'd18 : sccb_data <= {16'h380b, CMOS_V_PIXEL[ 7:0] }; //垂直像素点数低8位 8'd19 : sccb_data <= {16'h380c,{3'd0,TOTAL_H_PIXEL[12:8] }}; //水平总像素大小高5位 8'd20 : sccb_data <= {16'h380d, TOTAL_H_PIXEL[ 7:0] }; //水平总像素大小低8位 8'd21 : sccb_data <= {16'h380e,{3'd0,TOTAL_V_PIXEL[12:8] }}; //垂直总像素大小高5位 8'd22 : sccb_data <= {16'h380f, TOTAL_V_PIXEL[ 7:0] }; //垂直总像素大小低8位 //预缩放 -------------------------------------------------------------------------------------- 8'd23 : sccb_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8] 8'd24 : sccb_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0] 8'd25 : sccb_data <= {16'h3812,8'h00}; //Timing Voffset[10:8] 8'd26 : sccb_data <= {16'h3813,8'h06}; //Timing Voffset[7:0] 8'd27 : sccb_data <= {16'h3814,8'h31}; //Timing X INC 8'd28 : sccb_data <= {16'h3815,8'h31}; //Timing Y INC 8'd29 : sccb_data <= {16'h3820,8'h40}; //上下翻转:40/46 8'd30 : sccb_data <= {16'h3821,8'h07}; //左右翻转:01/07 //SCCB ---------------------------------------------------------------------------------------- 8'd31 : sccb_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock 8'd32 : sccb_data <= {16'h3108,8'h01}; //系统分频 //VCM ----------------------------------------------------------------------------------------- 8'd33 : sccb_data <= {16'h3600,8'h08}; //VCM控制,用于自动聚焦 8'd34 : sccb_data <= {16'h3601,8'h33}; //VCM控制,用于自动聚焦 //AEC/AGC ------------------------------------------------------------------------------------- 8'd35 : sccb_data <= {16'h3a02,8'h17}; //60Hz max exposure 8'd36 : sccb_data <= {16'h3a03,8'h10}; //60Hz max exposure 8'd37 : sccb_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high 8'd38 : sccb_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low 8'd39 : sccb_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high 8'd40 : sccb_data <= {16'h3a13,8'h43}; //AEC(自动曝光控制) 8'd41 : sccb_data <= {16'h3a14,8'h17}; //50Hz max exposure 8'd42 : sccb_data <= {16'h3a15,8'h10}; //50Hz max exposure 8'd43 : sccb_data <= {16'h3a18,8'h00}; //AEC 增益上限 8'd44 : sccb_data <= {16'h3a19,8'hf8}; //AEC 增益上限 8'd45 : sccb_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high 8'd46 : sccb_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low 8'd47 : sccb_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low //5060Hz -------------------------------------------------------------------------------------- 8'd48 : sccb_data <= {16'h3c01,8'h34}; 8'd49 : sccb_data <= {16'h3c04,8'h28}; 8'd50 : sccb_data <= {16'h3c05,8'h98}; 8'd51 : sccb_data <= {16'h3c06,8'h00}; //light meter 1 阈值[15:8] 8'd52 : sccb_data <= {16'h3c07,8'h08}; //light meter 1 阈值[7:0] 8'd53 : sccb_data <= {16'h3c08,8'h00}; //light meter 2 阈值[15:8] 8'd54 : sccb_data <= {16'h3c09,8'h1c}; //light meter 2 阈值[7:0] 8'd55 : sccb_data <= {16'h3c0a,8'h9c}; //sample number[15:8] 8'd56 : sccb_data <= {16'h3c0b,8'h40}; //sample number[7:0] //BLC ----------------------------------------------------------------------------------------- 8'd57 : sccb_data <= {16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号 8'd58 : sccb_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines 8'd59 : sccb_data <= {16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新 //ISP ----------------------------------------------------------------------------------------- 8'd60 : sccb_data <= {16'h5000,8'ha7}; //ISP 控制 8'd61 : sccb_data <= {16'h5001,8'ha3}; //ISP 控制 8'd62 : sccb_data <= {16'h501d,8'h40}; //ISP 控制 8'd63 : sccb_data <= {16'h501f,8'h01}; //ISP RGB //LENC(镜头校正)控制 16'h5800~16'h583d -------------------------------------------------------- 8'd64 : sccb_data <= {16'h5800,8'h23}; 8'd65 : sccb_data <= {16'h5801,8'h14}; 8'd66 : sccb_data <= {16'h5802,8'h0f}; 8'd67 : sccb_data <= {16'h5803,8'h0f}; 8'd68 : sccb_data <= {16'h5804,8'h12}; 8'd69 : sccb_data <= {16'h5805,8'h26}; 8'd70 : sccb_data <= {16'h5806,8'h0c}; 8'd71 : sccb_data <= {16'h5807,8'h08}; 8'd72 : sccb_data <= {16'h5808,8'h05}; 8'd73 : sccb_data <= {16'h5809,8'h05}; 8'd74 : sccb_data <= {16'h580a,8'h08}; 8'd75 : sccb_data <= {16'h580b,8'h0d}; 8'd76 : sccb_data <= {16'h580c,8'h08}; 8'd77 : sccb_data <= {16'h580d,8'h03}; 8'd78 : sccb_data <= {16'h580e,8'h00}; 8'd79 : sccb_data <= {16'h580f,8'h00}; 8'd80 : sccb_data <= {16'h5810,8'h03}; 8'd81 : sccb_data <= {16'h5811,8'h09}; 8'd82 : sccb_data <= {16'h5812,8'h07}; 8'd83 : sccb_data <= {16'h5813,8'h03}; 8'd84 : sccb_data <= {16'h5814,8'h00}; 8'd85 : sccb_data <= {16'h5815,8'h01}; 8'd86 : sccb_data <= {16'h5816,8'h03}; 8'd87 : sccb_data <= {16'h5817,8'h08}; 8'd88 : sccb_data <= {16'h5818,8'h0d}; 8'd89 : sccb_data <= {16'h5819,8'h08}; 8'd90 : sccb_data <= {16'h581a,8'h05}; 8'd91 : sccb_data <= {16'h581b,8'h06}; 8'd92 : sccb_data <= {16'h581c,8'h08}; 8'd93 : sccb_data <= {16'h581d,8'h0e}; 8'd94 : sccb_data <= {16'h581e,8'h29}; 8'd95 : sccb_data <= {16'h581f,8'h17}; 8'd96 : sccb_data <= {16'h5820,8'h11}; 8'd97 : sccb_data <= {16'h5821,8'h11}; 8'd98 : sccb_data <= {16'h5822,8'h15}; 8'd99 : sccb_data <= {16'h5823,8'h28}; 8'd100: sccb_data <= {16'h5824,8'h46}; 8'd101: sccb_data <= {16'h5825,8'h26}; 8'd102: sccb_data <= {16'h5826,8'h08}; 8'd103: sccb_data <= {16'h5827,8'h26}; 8'd104: sccb_data <= {16'h5828,8'h64}; 8'd105: sccb_data <= {16'h5829,8'h26}; 8'd106: sccb_data <= {16'h582a,8'h24}; 8'd107: sccb_data <= {16'h582b,8'h22}; 8'd108: sccb_data <= {16'h582c,8'h24}; 8'd109: sccb_data <= {16'h582d,8'h24}; 8'd110: sccb_data <= {16'h582e,8'h06}; 8'd111: sccb_data <= {16'h582f,8'h22}; 8'd112: sccb_data <= {16'h5830,8'h40}; 8'd113: sccb_data <= {16'h5831,8'h42}; 8'd114: sccb_data <= {16'h5832,8'h24}; 8'd115: sccb_data <= {16'h5833,8'h26}; 8'd116: sccb_data <= {16'h5834,8'h24}; 8'd117: sccb_data <= {16'h5835,8'h22}; 8'd118: sccb_data <= {16'h5836,8'h22}; 8'd119: sccb_data <= {16'h5837,8'h26}; 8'd120: sccb_data <= {16'h5838,8'h44}; 8'd121: sccb_data <= {16'h5839,8'h24}; 8'd122: sccb_data <= {16'h583a,8'h26}; 8'd123: sccb_data <= {16'h583b,8'h28}; 8'd124: sccb_data <= {16'h583c,8'h42}; 8'd125: sccb_data <= {16'h583d,8'hce}; //AWB(自动白平衡控制) 16'h5180~16'h519e ------------------------------------------------------- 8'd126: sccb_data <= {16'h5180,8'hff}; 8'd127: sccb_data <= {16'h5181,8'hf2}; 8'd128: sccb_data <= {16'h5182,8'h00}; 8'd129: sccb_data <= {16'h5183,8'h14}; 8'd130: sccb_data <= {16'h5184,8'h25}; 8'd131: sccb_data <= {16'h5185,8'h24}; 8'd132: sccb_data <= {16'h5186,8'h09}; 8'd133: sccb_data <= {16'h5187,8'h09}; 8'd134: sccb_data <= {16'h5188,8'h09}; 8'd135: sccb_data <= {16'h5189,8'h75}; 8'd136: sccb_data <= {16'h518a,8'h54}; 8'd137: sccb_data <= {16'h518b,8'he0}; 8'd138: sccb_data <= {16'h518c,8'hb2}; 8'd139: sccb_data <= {16'h518d,8'h42}; 8'd140: sccb_data <= {16'h518e,8'h3d}; 8'd141: sccb_data <= {16'h518f,8'h56}; 8'd142: sccb_data <= {16'h5190,8'h46}; 8'd143: sccb_data <= {16'h5191,8'hf8}; 8'd144: sccb_data <= {16'h5192,8'h04}; 8'd145: sccb_data <= {16'h5193,8'h70}; 8'd146: sccb_data <= {16'h5194,8'hf0}; 8'd147: sccb_data <= {16'h5195,8'hf0}; 8'd148: sccb_data <= {16'h5196,8'h03}; 8'd149: sccb_data <= {16'h5197,8'h01}; 8'd150: sccb_data <= {16'h5198,8'h04}; 8'd151: sccb_data <= {16'h5199,8'h12}; 8'd152: sccb_data <= {16'h519a,8'h04}; 8'd153: sccb_data <= {16'h519b,8'h00}; 8'd154: sccb_data <= {16'h519c,8'h06}; 8'd155: sccb_data <= {16'h519d,8'h82}; 8'd156: sccb_data <= {16'h519e,8'h38}; //Gamma(伽马)控制 16'h5480~16'h5490 ----------------------------------------------------------- 8'd157: sccb_data <= {16'h5480,8'h01}; 8'd158: sccb_data <= {16'h5481,8'h08}; 8'd159: sccb_data <= {16'h5482,8'h14}; 8'd160: sccb_data <= {16'h5483,8'h28}; 8'd161: sccb_data <= {16'h5484,8'h51}; 8'd162: sccb_data <= {16'h5485,8'h65}; 8'd163: sccb_data <= {16'h5486,8'h71}; 8'd164: sccb_data <= {16'h5487,8'h7d}; 8'd165: sccb_data <= {16'h5488,8'h87}; 8'd166: sccb_data <= {16'h5489,8'h91}; 8'd167: sccb_data <= {16'h548a,8'h9a}; 8'd168: sccb_data <= {16'h548b,8'haa}; 8'd169: sccb_data <= {16'h548c,8'hb8}; 8'd170: sccb_data <= {16'h548d,8'hcd}; 8'd171: sccb_data <= {16'h548e,8'hdd}; 8'd172: sccb_data <= {16'h548f,8'hea}; 8'd173: sccb_data <= {16'h5490,8'h1d}; //CMX(彩色矩阵控制) 16'h5381~16'h538b --------------------------------------------------------- 8'd174: sccb_data <= {16'h5381,8'h1e}; 8'd175: sccb_data <= {16'h5382,8'h5b}; 8'd176: sccb_data <= {16'h5383,8'h08}; 8'd177: sccb_data <= {16'h5384,8'h0a}; 8'd178: sccb_data <= {16'h5385,8'h7e}; 8'd179: sccb_data <= {16'h5386,8'h88}; 8'd180: sccb_data <= {16'h5387,8'h7c}; 8'd181: sccb_data <= {16'h5388,8'h6c}; 8'd182: sccb_data <= {16'h5389,8'h10}; 8'd183: sccb_data <= {16'h538a,8'h01}; 8'd184: sccb_data <= {16'h538b,8'h98}; //SDE(特殊数码效果)控制 16'h5580~16'h558b ----------------------------------------------------- 8'd185: sccb_data <= {16'h5580,8'h06}; 8'd186: sccb_data <= {16'h5583,8'h40}; 8'd187: sccb_data <= {16'h5584,8'h10}; 8'd188: sccb_data <= {16'h5589,8'h10}; 8'd189: sccb_data <= {16'h558a,8'h00}; 8'd190: sccb_data <= {16'h558b,8'hf8}; //CIP(颜色插值)控制 (16'h5300~16'h530c) ------------------------------------------------------- 8'd191: sccb_data <= {16'h5300,8'h08}; 8'd192: sccb_data <= {16'h5301,8'h30}; 8'd193: sccb_data <= {16'h5302,8'h10}; 8'd194: sccb_data <= {16'h5303,8'h00}; 8'd195: sccb_data <= {16'h5304,8'h08}; 8'd196: sccb_data <= {16'h5305,8'h30}; 8'd197: sccb_data <= {16'h5306,8'h08}; 8'd198: sccb_data <= {16'h5307,8'h16}; 8'd199: sccb_data <= {16'h5309,8'h08}; 8'd200: sccb_data <= {16'h530a,8'h30}; 8'd201: sccb_data <= {16'h530b,8'h04}; 8'd202: sccb_data <= {16'h530c,8'h06}; //测试闪光灯功能 ------------------------------------------------------------------------------ 8'd203: sccb_data <= {16'h3000,8'h00}; //系统块复位控制 8'd204: sccb_data <= {16'h3004,8'hff}; //时钟使能控制 8'd205: sccb_data <= {16'h3017,8'hff}; //I/O控制[3:0] 00:input ff:output 8'd206: sccb_data <= {16'h3018,8'hff}; //I/O控制[7:2] 00:input ff:output 8'd207: sccb_data <= {16'h3016,8'h02}; 8'd208: sccb_data <= {16'h301c,8'h02}; 8'd209: sccb_data <= {16'h3019,8'h02}; //打开闪光灯 8'd210: sccb_data <= {16'h3019,8'h00}; //关闭闪光灯 //others -------------------------------------------------------------------------------------- 8'd211: sccb_data <= {16'h3612,8'h29}; 8'd212: sccb_data <= {16'h3618,8'h00}; 8'd213: sccb_data <= {16'h3620,8'h52}; 8'd214: sccb_data <= {16'h3621,8'he0}; 8'd215: sccb_data <= {16'h3622,8'h01}; 8'd216: sccb_data <= {16'h302d,8'h60}; //系统控制 8'd217: sccb_data <= {16'h3630,8'h36}; 8'd218: sccb_data <= {16'h3631,8'h0e}; 8'd219: sccb_data <= {16'h3632,8'he2}; 8'd220: sccb_data <= {16'h3633,8'h12}; 8'd221: sccb_data <= {16'h3634,8'h40}; 8'd222: sccb_data <= {16'h3635,8'h13}; 8'd223: sccb_data <= {16'h3636,8'h03}; 8'd224: sccb_data <= {16'h3703,8'h5a}; 8'd225: sccb_data <= {16'h3704,8'ha0}; 8'd226: sccb_data <= {16'h3705,8'h1a}; 8'd227: sccb_data <= {16'h3708,8'h64}; 8'd228: sccb_data <= {16'h3709,8'h52}; 8'd229: sccb_data <= {16'h370b,8'h60}; 8'd230: sccb_data <= {16'h370c,8'h03}; 8'd231: sccb_data <= {16'h3715,8'h78}; 8'd232: sccb_data <= {16'h3717,8'h01}; 8'd233: sccb_data <= {16'h371b,8'h20}; 8'd234: sccb_data <= {16'h3731,8'h12}; 8'd235: sccb_data <= {16'h3901,8'h0a}; 8'd236: sccb_data <= {16'h3905,8'h02}; 8'd237: sccb_data <= {16'h3906,8'h10}; 8'd238: sccb_data <= {16'h3b07,8'h0a}; //帧曝光模式 8'd239: sccb_data <= {16'h4407,8'h04}; //量化标度 default:sccb_data <= {16'h300a,8'h00}; //器件ID高8位 endcase end end endmodule
该模块负责对摄像头输出的数据进行处理,让其符合输出要求。
tp为一个像素点的周期,在16位数据代表一个周期时,tp = 2*pclk;
从图中可以看到VSYNC持续了多个周期。如果要测帧数,需要测量的是VSYNC的上升沿在一秒内出现的次数,因为pclk不固定,所以需要一个固定的时钟来度量一秒。所以最好将信号同步到一个已知的固定时钟下。我猜的,反正我不做这个
摄像头输出有四路信号:
PCLK:像素时钟,其余三路信号与此时钟同步。
VSYNC:帧同步信号,高电平有效
HREF:数据有效信号
DATA[7:0]:数据位,由于一次只能传八位数据,所以个像素需要两个时钟周期
为了方便,图中简化了部分。
画出时序图为
// FPGA : 小梅哥AC620 // EDA : Quartus II 13.0sp1 (64-bit) and ModelSim SE-64 10.5 // Author : FPGA小白758 https://blog.csdn.net/q1594?spm=1010.2135.3001.5343 // File : ov5640_data.v // Create : 2022-05-13 19:05:10 // Revise : 2022-05-13 19:05:12 // Editor : sublime text3, tab size (4) // ----------------------------------------------------------------------------- module ov5640_data( input wire s_rst_n , input wire ov5640_pclk , //输出时钟 input wire ov5640_href , //数据有效信号 input wire ov5640_vsync, //帧同步 input wire [7:0] ov5640_data , //数据 output reg frame_vld , output wire [15:0] frame_rgb ); //裁剪范围是sccb_ov5640_cfg文件中的CMOS_H_PIXEL和CMOS_V_PIXEL(1024*768) parameter H_TOTAL = 1024; parameter V_TOTAL = 768 ; parameter H_SRART = 200 ;//裁剪后水平起始位置 parameter H_STOP = 1000;//裁剪后水平结束位置 parameter V_START = 200 ;//裁剪后垂直起始位置 parameter V_STOP = 680 ;//裁剪后垂直结束位置 reg ov5640_vsync_r ; //打拍 wire vsync_flag ; //帧标志 reg [4:0] vsync_cnt ; //帧计数 wire output_en ; //输出使能 reg data_flag ; //摄像头输出的数据是高位还是低位信号 reg [15:0] cmos_out_data ; //摄像头输出的数据 reg cmos_out_flag ; //摄像头输出的数据有效信号 reg [11:0] cmos_out_H_cnt ; //摄像头输出行计数 reg [11:0] cmos_out_V_cnt ; //摄像头输出场计数 //打拍 always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin // reset ov5640_vsync_r <= 'd0; end else begin ov5640_vsync_r <= ov5640_vsync; end end //上升沿标志 assign vsync_flag = ov5640_vsync & ~ov5640_vsync_r; //上升沿计数 always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin vsync_cnt <= 'd0; end else if(vsync_flag == 1'b1 && vsync_cnt <= 10) begin vsync_cnt <= vsync_cnt + 1'b1; end end //计数满后使能 assign output_en = (vsync_cnt >= 'd10)? 1'b1 : 1'b0; //输出高低位标志 always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin // reset data_flag <= 1'b0; end else if (ov5640_href == 1'b1) begin data_flag <= ~data_flag; end end //数据拼接 always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin // reset cmos_out_data <= 15'd0; end else if (data_flag == 1'b0 && ov5640_href == 1'b1) begin cmos_out_data[15:8] <= ov5640_data; end else if (data_flag == 1'b1 && ov5640_href == 1'b1) begin cmos_out_data[ 7:0] <= ov5640_data; end end //数据有效标志位 always @(posedge ov5640_pclk or negedge s_rst_n) begin if(s_rst_n == 1'b0) begin cmos_out_flag <= 1'b0; end else if (data_flag == 1'b1 & output_en == 1'b1) begin cmos_out_flag <= 1'b1; end else begin cmos_out_flag <= 1'b0; end end //数据行场计数 always @(posedge ov5640_pclk or negedge s_rst_n) begin if(s_rst_n == 1'b0)begin cmos_out_H_cnt <= 'd0; end else if (cmos_out_H_cnt == H_TOTAL)begin cmos_out_H_cnt <= 'd0; end else if (data_flag == 1'b1 & output_en == 1'b1) begin cmos_out_H_cnt <= cmos_out_H_cnt + 1'b1; end end always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin cmos_out_V_cnt <= 'd0; end else if (cmos_out_V_cnt == V_TOTAL)begin cmos_out_V_cnt <= 'd0; end else if (cmos_out_H_cnt == 'd0 && data_flag == 1'b1 & output_en == 1'b1) begin cmos_out_V_cnt <= cmos_out_V_cnt + 1'b1; end end //1024*768裁剪出800*480 always @(posedge ov5640_pclk or negedge s_rst_n) begin if (s_rst_n == 1'b0) begin frame_vld <= 1'b0; end else if (cmos_out_H_cnt - 1 >= H_SRART & cmos_out_H_cnt - 1'b1 < H_STOP & cmos_out_V_cnt - 1 >= V_START & cmos_out_V_cnt - 1'b1 < V_STOP & data_flag == 1'b1)begin frame_vld <= 1'b1; end else begin frame_vld <= 1'b0; end end assign frame_rgb = (cmos_out_H_cnt >= H_SRART & cmos_out_H_cnt < H_STOP & cmos_out_V_cnt >= V_START & cmos_out_V_cnt < V_STOP & data_flag == 'd0 & output_en == 1'b1 )? cmos_out_data:15'b0; endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。