16

健康奶 · 2023年07月12日 · 北京市

FPGA实现不同分辨率视频切换输出,串口协议帧控制

FPGA实现不同分辨率视频切换输出,串口协议帧控制,提供工程源码和技术支持

1、不同分辨率视频切换输出原理

不同分辨率的视频输出对应不同的时序和时钟,一般情况下是不存在同时或分时输出的,但现实项目中如果遇到这样的情况怎么办呢?
很好办,找我就行了。。。
不同分辨率的视频肯定有大有小,但输出分辨率却只有一个,很显然,要选择大的分辨率时序作为输出时序,大视频原样输出,小视频在大分辨率下降采样输出;
以本设计为例,输入分辨率有720P和1080P,那么输出时序当然就是1080P,720P视频在1080P时序下降采样输出;

2、设计思想和架构详解

设计架构如下:
在这里插入图片描述

输入1:ov5640摄像头1280x720@60Hz;
输入2:hdmi输入1920x1080@60Hz;
输出:hdmi输出1920x1080@60Hz;
具体哪一路作为输出视频,由串口协议帧控制;
具体协议如下:
串口协议帧有效数据0x00 0x00 0xaa 0xbb 输入2作为输出;
串口协议帧有效数据0x00 0x00 0xcc 0xdd 输入1作为输出;

ov5640摄像头输入:
ov5640摄像头输出rgb565格式视频,需要iic配置和数据采集,详细配置和采集源码请将文章看到最后;

HDMI输入和输出:
HDMI输入采用silicon9011解码,HDMI输出采用silicon9134编码,关于这一块的配置和使用,请参考我之前写的文章点击查看:silicon9011和silicon9134编解码

串口协议帧解析:
主要控制不同分辨率参数写入FDMA控制器,进而控制DDR读写,这个串口协议帧解析方案高度贴近真实项目,也是我常用的套路,关于串口协议帧解析这一块,请参考我之前写的文章点击查看:串口协议帧解析方案

FDMA数据缓存方案:
经典的图像缓存DDR3的方案,关于FDMA数据缓存方案点击查看:FDMA数据缓存方案这一块,请参考我之前写的文章

3、vivado工程详解

vivado版本:2019.1;
如果是高版本打开,升级IP即可;
如果是低版本打开,打开工程后需另存为,然后即可使用;
工程BD如下:
在这里插入图片描述
工程代码架构如下:
在这里插入图片描述
顶层源码如下:

