【【通过VDMA驱动HDMI显示的简易实验】】

发布时间:2024年01月19日

通过VDMA驱动HDMI显示的简易实验

资料介绍

在实际应用中,一般选择外置存储器作为帧缓存,而不是选择片内的 BRAM,这是由于 ZYNQ 片内的BRAM 存储容量非常小。我们知道 ZYNQ7010 的 BRAM 存储容量为 2.1Mbit,ZYNQ7020 的为 4.9Mbit,假设 RGB LCD 屏的分辨率为 800480,那么存储一帧图像需要的存储容量为(如:RGB888 数据格式=24 位)800480*24= 9216000bit≈8.79Mbit,整个 BRAM 的存储容量不足以存储单帧图像,更何况有时需要更多个显存的情况,因此使用外置存储器作为帧缓存。
在这里插入图片描述

VDMA的介绍

要想使用 VDMA 的多帧缓存功能缓冲图像数据,还需要启动 VDMA 的同步锁相模式,只有启动 VDMA的同步锁相模式,VDMA 的多帧缓存功能才能正常运行,否则 VDMA 多帧缓存功能也不能彻底解决多帧图像数据叠加到一起的情况。

同步锁相 (Genlock)
VDMA 支持四种同步锁相模式,分别是:
? Genlock Master(同步锁相主模式)
? Genlock Slave(同步锁相从模式)
? Dynamic Genlock Master(动态同步锁相主模式)
? Dynamic Genlock Slave(动态同步锁相从模式)

AXI Stream Video信号 不同于普通的AXI Stream 信号
它有 DATA VALID READY SOF EOL
SOF 信号 表示一帧的开始 EOL 表示 一帧的结束

在此之前介绍一下 VDMA
在这里插入图片描述

在这里插入图片描述

在block design 中点击 connect 之后会产生一个 额外的 Process System Reset
在这里插入图片描述

在 Vivado 中,Processor System Reset IP 是一个用于处理处理器系统复位的 IP 核。它的主要作用是提供一个可编程的复位信号,用于将处理器系统(例如,MicroBlaze 或 ARM 处理器)和相关的外设进行复位操作。
Processor System Reset IP 允许你在 FPGA 设计中实现对整个处理器系统的控制。一些主要的功能和作用包括:
1.复位控制:
允许你通过设置寄存器的值来控制何时、如何以及在何种条件下进行复位操作。
2.外设复位:
不仅仅是处理器本身,还可以通过该 IP 核对连接的外设进行复位。这有助于确保系统在重新启动时处于已知状态。
3.灵活性:
具有可编程的参数,例如复位持续时间、复位后的初始状态等,使得你可以根据设计需求调整处理器系统的复位行为。
4.中断:
支持将复位事件与中断相关联,以便在系统复位时触发相应的中断处理程序。
5.可编程触发条件:
允许你基于外部条件(例如输入引脚的状态)来触发复位。
在 FPGA 设计中,Processor System Reset IP 是确保处理器系统可控状态的关键组件,特别是在设计中需要在运行时对处理器系统进行复位或重新配置的情况下。

连接完成这个模块之后 我们会发现其实还留有输出的一个模块没有接上去 对于这样的video_out 的 IP核 我们可以通过 xillinx自己提供的指定模块来实现
我们在add IP 一栏搜索加上 video_out IP 核

下面展示 video_out 这个 IP核 是如何在 VTC的帮助下 实现

在这里插入图片描述

在 Vivado 中,VTC(Video Timing Controller)和 Video Out IP 核通常配合使用以生成正确的视频时序和输出图像。以下是它们的一般配合方式:
1.VTC(Video Timing Controller):

  • 作用:VTC 负责生成视频时序信号,包括水平同步、垂直同步、行和场的相关信号。
  • 配置:通过 VTC 的配置,你可以定义图像的分辨率、刷新率以及其他与视频时序相关的参数。
  • 输出:VTC 会生成同步信号,例如 hsync(水平同步)和 vsync(垂直同步),以及其他控制信号,用于同步整个视频系统。
    2.Video Out IP 核:
  • 作用:Video Out IP 核负责将输入的视频数据转换为输出图像,并在输出端口上生成与视频时序同步的图像。
  • 配置:你需要配置 Video Out IP 核以适应特定的视频输入和输出要求。这包括输入图像的格式、输出分辨率等参数。
  • 输入/输出:Video Out IP 核接收视频数据输入,处理并输出到显示设备。VTC 的同步信号会与 Video Out IP 核的输入和输出相结合,确保图像在屏幕上正确显示。
    配合方式:
    1.连接 VTC 和 Video Out IP 核:
    将 VTC 生成的同步信号(如 hsync 和 vsync)连接到 Video Out IP 核的对应输入端口。这样 Video Out IP 核就能根据正确的时序来处理输入图像数据。
    2.时序匹配:
    确保 VTC 生成的同步信号与 Video Out IP 核的输入图像数据相匹配。时序不匹配可能导致图像显示不正确。
    3.配置 Video Out IP 核:
    根据你的具体应用,配置 Video Out IP 核以适应所需的视频输出设置。

