OV5640摄像头驱动开发讲解

1.引脚

OV5640摄像头驱动开发讲解_第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.模块划分

OV5640摄像头驱动开发讲解_第2张图片

3.上电模块

3.1上电过程

OV5640摄像头驱动开发讲解_第3张图片
DOVDD和AVDD不需要代码控制
在使用摄像头时只需要控制PWDN和RESET即可,其余引脚均未开放。
PWDN在t2时刻拉低,RESETB在t3时刻拉高,拉高后t4时间可以开始用SCCB线传输数据。

3.2上电功能模块以及代码

OV5640摄像头驱动开发讲解_第4张图片

/*
	摄像头上电初始化过程
	上电时序
	初始: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协议时序

OV5640摄像头驱动开发讲解_第5张图片
ID Address(W) = 7’h78(低位补0后),在ov5640众多寄存器中,有些寄存器时可改写的,有些是只读的,只有可改写的才能正确输入。
关于SCCB协议可以参考其他文章,这里不详细介绍。在本模块中只用到了SCCB写功能。对SCCB协议精简后构建了以下模块

4.2 SCCB协议部分模块和代码

OV5640摄像头驱动开发讲解_第6张图片

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摄像头驱动开发讲解_第7张图片
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

6.数据输出模块

该模块负责对摄像头输出的数据进行处理,让其符合输出要求。

6.1摄像头输出数据模式

OV5640摄像头驱动开发讲解_第8张图片
OV5640摄像头驱动开发讲解_第9张图片
tp为一个像素点的周期,在16位数据代表一个周期时,tp = 2*pclk;
从图中可以看到VSYNC持续了多个周期。如果要测帧数,需要测量的是VSYNC的上升沿在一秒内出现的次数,因为pclk不固定,所以需要一个固定的时钟来度量一秒。所以最好将信号同步到一个已知的固定时钟下。我猜的,反正我不做这个

摄像头输出有四路信号:
PCLK:像素时钟,其余三路信号与此时钟同步。
VSYNC:帧同步信号,高电平有效
HREF:数据有效信号
DATA[7:0]:数据位,由于一次只能传八位数据,所以个像素需要两个时钟周期
为了方便,图中简化了部分。
OV5640摄像头驱动开发讲解_第10张图片
画出时序图为
OV5640摄像头驱动开发讲解_第11张图片

// 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

你可能感兴趣的:(fpga开发)