LoginSignup
4
2

More than 1 year has passed since last update.

1500円ZYNQ基板とPiCamV2でZyboのPcam5Cデモを動かす(後編)

Last updated at Posted at 2021-05-22

1. はじめに

1500円 ZYNQ 基板( EBAZ4205 )で、Digilent Zybo Z7-20 の Pcam 5C デモを動かしてみます。
このデモでは、CMOS カメラで撮影した映像を、HDMI 出力してモニタに映すことができます。

カメラは、手持ちの Raspberry Pi Camera Module V2 (SONY IMX219) を使用します。

記述が長くなりましたので、記事を前編と後編に分けています。

前編: Vivado での作業
後編: 接続ケーブルの作成と、Vitis での作業

後編の今回は、CSI-2 カメラ入力 + HDMI 出力ケーブルの作成と、Vitis での動作確認を行います。

2. 使用環境

  1. Windows10 Pro (20H2)
  2. Vivado 2020.1 (Windows 版)
  3. Vitis 2020.1 (同上)
  4. EBAZ4205
  5. Raspberry Pi Camera Module V2 (SONY IMX219 搭載)
  6. HDMI Monitor (Leader LV5382)

3. 参考資料

Zybo Z7 Pcam 5C Demo
OV5640 datasheet
IMX219 datasheet
XAPP894 D-PHY Solutions

4. ハードウェアの準備

前回の記事で作成した、ピン配置表を示します。
H1.png

4-1. HDMI 出力+CSI-2 入力ケーブルの作成

ピン配置表を元に、変換ケーブルの接続図を作成しました。
H2.png

これを、

aitendo の HDMI コネクタ with 基板 と、
H3.png

FFCコネクタwith基板★1.0/15P と、
H4.png

★2.0★2列ピンソケット [PS20DV] 2x10 を使って作成します。
H5.png

完成したケーブルは下記のようになりました。

HDMI と CSI-2 の高速差動信号が通る線は、波形改善のためツイストしてあります。
Zybo HDMIデモでは、ツイストで顕著に性能向上したので、撚り数を更に増やしてみました。
H6.jpg

( FFC ケーブルについて注意 )
Raspberry Pi Camera Module V2 に付属の FFC ケーブルは、端子が逆面タイプです。
H5_1.png

FFCコネクタwith基板では、端子が同一面の FFC ケーブルを使用しないと、1-15 番ピンが、裏返しの 15-1 番ピンに接続されてしまいます。
そのため、FFCケーブル★1.0-15P★ [FFC1015P] 150S も一緒に調達してください。
H5_2.png

4-2. ダンピング抵抗の 22Ω → 0Ω交換

CSI-2 受信、HDMI 送信どちらも、PL の I/O ピンから直結する回路にする必要があります。
HDMIデモの時と同様に、22Ω のダンピング抵抗を、0Ω に交換します。

※HDMI デモで使った EBAZ4205 基板は、一部に 3.3V プルアップ抵抗を付けたので、また別の EBAZ4205 基板を使用しています。いっぱい買ってあるので全然 OK。

DATA1~DATA3 コネクタへの途中にある 22Ω のダンピング抵抗を、すべて 0Ωに交換します。
H7.png

基板上のダンピング抵抗を、全て交換しました。
(写真は使いまわし)
H8.jpg

あと、DATA1 コネクタから接続する HDMI OUT の +5V Power 端子に電源を出力するため、逆流防止ダイオード D21 にジャンパを追加します。
(EBAZ4205 の電源供給は +5V で行っているので、V IN に直結でよい)

同様に、DATA2 コネクタから +3.3V を出力するため、逆流防止ダイオード D22-K に、基板上の +3.3V 電源ラインからジャンパを追加します。
H9.png

実基板の配線は、下記のようになりました。
+3.3V は C2321/C2319 から引き出し、+5V は D21 をバイパスしています。
H10.jpg

5. Vitis プロジェクトの作成

5-1. プログラムの読み込み

