OV2640からテスト用カラーバーを出力させる
ロジックアナライザーやオシロスコープでOV2640からの信号を見るためには、OV2640からテスト用のカラーバーを表示させた方が良いです。
その方法は、先に述べたようにSCCBインターフェースで、OV2640へレジスタ設定してやります。
まず、先に紹介したOV2640の初期化とフレームサイズ(画像サイズ)を設定し、次に以下のようにSCCBインターフェースでレジスタ設定してやります。
writeSCCB(0xff, 0x01); //bank sensor writeSCCB(0x12, 0b00100010); //COM7 CIF mode, color bar en
OV2640のレジスタ0xFF でバンク設定を0x01にして、センサー設定モードにしてやります。
その後、OV2640データシートに書いてあるように、レジスタ0x12 つまり、レジスタ名COM7 でCIFモードして、bit[1]を1にしてカラーバーON状態にしてやります。
すると、こんな感じに表示されます。
Framesize : 100 x 72 pixel
Display Size : 96 x 64 pixel
OUTW=25, OUTH=18
先ほど紹介したように、OV2640から出力するフレームサイズは、ディスプレイの 96×64 pixel に近い近似値のフレームサイズ 100 x 72 pixel としています。
当然、OV2640からのサイズの方が大きいので、ディスプレイ表示のところのプログラミングで、はみ出たところは捨てて表示させています。
ですから、この画像のカラーバーは、右側の黒色4pixel分カットされています。
それにしても、このテスト用のカラーバーは普通のカラーバーとちょっと違って不思議ですね。
ピンクと赤色のところに淵があります。
これは何なんでしょうね???
さて、CIFモードに限って言えば、画像は4倍ズームまで可能です。
そうすると、カラーバーの表示も変わりますので、それについては次の項で説明します。
ズーム設定
先ほどフレームサイズ設定で紹介したように、フレームサイズを替えると、あたかもズームしたようになります。
先に紹介したようにOV2640 のデジタルズームはCIFモードでは4倍までです。
SVGAモードは2倍まで。
UXGAモードはズーム不可。
これはどういうことかというと、個人的な想像で述べます。
先に紹介した画素のベイヤー配列を見て分かる通り、CIFモードでは画素を間引いて出力していて、SVGA では間引きがCIFの2分の1になっています。
UXGAでは目一杯画素全部を出力していて間引きはありません。
それに、先に紹介したロジックアナライザー波形を見ても、4倍までのサイズしかフレームサイズに入り切りません。
要するに、デジタルズームと言っても、低い解像度から最大の解像度までに切り替えているだけであって、あとはディスプレイで切り取っているだけということのようです。
分かってしまえば、
「な~んだ! そういうことか・・・」
と拍子抜けしてしまいますね。
実際、イメージセンサはそんなもんだろうと思います。
ズームをテストするためには、先ほど紹介したようにテスト用のカラーバーを表示させると良いです。
OUTW および OUTH の値を変えてやればよいです。
つまり、OV2640から出力させるフレームサイズを変えてやれば良いわけです。
96×64 pixel の SSD1331 に出力させると以下のようになります。
ディスプレイ表示側のプログラミングで、96×64に切り取って、はみ出た画素データは捨てています。
【等倍】
Framesize : 100 x 72 pixel
Display Size : 96 x 64 pixel
OUTW=25, OUTH=18
【2倍ズーム】
Framesize : 200 x 148 pixel
Display Size : 96 x 64 pixel
OUTW=50, OUTH=37
【等倍】
Framesize : 400 x 296 pixel
Display Size : 96 x 64 pixel
OUTW=100, OUTH=74
どうですか?
ちゃんとカラーバー表示もちゃんと2倍、4倍ズームしていますね。
ズームと言っても「なんちゃって」ズームですけど・・・。
DMA, I2S 割り込みみついて
さて、先に紹介したOV2640からのオシロやロジックアナライザー波形を見ると、ESP32 のGPIO端子で電圧のHIGHおよびLOWレベルを検知するプログラムを組めば、OV2640から送られてくる画素データのFIFOメモリ取り込みは出来そうな気がします。
例えば、以下のようにすれば、HREFがHIGHレベルになったかどうか検知できます。
while (getGpioLevel((gpio_num_t)cam_pin_HREF) == 1) { //HREFがHIGHレベルになっている間の処理 }
しかし、VSYNC(垂直同期)信号がLOWからHIGHレベルに立ち上がってから HREF(水平同期)信号が立ち上がるまでにかなりの時間(71.63μs)はありますが、HREFが立ち上がってからはPCLK ( Pixel Clock )信号で画素データが有効になるまではほぼ同時で、他の処理をする時間は全くありません。
これでは、GPIOのHIGH-LOWを検知するプログラミングでトリガーをかけても手遅れで、画素を取りこぼしてしまいます。
それに、先ほど説明したように、解像度を上げると、HREFがHIGHになる間が極端に短くなります。HREF信号のHIGH-LOWレベル検知なんてやっていると、その間にFIFOメモリから画素データを取り出す時間が圧倒的に足りなくなっていき、Arduinoプログラミングでは難しくなってきます。
そんな時、ESP32には嬉しい機能がありました。
それは、DMA, I2S用の割り込み機能です。
ESP32 Technical Reference Manual独自和訳 も合わせて参照してみてください。
先ほど紹介した DMA, I2S初期化を済ませておけば、Arduino core for the ESP32 で以下のようにすればDMA, I2S用割り込みが使えます。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include <driver/i2s.h> intr_handle_t i2s_intr_handle; static void IRAM_ATTR i2s_isr(void* arg); setup(){ esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, &i2s_isr, NULL, &i2s_intr_handle); I2S0.int_clr.val = I2S0.int_raw.val; I2S0.int_ena.val = 0; I2S0.int_ena.in_done = 1; esp_intr_enable(i2s_intr_handle); } loop(){ ・・・ } static void IRAM_ATTR i2s_isr(void* arg){ ・・・ }
esp_intr_alloc関数で、I2S0モジュールの割り込みを許可し、esp_intr_enable関数でI2S割り込みスタートという感じです。
すると、HREFがLOWからHIGHレベルになって、FIFOメモリにデータを取り込み、HREFがLOWレベルになった時点で強制的に割り込みが発生し、i2s_isr関数を実行するようです。
これは最近使ってみて、とっても便利だなと思いました。
正直言って詳しい仕組みは良く分かりませんが、これも改めてESP32の高機能にスゲーなと思ったところです。
と言っても、今時のマイコンではDMA, I2S割り込みなんて当たり前の機能かもしれませんね。
コメント
Hi aMiGO,
Excellent job! I appreciate it very much.
Although you don’t know much about DMA, you have come a long way.
Congratulations
Thanks
Gustavo Murta (from Brazil)
amigo (portuguese) = friend
Thanks for watching this blog post!!!