勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

ZYBOを進める - 24. カメラモジュールを動かす

今回から、カメラモジュールを動かして画像を取り込めるようにしていきたいと思います。

このカメラモジュール自体はかなり前々からあるもののようで、2011年くらいにこれを使ったFPGAデザインの記事があったりします。
かなり今更感…
とりあえずこのあたりのサイトも参考にしながら進めていきたいと思います。

FPGAの部屋 OV9655を使ってみる1(購入、変換基板設計)

OV9655で作るAXI USB3.0カメラ: なひたふJTAG日記

カメラモジュールの仕様

まず、今回のカメラモジュールの仕様を確認します。

概要

このモジュールは、2.54mmピッチピンヘッダとOmni Vision社のOV9655を搭載しています。
(aitendoのサイトからデータシートをダウンロードすると、OV9656と書いてあるが…?)

f:id:nokixa:20200524192306p:plain

一応、こちらから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→レジスタアドレス→レジスタデータ とすればいいようです。

f:id:nokixa:20200525011326p:plain

ついでに、ACタイミング特性も。

f:id:nokixa:20200525011719p:plain

f:id:nokixa:20200525011924p:plain

画像データ信号タイミング

基本的に、PCLKを基準としてデータが出てきます。

f:id:nokixa:20200525012049p:plain f:id:nokixa:20200525012543p:plain

  • PCLKの立ち下りでD9~D2にデータが出てくる、これをPCLKの立ち上がりで取り込めばよい
  • 1ラインデータ期間はHREFがHになる、HREFもPCLKの立ち下がりで出てくる
  • 1フレームの頭でVSYNCが数ライン期間Hになる(解像度により、VSYNCからのオーバヘッドやHREFの間隔が異なる)

D9~D2の中には、以下のようにピクセルデータが入ります。
2クロックで1ピクセル分になります。
ここで示しているのはRGB565フォーマットですが、他にもRGB555、YUVフォーマットで出力可能です。

f:id:nokixa:20200525081505p:plain

ACタイミング特性は以下の通りです。

f:id:nokixa:20200525081114p:plain

FPGA構成

FPGAは、以下のように構成していこうと思います。

  • SCCBインターフェース用にI2Cインターフェースを用意します。(互換なので)
    ただし、今まで使っているベースデザインでは、2つあるPS部のI2Cポートを両方使ってしまっています。
    なので、PL部にXilinx製のIPを追加してこれで制御しようと思います。
    f:id:nokixa:20200528002459p:plain
  • カメラモジュールからの画像データを受け取ってDDR3メモリに書き込むIPを自作し、これをブロックデザインに追加します。
    前回のHDMI出力デザインも、DDR3メモリに一度テストパターンデータを書き込み、これを読み出して表示していました。
    これでカメラ画像をHDMI表示することができると思います。 f:id:nokixa:20200528014327p:plain

コーディング

では、実際にデザインを作っていきます。

ブロックデザイン

まず、ブロックデザインにAXI IIC IPを追加します。
Vivadoでブロックデザインを開いて、ブロックデザイン上で右クリックして"Add IP"を選択します。
ブロックデザインウィンドウ上部の"+"ボタンからでも可能です。

f:id:nokixa:20200528081140p:plain

すると、IPの選択ウィンドウが現れます。この中に、使えるIPがいろいろ入っているので、見てみるのも一興です。

f:id:nokixa:20200528081407p:plain

今回は、検索バーに"iic"と入力します。"AXI IIC"が見つかるので、Enterキーを押します。すると、ブロックデザインにIPが追加されます。
また、ここでも前回のようなDesigner Assistance機能が使えるので、使います。

f:id:nokixa:20200529080613g:plain

DesignerAssistanceでは、追加したIPのポートをどうつなげるか、を自動でやってくれます。
が、どういう接続をするか確認します。

まず、"IIC"を選びます。Descriptionを見てみると、"IPのインターフェースをボードのインターフェースに接続する"とあります。
デフォルトの設定では、HDMIDDCポートにつながるようになっていたようでした。
今回は、"Custom"を選択します。 選択すると、"IIC"のチェックボックスに自動でチェックが入りました。入らなかったら、手動でチェックが必要です。

f:id:nokixa:20200529081541g:plain

次に、"S_AXI"の設定です。 デフォルトの設定では、"/processing_system7_0_axi_periph"に接続するとなっています。 今回は選択はできませんが、複数のInterconnect IPがある場合は、選択ができます。
クロックソースの選択はありますが、今回は"Auto"のままにします。
最後に、"S_AXI"のチェックボックスに手動でチェックを入れて、"OK"をクリックします。

f:id:nokixa:20200529081639p:plain

すると、IPのポート接続が行われます。

f:id:nokixa:20200530212424p:plain

Address Editorを見ると、processing_system7_0 (PS部ARMコア)の下にAXI IICが追加されていることが分かります。

f:id:nokixa:20200530212644p:plain

この段階ではまだ変更は保存されていないので、Ctrl+sで上書き保存しておきます。

カメラデータ受信部