設計ファイルから sdk_appsrc フォルダを展開して、適当なフォルダに置いておきます。
このフォルダに、前回の記事でエクスポートした system_wrapper.xsa をコピーします。
例では \デスクトップ\EBAZ です。
H11.png

Vitis を起動したら、File - Switch Workspace... - Other... で \デスクトップ\EBAZ をワークスペースに指定して、Launch します。

Create Platform Project で、EBAZ-PiCam としてプロジェクトを作成し、system_wrapper.xsa を指定して Finish。
H12.png
H13.png

プラットフォームプロジェクトを、先にビルドしておきます。ビルドで Out-of-date が解消します。
H14.png

次に File - New - Application Project... で EBAZ-PiCam を platform に選択し、EBAZ-PiCam_app という名前で Next。
H15.png

standalone on ps7_cortexa9_0 で Next した後、Empty Applicaion(C++) を選び Finish。
H16.png

EBAZ-PiCam_app の src フォルダで右クリックして、Inport Sources... を選びます。
H17.png

From directory: に \sdk_appsrc を指定します。
Import Sources で、sdk_appsrc チェック、Overwrite existing resources without warning にチェックを入れて Finish。
H18.png

5-2. カメラ設定の変更

OV5640 用の設定ファイルを、IMX219 用に差し替えます。
H19.png

OV5640.h を、下記のファイルで上書きします。
同ファイル名で、中身だけ IMX219 用に変更してあります。

namespace に digilent が残っていますが、ご愛敬ということで...

OV5640.h
/*
 * OV5640.h
 *
 *  Created on: May 15, 2021
 *      Author: kan573
 *   Retarget : IMX219 (Raspberry Pi Camera Module V2.1)
 */

#ifndef OV5640_H_
#define OV5640_H_

#include <sstream>
#include <iostream>
#include <cstdio>
#include <climits>

#include "I2C_Client.h"
#include "GPIO_Client.h"
#include "../hdmi/VideoOutput.h"

#define SIZEOF_ARRAY(x) sizeof(x)/sizeof(x[0])
#define MAP_ENUM_TO_CFG(en, cfg) en, cfg, SIZEOF_ARRAY(cfg)

namespace digilent {

typedef enum {OK=0, ERR_LOGICAL, ERR_GENERAL} Errc;

namespace OV5640_cfg {
    using config_word_t = struct { uint16_t addr; uint8_t data; } ;
    using mode_t = enum { MODE_720P_1280_720_60fps = 0, MODE_1080P_1920_1080_15fps,
        MODE_1080P_1920_1080_30fps, MODE_END } ;
    using config_modes_t = struct { mode_t mode; config_word_t const* cfg; size_t cfg_size; };
    using test_t = enum { TEST_DISABLED = 0, TEST_EIGHT_COLOR_BAR, TEST_END };
    using awb_t = enum { AWB_DISABLED = 0, AWB_SIMPLE, AWB_ADVANCED, AWB_END };
    using config_awb_t = struct { awb_t awb; config_word_t const* cfg; size_t cfg_size; };
    using isp_format_t = enum { ISP_RAW = 0, ISP_RGB, ISP_END };
    uint16_t const OV5640_REG_PRE_ISP_TEST_SET1 = 0x503D;
    uint16_t const OV5640_FORMAT_MUX_CONTROL = 0x501f;

    config_word_t const cfg_advanced_awb_[] =
    {
            {0x0100 ,0x01}      // Start Stream
    };

    config_word_t const cfg_simple_awb_[] =
    {   // No function assignment
    };

    config_word_t const cfg_disable_awb_[] =
    {   // No function assignment
    };

