Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する10(CDCでUnsafeにならない記述)”の続き。

前回は、CDC でUnsafe を 0 にすることを目標にaxis2video_out.v の Verilog HDL コードを修正した。今回は、アプリケーション・ソフトを作成して、回路を動作させてみよう。

最初に、ビットストリームの生成を行ったので、ホストパソコンで、fpga_dp.bin を作成した。
cd ~/Docker/vivado182ub16/masaaki/ultra96_design/ultra96_design_dp/ultra96_design.runs/impl_1/
bootgen -image fpga.bif -arch zynqmp -w -o fpga_dp.bin

fpga_dp.bin を生成した。

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する8(ブロックデザインの変更)”にも書いたが、v4l2.dts を書き換えた。

/dts-v1/;/plugin/;
/ {
    fragment@0 {
        target-path = "/amba_pl@0";
        #address-celss = <2>;
        #size-cells = <2>;
        __overlay__ {
            v4l2 {
                compatible = "fixstars,zynq-v4l2-1.0";
                #interrupt-cells = <0x3>;
                device-name="v4l2";
                interrupt-parent = <&gic>;
                interrupts = <0x0 0x59 0x4>;
            };
            
            axi_vdma_uio {
                compatible = "generic-uio";
                reg = <0x0 0xA0010000 0x0 0x1000>;
            };
            
            display_dmar_axis_vga_uio {
                compatible = "generic-uio";
                reg = <0x0 0xA0030000 0x0 0x10000>;
            };
                
            disp_gpio_uio {
                compatible = "generic-uio";
                reg = <0x0 0xA0011000 0x0 0x1000>;
            };
            
            gpio_uio {
                compatible = "generic-uio";
                reg = <0x0 0xA0012000 0x0 0x1000>;
            };
            
        };
    };
};


fpga-load.dts も fpga_dp.bin を使うように書き換えた。

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/fpga-full";
        __overlay__ {
            firmware-name = "fpga_dp.bin";
        };
    };
};


余計な部分はあるが、”カメラ画像をDisplayPortに出力する9(アプリを作成、完成)”を参照して、 pcam5c_disp_dp.cpp を作成した。ソースコードを示す。

// pcam5c_disp_dp.cpp
// 2019/07/24 by marsee
//
// This software converts the left and right of the camera image to BMP file.
// -b : bmp file name
// -n : Start File Number
// -h : help

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#define PIXEL_NUM_OF_BYTES    4
#define NUMBER_OF_WRITE_FRAMES  3

#define SVGA_HORIZONTAL_PIXELS  800
#define SVGA_VERTICAL_LINES     600
#define SVGA_ALL_DISP_ADDRESS   (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define SVGA_3_PICTURES         (SVGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

#define XGA_HORIZONTAL_PIXELS  1024
#define XGA_VERTICAL_LINES     768
#define XGA_ALL_DISP_ADDRESS   (XGA_HORIZONTAL_PIXELS * XGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define XGA_3_PICTURES         (XGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

#define HD_HORIZONTAL_PIXELS  1920
#define HD_VERTICAL_LINES     1080
#define HD_ALL_DISP_ADDRESS   (HD_HORIZONTAL_PIXELS * HD_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define HD_3_PICTURES         (HD_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