这种配合方式确保了图像的正确显示,VTC 提供了正确的时序信息,而 Video Out IP 核负责图像的处理和输出。在 Vivado 中,你可以使用图形界面配置这些 IP 核,并使用连接功能将它们连接在一起。

接下来 我们手把手教你实现 SOC 的HDMI传输路径

我们创建一个zynq核
我们对于zynq核进行修改

我们修改DDR
在这里插入图片描述

使能uart
在这里插入图片描述

再打开数据传输的HP口
在这里插入图片描述

完成之后点击run block automation
然后是完成之后的结构如下所示
在这里插入图片描述

注意 这样的zynq我们并没有修改其中的PL端的时钟是50Mhz

在这里插入图片描述

添加 VDMA的IP 进行相关配置
因为我们这样的设计 仅仅是传输数据仅仅是read 所以上述的 可以修改一下去掉write端
采用一帧缓存
在这里插入图片描述

(图片写错了 stream data_width 是 24 因为 RGB是 888 正好 24)

后续保持默认
再点击run connect 让其完成自动连接
现在配置
驱动 vdma 的 video out 端口和 video time control 端口
video out是一种stream流转 video 读写口的 IP
我们如此配置
在这里插入图片描述

接下来配置 video time control IP核
因为我们想要输入一个固定的数值 所以直接设置
在这里插入图片描述

因为想要匹配的是720P的分辨率这里直接省事照用了
在这里插入图片描述

接下来配置 PLL 来产生720P所需的频率参数
74.25Mhz 和 371.25Mhz
在这里插入图片描述

再添加一整个时钟复位系统
这是我们目前的 block design
在这里插入图片描述

先连接clock
在这里插入图片描述

reset连接
在这里插入图片描述

vtg_ce 端口驱动VTC
在这里插入图片描述

两根大数据流的连接
在这里插入图片描述

现在添加我们自己 上一个实验写的HDMI 转接代码
我没有封装成IP 独立显示 比较的具体
附上代码

dvi_transmitter_top.v

module dvi_transmitter_top(
    input                pclk          ,
    input                sys_rst_n     ,
    input                pclk_x5       ,
    input                video_hsync   ,
    input                video_vsync   ,
    input                video_de      ,
    input   [23 : 0]     video_din     ,
    output               tmds_clk_p    ,
    output               tmds_clk_n    ,
    output  [2 : 0]      tmds_data_p   ,
    output  [2 : 0]      tmds_data_n   ,
    output               tmds_oen  
);


assign tmds_oen = 1 ; 

// next is  define  
wire  reset ;
wire [9:0] blue_10bit ;
wire [9:0] green_10bit ;
wire [9:0] red_10bit ;


wire [2:0] tmds_data_serial ; 
wire       tmds_clk_serial  ;



reset_syn u_reset_syn(
    .pclk     ( pclk     ),
    .reset_n  ( sys_rst_n  ),
    .reset    ( reset    )
);


dvi_encoder u_dvi_encoder_blue(
    .clkin ( pclk ),
    .rstin ( reset ),
    .din   ( video_din[7:0]   ),
    .c0    ( video_hsync    ),
    .c1    ( video_vsync    ),
    .de    ( video_de    ),
    .dout  ( blue_10bit  )
);