    config_word_t const cfg_720p_60fps_[] =
    {       //1280 x 720 binned, RAW10, MIPISCLK=456M, SCLK=91.2Mz, PCLK=91.2M, 2lane_mipi
            {0x6620, 0x01},
            {0x6621, 0x01},
            {0x6622, 0x01},
            {0x6623, 0x01},

            {0x30EB, 0x05},
            {0x30EB, 0x0C},
            {0x300A, 0xFF},
            {0x300B, 0xFF},
            {0x30EB, 0x05},
            {0x30EB, 0x09},

            {0x0114, 0x01},
            {0x0128, 0x00},
            {0x012A, 0x18},
            {0x012B, 0x00},
            {0x0157, 0x50},
            {0x0158, 0x01},
            {0x0159, 0x00},
            {0x015A, 0x03},
            {0x015B, 0x68},
            {0x0160, 0x03},
            {0x0161, 0x6E},
            {0x0162, 0x0D},
            {0x0163, 0x80},
            {0x0164, 0x01},
            {0x0165, 0x68},
            {0x0166, 0x0B},
            {0x0167, 0x68},
            {0x0168, 0x02},
            {0x0169, 0x00},
            {0x016A, 0x07},
            {0x016B, 0xA0},
            {0x016C, 0x05},
            {0x016D, 0x00},
            {0x016E, 0x02},
            {0x016F, 0xD0},
            {0x0170, 0x01},
            {0x0171, 0x01},
            {0x0172, 0x00},
            {0x0174, 0x01},
            {0x0175, 0x01},
            {0x0176, 0x01},
            {0x0177, 0x01},
            {0x018C, 0x0A},
            {0x018D, 0x0A},
            {0xD1EA, 0x00},
            {0xD1EB, 0x20},
            {0x0301, 0x05},
            {0x0303, 0x01},
            {0x0304, 0x03},
            {0x0305, 0x03},
            {0x0306, 0x00},
            {0x0307, 0x39},
            {0x0309, 0x0A},
            {0x030B, 0x01},
            {0x030C, 0x00},
            {0x030D, 0x72},
            {0x455E, 0x00},
            {0x471E, 0x4B},
            {0x4767, 0x0F},
            {0x4750, 0x14},
            {0x4540, 0x00},
            {0x47B4, 0x14},
            {0x4713, 0x30},
            {0x478B, 0x10},
            {0x478F, 0x10},
            {0x4793, 0x10},
            {0x4797, 0x0E},
            {0x479B, 0x0E},

            {0x0601, 0x00},
            {0x0620, 0x00},
            {0x0621, 0x00},
            {0x0621, 0x00},
            {0x0623, 0x00},
            {0x0624, 0x05},
            {0x0625, 0x00},
            {0x0626, 0x02},
            {0x0627, 0xD0},
    };

    config_word_t const cfg_1080p_15fps_[] =
    {       //1920 x 1080 @ 30 fps, RAW10, MIPISCLK=288, SCLK=57.6MHz, PCLK=57.6M, 2lane_mipi
            {0x6620, 0x01},
            {0x6621, 0x01},
            {0x6622, 0x01},
            {0x6623, 0x01},

            {0x30EB, 0x05},
            {0x30EB, 0x0C},
            {0x300A, 0xFF},
            {0x300B, 0xFF},
            {0x30EB, 0x05},
            {0x30EB, 0x09},
            {0x0114, 0x01},
            {0x0128, 0x00},
            {0x012A, 0x18},
            {0x012B, 0x00},

            {0x0157, 0x80},
            {0x0158, 0x01},
            {0x0159, 0x00},

            {0x015A, 0x04},
            {0x015B, 0x52},
            {0x0160, 0x04},
            {0x0161, 0x58},
            {0x0162, 0x0D},
            {0x0163, 0x78},
            {0x0164, 0x02},
            {0x0165, 0xA8},
            {0x0166, 0x0A},
            {0x0167, 0x28},
            {0x0168, 0x02},
            {0x0169, 0xB4},
            {0x016A, 0x06},
            {0x016B, 0xEC},
            {0x016C, 0x07},
            {0x016D, 0x80},
            {0x016E, 0x04},
            {0x016F, 0x38},

            {0x0170, 0x01},
            {0x0171, 0x01},
            {0x0172, 0x00},
            {0x0174, 0x00},
            {0x0175, 0x00},
            {0x0176, 0x00},
            {0x0177, 0x00},

            {0x018C, 0x0A},
            {0x018D, 0x0A},
            {0xD1EA, 0x00},
            {0xD1EB, 0x20},
            {0x0301, 0x05},
            {0x0303, 0x01},
            {0x0304, 0x02},
            {0x0305, 0x02},
            {0x0306, 0x00},
            {0x0307, 0x24},
            {0x0309, 0x0A},
            {0x030B, 0x01},
            {0x030C, 0x00},
            {0x030D, 0x48},
            {0x455E, 0x00},
            {0x471E, 0x4B},
            {0x4767, 0x0F},
            {0x4750, 0x14},
            {0x4540, 0x00},
            {0x47B4, 0x14},
            {0x4713, 0x30},
            {0x478B, 0x10},
            {0x478F, 0x10},
            {0x4793, 0x10},
            {0x4797, 0x0E},
            {0x479B, 0x0E},
            {0x0601, 0x00},
            {0x0620, 0x00},
            {0x0621, 0x00},
            {0x0621, 0x00},
            {0x0623, 0x00},
            {0x0624, 0x07},
            {0x0625, 0x80},
            {0x0626, 0x04},
            {0x0627, 0x38},
    };

