基于FPGA的DVP协议实现标准图像数据流转换

描述

一、什么是DVP?

DVP(Digital Video Port) 是传统的sensor输出接口,采用并行输出方式,d数据位宽有8bit、10bit、12bit、16bit,是CMOS电平信号(重点是非差分信号),PCLK最大速率为96MHz,接口如下图:

接口

PCLK:pixel clock ,像素时钟,每个时钟对应一个像素数据;

HSYNC:horizonal synchronization,行同步信号

VSYNC:vertical synchronization,帧同步信号;

DATA:像素数据,视频数据,具体位宽要看ISP是否支持;

XCLK:或者MCLK,ISP芯片输出给驱动sensor的时钟;

SCL,SDA:IIC用来读写sensor的寄存器,配置sensor。

DVP协议是摄像头中常用的协议,除了DVP协议摄像头中还有其他常用的协议如:MIPI、LVDS等协议。其整体协议时序图如图:

接口

接口

图中PIXCLK信号即是PCLk信号,FV是帧同步信号,LV是行同步信号,输出的P0~Pn就是像素数据。

二、OV7670摄像头的DVP协议时序

(1)水平时序

接口

(2)和VGA对应的帧时序

DVP协议和VGA接口协议基本一样,只是VSYNC信号高低电平相反了。还有DVP协议的HREF信号是在HERF为高电平是直接输出像素数据,而VGA接口的HSYNC信号在HSYNC为高时先后输出显示后沿、有效图像数据、显示前沿。

接口

(3)这里对OV7670摄像头配置输出的是RGB565图像数据,其时序为:

接口

(4)配置成RGB555和RGB444输出时序如图:

接口

RGB555

接口

RGB444

三、RTL设计

从上边所看的时序图可以将DVP协议转换成标准的图像的数据流,代码如下:

// Company  :

// Engineer :

// -----------------------------------------------------------------------------

// https://blog.csdn.net/qq_33231534    PHF's CSDN blog

// -----------------------------------------------------------------------------

// Create Date    : 2020-09-24 2257

// Revise Data    : 2020-09-24 2257

// File Name      : ov7670_data_16rgb565.v

// Target Devices : XC7Z015-CLG485-2

// Tool Versions  : Vivado 2019.2

// Revision       : V1.1

// Editor         : sublime text3, tab size (4)

// Description    : DVP协议(digital video port)获取ov7670数据并转换成16位RGB565图像数据

module ov7670_data_16rgb565(

inputclk,//输入为摄像头输入时钟pclk 25MHz

inputrst_n,//系统复位

inputvsync,//场同步信号

inputhref,//行同步信号

input[7:0]din,//ov7670摄像头数据输入

inputinit_done,//ov7670摄像头初始化结束标志

outputreg[15:0]data_rgb565,//转换成16位RGB565图像数据

outputregdata_rgb565_vld //16位RGB565图像数据有效标志

);

regvsync_r;

reghref_r;

reg[7:0]din_r;

regvsync_r_ff0;

regvsync_r_ff1;

regdata_start;

reg[3:0]frame_cnt;

regframe_vaild;

wirevsync_r_pos;

regdata_en;

//外部信号打一拍

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

vsync_r <= 0;

href_r <= 0;

din_r <= 8'd0;

end

else begin

vsync_r <= vsync;

href_r <= href;

din_r <= din;

end

end

//场同步信号上升沿检测

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

vsync_r_ff0 <= 0;

vsync_r_ff1 <= 0;

end

else begin

vsync_r_ff0 <= vsync_r;

vsync_r_ff1 <= vsync_r_ff0;

end

end

assign vsync_r_pos = (vsync_r_ff0 && ~vsync_r_ff1);

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

data_start <= 0;

end

else if (init_done) begin

data_start <= 1;

end

else begin

data_start <= data_start;

end

end

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

frame_cnt <= 0;

end

else if (data_start && frame_vaild==0 && vsync_r_pos) begin

frame_cnt <= frame_cnt + 1'b1;

end

else begin

frame_cnt <= frame_cnt;

end

end

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

frame_vaild <= 0;

end

else if (frame_cnt >= 10) begin

frame_vaild <= 1;

end

else begin

frame_vaild <= frame_vaild;

end

end

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

data_en <= 0;

end

else if (href_r && frame_vaild) begin

data_en <= ~data_en;

end

else begin

data_en <= 0;

end

end

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

data_rgb565_vld <= 0;

end

else if (data_en) begin

data_rgb565_vld <= 1;

end

else begin

data_rgb565_vld <= 0;

end

end

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

data_rgb565 <= 16'd0;

end

else if (data_en) begin

data_rgb565 <= {data_rgb565[15:8],din_r};

end

else begin

data_rgb565 <= {din_r,data_rgb565[7:0]};

end

end

endmodule

测试代码:

`timescale 1ns/1ns

module ov7670_data_16rgb565_tb (); /* this is automatically generated */

reg rst_n;

reg clk;

localparam clk_period = 20;

reg        vsync;

reg        href;

reg  [7:0] din;

reg        init_done;

wire [15:0] data_rgb565;

wire        data_rgb565_vld;

ov7670_data_16rgb565 inst_ov7670_data_16rgb565

(

.clk             (clk),

.rst_n           (rst_n),

.vsync           (vsync),

.href            (href),

.din             (din),

.init_done       (init_done),

.data_rgb565     (data_rgb565),

.data_rgb565_vld (data_rgb565_vld)

);

initial clk = 1;

always #(clk_period/2) clk = ~clk;

initial begin

#2;

rst_n = 0;

vsync = 0;

href = 0;

din = 0;

init_done = 0;

#(clk_period*20);

rst_n = 1;

#(clk_period*20);

init_done = 1;

#clk_period;

init_done = 0;

#(clk_period*20);

repeat(12)begin

#(clk_period*500);

dvp_data();

end

#(clk_period*20);

$stop;

end

task dvp_data;

integer i,j;

begin

vsync = 0;

#(clk_period*10);

vsync = 1;

#(clk_period*10);

vsync = 0;

#(clk_period*100);

for(i=0;i<480;i=i+1)begin

for(j=0;j<640*2;j=j+1)begin

href = 1;

#(clk_period);

din = din + 1'b1;

end

href = 0;

#(clk_period*100);

end

din = 0;

end

endtask

endmodule

仿真图如图所示:

(1)传输12帧图像数据

接口

可以看到,data_rgb565_vld数据有效信号在10帧图像数据后才有输出,这是为了在初始化后图像数据可能会有不稳定情况,因此将前十帧图像丢弃。

(2)1帧图像数据(注意看帧同步信号)

接口

(3)传输数据部分细节

接口

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分