问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Verilog实现DVP接口:从原理到代码详解

创作时间:
作者:
@小白创作中心

Verilog实现DVP接口:从原理到代码详解

引用
CSDN
1.
https://blog.csdn.net/2303_76814451/article/details/146095450

一、DVP简介

DVP接口(Digital Video Port)是一种用于数字视频传输的并行接口,常见于嵌入式系统和图像传感器中。DVP直接传输数字视频信号,减少模数转换需求,适合中低速视频传输。数据线:通常为8、10、12或16根数据线,用于传输像素数据。控制信号包括像素时钟(PCLK)、行同步(HSYNC 或 HREF)信号、场同步(VSYNC)等,用于同步数据传输。

DVP和VGA时序有相似之处,都使用行同步(HSYNC)和场同步(VSYNC)信号来同步图像数据。区别在于DVP是数字接口,直接传输数字像素数据,包含像素时钟(PCLK)和数据线;VGA是模拟接口,传输模拟RGB信号,需数模转换(VGA的介绍可参考Verilog:VGA控制器) 。

本例介绍的DVP行同步信号采用 HREF,用于 ov5640 DVP数据接收采集。

(1)行时序

1.高电平有效:HREF为高电平时,表示正在传输有效像素数据。

2.像素传输:在HREF高电平期间,每个PCLK周期传输一个像素数据。

3.行结束:HREF从高电平变为低电平,标志一行数据传输完成。

4.像素数据周期:ov5640 DVP每个有效数据为8位,数据采集时需要根据输出格式进行调整,输出RGB565格式需要两个像素时钟才能完成传输,先传输高8位,后第8位。

(2)场时序

1.同步脉冲:VSYNC产生高电平脉冲(取决于极性,ov5640为高)表示新一帧开始。

2.有效数据期:新一帧开始一段时间后,每行数据通过HSYNC或HREF信号同步。

3.帧结束:一帧数据传输完成后,VSYNC再次产生脉冲,标志下一帧的开始。

二、Verilog 实现

(1)设计要求

  1. 对 ov5640 输出RGB565格式的图像数据进行接收
  2. 接收数据输出给存储器(如rom、fifo、ddr)进行存储,包含必要输出端口

(2)设计要点

  1. 场同步:每当 vsync 产生一次上升沿即代表新一帧的开始(上升沿通过打拍判断)
  2. 行同步:href 的每段高电平代表一行有效数据
  3. 数据格式:一个pclk像素时钟传8位,而RGB565一个像素16位,需要两个pclk像素时钟才传输完一个像素数据,因此需要进行先缓存高8位数据,并在接收第8位时拼接数据并输出
  4. 数据有效信号:高电平时代表当前RGB565数据有效,可供后续存储器作写使能使用

(3)模块代码

`timescale 1ns / 1ps
module DVP_ctrl#(
    parameter PIC_CNT_MAX = 4'd10   //舍弃前10帧不稳定图像数据
)(
    input  wire rst_n,
    input  wire ov5640_pclk,        //摄像头像素时钟
    input  wire ov5640_href,        //摄像头行同步信号
    input  wire ov5640_vsync,       //摄像头场同步信号
    input  wire [7:0] ov5640_data,  //摄像头场数据输入
    output reg  [15:0] RGB565_data, //图像数据输出(RGB565格式)
    output wire data_valid          //数据有效信号(给存储器的写使能信号)
);
reg  pix_flag;               //一像素数据结束标志位
wire pic_flag;               //一帧图像结束标志位
reg  pic_valid;              //帧有效标志位
reg  [3:0] pic_cnt;          //帧计数器
reg  [7:0] r_ov5640_data;    //输入数据缓存
reg  ov5640_vsync_delay;     //场同步信号打拍
reg  pix_flag_delay;         //一像素数据结束标志位打拍

//***************************** 场同步 ****************************//
//场同步信号打拍(用于检测vsync上升沿)
always@(posedge ov5640_pclk or negedge rst_n)
    if(rst_n == 1'b0)
        ov5640_vsync_delay <= 1'b0;
    else
        ov5640_vsync_delay <= ov5640_vsync;

//一帧图像结束标志位(vsync上升沿产生一次)
assign pic_flag = ((ov5640_vsync_delay == 1'b0) &&
                   (ov5640_vsync == 1'b1)) ? 1'b1 : 1'b0;

//前几帧计数,计满产生帧有效信号
always @(posedge ov5640_pclk or negedge rst_n) begin
    if (!rst_n) begin
        pic_cnt   <= 4'd0;
        pic_valid <= 1'b0;
    end else if (pic_flag) begin
        if (pic_cnt == PIC_CNT_MAX) begin
            pic_cnt   <= 4'd0;
            pic_valid <= 1'b1;
        end else
            pic_cnt <= pic_cnt + 4'd1;
    end
end

//***************************** 行同步 ****************************//
//行同步
always @(posedge ov5640_pclk or negedge rst_n) begin
    if (!rst_n) begin
        pix_flag      <= 1'b0;
        r_ov5640_data <= 8'b0;
        RGB565_data   <= 8'b0;
    end else if (ov5640_href) begin
        if (!pix_flag) begin
            r_ov5640_data <= ov5640_data; //先缓存高8位
            pix_flag <= 1'b1;
        end else begin
            RGB565_data <= {r_ov5640_data , ov5640_data};//后拼接低8位输出
            pix_flag <= 1'b0;
        end
    end
end

//一像素数据结束标志位打拍(用于产生像素数据有效信号)
always@(posedge ov5640_pclk or negedge rst_n)
    if(rst_n == 1'b0)
        pix_flag_delay <= 1'b0;
    else
        pix_flag_delay <= pix_flag;

//像素数据有效信号
assign data_valid = pic_valid & pix_flag_delay;
endmodule

(4)仿真代码

仿真就是给DVP模块模拟ov5640产生的图像数据,我用的deepseek写了一版,但经过测试发现不能直接使用,于是根据它的框架自己进行了一些修改,可以通过参数设置模拟图像数据的参数(仿真多少帧、一帧多少行、一行多少像素)。以下模拟输出了10帧、一帧8行数据、一行16个像素点,同时DVP舍去前3帧图像数据。

`timescale 1ns / 1ps
module DVP_data_gen_tb();
reg pclk;                 //像素时钟 (10ns周期)
reg rst_n;               //复位信号 (低电平有效)
reg vsync;               //场同步信号
reg href;                //行同步信号
reg [7:0] data;          //像素数据 (8bit)
wire data_valid;         //数据有效信号
wire [15:0] RGB565_data; //输出RGB565格式数据 (16bit)