    config_word_t const cfg_1080p_30fps_[] =
    {       //1920 x 1080 @ 60fps, RAW10, MIPISCLK=520, SCLK=130MHz, PCLK=130M, 2lane_mipi
            {0x6620, 0x01},
            {0x6621, 0x01},
            {0x6622, 0x01},
            {0x6623, 0x01},

            {0x30EB, 0x05},
            {0x30EB, 0x0C},
            {0x300A, 0xFF},
            {0x300B, 0xFF},
            {0x30EB, 0x05},
            {0x30EB, 0x09},
            {0x0114, 0x01},
            {0x0128, 0x00},
            {0x012A, 0x18},
            {0x012B, 0x00},

            {0x0157, 0xB0},
            {0x0158, 0x01},
            {0x0159, 0x00},

            {0x015A, 0x04},
            {0x015B, 0xE0},
            {0x0160, 0x04},
            {0x0161, 0xE8},
            {0x0162, 0x0D},
            {0x0163, 0x78},
            {0x0164, 0x02},
            {0x0165, 0xA8},
            {0x0166, 0x0A},
            {0x0167, 0x28},
            {0x0168, 0x02},
            {0x0169, 0xB4},
            {0x016A, 0x06},
            {0x016B, 0xEC},
            {0x016C, 0x07},
            {0x016D, 0x80},
            {0x016E, 0x04},
            {0x016F, 0x38},

            {0x0170, 0x01},
            {0x0171, 0x01},
            {0x0172, 0x00},
            {0x0174, 0x00},
            {0x0175, 0x00},
            {0x0176, 0x00},
            {0x0177, 0x00},

            {0x018C, 0x0A},
            {0x018D, 0x0A},
            {0xD1EA, 0x00},
            {0xD1EB, 0x20},
            {0x0301, 0x04},
            {0x0303, 0x01},
            {0x0304, 0x02},
            {0x0305, 0x02},
            {0x0306, 0x00},
            {0x0307, 0x41},
            {0x0309, 0x0A},
            {0x030B, 0x01},
            {0x030C, 0x00},
            {0x030D, 0x82},
            {0x455E, 0x00},
            {0x471E, 0x4B},
            {0x4767, 0x0F},
            {0x4750, 0x14},
            {0x4540, 0x00},
            {0x47B4, 0x14},
            {0x4713, 0x30},
            {0x478B, 0x10},
            {0x478F, 0x10},
            {0x4793, 0x10},
            {0x4797, 0x0E},
            {0x479B, 0x0E},
            {0x0601, 0x00},
            {0x0620, 0x00},
            {0x0621, 0x00},
            {0x0621, 0x00},
            {0x0623, 0x00},
            {0x0624, 0x07},
            {0x0625, 0x80},
            {0x0626, 0x04},
            {0x0627, 0x38},
    };


