Zynq-PS-SDK(14) 之 OV5640-HDMI 视频通路硬件搭建

目录

1、OV5640

1.1、OV5640 Input Signals

1.2、OV5640 Output Signals

2、Video In to AXI4-Stream


 

在前面介绍了使用 VDMA + VTC + Video Out 搭建的显示通路,后端接转码 TMDS 差分信号输出到 HDMI 上;这里不再使用静态图片,使用 OV5640 摄像头作为 Video 输入,最终将视频信息输出到 HDMI 屏上;数据的输出部分,我们沿用之前的 VDMA + VTC + Dynamic Clock + rgb2dvi 那套通路;数据的输入部分需要研究一下 OV5640 的时序,以及如何将数据抓到我们内部并进行传输;

这里有几个点需要进行分析:

1、如何配置 OV5640 让其输出视频;

2、如何将 OV5640 的数据,怼到我们内部;

3、之前 VDMA 只有将数据从内存搬运出来,这里增加了数据源的输入通路,如何处理竞争;

 

1、OV5640

介绍 OV5640 的资料很多很多,这里简单的叙述一下即可,他是一颗 CMOS 摄像头,其感光阵列达到2592*1944(即500W像素),能实现最快15fps QSXVGA(2592*1944)或者90fps VGA(640*480)分辨率的图像采集。传感器采用OmniVision推出的OmniBSI(背面照度)技术,使传感器达到更高的性能,如高灵敏度、低串扰和低噪声。传感器内部集成了图像处理的功能,包括自动曝光控制(AEC)、自动白平衡(AWB)等。同时该传感器支持LED补光、MIPI(移动产业处理器接口)输出接口和 DVP(数字视频并行)输出接口选择、ISP(图像信号处理)以及AFC(自动聚焦控制)等功能;

它的内部框图如下所示:

由上图可知,时序发生器(timing generator)控制着感光阵列(image array)、放大器(AMP)、AD转换以及输出外部时序信号(VSYNC、HREF 和 PCLK),外部时钟XVCLK经过PLL锁相环后输出的时钟作为系统的控制时钟;感光阵列将光信号转化成模拟信号,经过增益放大器之后进入10位AD转换器;AD转换器将模拟信号转化成数字信号,并且经过ISP进行相关图像处理,最终输出所配置格式的10位视频数据流。增益放大器控制以及ISP等都可以通过寄存器(Registers)来配置,配置寄存器的接口就是SCCB接口,该接口协议兼容 IIC 协议;

1.1、OV5640 Input Signals

我们整理一个简单的 OV5640 的输入输出信号:

XVCLK 输入时钟,SCCB 用于对 OV5640 进行配置,配置的内容可以参考 OV5640 的datasheet 和《OV5640_自动对焦照相模组应用指南(DVP_接口)》;

SCCB 的协议具体描述,参考文档:《OmniVision Technologies Seril Camera Control Bus(SCCB) Specification》,网上很多介绍,这里我们不再多说;

SCCB 可以配置很多 OV5640 的关键特性,包括 XVCLK 输入后的 PLL,输出图像的格式(YUV/RGB),输出方式 MIPI/DVP,是否需要使用 ISP 进行处理,等等,有兴趣可以研究一下 datasheet 并进行尝试;

 

1.2、OV5640 Output Signals

通过 SCCB 配置完 OV5640 后,那么 OV5640 便开始输出图像数据了,它支持 1080p,720p 等等,以及不同分辨率,这些都囊括在 SCCB 配置中;同样分辨率下,不同帧率的输出,主要体现在 pclk 信号上,pclk 信号是输出的时钟,提频导致输出更多的数据,也就是帧率的含义了;

打个比如:

VSYNC:场同步信号,由摄像头输出,用于标志一帧数据的开始与结束。上图中VSYNC的高电平作为一帧的同步信号,在低电平时输出的数据有效。需要注意的是场同步信号是可以通过设置寄存器位进行取反的,即低电平同步高电平有效,这里和上图一致的默认设置;

HREF/HSYNC:行同步信号,由摄像头输出,用于标志一行数据的开始与结束。上图中的HREF和HSYNC是由同一引脚输出的,只是数据的同步方式不一样。本次实验使用的是HREF格式输出,当HREF为高电平时,图像输出有效,可以通过寄存器进行配置。这里使用的是HREF格式输出;

D[9:0]:数据信号,由摄像头输出,在RGB格式输出中,只有高8位D[9:2]是有效的;