次に、OV9655からのデータを受け取ってDDR3に書き込むロジックを作成します。
ここまでまともにRTLを書いてきませんでしたが、やっとRTLコーディングです。私はVerilogを使って来ているので今回もVerilogで書きます。 SystemVerilogも使ってみようかな...

このロジックは、以下のように構成します。

  • FIFO形式のバッファを用意
    • 入力データは、OV9655からのPCLKに同期して送られる
    • データを取り出して、AXI4 MasterインターフェースでDDR3へ転送する
  • OV9655からのデータおよびHREF、VSYNCをFIFOへ書き込み
    • 書き込みのクロックはPCLK
    • XCLKに24MHzを入力することとする → PCLKも24MHz
    • 毎クロック書き込みを行う
      HREF、VSYNCを見て書き込みをしてもいいですが、その場合、PCLKドメインからm_axi_aclkドメインへの非同期クロック間パスを処理しないといけないので、もう全部FIFOで非同期部は済ましたほうがすっきりするかな、という考えです。
  • FIFOからこれを読み出し、AXI4 MasterインターフェースでDDR3へ書き込む
    • 読み出しのクロックは任意に設定できる(AXI interconnectの設定による)が、axi_mem_interconに接続されている他のポートとそろえることとする
    • このクロックはprocessing_system7_0のFCLK_CLK1に接続されていて、140MHzとなっている
    • 読み出しデータからHREF、VSYNCを取り出し、データの有効性およびフレーム区切り、ライン区切りを確認する
  • AXI4 Slave Liteインターフェースを用意し、これをCPUに接続して、CPUから画像データ取得の制御を行う

f:id:nokixa:20200531022304p:plain

DDR3に格納した画像データをHDMI出力できるように、前回のHDMI出力デザインのときのフォーマットに合わせて画像の格納を行う

HDMI出力デザインのCPUコードで、テストパターン生成の処理を見ると、どのように画像データを用意すればいいかがわかります。

ピクセルデータのフォーマットは、以下のようになっているようでした。4byteの中に1pixel分のデータが入っていて、1byteは空きになっているようでした。

f:id:nokixa:20200531020856p:plain

また、1フレームのデータは以下のように配置されています。
水平方向から先に出ます。

f:id:nokixa:20200601012255p:plain

IP作成

今回は、上記のロジックを、ブロックデザインに追加するIPの形で作成します。
Vivadoで、"Tools"メニューを開くと、"Create and Package New IP"というのがあるので、これを選択します。

f:id:nokixa:20200604024539p:plain

"Create and Package New IP"のウィザードが立ち上がります。
Nextをクリックします。

f:id:nokixa:20200604080718p:plain

ここでは、AXI4インターフェースを持ったIPを新規で作成するので、"Create AXI4 Peripheral"にチェックを入れて、Nextをクリックします。

f:id:nokixa:20200604080928p:plain

IPの名前、情報を設定します。
IPの置き場も設定します。元々Digilentリポジトリからクローンしてきた段階で、ip_repoディレクトリが用意されていたので、ここをIP置き場にします。

f:id:nokixa:20200604081105p:plain

IP外部インターフェースの設定画面です。
まずAXI Lite Slaveポートが設定されています。これは、今回CPUからの設定用ポートとして使うこととします。

f:id:nokixa:20200604081311p:plain

+ボタンをクリックすると、AXIポートを追加することができます。

f:id:nokixa:20200604081810p:plain

ここでは、画像データを出力するAXI Masterポートを追加したいので、追加したポートの設定を以下のように変更します。

  • Name : M00_AXI
  • Interface Type : Full
    バーストアクセスが必要の場合は"Full"に設定が必要(今回は必ずしも必要ではない)
  • Interface Mode : Master (必須)

"Interface Mode"の設定は必須です。

f:id:nokixa:20200604081841p:plain

最後にサマリーが表示されます。
"Edit IP"にチェックを入れて、"Finish"をクリックします。
ウィザードが終了して、新しくVivadoが立ち上がります。

f:id:nokixa:20200605081917p:plain

f:id:nokixa:20200605082105p:plain

IP編集

Vivado上で、IPの編集を行います。

と思ったのですが、よく見たらプロジェクトの言語設定がVHDLでしたorz
生成されたテンプレートがVHDLになっています。

f:id:nokixa:20200606142707p:plain

今からVHDLを勉強するのも面倒なので、設定を変えます。
一旦IP編集のVivadoプロジェクトを閉じて、ip_repoフォルダの中身を全部消しておきます。

f:id:nokixa:20200606143205p:plain

元のプロジェクトに戻って、Project Summaryを見ると、Target languageがVHDLになっているので、これを変更します。

f:id:nokixa:20200606143249p:plain

VHDLのところをクリックすると、プロジェクトの設定ウィンドウが出てきます。Target languageをVerilogに変更します。

f:id:nokixa:20200606143657p:plain

インプリメント結果を残しておくか、と聞かれましたが、不要なのでNoとします。

f:id:nokixa:20200606143715p:plain

この後、先ほどと同じ手順でIP編集まで行くと、無事Verilogでテンプレートが出てきました。

f:id:nokixa:20200606145017p:plain

とりあえずここまで

長くなったので、いったんここで区切ります。
次回はVerilogのコーディングからになります。