FPGA——OV5640摄像头DVP接口实现及仿真
一、设计思路
行同步计数器
场同步计数器
图像帧数计数器
摄像头输出3*3像素图像的输入信号图解
场信号vsync脉冲,表示一帧图像的开始
行信号hsync有效(高有效),摄像头输出数据
一个像素是16位数据,传入一次是8位数据,所有一个像素传了6次数据
二、代码实现
module dvp_capture(
rst_n ,
clk ,
vsync ,
hsync ,
data ,
pix_data ,
image_state ,
haddr ,
vaddr ,
data_vld
);
parameter IMAGE_WIDTH = 800;
parameter IMAGE_HEIGHT = 480;
localparam DATA_W = 8;
localparam DATA_PIX = 16;
localparam HADDR_W = 12;
localparam VADDR_W = 12;
input rst_n;
input clk;
input vsync; //场同步信号
input hsync; //行同步信号
input [DATA_W-1:0] data;
output [DATA_PIX-1:0] pix_data; //摄像头输出的565像素数据
output image_state; //摄像头初始化完成开始输出数据标志信号
output [HADDR_W-1:0] haddr; //行地址
output [VADDR_W-1:0] vaddr; //场地址
output data_vld; //输出数据有效信号
reg [DATA_PIX-1:0] pix_data; //摄像头输出的565像素数据
reg image_state; //摄像头初始化完成开始输出数据标志信号
reg [HADDR_W-1:0] haddr; //行地址
reg [VADDR_W-1:0] vaddr; //场地址
reg data_vld; //输出数据有效信号
//中间变量
reg pre_vsync;
reg pre_hsync;
reg [DATA_W-1:0] pre_data;
//计数器变量
reg [HADDR_W-1:0] cnt_hsync;
wire add_cnt_hsync;
wire end_cnt_hsync;
reg [VADDR_W-1:0] cnt_vsync;
wire add_cnt_vsync;
wire end_cnt_vsync;
reg [4-1:0] cnt_frame;
wire add_cnt_frame;
wire end_cnt_frame;
reg output_frame;
//打一拍,快速IO,优化时序,边沿检测
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
pre_vsync <= 0;
pre_hsync <= 0;
pre_data <= 0;
end
else begin
pre_vsync <= vsync;
pre_hsync <= hsync;
pre_data <= data;
end
end
//行同步计数器,行同步有效时加1,行同步信号失效时结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_hsync <= 0;
else if(add_cnt_hsync)begin
if(end_cnt_hsync)
cnt_hsync <= 0;
else
cnt_hsync <= cnt_hsync + 1'b1;
end
end
assign add_cnt_hsync = pre_hsync;
assign end_cnt_hsync = add_cnt_hsync && {pre_hsync,hsync} == 2'b10;
//场同步信号计数器,行同步计数完成时加1,场同步达到要求的像素值结束,当输出图像不稳定在场同步拉高时清0,
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_vsync <= 0;
else if(add_cnt_vsync)begin
if(end_cnt_vsync)
cnt_vsync <= 0;
else
cnt_vsync <= cnt_vsync + 1'b1;
end
else if({pre_vsync,vsync} == 2'b01)
cnt_vsync <= 0;
end
assign add_cnt_vsync = end_cnt_hsync;
assign end_cnt_vsync = add_cnt_vsync && cnt_vsync == IMAGE_HEIGHT - 1;
//图像帧数计数器,出现场同步信号脉冲时加一,数到十帧图像结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_frame <= 0;
else if(add_cnt_frame)begin
if(end_cnt_frame)
cnt_frame <= 0;
else
cnt_frame <= cnt_frame + 1'b1;
end
end
assign add_cnt_frame = {pre_vsync,vsync} == 2'b10 && !output_frame;
assign end_cnt_frame = add_cnt_frame && cnt_frame == 10 - 1;
//舍弃每次系统开始运行后的前 10 帧图像的数据,以确保输出图像稳定
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
output_frame <= 0;
else if(end_cnt_frame)
output_frame <= 1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
pix_data <= 0;
else
pix_data <= {pix_data[7:0],pre_data};
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_vld <= 0;
else if(cnt_hsync[0] == 1 && add_cnt_hsync && output_frame)
data_vld <= 1;
else
data_vld <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
image_state <= 0;
else if(pre_vsync)
image_state <= 1;
else if(end_cnt_vsync)
image_state <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
vaddr <= 0;
else
vaddr <= cnt_vsync;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
haddr <= 0;
else
haddr = cnt_hsync[7:1];
end
endmodule
三、仿真验证
`timescale 1ns/1ns
module dvp_capture_tb();
//时钟周期,单位ns,在这里修改时钟周期
parameter CYCLE = 80;
//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;
parameter WIDTH = 3;
parameter HIGHT = 3;
localparam DATA_W = 8;
localparam DATA_PIX = 16;
localparam HADDR_W = 12;
localparam VADDR_W = 12;
reg rst_n;
reg clk;
reg vsync; //场同步信号
reg hsync; //行同步信号
reg [DATA_W-1:0] data;
wire [DATA_PIX-1:0] pix_data; //摄像头输出的565像素数据
wire image_state; //摄像头初始化完成开始输出数据标志信号
wire [HADDR_W-1:0] haddr; //行地址
wire [VADDR_W-1:0] vaddr; //场地址
wire data_vld; //输出数据有效信
dvp_capture #(
.IMAGE_WIDTH(WIDTH) ,
.IMAGE_HEIGHT(HIGHT)
)
dvp_capture(
.rst_n (rst_n),
.clk (clk),
.vsync (vsync),
.hsync (hsync),
.data (data),
.pix_data (pix_data),
.image_state (image_state),
.haddr (haddr),
.vaddr (vaddr),
.data_vld (data_vld)
);
//生成本地时钟50M
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
//产生复位信号
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE * RST_TIME);
rst_n = 1;
end
integer i = 0;
integer j = 0;
//输入信号din0赋值方式
initial begin
#1;
data = 8'hff;
vsync = 0;
hsync = 0;
#(10*CYCLE);
//开始赋值
repeat(15)begin
vsync = 1;
#(10*CYCLE)
vsync = 0;
#(20*CYCLE)
for(i = 0;i < HIGHT;i = i + 1)begin
for(j = 0;j < (WIDTH<<1);j = j + 1)begin
hsync = 1;
data = data - 1;
#80;
end
hsync = 0;
#(20*CYCLE);
end
end
#1000
$stop;
end
endmodule