FC2カウンター FPGAの部屋 Ultra96用PMOD拡張ボードでカメラ入力13(Debian上でカメラ画像を画像ファイルに3)
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Ultra96用PMOD拡張ボードでカメラ入力13(Debian上でカメラ画像を画像ファイルに3)

Ultra96用PMOD拡張ボードでカメラ入力12(Debian上でカメラ画像を画像ファイルに2)”の続き。

前回は、、uio と udmabuf のロードを行った。今回はいよいよ cam_capture.cpp を作成して、カメラ画像をBMPファイルにしてみよう。

最初に、デバイスツリーをロードして、uio と udmabuf をユーザー・モードから使用できるようにするスクリプト lddtovray.sh を書いた。
Ultra97_cam_capture_25_181127.png

#!/bin/bash

sudo mkdir /config/device-tree/overlays/fpga
sudo cp fpga-load.dtb /config/device-tree/overlays/fpga/dtbo
sudo mkdir /config/device-tree/overlays/fclk01
sudo cp fclk01-zynqmp.dtb /config/device-tree/overlays/fclk01/dtbo
sudo mkdir /config/device-tree/overlays/cam_capture
sudo cp cam_capture.dtb /config/device-tree/overlays/cam_capture/dtbo

sleep 0.5
sudo chmod 666 /dev/uio*
sudo chmod 666 /dev/udmabuf4


デバイスツリーを削除する rmdtovray.sh も作成した。
Ultra97_cam_capture_26_181127.png

#!/bin/bash

sudo rmdir /config/device-tree/overlays/cam_capture/
sudo rmdir /config/device-tree/overlays/fclk01
sudo rmdir /config/device-tree/overlays/fpga/


最初に、 lddtovray.sh を実行してから、cam_capture を実行してBMP ファイルを作成する。
なお、~/examples/cam_capture/build で cam_capture.cpp を g++_opencv cam_capture.cpp でビルドして、 cam_capture 実行ファイルを作成してある。g++_opencv コマンドについてはこちらを参照のこと。
./lddtovray.sh
cd build
./cam_capture

そして、 w コマンドを 2 回入力した。
Ultra97_cam_capture_21_181127.png

nautilus をみると、bmp_file0.bmp と bmp_file1.bmp が作成されていた。成功だ。。。
Ultra97_cam_capture_22_181127.png

bmp_file0.bmp を示す。
Ultra97_cam_capture_23_181127.jpg

bmp_file1.bmp を示す。
Ultra97_cam_capture_24_181127.jpg

部屋が散らかっているが気にしないで。。。
無線LAN経由でカメラ画像のBMPファイルが取得できた。なお、今回はOpenCV を使用している。

cam_capture.cpp を示す。
Ultra97_cam_capture_27_181127.png

ソースコードを貼っておく。

// cam_capture.cpp (for Ultra96)
// 2018/11/25 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 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)

int WriteBMPfile(char *bmp_file, volatile unsigned int *frame_buffer, int active_frame);