    config_word_t const cfg_init_[] =
    {
        {0x0100, 0x00},         // Stream off
        {0x0103, 0x01}          // Software reset
    };
    config_modes_t const modes[] =
    {
            { MAP_ENUM_TO_CFG(MODE_720P_1280_720_60fps, cfg_720p_60fps_) },
            { MAP_ENUM_TO_CFG(MODE_1080P_1920_1080_15fps, cfg_1080p_15fps_) },
            { MAP_ENUM_TO_CFG(MODE_1080P_1920_1080_30fps, cfg_1080p_30fps_), },
    };
    config_awb_t const awbs[] =
    {
            { MAP_ENUM_TO_CFG(AWB_DISABLED, cfg_disable_awb_) },
            { MAP_ENUM_TO_CFG(AWB_SIMPLE, cfg_simple_awb_) },
            { MAP_ENUM_TO_CFG(AWB_ADVANCED, cfg_advanced_awb_) }
    };
}

class OV5640 {
public:
    class HardwareError;

    OV5640(I2C_Client& iic, GPIO_Client& gpio) :
        iic_(iic), gpio_(gpio)
    {
        reset();
        init();
    }

    void init()
    {
        usleep(1000000);

        size_t i;
        for (i=0;i<sizeof(OV5640_cfg::cfg_init_)/sizeof(OV5640_cfg::cfg_init_[0]); ++i)
        {
            writeReg(OV5640_cfg::cfg_init_[i].addr, OV5640_cfg::cfg_init_[i].data);
        }

        usleep(2000000);    // wait 24ms after reset

    }

    Errc reset()
    {
        //Power cycle
        gpio_.clearBit(gpio_.Bits::CAM_GPIO0);
        usleep(1000000);
        gpio_.setBit(gpio_.Bits::CAM_GPIO0);
        usleep(1000000);

        return OK;
    }

    Errc set_mode(OV5640_cfg::mode_t mode)
    {
        if (mode >= OV5640_cfg::mode_t::MODE_END)
            return ERR_LOGICAL;

        auto cfg_mode = &OV5640_cfg::modes[mode];
        writeConfig(cfg_mode->cfg, cfg_mode->cfg_size);

        return OK;
    }

    Errc set_awb(OV5640_cfg::awb_t awb)
    {
        if (awb >= OV5640_cfg::awb_t::AWB_END)
            return ERR_LOGICAL;

        auto cfg_mode = &OV5640_cfg::awbs[awb];
        writeConfig(cfg_mode->cfg, cfg_mode->cfg_size);

        return OK;

    }

    Errc set_isp_format(OV5640_cfg::isp_format_t isp)
    {
        if (isp >= OV5640_cfg::isp_format_t::ISP_END)
            return ERR_LOGICAL;

        switch (isp)
        {
            case OV5640_cfg::isp_format_t::ISP_RGB:
//              writeReg(OV5640_cfg::OV5640_FORMAT_MUX_CONTROL, 0x01);
                break;
            case OV5640_cfg::isp_format_t::ISP_RAW:
//              writeReg(OV5640_cfg::OV5640_FORMAT_MUX_CONTROL, 0x03);
                break;
            default:
                break;
                }

        return OK;
    }

