今回から、カメラモジュールを動かして画像を取り込めるようにしていきたいと思います。
このカメラモジュール自体はかなり前々からあるもののようで、2011年くらいにこれを使ったFPGAデザインの記事があったりします。
かなり今更感…
とりあえずこのあたりのサイトも参考にしながら進めていきたいと思います。
FPGAの部屋 OV9655を使ってみる1(購入、変換基板設計)
OV9655で作るAXI USB3.0カメラ: なひたふJTAG日記
カメラモジュールの仕様
まず、今回のカメラモジュールの仕様を確認します。
概要
このモジュールは、2.54mmピッチピンヘッダとOmni Vision社のOV9655を搭載しています。
(aitendoのサイトからデータシートをダウンロードすると、OV9656と書いてあるが…?)
一応、こちらからOV9655のデータシートをダウンロードできました。以下では、このデータシートの内容を参照します。
http://www.arducam.com/downloads/modules/OV9655/ov9655_full.pdf
OV9655は、以下のような仕様のCMOSイメージセンサです。
- 解像度 SXGA (1280x1024)、その他解像度も設定可能
- SXGA解像度で15fps
- 露出制御、ガンマ補正、ホワイトバランス、といった画像処理機能を内蔵
- SCCBインターフェースで各種設定が可能
IO
上の写真にもありますが、以下のようなIOピンがあります。 モジュール基板上の接続は、aitendoサイトから回路図を参照。
ポート名 | 機能 |
---|---|
3.3V | 電源 |
GND | グランド |
SIOC | SCCBインターフェース : クロック |
SOID | SCCBインターフェース : データ |
VSYNC | 垂直同期信号 |
HREF | 水平同期信号 |
PCLK | 出力データクロック 明記はないが、XCLKが遅延してほぼそのまま出てくるよう |
XCLK | 入力クロック 10MHz~48MHz (標準24MHz)を入力する |
D9~D2 | 出力データ (8bit) OV9655自体は10bitのデータ出力ポートを持っていますが、このモジュールでは上の8bitだけ取り出されています。 |
RET | リセット信号 OV9655のRESETに接続されている 回路図を見る限り、Low-active (Lでリセット、Hで通常動作) |
PWDN | パワーダウン (Lで通常動作、Hでパワーダウンモード) |
SCCBインターフェース
カメラの設定を行うSCCBインターフェースについては、以下に仕様が記載されています。
http://www4.cs.umanitoba.ca/~jacky/Teaching/Courses/74.795-LocalVision/ReadingList/ov-sccb.pdf
特にこの中での言及はありませんが、IICインターフェースと互換の仕様となっています。
レジスタ書き込みの場合は、以下のように、スレーブID→レジスタアドレス→レジスタデータ とすればいいようです。
ついでに、ACタイミング特性も。
画像データ信号タイミング
基本的に、PCLKを基準としてデータが出てきます。
- PCLKの立ち下りでD9~D2にデータが出てくる、これをPCLKの立ち上がりで取り込めばよい
- 1ラインデータ期間はHREFがHになる、HREFもPCLKの立ち下がりで出てくる
- 1フレームの頭でVSYNCが数ライン期間Hになる(解像度により、VSYNCからのオーバヘッドやHREFの間隔が異なる)
D9~D2の中には、以下のようにピクセルデータが入ります。
2クロックで1ピクセル分になります。
ここで示しているのはRGB565フォーマットですが、他にもRGB555、YUVフォーマットで出力可能です。
ACタイミング特性は以下の通りです。
FPGA構成
FPGAは、以下のように構成していこうと思います。
- SCCBインターフェース用にI2Cインターフェースを用意します。(互換なので)
ただし、今まで使っているベースデザインでは、2つあるPS部のI2Cポートを両方使ってしまっています。
なので、PL部にXilinx製のIPを追加してこれで制御しようと思います。
- カメラモジュールからの画像データを受け取ってDDR3メモリに書き込むIPを自作し、これをブロックデザインに追加します。
前回のHDMI出力デザインも、DDR3メモリに一度テストパターンデータを書き込み、これを読み出して表示していました。
これでカメラ画像をHDMI表示することができると思います。
コーディング
では、実際にデザインを作っていきます。
ブロックデザイン
まず、ブロックデザインにAXI IIC IPを追加します。
Vivadoでブロックデザインを開いて、ブロックデザイン上で右クリックして"Add IP"を選択します。
ブロックデザインウィンドウ上部の"+"ボタンからでも可能です。
すると、IPの選択ウィンドウが現れます。この中に、使えるIPがいろいろ入っているので、見てみるのも一興です。
今回は、検索バーに"iic"と入力します。"AXI IIC"が見つかるので、Enterキーを押します。すると、ブロックデザインにIPが追加されます。
また、ここでも前回のようなDesigner Assistance機能が使えるので、使います。
DesignerAssistanceでは、追加したIPのポートをどうつなげるか、を自動でやってくれます。
が、どういう接続をするか確認します。
まず、"IIC"を選びます。Descriptionを見てみると、"IPのインターフェースをボードのインターフェースに接続する"とあります。
デフォルトの設定では、HDMIのDDCポートにつながるようになっていたようでした。
今回は、"Custom"を選択します。
選択すると、"IIC"のチェックボックスに自動でチェックが入りました。入らなかったら、手動でチェックが必要です。
次に、"S_AXI"の設定です。
デフォルトの設定では、"/processing_system7_0_axi_periph"に接続するとなっています。
今回は選択はできませんが、複数のInterconnect IPがある場合は、選択ができます。
クロックソースの選択はありますが、今回は"Auto"のままにします。
最後に、"S_AXI"のチェックボックスに手動でチェックを入れて、"OK"をクリックします。
すると、IPのポート接続が行われます。
Address Editorを見ると、processing_system7_0 (PS部ARMコア)の下にAXI IICが追加されていることが分かります。
この段階ではまだ変更は保存されていないので、Ctrl+sで上書き保存しておきます。
カメラデータ受信部
次に、OV9655からのデータを受け取ってDDR3に書き込むロジックを作成します。
ここまでまともにRTLを書いてきませんでしたが、やっとRTLコーディングです。私はVerilogを使って来ているので今回もVerilogで書きます。 SystemVerilogも使ってみようかな...
このロジックは、以下のように構成します。
- FIFO形式のバッファを用意
- 入力データは、OV9655からのPCLKに同期して送られる
- データを取り出して、AXI4 MasterインターフェースでDDR3へ転送する
- OV9655からのデータおよびHREF、VSYNCをFIFOへ書き込み
- FIFOからこれを読み出し、AXI4 MasterインターフェースでDDR3へ書き込む
- 読み出しのクロックは任意に設定できる(AXI interconnectの設定による)が、axi_mem_interconに接続されている他のポートとそろえることとする
- このクロックはprocessing_system7_0のFCLK_CLK1に接続されていて、140MHzとなっている
- 読み出しデータからHREF、VSYNCを取り出し、データの有効性およびフレーム区切り、ライン区切りを確認する
- AXI4 Slave Liteインターフェースを用意し、これをCPUに接続して、CPUから画像データ取得の制御を行う
DDR3に格納した画像データをHDMI出力できるように、前回のHDMI出力デザインのときのフォーマットに合わせて画像の格納を行う
HDMI出力デザインのCPUコードで、テストパターン生成の処理を見ると、どのように画像データを用意すればいいかがわかります。
ピクセルデータのフォーマットは、以下のようになっているようでした。4byteの中に1pixel分のデータが入っていて、1byteは空きになっているようでした。
また、1フレームのデータは以下のように配置されています。
水平方向から先に出ます。
IP作成
今回は、上記のロジックを、ブロックデザインに追加するIPの形で作成します。
Vivadoで、"Tools"メニューを開くと、"Create and Package New IP"というのがあるので、これを選択します。
"Create and Package New IP"のウィザードが立ち上がります。
Nextをクリックします。
ここでは、AXI4インターフェースを持ったIPを新規で作成するので、"Create AXI4 Peripheral"にチェックを入れて、Nextをクリックします。
IPの名前、情報を設定します。
IPの置き場も設定します。元々Digilent社リポジトリからクローンしてきた段階で、ip_repoディレクトリが用意されていたので、ここをIP置き場にします。
IP外部インターフェースの設定画面です。
まずAXI Lite Slaveポートが設定されています。これは、今回CPUからの設定用ポートとして使うこととします。
+ボタンをクリックすると、AXIポートを追加することができます。
ここでは、画像データを出力するAXI Masterポートを追加したいので、追加したポートの設定を以下のように変更します。
- Name : M00_AXI
- Interface Type : Full
バーストアクセスが必要の場合は"Full"に設定が必要(今回は必ずしも必要ではない) - Interface Mode : Master (必須)
"Interface Mode"の設定は必須です。
最後にサマリーが表示されます。
"Edit IP"にチェックを入れて、"Finish"をクリックします。
ウィザードが終了して、新しくVivadoが立ち上がります。
IP編集
Vivado上で、IPの編集を行います。
と思ったのですが、よく見たらプロジェクトの言語設定がVHDLでしたorz
生成されたテンプレートがVHDLになっています。
今からVHDLを勉強するのも面倒なので、設定を変えます。
一旦IP編集のVivadoプロジェクトを閉じて、ip_repoフォルダの中身を全部消しておきます。
元のプロジェクトに戻って、Project Summaryを見ると、Target languageがVHDLになっているので、これを変更します。
VHDLのところをクリックすると、プロジェクトの設定ウィンドウが出てきます。Target languageをVerilogに変更します。
インプリメント結果を残しておくか、と聞かれましたが、不要なのでNoとします。
この後、先ほどと同じ手順でIP編集まで行くと、無事Verilogでテンプレートが出てきました。
とりあえずここまで
長くなったので、いったんここで区切ります。
次回はVerilogのコーディングからになります。