void cam_i2c_init(volatile unsigned *mt9d111_axi_iic) {
    mt9d111_axi_iic[64] = 0x2; // reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_axi_iic[64] = 0x1; // enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
    // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_axi_iic, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_axi_iic[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_axi_iic[66] = write_addr;
    mt9d111_axi_iic[66] = (write_data >> 8)|0xff;           // first data
    mt9d111_axi_iic[66] = 0x200 | (write_data & 0xff);      // second data
    cam_i2x_write_sync();
}

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, fd10, fd11;
    volatile unsigned int *mt9d111_inf_axis, *axi_iic, *DMA_Write_sFB;
    volatile unsigned int *frame_buffer;
    int active_frame;
    
     while ((opt=getopt(argc, argv, "b:n:h")) != -1){
        switch (opt){
            case 'b':
                strcpy(bmp_fn, optarg);
                break;
            case 'n':
                file_no = atoi(optarg);
                break;
            case 'h':
                help_flag = 1;
                break;
        }
    }

    if (help_flag == 1){ // help
        printf("Usage : cam_capture [-b <bmp file name>] [-n <Start File Number>] [-h]\n");
        exit(0);
    }
    
    // mt9d111_inf_axis-uio IP
    fd1 = open("/dev/uio1", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (mt9d111_inf_axis) open error\n");
        exit(-1);
    }
    mt9d111_inf_axis = (volatile unsigned *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!mt9d111_inf_axis){
        fprintf(stderr, "mt9d111_inf_axis mmap error\n");
        exit(-1);
    }
    
    // axi_iic-uio IP
    fd2 = open("/dev/uio2", O_RDWR|O_SYNC); // Read/Write, The chache is disable
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axi_iic) open error\n");
        exit(-1);
    }
    axi_iic = (volatile unsigned int *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!axi_iic){
        fprintf(stderr, "axi_iic mmap error\n");
        exit(-1);
    }

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

    // udmabuf4
    fd10 = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd10 == -1){
        fprintf(stderr, "/dev/udmabuf4 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, 5760000, PROT_READ|PROT_WRITE, MAP_SHARED, fd10, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer4 mmap error\n");
        exit(-1);
    }
    
    // phys_addr of udmabuf4
    fd11 = open("/sys/class/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd11 == -1){
        fprintf(stderr, "/sys/class/udmabuf/udmabuf4/phys_addr open error\n");
        exit(-1);
    }
    read(fd11, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd11);
    printf("phys_addr = %x\n", (int)phys_addr);
    
    // XDMA_Write_sFB start
    DMA_Write_sFB[6] = phys_addr; // fb0
    DMA_Write_sFB[8] = phys_addr+SVGA_ALL_DISP_ADDRESS; // fb1
    DMA_Write_sFB[10] = phys_addr+2*SVGA_ALL_DISP_ADDRESS; // fb2
    DMA_Write_sFB[0] = 0x1; // start
    DMA_Write_sFB[0] = 0x80; // EnableAutoRestart
    
    mt9d111_inf_axis[0] = phys_addr; // MT9D111 AXI4-Stream Start
    
    // CMOS Camera initialize, MT9D111
    cam_i2c_init(axi_iic);
    
    cam_i2c_write(axi_iic, 0xba, 0xf0, 0x1);        // Changed regster map to IFP page 1
    cam_i2c_write(axi_iic, 0xba, 0x97, 0x20);   // RGB Mode, RGB565

    mt9d111_inf_axis[1] = 0;
    
    char bmp_file[256];

    // w - writed the left and right eye's bmp files.  q - exit.
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case 'w' : // w - writed a bmp files.
                // writed the frame buffer
                file_no++;
                sprintf(bmp_file, "%s%d.bmp", bmp_fn, file_no);
                active_frame = (int)(DMA_Write_sFB[12] & 0x3); // Data signal of active_frame_V
                WriteBMPfile(bmp_file, frame_buffer, active_frame);
                
                printf("file No. = %d\n", file_no);

                break;
            case 'e' : // e - writed a same bmp files.
                // writed the frame buffer
                if (file_no == -1)
                    file_no = 0;
                
                sprintf(bmp_file, "%s%d.bmp", bmp_fn, file_no);
                active_frame = (int)(DMA_Write_sFB[12] & 0x3); // Data signal of active_frame_V
                WriteBMPfile(bmp_file, frame_buffer, active_frame);
                
                printf("file No. = %d\n", file_no);

                break;
        }
        c = getc(stdin);
    }
    
    munmap((void *)mt9d111_inf_axis, 0x1000);
    munmap((void *)axi_iic, 0x1000);
    munmap((void *)DMA_Write_sFB, 0x10000);
    munmap((void *)frame_buffer, 576000);
    
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd10);
    
    return(0);
}

int WriteBMPfile(char *bmp_file, volatile unsigned int *frame_buffer, int active_frame){
    int read_frame;
    
    if (active_frame == 0)
        read_frame = 2;
    else if (active_frame == 1)
        read_frame = 0;
    else // active_frame == 2
        read_frame = 1;
    int offset_addr = read_frame * SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES;
    
    cv::Mat img(SVGA_VERTICAL_LINES, SVGA_HORIZONTAL_PIXELS, CV_8UC3);

    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for(int y=0; y<img.rows; y++){
        for(int x=0; x<img.cols; x++){
            cv::Vec3b pixel;
            int rgb = frame_buffer[offset_addr+y*img.cols+x];
            pixel[0] = (rgb & 0xff); // blue
            pixel[1] = (rgb & 0xff00) >> 8; // green
            pixel[2] = (rgb & 0xff0000) >> 16; // red
            dst_vec3b(y,x) = pixel;
        }
    }
    
    imwrite(bmp_file, img);
    
    return(0);
}


cam_capture.cpp を作成した際の参照ブログ
ZYBOt のコースの写真撮影用アプリケーションソフトの開発
Ultra96用PMOD拡張ボードでカメラ入力9(Vivado 2018.2のcam_test_182プロジェクト6)
  1. 2018年11月27日 05:15 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog19.fc2.com/tb.php/4370-7d7d831a
この記事にトラックバックする(FC2ブログユーザー)