公開日:2024/06/15
30th Season
#30-7 VGAカメラ再び:第3話
Arduino DUEのCPUパワー(@84MHz)を最大限使って制御した、VGAカメラOV7670モジュール(下図、および記事)を、今度はCmod A7ボード(FPGA)を使って、サクッと、制御してみたいと思います。
OV7670モジュールを、「再び、三度、四度、……」と、もっと気軽にサクッと使える様にハードウェア(FPGA)の底力を存分に発揮しようとするテーマの第3話です(第1話は、こちら)。
CAM7670ユニット
ここからは具体的に、OV7670を制御してデータを取り込むための回路設計を進めて行きます。
ブロック図
以下図にOV7670カメラインターフェイスCAM7670ユニットのブロック図を示し、解説を続けます。
※薄黄色の箱はCPUからアクセス可能なI/Oレジスタ、薄緑色の箱はハードマクロ、青破線はRTLとハードマクロの境界線です。
解説:
- OV7670とのインターフェイスはPCLK同期、MoscoviumとのインターフェイスはCPUクロックclk同期。
- 非同期クロック間のデータ転送は、非同期FIFOが吸収する。
- XCLKは、ピクセルクロックPCLKの源となる、クロック出力。
- OV7670内部でPLLへ通して逓倍したり分周したりして、PCLKが生成される。
- 逓倍比や分周比はOV7670のI/Oレジスタで設定可能。
- VGAサイズ30FPSで使う場合、fPCLK≒24MHzなので、XCLKとしては24MHzの定数倍または定数分周であることが望ましい。
- MoscoviumのCPUクロックは24MHzなので、これを1/2分周した12MHzを生成し、XCLKとして出力する。
- 1/2分周するのは、出力クロックXCLKのデューティ50%を担保するのと、ノイズ源となる高周波数信号出力を抑制するため。
- cmifブロック: (PCLKドメイン)
- OV7670からのストローブに従ってデータを取り込み、FIFOに流す⏩。
- fifoブロック: (PCLK書き込み、clk読み出し)
- このFIFOを使って、非同期クロック間のデータ転送を容易に実現する。
- PCLKの周波数次第では、かなり高速にデータが送られてくる。
- Moscovium側のハンドリングを容易にするため、少なくとも1ライン(640ピクセル)以上のFIFOとするのが望ましい。
- 1バイト/1ピクセルなので、最低でも640バイトのFIFOを形成する。
- Cmod A7ボードのFPGAチップXC7A35Tに備わった、36KビットRAMマクロを使ったFIFOマクロでは、4KバイトのFIFOとするのが、一番効率が良く、無駄もない。
- 4Kバイトは、VGAサイズのストリームなら、約6.4ライン分に相当する。
- ctrlブロック: (clkドメイン)
- 全体の制御回路。
- FIFOの状態、cmifブロックの初期化、取り込み許可信号など、制御用のI/Oレジスタを有する。
- また、CPUからメモリバス経由で読み出し要求が来ると、FIFOからデータを読み出して出力するI/Oレジスタを持つ。
I/Oレジスタ仕様
以下にCAM7670ユニットのI/Oレジスタ表を示し、解説を続けます。
Addr |
Name |
R/W |
Bit:15〜0 |
Memo |
0xf180 |
cm76ctl |
RW |
− |
− |
− |
− |
− |
− |
− |
− |
FF
UL
|
FA
BL
|
FR
BS
|
PC
KV
|
− |
− |
CE
NB
|
XC
KE
|
camera control |
0xf182 |
cm76pix |
R |
− |
− |
− |
− |
− |
− |
− |
− |
VS
YN
|
HS
YN
|
R[1:0] |
G[1:0] |
B[1:0] |
pixel data |
0xf184 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf186 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf188 |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf18a |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf18c |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
0xf18e |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
− |
reserved |
BUSC出力のセレクト信号はbcs_cm76_nです。上表の範囲でアサートされるので、CAM7670ユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
I/Oレジスタ仕様:
- cm76ctl: 制御レジスタ
- 初期値: 0x0000 (フラグ値は除く)
- FFULフラグ: FIFOフルフラグ
- OV7670からのデータ取り込みが先行した結果、FIFOがオーバフロウして書き込みできなかった場合に1となるフラグです。
- FFUL=1の間、ステートマシンが停止して、データ取り込みは中断されます。
- CENBビット=0でクリアすることができます。こちらのFIFOリセット手順をご確認ください。
- FABLフラグ: FIFOデータ有効フラグ
- OV7670からのデータ取り込みで、FIFOに1個以上の読み出し可能データが存在すると、1となるフラグです。
- ピクセルデータcm76pixレジスタは、FABL=1を確認後に読み出し可能です。
- FRBSフラグ: FIFOリセット動作中フラグ
- 非同期FIFOマクロが出力してくる、リセット動作中であることを示すフラグです。
- こちらのFIFOリセット手順をご確認ください。
- PCKVフラグ: PCLK有効フラグ
- PCLKが供給されるとセットされるフラグです。
- XCKEビット=0でクリアされます。
- XCLKを出力(XCKE=1設定)してから、OV7670が安定したPCLKを出力してくるまでの時間に規定が無い(データシートに見つからない)ため、本フラグは目安です。
- PCLKを65,536回カウントすると、PCKVフラグ=1となります。
- CENBビット: カメラI/F有効ビット
- OV7670からのデータ取り込み機能を有効化するビットです。
- CENB=0の間、カメラI/FおよびFIFOにリセットがかかります。
- ただし、リセットには、有効なPCLK入力(従って、XCLK出力)が必要です。
- XCKEビット: XCLK出力有効ビット
- OV7670へのXCLK(12MHz)出力を有効化するビットです。
- OV7670の動作クロックですので、OV7670を使用(制御)する間、XCKE=1を保持してください。
- cm76pix: ピクセルデータレジスタ
- FIFOからデータを読み出すレジスタです。
- cm76ctlレジスタのFABL=1を確認後に読み出し可能です。
- 読み出し値は{VSYN,HSYN,RGB222}の8ビットデータです。
- VSYN=1となるのは、フレーム最初のピクセルのみです。
- HSYN=1となるのは、ライン最初のピクセルのみです。
- RGB222は、6ビットカラー(64色)のデータです。
- 詳しくはこちらを参照してください。
カメラ画像の取り込み手順
OV7670を制御して、カメラ画像を取り込む手順を以下に示します。
- 1)OV7670へ、クロックを供給する。
- cm76ctlレジスタのXCKEビット=1に設定して、OV7670へクロックを供給する。
- クロックが安定するまで待機する。
- クロック安定までの時間は、OV7670データシートに記載が無いので不明。
- cm76ctlレジスタのPCKVフラグ=1になるまで待つのは、目安。
- PCKVフラグは、OV7670が出力してくるピクセルクロックPCLKを65,536回カウントすると、1になる。
- 尚、以降の処理では、XCKE=1の状態を保持する事。
- 2)OV7670をリセットする。
- OV7670のリセット端子(RET)へ接続した汎用I/OポートにLOWを出力する(アサート)。
- 数ms以上保持したら、リセット端子をHIGHに戻す(ネゲート)。
- 3)OV7670を初期設定する。
- 4)CAM7670ユニットを起動する。
- cm76ctlレジスタのCENBビット=1を設定する。
- 5)カメラ画像を取り込む
- 5−1)cm76ctlレジスタのFABLフラグを参照して、FABL=1まで待機。
- 同時にcm76ctlレジスタのFFULフラグを参照して、FFUL=0である事を確認する。
- FFUL=1を検出した場合は、FIFOがフルになってデータを受信できなかったことを示しているため、エラー終了。
- 5−2)cm76pixレジスタを読み出す。
- 下位8ビットにピクセルデータが格納されている。
- VSYNビット=1が見つかるまで5−1)に戻って繰り返し。
- VSYN=1が見つかったら、そのピクセルが、カメラ画像(フレーム)の最初のピクセルデータ。5−3)へ進む。
- 5−3)読み出したピクセルデータをCmod A7ボードのSRAM(512Kバイト、SRAMC512Kユニット)へ書き出す。
- VGAサイズ(640×480[ドット])で、1バイト/1ピクセルデータなので、1フレームのデータ容量は300Kバイト。
- オンボードSRAM(512Kバイト)なら、フルに取り込む事が可能。
- 5−4)2回目のVSYN=1ピクセルが見つかるまで、5−1)〜5−3)を繰り返す。
- 2回目のVSYN=1が見つかったら、それは、次のフレームデータの始まり。
- 1フレーム分のデータ取り込みが完了したので終了。6)へ進む。
- 6)CAM7670ユニットを停止する。
- cm76ctlレジスタのCENB=0を書き込んで、取り込み動作を停止する。
- 2回目以降、次のフレームデータを取り込み開始したい場合は、手順4)からの実行で良い。
- OV7670からは、ひたすらデータが送信されてくるため、CENB=0にクリアせず放置すると、いずれFIFOがフルになってエラー停止する。
- エラー停止後、FIFOをリセットする場合は、こちらの手順を参照。
- 7)取り込んだフレームデータの解釈。
- SRAM(512Kバイト)に1バイト/1ピクセルのデータが格納されている。
- 先頭データから、フレームの横方向へのピクセルデータが続く。
- 途中、HSYNビット=1のピクセルが見つかったら、そこで改行して、1ライン下の先頭ピクセルに戻る。
- 2回目のVSYN=1となるピクセルまでが、1フレーム分のデータ。
OV7670が垂れ流してくるデータから、1フレーム分のデータを切り出してSRAMに格納できたので、フレームデータを処理するなり、UART経由でPCにアップロードするなり、所望の処理に移行することができます。
※例えば、OV7670モジュールをQVGA(320×240[ドット])出力に設定した場合、横方向に320ピクセル、縦方向に240ラインのデータが受信できると想像すると思います。しかし実際に試してみると、OV7670の設定値次第では、ピクセル数が足りなかったり、逆にライン数が増えたりする場合があります。OV7670はバグだらけなので、要注意。
FIFOフル時のリセット手順
FIFOがフルとなって停止した場合のリセット手順は、以下の通りに実行してください。
- 1)cm76ctlレジスタのCENBビット=0としてCAM7670ユニットの動作を停止する。
- この処理によって、FIFOマクロに対してリセット信号がアサートされる。
- 2)FRBSフラグ=1と、なるまで待機。
- 3)CENB=1として、CAM7670ユニットを起動する。
- この処理によって、FIFOマクロのリセット信号がネゲートされる。
- 4)FRBS=0と、なるまで待機してリセット完了。
FIFOをリセットして、そのまま機能を停止させるなら、1)の手順だけ実行すればOKです。リセットしてすぐに再起動したい場合には、1)〜4)の手順を遂行してください。リセットには、PCLKの発振が必要なので、XCKEビット=1の状態を維持してください。
デザイン
次回、Moscoviumで実行するプログラムをご覧いただくと判りますが、コードサイズが4Kバイトを超えてしまいました。抽象度の低いアセンブラ言語でプログラミングしていると、大抵はコードサイズ4Kバイトもあったら十分です。しかし時々、調子に乗ってプログラミングしていると、4Kバイトでは足りなくなる場合があります。
そこで、今回設計したCAM7670ユニットを搭載したマイコンとして、命令ROM容量を8Kバイトに設定したMCOC115SV0832(mcoc115sv0832.vh)を起こしました(型名規則は、こちら)。
Cmod A7ボード用Moscoviumマイコンの設計データ(Vivadoプロジェクト)は、GitHubにて配布しております。以下バナーよりアクセスしてください。
ダウンロード方法などについては、トップのREADME.mdをご覧ください。
デザイン(回路)をCmod A7ボードに書き込む手順については、こちらをご確認ください。
コンパイル
カメラモジュールOV7670インターフェイス対応のCAM7670ユニットを搭載した、MCOC115SV0832をコンパイル(論理合成・配置配線)してみました。結果は以下の通りです。
コンパイル結果:
- mcoc115sv0832.vh
- コンパイル時間: 約6分(SYNTHESIS+IMPLEMENTATION)
- LUT数: 5,126個 (最大20,800個)
- F/F数: 2,688個 (最大41,600個)
- 36KビットRAMマクロ数: 11個 (最大50個)
- 4Kバイト分のCAM7670ユニット用FIFOマクロで1個使用。
- 8Kバイト分の命令ROM用RAMマクロで2個使用。
- 32Kバイト分のデータRAM用RAMマクロで8個使用。
- 乗算器マクロ数: 1個 (最大90個)
- MMCMマクロ数: 1個 (最大5個)
- 12MHz → 24MHzクロック生成回路で使用。
- タイミング解析(STA)結果:
- 【24MHzドメイン】
- Setup Slack: 9.044ns (MET)
- Hold Slack: 0.045ns (MET)
- 確認時のクリティカルパスは、命令レジスタから、ステートマシン、Aバス、アドレスbadr出力、アドレスデコーダと抜けて、メインメモリ(RAM)の書き込みストローブ信号に到達するパス(真パス)。
- タイミングはメットしており、問題なし。
- 【PCLK(31.25MHz)ドメイン】
- Setup Slack: 28.363ns (MET)
- Hold Slack: 1.131ns (MET)
OV7670インターフェイスのPCLKは、取り込み速度30FPSなら約24MHzで良いのですが、マージン込みで31.25MHzの制約をかけています。少しマージン取り過ぎ、な感もありますが、こちら側の回路(CAM7670ユニット)には、タイミング的に余裕があるので問題なしです。
FPGAの回路設計まで終了しました。残るは、オリジナル16ビットCPUコアMoscoviumで実行するプログラムの設計と、実際に動かしてみる実験を残すのみです(次回最終話)。