    ~OV5640() { }
    void set_test(OV5640_cfg::test_t test)
    {
        switch(test)
        {
            case OV5640_cfg::test_t::TEST_DISABLED:
//              writeReg(OV5640_cfg::OV5640_REG_PRE_ISP_TEST_SET1, 0x00);
                break;
            case OV5640_cfg::test_t::TEST_EIGHT_COLOR_BAR:
//              writeReg(OV5640_cfg::OV5640_REG_PRE_ISP_TEST_SET1, 0x80);
                break;
            default:
                break;
        }
    }
    void readReg(uint16_t reg_addr, uint8_t& buf)
    {
        for(auto retry_count = retry_count_; retry_count > 0; --retry_count)
        {
            try
            {
                auto buf_addr = std::vector<uint8_t>{(uint8_t)(reg_addr>>8), (uint8_t)reg_addr};
                iic_.write(dev_address_, buf_addr.data(), buf_addr.size());
                iic_.read(dev_address_, &buf, 1);
                break; //If no exceptions, no mo retries
            }
            catch (I2C_Client::TransmitError const& e)
            {
                if (retry_count > 0)
                {
                    continue;
                }
                else
                {
                    throw HardwareError(HardwareError::IIC_NACK, e.what());
                }
            }
        }
    }
    void writeReg(uint16_t reg_addr, uint8_t const reg_data)
    {
        for(auto retry_count = retry_count_; retry_count > 0; --retry_count)
        {
            try
            {
                auto buf = std::vector<uint8_t>{(uint8_t)(reg_addr>>8), (uint8_t)reg_addr, reg_data};
                iic_.write(dev_address_, buf.data(), buf.size());
                break; //If no exceptions, no mo retries
            }
            catch (I2C_Client::TransmitError const& e)
            {
                if (retry_count > 0) continue;
                else throw HardwareError(HardwareError::IIC_NACK, e.what());
            }
        }
    }

    void writeRegLiquid(uint8_t const reg_data)
        {
            for(auto retry_count = retry_count_; retry_count > 0; --retry_count)
            {
                try
                {
                    auto buf = std::vector<uint8_t>{0x06, 0x01, reg_data};      // 0x0601 = test_pattern_mode
                    iic_.write(dev_address2_, buf.data(), buf.size());
                    break; //If no exceptions, no mo retries
                }
                catch (I2C_Client::TransmitError const& e)
                {
                    if (retry_count > 0) continue;
                    else throw HardwareError(HardwareError::IIC_NACK, e.what());
                }
            }
        }
    class HardwareError : public std::runtime_error
    {
    public:
        using Errc = enum {WRONG_ID = 1, IIC_NACK};
        HardwareError(Errc errc, char const* msg) : std::runtime_error(msg), errc_(errc) {}
        Errc errc() const { return errc_; }
    private:
        Errc errc_;
    };
private:
    void usleep(uint32_t time)
    {//TODO couldn't think of anything better
        for (uint32_t i=0; i<time; i++) ;
    }
    void writeConfig(OV5640_cfg::config_word_t const* cfg, size_t cfg_size)
    {
        for (size_t i=0; i<cfg_size; ++i)
        {
            writeReg(cfg[i].addr, cfg[i].data);
        }
    }
private:
    I2C_Client& iic_;
    GPIO_Client& gpio_;
    uint8_t dev_address_ = (0x20 >> 1);
    uint8_t dev_address2_ = (0x20 >> 1);
    unsigned int const retry_count_ = 10;
};

} /* namespace digilent */

#endif /* OV5640_H_ */

5-3. コンパイル

これで準備はできましたので、アプリケーションプロジェクトをビルドします。
H20.png

ビルドが終わったら準備完了です。

6. 動作確認

EBAZ4205 基板と HDMI出力/CSI-2 入力ケーブル、HDMI モニタ、他一式を接続しておきます。
H28.jpg

EBAZ4205 基板と HDMI モニタの電源を入れ、Vitis から認識できるようにしておきます。

TeraTerm を起動して、設定で Local Echo にチェックを入れて OK します。
H21.png

6-1. プログラム起動

Run ボタンを押して、アプリケーションを起動します。
H22.png

TeraTerm にアプリケーションの操作画面が表示され、HDMI モニタにカメラの映像が映ります。
H23.png
H24.jpg

6-2. プログラムの操作方法

HDMI Demo の時とは、操作が違うので注意してください。
指定のアルファベットや数値を入力した後、ENTER する必要があります。

main.cc はそのまま流用していますので、操作内容が画面と合っていません。
使える操作は、下記の通りです。