int main(int argc, char *argv[]){
    int opt;
    int c, help_flag=0;
    char bmp_fn[256] = "bmp_file";
    char  attr[1024];
    unsigned long  phys_addr;
    int file_no = -1;
    int fd1, fd2, fd3, fd4;
    volatile unsigned int *axi_vdma_uio, *display_dmar_axis_vga_uio, *disp_gpio_uio, *gpio_uio;
    int active_frame;
    int resolution;
    int all_disp_addr;
    uint32_t row, col;
    int disp_active_frame=0;
    
    resolution = 1; // XGA
    while ((opt=getopt(argc, argv, "b:n:h:r:")) != -1){
    switch (opt){
        case 'b':
            strcpy(bmp_fn, optarg);
            break;
        case 'n':
            file_no = atoi(optarg);
            printf("file_no = %d\n", file_no+1);
            break;
        case 'r':
            resolution = atoi(optarg);
            break;
        case 'h':
            help_flag = 1;
            break;
        }
    }
    if(resolution == 0){
        printf("SVGA\n");
    } else if(resolution == 1){
        printf("XGA\n");
    } else {
        printf("HD\n");
    }
       
    // all_disp_addr
    switch(resolution){
        case 0 :
            all_disp_addr = SVGA_ALL_DISP_ADDRESS;
            row = SVGA_VERTICAL_LINES; col = SVGA_HORIZONTAL_PIXELS;
            break;
        case 1 :
            all_disp_addr = XGA_ALL_DISP_ADDRESS;
            row = XGA_VERTICAL_LINES; col = XGA_HORIZONTAL_PIXELS;
            break;
        default : // 2
            all_disp_addr = HD_ALL_DISP_ADDRESS;
            row = HD_VERTICAL_LINES; col = HD_HORIZONTAL_PIXELS;
            break;
    }
    

    // axi_vdma_uio IP
    fd1 = open("/dev/uio1", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (axi_vdma_uio) open error\n");
        exit(-1);
    }
    axi_vdma_uio = (volatile unsigned *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!axi_vdma_uio){
        fprintf(stderr, "axi_vdma_uio mmap error\n");
        exit(-1);
    }

    // display_dmar_axis_vga_uio IP
    fd2 = open("/dev/uio2", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (display_dmar_axis_vga_uio) open error\n");
        exit(-1);
    }
    display_dmar_axis_vga_uio = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!display_dmar_axis_vga_uio){
        fprintf(stderr, "display_dmar_axis_vga_uio mmap error\n");
        exit(-1);
    }

    // disp_gpio_uio IP
    fd3 = open("/dev/uio3", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (disp_gpio_uio) open error\n");
        exit(-1);
    }
    disp_gpio_uio = (volatile unsigned *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!disp_gpio_uio){
        fprintf(stderr, "disp_gpio_uio mmap error\n");
        exit(-1);
    }

    // gpio_uio IP
    fd4 = open("/dev/uio4", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (gpio_uio) open error\n");
        exit(-1);
    }
    gpio_uio = (volatile unsigned *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!gpio_uio){
        fprintf(stderr, "gpio_uio mmap error\n");
        exit(-1);
    }
    
    uint32_t framebuf0, framebuf1, framebuf2;
    framebuf0 = axi_vdma_uio[43]; // 0xac
    framebuf1 = axi_vdma_uio[44]; // 0xb0
    framebuf2 = axi_vdma_uio[45]; // 0xb4
    printf("framebuf0 = %x; framebuf1 = %x; framebuf2 = %x\n", framebuf0, framebuf1, framebuf2);
    
    display_dmar_axis_vga_uio[4] = framebuf0; // 0x10, fb0_V
    display_dmar_axis_vga_uio[6] = framebuf1; // 0x18, fb1_V
    display_dmar_axis_vga_uio[8] = framebuf2; // 0x20, fb2_V
    display_dmar_axis_vga_uio[10] = row; // 0x28, row_V
    display_dmar_axis_vga_uio[12] = col; // 0x30, col_V
    disp_gpio_uio[0] = 1; // disp_dmar_axis_vga start(init_done = 1)

    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case 'f':
                printf("active frame = %d\n", gpio_uio[0]);
                break;
        }
        c = getc(stdin);
    }
    
    return(0);
}


Ultra96 上のDebian で g++_opencv コマンドで pcam5c_disp_dp.cpp をコンパイルした。
g++_opencv pcam5c_disp_dp.cpp

~/examples/Pcam5C_DP/ ディレクトリで以下のコマンドを実行した。
sudo ./init_camera.sh
sudo ./pcam5c_disp_dp

334487c4.png


f リターン・キーを押すと、VDMA が使用しているフレームバッファ番号が表示される。それによると 1, 2, 3 の 3 個だった。 0 〜 2 だと思っていたので、失敗した。disp_dmar_axis_vga IP を修正する必要がある。

それ以前に、フレームバッファのアドレスから見たフレームバッファ領域は、0xe1000 のようだ。
0xe1000 は、921600 / 640 / 480 = 3 バイトなので、現在の私のdisp_dmar_axis_vga IP ではうまく行かないのがわかった。私の考えるフォーマットは4バイト、32ビット単位となっているのだ。

また、”カメラ画像をDisplayPortに出力する9(アプリを作成、完成)”の ./disp_pattern.sh をコピーして実行したが、DisplayPort の画面は真っ暗だった。