tPCLK:一个像素时钟周期;

tp:单个数据周期,这里需要注意的是上图中左下角红框标注的部分,在RGB模式中,tp代表两个tPCLK(像素时钟)。以RGB565数据格式为例,RGB565采用16bit数据表示一个像素点,而OV5640在一个像素周期(tPCLK)内只能传输8bit数据,因此需要两个时钟周期才能输出一个RGB565数据;

这里使用 DVP + RGB565 方式进行输出,每两个 PCLK 传输一个像素,它的定义为:

 

2、Video In to AXI4-Stream

上面描述了如何配置和使用 OV5460 进行输出图像数据,它输出的数据是 8bit + 8bits 的图像数据,这里我们需要将这个数据怼到我们的系统中(内存中),为何要放到内存中呢,因为 VDMA 的输出是从 DDR framebuffer 去拿数据,所以呢,我们要想办法将视频数据仍到 DDR 中,让 VDMA 去拿;

VDMA 管理的到内存的写入是通过 S2MM 接口进行的,输入端是 AXI-Stream 的数据流,输出端是 AXI HP 到 memory controller,所以呢,这里,我们需要将图像数据转成为 AXI-Stream 数据,并接到 VDMA 的 S2MM 接口;

这里我们写一个 IP,来对 OV5640 的数据进行抓取,同时将数据怼到 Video In to AXI4-Stream IP ,利用 Video In to AXI4-Stream 进行输出 AXI-Stream 到 VDMA,在到 DDR;既然要用到 Video In to AXI4-Stream,那么我们大概分析一下他的输入,这样我们才能够去设计一个采集 OV5640 的 IP,将数据灌入到 DDR;

这里我们为了将数据输入到  Video In to AXI4-Stream,我们需要了解 Video In to AXI4-Stream 输入的数据和时序:

Video In to AXI4-Stream

为了简化设计,我们这里设计的 IP 包含以下几个信号,并且直接输入到 Video In to AXI4-Stream:

1、clk:时钟信号;

2、clk_en:时钟有效信号;

3、vsync:一帧的起始;

4、active:图像有效信号;

5、data[23:0]:RGB888 的数据信号;

这里需注意的一点:我们采集到数据是 2 个 pclk 产生一个像素,像素是 16bits 的 RGB565,我们需要将 RGB565 -> RGB888,并输入到 Video In to AXI4-Stream,我们的设计文件为:

`timescale 1ns / 1ps
//
// Company          : Stephen Zhou Tech
// Engineer         : StephenZhou
// 
// Create Date      : 2021/05/20 14:57:44
// Design Name      : 
// Module Name      : Steph_OVSensorDVPCaptureV2
// Project Name     : 
// Target Devices   : Zynq-7000
// Tool Versions    : 
// Description      : Capture the OV5640 Image data
// 
// Dependencies     : 
// 
// Revision:
// Revision 0.02 - File Created
// Additional Comments:
// 
//


module Steph_OVSensorDVPCaptureV2(
    // enable
    input           ov_en_i         ,
    // OV5640 Signals
    input           ov_pclk_i       ,
    input           ov_vsync_i      ,
    input           ov_href_i       ,
    input    [9:0]  ov_data_i       ,
    // Output to Video In AXI Stream
    output          vid_clk_o       ,
    output          vid_clk_en_o    ,
    output          vid_vsync_o     ,
    output          vid_active_o    ,
    output   [23:0] vid_data_o
);

    // Wair for video valid counter
    localparam VIDEO_STABLE_FRAME_CNT = 4'd15;

    reg             vsync_d0        ;
    reg             vsync_d1        ;
    wire            vsync_pos       ;
    reg      [3:0]  vsync_cnt       ;
    reg             wait_done       ;
    reg             ov_href_d0      ;
    reg             ov_href_d1      ;

    reg      [7:0]  data_in_d0      ;
    reg      [15:0] data_16bits     ;
    reg             data_merge      ;
    reg             data_merge_d0   ;

    assign vsync_pos    = (~vsync_d1) & vsync_d0;
    assign vid_clk_o    = ov_pclk_i;
    assign vid_vsync_o  = wait_done ? (vsync_d1)   : 1'b0;
    assign vid_active_o = wait_done ? (ov_href_d1) : 1'b0;
    assign vid_clk_en_o = wait_done ? ((data_merge_d0 & vid_active_o) || (!vid_active_o)) : 1'b0;
    assign vid_data_o   = wait_done ? { data_16bits[15:11],3'd0 , data_16bits[10:5],2'd0 , data_16bits[4:0],3'd0 } : 24'd0;

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            vsync_d0 <= 1'b0;
            vsync_d1 <= 1'b0;
        end
        else begin
            vsync_d0 <= ov_vsync_i;
            vsync_d1 <= vsync_d0;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            vsync_cnt <= 4'd0;
        end
        else if (vsync_pos && (vsync_cnt < VIDEO_STABLE_FRAME_CNT)) begin
            vsync_cnt <= vsync_cnt + 1'b1;
        end
        else begin
            vsync_cnt <= vsync_cnt;
        end
    end

    // Generate wait done signal
    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            wait_done <= 4'd0;
        end
        else if (vsync_pos && (vsync_cnt == VIDEO_STABLE_FRAME_CNT)) begin
            wait_done <= 1'b1;
        end
        else begin
            wait_done <= wait_done;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            ov_href_d0 <= 4'd0;
            ov_href_d1 <= 4'd0;
        end
        else begin
            ov_href_d0 <= ov_href_i;
            ov_href_d1 <= ov_href_d0;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            data_in_d0 <= 8'd0;
        end
        else if (ov_href_i) begin
            data_in_d0 <= ov_data_i[9:2];
        end
        else begin
            data_in_d0 <= 8'd0;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            data_merge <= 1'b0;
        end
        else if (ov_href_i) begin
            data_merge <= ~data_merge;
        end
        else begin
            data_merge <= 1'b0;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            data_16bits <= 1'b0;
        end
        else if (ov_href_i && data_merge) begin
            data_16bits <= {data_in_d0, ov_data_i[9:2]};
        end
        else begin
            data_16bits <= data_16bits;
        end
    end

    always @ (posedge ov_pclk_i) begin
        if(~ov_en_i) begin
            data_merge_d0 <= 1'b0;
        end
        else begin
            data_merge_d0 <= data_merge;
        end
    end

endmodule

这部分设计完毕后,设计流程就成为如下:

在之前的 《Zynq-PS-SDK(12) 之 VDMA+VTC+AXI4S-VideoOut 视频通路硬件搭建》我们的 VDMA 只处理一帧数据,现在情况比较复杂了,又有输入,又有输出,如果不处理这种冲突的话,势必导致对同一帧数据,同时进行读写的情况,导致图像撕裂;我们这里整成 3 块 framebuffer,同时开启 VDMA 的 Dynamic GenLock,避免出现同时读写一帧的缓存;

配置完毕后,Generate Output Product,然后进行生成 Bitstream;

BD 的设计图中,抓取摄像头数据导入 Video In AXI-Stream 部分的连线如下所示:

整个 BD 的设计如下所示:

 

  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Zynq-7000是由Xilinx公司生产的一款集成了ARM处理器和可编程逻辑(PL)的片上系统(SoC)。它的可编程逻辑部分可以通过重新配置(Reconfigure)来进行定制和优化。 Zynq-7000的可编程逻辑部分由一系列可编程逻辑单元(PLU)组成,可以通过Vivado开发环境进行配置和定制。重配置PL意味着可以改变PL中的逻辑电路,以满足不同的需求。 重配置PL的过程大致分为三个步骤。首先,需要使用Vivado开发环境创建和配置逻辑电路。这可以通过使用硬件描述语言(如Verilog或VHDL)来实现,也可以通过使用硬件抽象层次语言(如C、C++或OpenCL)来实现。 其次,需要将配置好的逻辑电路编译成比特流(Bitstream)。比特流是一种描述逻辑电路的中间文件,可以被FPGA芯片理解和加载。 最后,将生成的比特流加载到Zynq-7000的可编程逻辑部分。这可以通过JTAG接口或SD卡等方式实现。一旦比特流被加载,Zynq-7000的可编程逻辑部分就被重新配置为新的逻辑电路。 通过重配置PL,可以实现许多应用场景,例如加速计算、优化算法、嵌入式系统的定制等。它提供了一种便捷的方式来优化和个性化Zynq-7000的使用。 总的来说,Zynq-7000的可编程逻辑部分可以通过Vivado开发环境进行重新配置,以满足不同的需求。这种重配置的过程包括创建和配置逻辑电路、生成比特流、加载比特流到Zynq-7000,并可以应用于各种应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值