dvi_encoder u_dvi_encoder_green(
    .clkin ( pclk ),
    .rstin ( reset ),
    .din   ( video_din[15:8]   ),
    .c0    ( 1'b0    ),
    .c1    ( 1'b0    ),
    .de    ( video_de    ),
    .dout  ( green_10bit  )
);


dvi_encoder u_dvi_encoder_red(
    .clkin ( pclk ),
    .rstin ( reset ),
    .din   ( video_din[23:16]   ),
    .c0    ( 1'b0    ),
    .c1    ( 1'b0    ),
    .de    ( video_de    ),
    .dout  ( red_10bit  )
);

serializer10 u_serializer10_blue(
    .reset          ( reset          ),
    .paralell_clk   ( pclk   ),
    .serial_clk_5x  ( pclk_x5  ),
    .paralell_data  ( blue_10bit  ),
    .serial_data_out  ( tmds_data_serial[0]  )
);

serializer10 u_serializer10_green(
    .reset          ( reset          ),
    .paralell_clk   ( pclk   ),
    .serial_clk_5x  ( pclk_x5  ),
    .paralell_data  ( green_10bit  ),
    .serial_data_out  ( tmds_data_serial[1]  )
);

serializer10 u_serializer10_red(
    .reset          ( reset          ),
    .paralell_clk   ( pclk   ),
    .serial_clk_5x  ( pclk_x5  ),
    .paralell_data  ( red_10bit  ),
    .serial_data_out  ( tmds_data_serial[2]  )
);

serializer10 u_serializer10_clk(
    .reset          ( reset          ),
    .paralell_clk   ( pclk   ),
    .serial_clk_5x  ( pclk_x5  ),
    .paralell_data  ( 10'b1111100000  ),
    .serial_data_out  ( tmds_clk_serial  )
);


//转换差分信号  
OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS0 (
    .I                  (tmds_data_serial[0]),
    .O                  (tmds_data_p[0]),
    .OB                 (tmds_data_n[0]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS1 (
    .I                  (tmds_data_serial[1]),
    .O                  (tmds_data_p[1]),
    .OB                 (tmds_data_n[1]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS2 (
    .I                  (tmds_data_serial[2]), 
    .O                  (tmds_data_p[2]), 
    .OB                 (tmds_data_n[2])  
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS3 (
    .I                  (tmds_clk_serial), 
    .O                  (tmds_clk_p),
    .OB                 (tmds_clk_n) 
);
endmodule  

encoder.v

module dvi_encoder (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din,      // data inputs: expect registered
  input            c0,       // c0 input
  input            c1,       // c1 input
  input            de,       // de input
  output reg [9:0] dout      // data outputs
);

  
  // Counting number of 1s and 0s for each incoming pixel
  // component. Pipe line the result.
  // Register Data Input so it matches the pipe lined adder
  // output
  
  reg [3:0] n1d; //number of 1s in din
  reg [7:0] din_q;

//计算像素数据中“1”的个数
  always @ (posedge clkin) begin
    n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

    din_q <=#1 din;
  end

  ///
  // Stage 1: 8 bit -> 9 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  ///
  wire decision1;

  assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));

  wire [8:0] q_m;
  assign q_m[0] = din_q[0];
  assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
  assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
  assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
  assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
  assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
  assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
  assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
  assign q_m[8] = (decision1) ? 1'b0 : 1'b1;

  /
  // Stage 2: 9 bit -> 10 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  /
  reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
  always @ (posedge clkin) begin
    n1q_m  <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
    n0q_m  <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
  end

  parameter CTRLTOKEN0 = 10'b1101010100;
  parameter CTRLTOKEN1 = 10'b0010101011;
  parameter CTRLTOKEN2 = 10'b0101010100;
  parameter CTRLTOKEN3 = 10'b1010101011;

  reg [4:0] cnt; //disparity counter, MSB is the sign bit
  wire decision2, decision3;

  assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
  /
  // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
  /
  assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));

  
  // pipe line alignment
  
  reg       de_q, de_reg;
  reg       c0_q, c1_q;
  reg       c0_reg, c1_reg;
  reg [8:0] q_m_reg;

  always @ (posedge clkin) begin
    de_q    <=#1 de;
    de_reg  <=#1 de_q;
    
    c0_q    <=#1 c0;
    c0_reg  <=#1 c0_q;
    c1_q    <=#1 c1;
    c1_reg  <=#1 c1_q;

    q_m_reg <=#1 q_m;
  end

  ///
  // 10-bit out
  // disparity counter
  ///
  always @ (posedge clkin or posedge rstin) begin
    if(rstin) begin
      dout <= 10'h0;
      cnt <= 5'h0;
    end else begin
      if (de_reg) begin
        if(decision2) begin
          dout[9]   <=#1 ~q_m_reg[8]; 
          dout[8]   <=#1 q_m_reg[8]; 
          dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];

          cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
        end else begin
          if(decision3) begin
            dout[9]   <=#1 1'b1;
            dout[8]   <=#1 q_m_reg[8];
            dout[7:0] <=#1 ~q_m_reg[7:0];

            cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
          end else begin
            dout[9]   <=#1 1'b0;
            dout[8]   <=#1 q_m_reg[8];
            dout[7:0] <=#1 q_m_reg[7:0];

            cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
          end
        end
      end else begin
        case ({c1_reg, c0_reg})
          2'b00:   dout <=#1 CTRLTOKEN0;
          2'b01:   dout <=#1 CTRLTOKEN1;
          2'b10:   dout <=#1 CTRLTOKEN2;
          default: dout <=#1 CTRLTOKEN3;
        endcase

        cnt <=#1 5'h0;
      end
    end
  end
  
endmodule 

reset_syn.v

module  reset_syn(
    input            pclk     ,
    input            reset_n  ,
    output   reg     reset
  );

  reg reset1 ;


  always@( posedge pclk   or  negedge reset_n)
  begin
    if( reset_n == 0)
    begin
      reset1 <= 1 ;
    end
    else
    begin
      reset1 <= 0      ;
      reset  <= reset1 ;
    end
  end
endmodule

serualizer.v

module serializer10 (
    input                       reset           ,  // 复位,高有效
    input                       paralell_clk    ,  // 输入并行数据时钟 
    input                       serial_clk_5x   ,   // 输入串行数据时钟
    input        [9 : 0]        paralell_data   ,   // 输入并行数据
    output                      serial_data_out          // 输出串行数据
);



//wire define
 wire cascade1 ; //用于两个 OSERDESE2 级联的信号
 wire cascade2 ;



// 此处的代码 来自 vivado的 原语 和 正点原子的同时调配 
// 这是 master接口 



OSERDESE2 #(
    .DATA_RATE_OQ("DDR"),   // 设置双倍数据速率
    .DATA_RATE_TQ("DDR"),   // DDR, BUF, SDR
    .DATA_WIDTH(10),         // 输入的并行数据宽度为 10bit
   // .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
   // .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
    .SERDES_MODE("MASTER"), // MASTER, SLAVE
    //.SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
   // .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
    .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
    .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
    .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
 )
 OSERDESE2_MASTER (
    .OFB(),             // 未使用
    .OQ(serial_data_out),               // 串行输出数据
    // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
    .SHIFTOUT1(),  // SHIFTIN1 用于位宽扩展
    .SHIFTOUT2(),  // SHIFTIN2 用于位宽扩展
    .TBYTEOUT(),   // 未使用
    .TFB(),             // 未使用
    .TQ(),               // 未使用
    .CLK(serial_clk_5x),    // 串行数据时钟,5 倍时钟频率
    .CLKDIV(paralell_clk),  // 并行数据时钟
    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1(paralell_data[0]),
    .D2(paralell_data[1]),
    .D3(paralell_data[2]),
    .D4(paralell_data[3]),
    .D5(paralell_data[4]),
    .D6(paralell_data[5]),
    .D7(paralell_data[6]),
    .D8(paralell_data[7]),
    .OCE(1'b1),             // 1-bit input: Output data clock enable
    .RST(reset),             // 1-bit input: Reset
    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
    .SHIFTIN1(cascade1),     // SHIFTIN1 用于位宽扩展
    .SHIFTIN2(cascade2),     // SHIFTIN2  用于位宽扩展
    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
    .T1(1'b0),                // 未使用
    .T2(1'b0),                // 未使用
    .T3(1'b0),                // 未使用
    .T4(1'b0),                // 未使用
    .TBYTEIN(1'b0),     // 未使用
    .TCE(1'b0)              // 未使用
 );


// slave接口 
 OSERDESE2 #(
    .DATA_RATE_OQ("DDR"),   // 设置双倍数据速率
    .DATA_RATE_TQ("DDR"),   // DDR, BUF, SDR
    .DATA_WIDTH(10),         // 输入的并行数据宽度为 10bit
   // .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
   // .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
    .SERDES_MODE("SLAVE"), // MASTER, SLAVE
    //.SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
   // .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
    .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
    .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
    .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
 )
 OSERDESE2_SLAVE (
    .OFB(),             // 未使用
    .OQ(),               // 串行输出数据
    // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
    .SHIFTOUT1(cascade1),  // SHIFTIN1 用于位宽扩展
    .SHIFTOUT2(cascade2),  // SHIFTIN2 用于位宽扩展
    .TBYTEOUT(),   // 未使用
    .TFB(),             // 未使用
    .TQ(),               // 未使用
    .CLK(serial_clk_5x),    // 串行数据时钟,5 倍时钟频率
    .CLKDIV(paralell_clk),  // 并行数据时钟
    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1(1'b0),
    .D2(1'b0),
    .D3(paralell_data[8]),
    .D4(paralell_data[9]),
    .D5(1'b0),
    .D6(1'b0),
    .D7(1'b0),
    .D8(1'b0),
    .OCE(1'b1),             // 1-bit input: Output data clock enable
    .RST(reset),             // 1-bit input: Reset
    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
    .SHIFTIN1(),     // SHIFTIN1 用于位宽扩展
    .SHIFTIN2(),     // SHIFTIN2  用于位宽扩展
    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
    .T1(1'b0),                // 未使用
    .T2(1'b0),                // 未使用
    .T3(1'b0),                // 未使用
    .T4(1'b0),                // 未使用
    .TBYTEIN(1'b0),     // 未使用
    .TCE(1'b0)              // 未使用
 );

endmodule 

第一个pclk 连接到 74.25M 的端口 另一个连接至371.25Mhz 的端口
reset_n 连接至 clock_wizard 的 locked端口

在这里插入图片描述

其他的照旧连接
在这里插入图片描述

剩下的 我们单独引出管脚
在这里插入图片描述

接下来我们验证一下 连接
在这里插入图片描述

没问题
我们创建一个 XDC的管脚约束

#HDMI
set_property -dict {PACKAGE_PIN J20 IOSTANDARD TMDS_33 } [get_ports {tmds_data_p[2]}]
set_property -dict {PACKAGE_PIN K19 IOSTANDARD TMDS_33 } [get_ports {tmds_data_p[1]}]
set_property -dict {PACKAGE_PIN G19 IOSTANDARD TMDS_33 } [get_ports {tmds_data_p[0]}]
set_property -dict {PACKAGE_PIN J18 IOSTANDARD TMDS_33 } [get_ports tmds_clk_p]

在这里插入图片描述

这是总的blockdesign
在这里插入图片描述

接下来 创建顶层 generate output
请注意把 system_wrapper设置为顶层
在这里插入图片描述

然后再生成比特流
再打开 vitis
我们创建一个 plantform 和 application
我们从BSP中获取 xilinx官方提供的VDMA驱动代码
点击
在这里插入图片描述

我们将 vdma_api.c 复制进入我们的src文件中
再创建一个 main 和 vdma_api.h文件
所以整个src的文件包应该包括
main.c vdma_api.c vdma_api.h
在这里插入图片描述

下面附上 main.c 代码

#include"stdio.h"
#include"xparameters.h"
#include "xaxivdma.h"
#include "vdma_api.h"
#include "xil_cache.h"
#define VDMA_ID             XPAR_AXI_VDMA_0_DEVICE_ID
#define WIDTH               1280  // 图像宽度
#define HEIGHT              720  // 图像的高度
#define DDR_BASE_ADDR       XPAR_PS7_DDR_0_S_AXI_BASEADDR
// VDMA帧缓存的地址
int  frame_buffer_addr  =  DDR_BASE_ADDR + 0x1000000 ;
int main()
{
Xil_DCacheDisable();
XAxiVdma vdma_inst;  // 这是例化的驱动实例
int  i , j ;
u8* vdma_buffer_addr   =  (u8*)frame_buffer_addr ;
// 往VDMA的帧缓存写入图案
for(j=0 ; j<HEIGHT ; j++  ){
for(i = 0 ; i<WIDTH ; i++){
(vdma_buffer_addr +jWIDTH3 + i3 + 0) = 0x00 ;   // 往像素蓝色通道写入00
(vdma_buffer_addr +jWIDTH3 + i3 + 1) = 0x00 ;   // 往像素绿色通道写入00
(vdma_buffer_addr +jWIDTH3 + i3 + 2) = 0xff ;   // 往像素红色通道写入ff
}
}
// 配置并启动VDMA
run_triple_frame_buffer(
&vdma_inst,        // VDMA 驱动实例
VDMA_ID,           // VDMA 器件ID
WIDTH,             // 图像宽度
HEIGHT,            // 图像高度
frame_buffer_addr, // VDMA帧缓存的起始地址
0,
0);
return 0 ;
}

下面附上 vdma_api.h代码

#include "xaxivdma.h"
#include "xparameters.h"
#include "xil_exception.h"
int run_triple_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize,
int vsize, int buf_base_addr, int number_frame_count,
int enable_frm_cnt_intr) ;

最后 build project
再点击烧录
在这里插入图片描述

实验结果

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_50965981/article/details/135705232
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。