a: カメラと HDMI 出力のモード切り替え
1, CAMERA: 1280x 720/60p, HDMI OUT: 1280x720/60p
2, CAMERA: 1920x1080/30p, HDMI OUT: 1920x1080/60p
3, CAMERA: 1920x1080/60p, HDMI OUT: 1920x1080/60p

b: IMX219 内蔵テストパターン出力
00, テストオフ(通常出力)
01, 全黒
02, 100% カラーバー
03, グレーフェード付 100% カラーバー
04, PN9(9ビット疑似ノイズ)
05, 16分割カラーバー
06, 16分割逆カラーバー
07, カラムカウンタ
08, 逆カラムカウンタ
09, PN31(31ビット疑似ノイズ)

d: 機能の割り当て無し

e: 指定レジスタに書き込み

f: 指定レジスタから読み出し

g: ガンマカーブ変更(Bayer2RGB IP)
1, Gamma Factor = 1
2, Gamma Factor = 1/1.2
3, Gamma Factor = 1/1.5
4, Gamma Factor = 1/1.8 (Default)
5, Gamma Factor = 1/2.2

h: 機能の割り当て無し

メニューの場所により、アルファベットまたは数字 1 文字を入力して ENTER のところと、
2桁 or 4桁の HEX 値を入力して ENTER のところがあります。

メニューのアルファベット 1 文字を入力する所で返答がおかしくなったら、
aa とか bb というように 2 文字を入力して ENTER すると戻ります。
(コマンド解釈に癖が多い)

6-3. テストパターンの表示

メニューの b で、IMX219 に内蔵されたテストパターンを表示することができます。
全部で 10 種類のテストパターンがあり、CSI-2 信号経路の評価に使えます。
パターンの詳細は、データシートを参照してください。

例は 03, グレーフェード付 100% カラーバー です。
H27.jpg

00, テストオフ(通常出力) で、元のカメラ画像に戻ります。

6-4. レジスタの Read/Write

メニューの e と f で、カメラのレジスタを直接読み書きできるのが、非常に便利です。

カメラの露出が、0x0157 で操作できますので、映像が明るすぎる/暗すぎるときは、値を変更してみてください。

大きい値を書くと露出量を増やす(明るく映る)、小さい値では露出量を減らす(暗く映る)
という操作ができます。
H26.png

全てのレジスタを読み書きできますので、IMX219 データシートを見ながら触ってみてください。

7. IMX219 の設定と、ケーブルの調整について

データシートで設定用レジスタの詳細を読めますが、パラメータ値を実際に書いてみると、いろいろと制約があることがわかりました。
raspivid でカメラをいろいろ操作して、I2C のデータを全ダンプして確認し、IMX219 の設定方法を理解しました。

今回の実習では、ケーブルと Zynq の実力値で調整した設定を ov5640.h に記載しています。

Zynq の LVDS_25 な I/O ピンで受信できる信号周波数は、スピードグレード -1 の場合、最大 950 Mb/s となっています(DS187)
1080/60p の設定では、1040 Mbps で受信している計算になりますので、受信エラーで画面にノイズが見えます。

03 グレーフェード付 100% カラーバーで、特定の明るさの所にノイズが見えやすいです。
FFC ケーブルや、CSI-2 - DATA2 ケーブルを指でつまむと、ノイズの見え方が変わります。

ピンセットで CSI-2 の信号線を触れて、良くなる(⇒ ディレイさせると受信状態が改善する)ところを探し、配線を適度に延長すると、ノイズを減らすことができます。

手元のケーブルでは、1080/60p でも、ほぼノイズレスが実現できています。
H25.jpg

上にあるグレーフェード付 100% カラーバーの画像を拡大してみると、右側のブルーの帯とグレーの帯の中間あたりに、それぞれ少しノイズが出ているのが見えると思います。

8. 全体のまとめ

EBAZ4205 で、Zybo Z7 Pcam 5C Demo を実行することができました。

RasPi にカメラを接続してHDMI出力するのと違い、スケーラが噛まない素のカメラ画像を確認することができます。
汎用の CSI-2 カメラテストベッドとして利用してもよさそうです。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2