1.引脚
有的摄像头输出八位像素,有的十位像素。使用时注意筛选。
2.参数
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 |
3.模块划分
3.上电模块
3.1上电过程
DOVDD和AVDD不需要代码控制 使用摄像头时只需要控制PWDN和RESET可以,其他引脚都没有打开。 PWDN在t2时刻拉低,RESETB在t3时拉高,拉高后t四个时间可以开始使用SCCB线传输数据。
3.2上电功能模块和代码
/* 摄像头上电的初始过程 上电时序 初始: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
4.SCCB模块
4.1SCCB协议时序
ID Address(W) = 7’h78(低位补0后),在ov5640众多寄存器中,有些寄存器时可改写的,有些是只读的,只有可改写的才能正确输入。 关于SCCB协议可以参考其他文章,这里不详细介绍。在本模块中只用到了SCCB写功能。对SCCB协议精简后构建了以下模块
4.2 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
5.寄存器配置模块
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