`timescale 1ns / 1ps

module top(  
//ddr3  
  output [14:0]DDR3_0_addr,
  output [2:0]DDR3_0_ba   ,
  output DDR3_0_cas_n     ,
  output [0:0]DDR3_0_ck_n ,
  output [0:0]DDR3_0_ck_p ,
  output [0:0]DDR3_0_cke  ,
  output [0:0]DDR3_0_cs_n ,
  output [3:0]DDR3_0_dm   ,
  inout [31:0]DDR3_0_dq   ,
  inout [3:0]DDR3_0_dqs_n ,
  inout [3:0]DDR3_0_dqs_p ,
  output [0:0]DDR3_0_odt  ,
  output DDR3_0_ras_n     ,
  output DDR3_0_reset_n   ,
  output DDR3_0_we_n      , 
  
  input        CLK_IN1_D_0_clk_n,
  input        CLK_IN1_D_0_clk_p,
  output       ddr3_ok          ,  

  inout        cmos_scl,          //cmos i2c clock
  inout        cmos_sda,          //cmos i2c data
  input        cmos_vsync,        //cmos vsync
  input        cmos_href,         //cmos hsync refrence,data valid
  input        cmos_pclk,         //cmos pxiel clock
  output       cmos_xclk,         //cmos externl clock
  input   [7:0]cmos_db,           //cmos data
  output       cmos_rst_n,        //cmos reset
  output       cmos_pwdn,         //cmos power down
  
  output       hdmi_in_nreset   ,     //9011/9013 reset
  input        vin_clk          ,            //clock for 9111/9013
  input        vin_hs           ,             //horizontal synchronization for 9011/9013
  input        vin_vs           ,             //vertical synchronization for 9011/9013
  input        vin_de           ,             //data valid for 9011/9013
  input[23:0]  vin_data         ,           //data for 9011/9013   
  inout        hdmi_scl         ,           //HDMI I2C clock
  inout        hdmi_sda         ,           //HDMI I2C data
   
//hdmi_out  
  output       vout_hs          ,            //horizontal synchronization for 9134
  output       vout_vs          ,            //vertical synchronization for 9134
  output       vout_de          ,            //data valid for 9134
  output       vout_clk         ,           //clock for 9134
  output[23:0] vout_data        ,            //data for 9134
  output       hdmi_nreset      ,

  input            i_uart_rx ,
  output           o_uart_tx 
  
);

wire        clk_25m   ;
wire        clk_200m  ;
wire        clk_hdmi  ;
wire        pll_resetn;
wire [0:0]  resetn;
wire        ud_r_0_ud_rclk;
wire [31:0] ud_r_0_ud_rdata;
wire        ud_r_0_ud_rde;
wire        ud_r_0_ud_rvs;

reg        ud_w_0_ud_wclk ;
reg [23:0] ud_w_0_ud_wdata;
reg        ud_w_0_ud_wde  ;
reg        ud_w_0_ud_wvs  ;
reg [10:0] dis_h;
reg [10:0] dis_v;

wire        ui_clk_100m;

wire [9:0]   lut_index;
wire [31:0]  lut_data; 
wire [9:0]   lut_index_hdmi;
wire [31:0]  lut_data_hdmi ; 

wire [23:0] ov5640_rgb;
wire         ov5640_de ;
wire         ov5640_vs ;
wire        o_data_req;


wire [23:0] i_rgb;  
wire         o_hs ;  
wire         o_vs ;  
wire         o_de ;  
wire [23:0] o_rgb;  
wire hdmi_clk_rstn;

wire       o_rx_done;
wire [31:0]o_rx_data;
reg  [31:0] _rx_data;

assign hdmi_nreset   =pll_resetn;  
assign hdmi_in_nreset=pll_resetn;

assign ud_r_0_ud_rclk=clk_hdmi;
assign ud_r_0_ud_rvs=o_vs;
assign ud_r_0_ud_rde=o_data_req;
assign i_rgb=ud_r_0_ud_rdata[23:0];
assign vout_clk=clk_hdmi;
assign vout_hs=o_hs;
assign vout_vs=o_vs;
assign vout_de=o_de;
assign vout_data=o_rgb;
assign cmos_rst_n = 1'b1;
assign cmos_pwdn  = 1'b0;

i2c_config i2c_config_ov5640(
.rst            (~pll_resetn    ),
.clk            (clk_200m       ),
.clk_div_cnt    (16'd500        ),
.i2c_addr_2byte (1'b1           ),
.lut_index      (lut_index      ),
.lut_dev_addr   (lut_data[31:24]),
.lut_reg_addr   (lut_data[23:8] ),
.lut_reg_data   (lut_data[7:0]  ),
.error          (               ),
.done           (               ),
.i2c_scl        (cmos_scl       ),
.i2c_sda        (cmos_sda       )
);

ov5640_reg_cfg #(
    .DISPAY_H(1280),
    .DISPAY_V(720 )
)
u_ov5640_reg_cfg(
    .lut_index(lut_index),   //Look-up table address
    .lut_data (lut_data )    //Device address (8bit I2C address), register address, register data
);

i2c_config i2c_config_hdmi(
    .rst            (~pll_resetn    ),
    .clk            (clk_200m       ),
    .clk_div_cnt    (16'd500        ),
    .i2c_addr_2byte (1'b0           ),
    .lut_index      (lut_index_hdmi      ),
    .lut_dev_addr   (lut_data_hdmi[31:24]),
    .lut_reg_addr   (lut_data_hdmi[23:8] ),
    .lut_reg_data   (lut_data_hdmi[7:0]  ),
    .error          (               ),
    .done           (               ),
    .i2c_scl        (hdmi_scl       ),
    .i2c_sda        (hdmi_sda       )
);

lut_hdmi u_lut_hdmi(
    .lut_index(lut_index_hdmi),   //Look-up table address
    .lut_data (lut_data_hdmi)    //Device address (8bit I2C address), register address, register data
);

uiSensorRGB565 u_uiSensorRGB565(
    .cmos_clk_i  (clk_25m),//cmos senseor clock.
    .rst_n_i     (resetn ),//system reset.active low.
    .cmos_pclk_i (cmos_pclk),//input pixel clock.
    .cmos_href_i (cmos_href),//input pixel hs signal.
    .cmos_vsync_i(cmos_vsync),//input pixel vs signal.
    .cmos_data_i (cmos_db),//data.
    .cmos_xclk_o (cmos_xclk),//output clock to cmos sensor.
    .rgb_o       (ov5640_rgb),
    .de_o        (ov5640_de ),
    .vs_o        (ov5640_vs ),
    .hs_o        ()
    );

uart_rx_analysis_top #(
    .CLK_FREQ(200_000_000),  //系统时钟频率
    .UART_BPS(115200     )   //串口波特率
)
uart_rx_analysis(
    .clk       (clk_200m),
    .rst_n     (resetn  ),
    .i_uart_rx (i_uart_rx),
    .o_uart_tx (o_uart_tx),
    .o_rx_done (o_rx_done),
    .o_rx_data (o_rx_data)
);

ila_0 u_ila_0 (
    .clk(clk_200m), // input wire clk
    .probe0(o_rx_done), // input wire [0:0]  probe0  
    .probe1(o_rx_data) // input wire [31:0]  probe1
);

always @(posedge clk_200m) begin
    if(~resetn) _rx_data<=32'h00000000;
    else if(o_rx_done) _rx_data<=o_rx_data;
end

always @(*) begin
    if(_rx_data==32'h0000aabb) begin
        ud_w_0_ud_wclk =vin_clk ;
        ud_w_0_ud_wdata=vin_data;
        ud_w_0_ud_wde  =vin_de  ;
        ud_w_0_ud_wvs  =vin_vs  ;
        dis_h          =11'd1920;
        dis_v          =11'd1080;        
    end
    else if(_rx_data==32'h0000ccdd) begin
        ud_w_0_ud_wclk =cmos_pclk ;
        ud_w_0_ud_wdata={ov5640_rgb[7:0],ov5640_rgb[15:8],ov5640_rgb[23:16]};
        ud_w_0_ud_wde  =ov5640_de  ;
        ud_w_0_ud_wvs  =ov5640_vs  ;    
        dis_h          =11'd1280;
        dis_v          =11'd720 ;            
    end
    else begin
        ud_w_0_ud_wclk =vin_clk ;
        ud_w_0_ud_wdata=vin_data;
        ud_w_0_ud_wde  =vin_de  ;
        ud_w_0_ud_wvs  =vin_vs  ;    
        dis_h          =11'd1920;
        dis_v          =11'd1080;        
    end
end

design_1_wrapper u_design_1_wrapper (
    .CLK_IN1_D_0_clk_n(CLK_IN1_D_0_clk_n),
    .CLK_IN1_D_0_clk_p(CLK_IN1_D_0_clk_p),
    .DDR3_0_addr      (DDR3_0_addr      ),
    .DDR3_0_ba        (DDR3_0_ba        ),
    .DDR3_0_cas_n     (DDR3_0_cas_n     ),
    .DDR3_0_ck_n      (DDR3_0_ck_n      ),
    .DDR3_0_ck_p      (DDR3_0_ck_p      ),
    .DDR3_0_cke       (DDR3_0_cke       ),
    .DDR3_0_cs_n      (DDR3_0_cs_n      ),
    .DDR3_0_dm        (DDR3_0_dm        ),
    .DDR3_0_dq        (DDR3_0_dq        ),
    .DDR3_0_dqs_n     (DDR3_0_dqs_n     ),
    .DDR3_0_dqs_p     (DDR3_0_dqs_p     ),
    .DDR3_0_odt       (DDR3_0_odt       ),
    .DDR3_0_ras_n     (DDR3_0_ras_n     ),
    .DDR3_0_reset_n   (DDR3_0_reset_n   ),
    .DDR3_0_we_n      (DDR3_0_we_n      ),
    .clk_200m         (clk_200m         ),
    .clk_hdmi         (clk_hdmi         ),
    .ddr3_ok          (ddr3_ok          ),
    .pll_resetn       (pll_resetn       ),
    .resetn           (resetn           ),
    .ud_r_0_ud_rclk   (ud_r_0_ud_rclk   ),
    .ud_r_0_ud_rdata  (ud_r_0_ud_rdata  ),
    .ud_r_0_ud_rde    (ud_r_0_ud_rde    ),
    .ud_r_0_ud_rempty (ud_r_0_ud_rempty ),
    .ud_r_0_ud_rvs    (ud_r_0_ud_rvs    ),
    .ud_w_0_ud_wclk   (ud_w_0_ud_wclk   ),
    .ud_w_0_ud_wdata  (ud_w_0_ud_wdata  ),
    .ud_w_0_ud_wde    (ud_w_0_ud_wde    ),
    .ud_w_0_ud_wfull  (ud_w_0_ud_wfull  ),
    .ud_w_0_ud_wvs    (ud_w_0_ud_wvs    ),
    .ui_clk_100m      (ui_clk_100m      ),
    .clk_25m          (clk_25m          ),
    .R_XSIZE_0        (dis_h            ),    
    .R_YSIZE_0        (dis_v            ),    
    .W_XSIZE_0        (dis_h            ),    
    .W_YSIZE_0        (dis_v            )    
    );    

video_timing_control vga(
    .i_clk     (clk_hdmi   ),    
    .i_rst_n   (pll_resetn ), 
    .i_start_x (0),
    .i_start_y (0),
    .i_disp_h  (dis_h),
    .i_disp_v  (dis_v),    
    .i_rgb     (i_rgb      ),
    .o_hs      (o_hs       ),
    .o_vs      (o_vs       ),
    .o_de      (o_de       ),
    .o_rgb     (o_rgb      ),
    .o_data_req(o_data_req )
);
endmodule

资源消耗和功耗如下:使用的是Xilinx Artix7-35T;
在这里插入图片描述

4、上板调试验证并演示

开发板:Xilinx Artix7-35T开发板; 开发环境:vivado2019.1;
输入1:ov5640摄像头1280x720@60Hz;
输入2:hdmi输入1920x1080@60Hz;
输出:hdmi输出1920x1080@60Hz;

输出演示视频如下:
直接点击查看输出结果

5、福利:工程代码的获取

福利:工程代码的获取
代码太大,无法邮箱发送,以百度网盘链接方式发送,
通过微信获取资料:
微信图片_20230604203730.jpg
网盘资料如下:
在这里插入图片描述

推荐阅读
关注数
12
内容数
112
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息