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