FPGAでカメラからMIPI CSI-2の信号を出力させてみました。
Xilinxのデフォルトで利用できるフリー(無料)のIPを使っています。
MIPI CSI-2のカメラをFPGAで動かすための準備内容を紹介します。
FPGAでMIPI CSI-2をカメラから出力させてみた
MIPI CSI-2を使ってFPGAでカメラを動かしてみました。
カメラにI2C経由でレジスタを設定して、MIPI CSI-2信号を出力させています。
実際にオシロスコープでMIPI CSI-2の波形を確認しています。
残念ながらFPGA内のIPを正常に動作させて、画像処理まではできていません。
カメラからMIPI CSI-2の出力までを確認した記事となります。
IPの構成、デバッグの仕方などポイントを忘備録として残しておきたいと思います。
動画含めて紹介していますので是非一緒にご覧ください。
XilinxのFPGAの評価ボードを使用
XilinxのFPGAの中でもスペックの低いSpartan7の評価ボードでテストしています。
I2CとMIPI CSI-2の2つのIFを試しています。
- I2C…カメラIF(MIPI CSI-2)の初期設定用
- MIPI CSI-2 …カメラIF用
今回使った評価ボードに関しては下記記事で紹介しています。(リンク先はこちら)
XilinxのFPGAの評価ボードを購入してみた。Spartan7に入門!
MIPI CSI-2のカメラ
今回FPGAの評価ボードに取り付けるカメラは汎用的なラズパイ用を使用しています。
ラズベリーパイのカメラもMIPI CSI-2で動いています。
MIPI CSI-2のカメラの詳細・規格に関しては下記記事で紹介しています。
I2CはXilinxのIP Coreを使用
I2Cに関してはXilinxのIP Coreを使って(楽して)通信しました。
デフォルトで使えるIPのため、誰でも自由に使用することができます。
AXIバスを使う必要があるため、ソフトCPU(MicroBlaze)からI2Cを制御します。
IPコアを使ったハードウェア構築方法・配線に関しては下記記事に記載しています。
FPGAの開発環境を用意
C言語でプログラムするため、XilinxのFPGAのソフト開発環境(Vitis)を用意します。
本来VitisはOS,メモリなどにインストール要件があります。
但し、今回のテストレベルならば家庭用のノートPCでも十分に実施可能でした。
Vitisの簡単な使い方・インストールに関しては下記記事に記載しています。
MIPI CSI-2とI2C
ラズパイのカメラに電源入れるだけでは、MIPI CSI-2の信号は出てきません。
カメラIF(MIPI CSI-2)のデバイスは初期設定でI2Cを使うケースが多いです。
今回のラズパイ用のカメラもI2Cで設定します。
ラズパイのカメラ側には設定を記憶保持するROMはありません。
毎回電源ON時に解像度など必要なレジスタを書き込む必要があります。
FPGAでカメラIC(OV5647)にI2C通信する方法は下記記事に記載しています。
OmniVision OV5647のカメラ
今回のプログラムはカメラIC_OV5647に合わせています。
カメラICが違うとI2Cで設定するレジスタも異なりますのでご注意ください。
ラズパイのカメラにも種類ありますが、OV5647が一番安く購入できます。
- OmniVision OV5647…Camera Module v1(5Mピクセル)
- Sony IMX219…Camera Module v2(8Mピクセル)
- Sony IMX477…HQ Camera(12.3Mピクセル)
プログラム(C言語)
FPGA内のソフトCPUを使っていますので、C言語でプログラムしています。
一応640x480の解像度の設定のつもりで、参考記事内のコードを合わせて作りました。
プログラム実行後、カメラからMIPI CSI-2の波形は確認できました。
1280x720などの解像度もプログラムを変更すれば可能です。
但し、実際に画像処理まではしていないので本当に設定できているのかは不明です。
あくまで参考レベルでお願いします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
#include "xparameters.h" #include "XIic.h" struct config_word_t { u16 addr; u8 data; } ; struct config_word_t cfg_ov5647_init[] = { //ov5647_640x480_10bpp {0x0100, 0x00}, {0x0103, 0x01}, {0x3035, 0x11}, {0x3036, 0x46}, {0x303c, 0x11}, {0x3821, 0x07}, {0x3820, 0x41}, {0x370c, 0x03}, {0x3612, 0x59}, {0x3618, 0x00}, {0x5000, 0x06}, {0x5003, 0x08}, {0x5a00, 0x08}, {0x3000, 0xff}, {0x3001, 0xff}, {0x3002, 0xff}, {0x301d, 0xf0}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3c01, 0x80}, {0x3b07, 0x0c}, {0x380c, 0x07}, {0x380d, 0x3c}, {0x3814, 0x35}, {0x3815, 0x35}, {0x3708, 0x64}, {0x3709, 0x52}, {0x3808, 0x02}, {0x3809, 0x80}, {0x380a, 0x01}, {0x380b, 0xe0}, {0x3800, 0x00}, {0x3801, 0x10}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x2f}, {0x3806, 0x07}, {0x3807, 0x9f}, {0x3630, 0x2e}, {0x3632, 0xe2}, {0x3633, 0x23}, {0x3634, 0x44}, {0x3620, 0x64}, {0x3621, 0xe0}, {0x3600, 0x37}, {0x3704, 0xa0}, {0x3703, 0x5a}, {0x3715, 0x78}, {0x3717, 0x01}, {0x3731, 0x02}, {0x370b, 0x60}, {0x3705, 0x1a}, {0x3f05, 0x02}, {0x3f06, 0x10}, {0x3f01, 0x0a}, {0x3a08, 0x01}, {0x3a09, 0x2e}, {0x3a0a, 0x00}, {0x3a0b, 0xfb}, {0x3a0d, 0x02}, {0x3a0e, 0x01}, {0x3a0f, 0x58}, {0x3a10, 0x50}, {0x3a1b, 0x58}, {0x3a1e, 0x50}, {0x3a11, 0x60}, {0x3a1f, 0x28}, {0x4001, 0x02}, {0x4004, 0x02}, {0x4000, 0x09}, {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, {0x3017, 0xe0}, {0x301c, 0xfc}, {0x3636, 0x06}, {0x3016, 0x08}, {0x3827, 0xec}, {0x3018, 0x44}, {0x3035, 0x21}, {0x3106, 0xf5}, {0x3034, 0x1a}, {0x301c, 0xf8}, {0x4800, 0x34}, {0x3503, 0x03}, {0x0100, 0x01}, {0xffff, 0xff}, }; void writeReg(u16 reg_addr, u8 reg_data) { u8 buf[3]; buf[0] = (reg_addr >> 8) & 0xff; buf[1] = reg_addr & 0xff; buf[2] = reg_data; XIic_Send(0x40800000, 0x36, buf, 3, 0); } u8 readReg(u16 reg_addr) { u8 write_buf[3]; write_buf[0] = (reg_addr >> 8) & 0xff; write_buf[1] = reg_addr & 0xff; u8 read_buf; XIic_Send(0x40800000, 0x36, write_buf, 2, 0); XIic_Recv(0x40800000, 0x36, &read_buf, 1, 0); return read_buf; } int main() { u8 data; data = readReg(0x300a); //xil_printf("%x\r\n", data); data = readReg(0x300b); //xil_printf("%x\r\n", data); int i; //usleep(10000000); // 10sec sleep for(i = 0; cfg_ov5647_init[i].addr != 0xffff; i++){ writeReg(cfg_ov5647_init[i].addr, cfg_ov5647_init[i].data); //xil_printf("%x %x\r\n", cfg_ov5647_init[i].addr,cfg_ov5647_init[i].data); } //xil_printf("init done\r\n"); return 0; } |
MIPI CSI-2の出力確認
冒頭でも紹介しましたがテスト動画含めて紹介しています。是非一緒にご覧ください。
実際にFPGAが起動すると、カメラからMIPI CSI-2が出力されていました。
オシロスコープのプローブを当てた測定箇所はデータ0の箇所です。
高速信号のMIPI CSI-2ですが、デバッグレベルなら個人のオシロでは確認可能です。
MIPI CSI-2の物理層であるMIPI D-PHYには2つのモードがあります。
LP(Low Power)モードとHS(High Speed)モードです。通信中はモードが切り替わります。
モードで回路構成が変わり、電圧レベルが変わることを確認できています。
MIPI CSI-2の詳細・規格に関しては下記記事で紹介しています。
参考にした記事
I2Cのプログラム、データシート含めて下記記事を参考にさせていただきました。
運営者様・管理者様にはこの場を借りて深くお礼申し上げます。
https://github.com/torvalds/linux/blob/master/drivers/media/i2c/ov5647.c
https://cdn.sparkfun.com/datasheets/Dev/RaspberryPi/ov5647_full.pdf
MIPI CSI-2 RX Subsystem
XilinxのIPを使って、カメラからのMIPI CSI-2の信号をFPGAで受信できます。
MIPI CSI-2 RX Subsystemという名前でデフォルトで誰でも使用可能です。
物理層の受信から、指定された画像出力まで対応しています。
今回は失敗例のテスト接続です。(クロック・リセットの入れ方も適当です)
案の定、テストではIP内部の物理層のMIPI-DPHYの初期化すら完了できていません。
まだ途中ですが、忘備録としてポイントだった箇所を残しておきます。
200MHzのクロックが必要
MIPI CSI-2のIPが200MHzを必要としていますので、PLLで設定しました。
MicroBlazeや他のI2CのAXIバス含めて200MHzにしています。
IPのレジスタのIFを接続
デバッグする上で楽になりますので、IPのコントローラレジスタは接続を推奨します。
CSI-2だけでなく、内部の物理層のD-PHYも確認できます。
「D-PHY Register interface」「CSI2 Controller Register interface」にチェックします。
IPの「csirxss_s_axi」をAXIバス経由でMicroBlazeに接続します。
Address EditorでMIPI CSI-2のIPのレジスタの割り当てもしておきます。
Vitisのデバッグ時にMemory箇所でレジスタを確認できるようになります。
初期化・リセットを完了しているのか確認できますので、調査が楽になります。
IPの設定に従って、xparameters.hにも定義されるようになります。
IPのデータシートにも記載されていますが、下記オフセットでした。
- MIPI CSI-2 RX Controller…0x0_0000(今回は0x44A00000~0x44A00FFF)
- MIPI D-PHY…0X0_1000(今回は0x44A01000~0x44A01FFF)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/******************************************************************/ /* Definitions for driver MIPICSISS */ #define XPAR_XCSISS_NUM_INSTANCES 1 /* Definitions for peripheral MIPI_CSI2_RX_SUBSYST_0 */ #define XPAR_MIPI_CSI2_RX_SUBSYST_0_BASEADDR 0x44A00000 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_HIGHADDR 0x44A01FFF #define XPAR_MIPI_CSI2_RX_SUBSYST_0_DEVICE_ID 0 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CMN_INC_IIC 0 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CMN_NUM_LANES 2 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CMN_NUM_PIXELS 1 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CMN_PXL_FORMAT 0x2B #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CMN_VC 4 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CSI_BUF_DEPTH 2048 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CSI_EMB_NON_IMG 0 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_DPY_EN_REG_IF 1 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_DPY_LINE_RATE 900 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CSI_EN_CRC 1 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_CSI_EN_ACTIVELANES 0 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_EN_CSI_V2_0 0 #define XPAR_MIPI_CSI2_RX_SUBSYST_0_EN_VCX 0 /******************************************************************/ /* Canonical definitions for peripheral MIPI_CSI2_RX_SUBSYST_0 */ #define XPAR_CSISS_0_BASEADDR 0x44A00000 #define XPAR_CSISS_0_HIGHADDR 0x44A01FFF #define XPAR_CSISS_0_DEVICE_ID XPAR_MIPI_CSI2_RX_SUBSYST_0_DEVICE_ID #define XPAR_CSISS_0_CMN_INC_IIC 0 #define XPAR_CSISS_0_CMN_NUM_LANES 2 #define XPAR_CSISS_0_CMN_NUM_PIXELS 1 #define XPAR_CSISS_0_CMN_PXL_FORMAT 0x2B #define XPAR_CSISS_0_CMN_VC 16 #define XPAR_CSISS_0_CSI_BUF_DEPTH 2048 #define XPAR_CSISS_0_CSI_EMB_NON_IMG 0 #define XPAR_CSISS_0_DPY_EN_REG_IF 1 #define XPAR_CSISS_0_DPY_LINE_RATE 900 #define XPAR_CSISS_0_CSI_EN_CRC 1 #define XPAR_CSISS_0_CSI_EN_ACTIVELANES 0 #define XPAR_CSISS_0_EN_CSI_V2_0 0 #define XPAR_CSISS_0_EN_VCX 0 |
MIPI D-PHYの初期化の確認
今回は適当な配線のため、MIPI D-PHYの初期化(init_done)も失敗していました。
クロックのステータスレジスタ(CL_STATUS)箇所を見ると全て0でした。
カメラからMIPI CSI-2は出力されていますが、FPGAでは受信できていない結果です。
video_outに接続するとRAMを使う
デバッグ用にMIPI CSI-2のIPの画像出力を接続し始めるとエラーが出ました。
テスト用に「video_out_tvalid」「system_rst_out」を接続するだけでも発生しました。
- video_out_tvalid…画像データ出力が有効かの信号
- system_rst_out…IPのリセット確認用の信号
エラー内容はRAM(メモリ)が一杯という内容でした。
1 2 3 4 |
[DRC UTLZ-1] Resource utilization: RAMB18 and RAMB36/FIFO over-utilized in Top Level Design (This design requires more RAMB18 and RAMB36/FIFO cells than are available in the target device. This design requires 23 of such cell types but only 20 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.) |
今回使ったFPGAの(外付けのRAMも無く)メモリ容量が小さいのが原因と思われます。
使用した型番「XC7S15」はRAMがMAXで360Kbしかありません。
Spartan7でもかなりスペックが低い型番です。
最初はMicroBlazeデバッグ用に32KB使っていたのですが、8Kbにして対応しました。
基本的に発生しないエラーだと思いますが、参考までに記載しときます。
参考にした資料
MIPI CSI-2・D-PHYのIPのデータシート、またXilinxのフォーラムの情報が非常に役立ちました。
https://japan.xilinx.com/products/intellectual-property/ef-di-mipi-csi-rx.html#documentation
MIPI CSI-2 RX Subsystem と MIPI D-PHY RX IPのはじめてデバッグ手順
Xilinxの評価ボードのデザイン
他の評価ボードのMIPI CSI-2を参考にしたい場合についてです。
MIPI CSI-2のIPを右クリックして「Open IP Example Design…」を選択します。
Xilinxの(純正の)評価ボードのデザインを参考にすることが出来ます。
評価ボードのデモプログラム
下記記事でも紹介しましたが、評価ボードのメーカのデモプログラムもあります。
MIPI CSI-2のカメラ画像信号をそのままHDMIコネクタ画像出力するものです。
XilinxのFPGAの評価ボードを購入してみた。Spartan7に入門!
ただMIPI CSI-2のIPがTrenzというメーカの独自のものを使用していました。
興味がある方は下記プロジェクトをVivadoで開いてみて下さい
https://github.com/Pillar1989/Demo_project/tree/master/MIPI-In-HDMI-Out/spartan_cam
まとめ
今回はFPGAでMIPI CSI-2のカメラを動かす方法に関して紹介させていただきました。
記事をまとめますと下記になります。
MIPI CSI-2のより詳細の情報を下記記事で紹介しています。是非一緒にご覧ください。
コメント