//模拟OV5640视频数据生成
parameter WIDTH   = 16, //宽(一行多少个像素)
          HIGTH   = 8,  //高(一帧多少行数据)
          FRAME   = 10; //帧(模拟发送多少帧数据)
integer pixel_cnt = 0,  //像素计数器
        row_cnt   = 0,  //行计数器
        frame_cnt = 0;  //帧计数器

//时钟(10ns周期)
always #5 pclk = ~pclk; 
initial begin //初始化复位
    pclk  = 0; 
    rst_n = 0; #20;    
    rst_n = 1;   
end

always @(posedge pclk or negedge rst_n) begin
    if (!rst_n) begin
        vsync     <= 0;
        href      <= 0;
        data      <= 0;
        pixel_cnt <= 0;
        row_cnt   <= 0;
        frame_cnt <= 0;
    end else begin
        //******************************************模拟场同步信号 (VSYNC)
        if (pixel_cnt == 0 && row_cnt == 0) begin
            vsync <= 1; // 一帧开始,VSYNC拉高一个时钟周期
        end else begin
            vsync <= 0; // VSYNC拉低
        end

        //******************************************模拟行同步信号 (HREF)
        if (row_cnt < HIGTH*10 + 10) begin//一帧模拟HIGTH行,多余行模拟行与行之间的输出间隔
            if (row_cnt <10 || (row_cnt % 10)!=0)
                row_cnt <= row_cnt + 1;
            else begin
                if (pixel_cnt < WIDTH) begin
                    href      <= 1; //HREF高电平表示行数据传输
                    data      <= data + 1; //像素数据每次自增1
                    pixel_cnt <= pixel_cnt + 1;
                end else begin
                    //一行结束
                    href      <= 0; //HREF低电平表示行结束
                    pixel_cnt <= 0;
                    row_cnt   <= row_cnt + 1; //行计数器加1
                end
            end
        end else begin//一帧结束
            href      <= 0;
            data      <= 0;
            row_cnt   <= 0; //重置行计数器
            frame_cnt <= frame_cnt + 1; //帧计数器加1
        end

        //******************************************模拟FRAME帧后结束测试
        if (frame_cnt == FRAME) begin
            $finish; // 结束仿真
        end
    end
end

DVP_ctrl #(
    .PIC_CNT_MAX (4'd3) //舍去前三帧图像
) DVP_ctrl (
    .ov5640_pclk    (pclk),
    .rst_n          (rst_n),
    .ov5640_vsync   (vsync),
    .ov5640_href    (href),
    .ov5640_data    (data),
    .data_valid     (data_valid),
    .RGB565_data    (RGB565_data)
);
endmodule  

三、仿真波形

完整波形:可以看到一共发送了10帧数据,同时data_valid在前三帧保持为0,后面才开始变化,说明前三帧数据被成功舍去。

一帧波形:1帧包含8行,1行有16个8位数据。

一行波形:一个RGB565像素数据对应两个8位数据,可以看到每接收2个数据就相应拼接输出1个RGB565数据,同时data_valid数据有效信号与数据同步产生。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号