今日もSpartan-7ボードでMIPI CSIを通じてRasPiカメラV2を扱うための開発をしています。
昨日までにRasPiカメラV2の画像を1280×720で取り込めるようになってきたのですが、より高い解像度で取り込んでみたくなりました。このカメラは3280×2464ピクセルがあるので、もっと高い解像度が出せるはずです。
それから、解像度とクロックレートの関係もいろいろと実験したいと思っていました。
そのためには、カメラレジスタの設定をいろいろと変えなければならないのですが、これを理解するのにはクロック関係の内部構造について理解しておかねばなりません。
1920×1080や、3280×2464で出力するときの、標準的なクロックの設定は以下のようになっています。
まず、入力した24MHzのクロックを3分周して8MHzを作り、それを114倍して91.2MHzにします。この周波数はFIFOの左側と右側で別々に設定することもできるのですが、標準設定では同じく91.2MHzになるようにしています。
読み出しピクセルレートは91.2MHzですが2画素を同時に読みだすので192.4MHz相当の速度となります。
この91.2MHzの10倍の912MHzがMIPIのデータレーンに送り出される信号のビットレートとなります。
信号は8bitごとにカプセル化されるのですが、5バイト(40bit)を使って4ピクセルを送ります。5バイト分に4×10bitが入るように5バイト目に2bitずつ詰めるようなやり方でエンコードされます。(4pixelを5バイトに詰め込んでいるということは、有効ピクセルを読み出す時間の1.25倍の時間をかけてデータを送り出しているのだろう)
MIPI CSI RX Subsystemが出力するrxbyteclkhsは912Mbpsの8分の1の速度なので114MHzになります。ただし垂直ブランキング期間にはrxbyteclkは停止するようです。
iMX219では解像度によらず水平ラインのクロック数は3448で固定なので、3448÷91.2MHz=17.92μ秒というのが水平ラインの時間になります。
画面の解像度によらず、上の図のPLLの倍率(114というもの)を上げない限り、水平期間は17.92μ秒で変わりません。基本的にはフレームレートは1画面中のライン数で決まります。
下の図は横3280pixelの最大解像度の時のFPGA内部の波形です。水平ラインの長さ(tlastの間隔)は18usであることがわかります。
約18usの間に3280pixelのデータがやって来るのでデータレートは182Mpixel/sとなります。そのため、AXI Streamのクロック速度を200MHz以上にするか、2ピクセルを同時に扱えるようにAXI Streamのビット幅を広げる必要があります。
水平時間は17.92μ秒になるので、2500ラインで44.75msとなり、約22fpsになります。
3280×2464のときのI2Cの設定例を示します。
x"0114" & x"01",-- CSI_LANE_MODE = 2-lane
x"012A" & x"18",-- EXCLK_FREQ[15:8]
x"012B" & x"00",-- EXCLK_FREQ[7:0] = 24 MHz
x"015A" & x"01",-- INTEG_TIME[15:8]
x"015B" & x"F4",-- INTEG_TIME[7:0]
x"0160" & x"09",-- FRM_LENGTH_A[15:8]
x"0161" & x"C4",-- FRM_LENGTH_A[7:0] = 2500
x"0162" & x"0D",-- LINE_LENGTH_A[15:8]
x"0163" & x"78",-- LINE_LENGTH_A[7:0] = 3448
x"0260" & x"09",-- FRM_LENGTH_B[15:8]
x"0261" & x"C4",-- FRM_LENGTH_B[7:0] = 2500
x"0262" & x"0D",-- LINE_LENGTH_B[15:8]
x"0263" & x"78",-- LINE_LENGTH_B[7:0] = 3448
x"0170" & x"01",-- X_ODD_INC_A
x"0171" & x"01",-- Y_ODD_INC_A
x"0270" & x"01",-- X_ODD_INC_A
x"0271" & x"01",-- Y_ODD_INC_A
x"0174" & x"00",-- BINNING_MODE_H_A = x1 no binning
x"0175" & x"00",-- BINNING_MODE_V_A = x1 no binning
x"0274" & x"00",-- BINNING_MODE_H_B = x1 no binning
x"0275" & x"00",-- BINNING_MODE_V_B = x1 no binning
x"018C" & x"0A",-- CSI_DATA_FORMAT_A[15:8]
x"018D" & x"0A",-- CSI_DATA_FORMAT_A[7:0]
x"028C" & x"0A",-- CSI_DATA_FORMAT_B[15:8]
x"028D" & x"0A",-- CSI_DATA_FORMAT_B[7:0]
x"0301" & x"05",
x"0303" & x"01",
x"0304" & x"03",--最初で3分の1して8MHz
x"0305" & x"03",--最初で3分の1して8MHz
x"0306" & x"00",-- PLL_VT_MPY[10:8]
x"0307" & x"39",-- PLL_VT_MPY[7:0] = 57倍。左は456MHz
x"0309" & x"0A",-- OPPXCK_DIV
x"030B" & x"01",-- OPSYCK_DIV
x"030C" & x"00",-- PLL_OP_MPY[10:8]
x"030D" & x"72",-- PLL_OP_MPY[7:0] = 114倍。右は912MHzつ
次の波形は1920×1080のものです。
19usの水平間隔の間に送られてくるデータ量が少し減って、AXI Streamの占有率に余裕が出てきたことがわかります。
次の波形は1280×720のものです。ラインレートは上の2つの波形とは異なります。
レジスタ設定はZynqberryで使われていたもので、以下のようになっています。
x"012A" & x"13",-- EXCLK_FREQ[15:8]おそらく間違い
x"012B" & x"34",-- EXCLK_FREQ[7:0] = 4916 MHzおそらく間違い
x"0160" & x"04",-- FRM_LENGTH_A[15:8]
x"0161" & x"60",-- FRM_LENGTH_A[7:0] = 1120
x"0162" & x"0D",-- LINE_LENGTH_A[15:8]
x"0163" & x"78",-- LINE_LENGTH_A[7:0] = 3448
x"0164" & x"01",-- XADD_STA_A[11:8]
x"0165" & x"58",-- XADD_STA_A[7:0] = X top left = 344
x"0166" & x"0B",-- XADD_END_A[11:8]
x"0167" & x"77",-- XADD_END_A[7:0] = X bottom right = 2935
x"0168" & x"01",-- YADD_STA_A[11:8]
x"0169" & x"F0",-- YADD_STA_A[7:0] = Y top left = 496
x"016A" & x"07",-- YADD_END_A[11:8]
x"016B" & x"AF",-- YADD_END_A[7:0] = Y bottom right = 1967
x"016C" & x"05",-- x_output_size[11:8]
x"016D" & x"10",-- x_output_size[7:0] = 1296
x"016E" & x"02",-- y_output_size[11:8]
x"016F" & x"E0",-- y_output_size[7:0] = 736
x"0170" & x"01",-- X_ODD_INC_A
x"0171" & x"01",-- Y_ODD_INC_A
x"0174" & x"01",-- BINNING_MODE_H_A = x2-binning
x"0175" & x"01",-- BINNING_MODE_V_A = x2-binning
x"0176" & x"01",-- BINNING_CAL_MODE_H_A
x"0177" & x"01",-- BINNING_CAL_MODE_V_A
x"018C" & x"0A",-- CSI_DATA_FORMAT_A[15:8]
x"018D" & x"0A",-- CSI_DATA_FORMAT_A[7:0]
x"0301" & x"05",
x"0303" & x"01",
x"0304" & x"02",-- 最初は2分の1で分周。12MHzを作る
x"0305" & x"02",-- 最初は2分の1で分周。12MHzを作る
x"0309" & x"0A",-- OPPXCK_DIV
x"030B" & x"01",-- OPSYCK_DIV
x"0306" & x"00",-- PLL_VT_MPY[10:8]
x"0307" & x"17",-- PLL_VT_MPY[7:0] = 23。左は12MHz×23=276MHz
x"030C" & x"00",-- PLL_OP_MPY[10:8]
x"030D" & x"2E",-- PLL_OP_MPY[7:0] = 46。右は12MHz×46=552MHz
データレーンのラインレートは552Mbpsで、ピクセルレートは110.4MHzとなります。1水平時間は3448÷110.4=31.23usとなります。フレーム長が1120ラインなので、垂直時間は34.9776msとなって、フレームレートは28.65fpsとなります。
なお、この設定ではEXCLK_FREQレジスタの設定がおそらく間違えていて、コメントにあるような4916MHzであるわけがありません。
おそらく、EXCK_FREQレジスタの[15:8](0x12A番地)にはクロック周波数(MHz)の整数部を設定し、[7:0](0x12B番地)にはクロック周波数(MHz)の小数点以下の部を設定するのが正しいのでしょう。
だから、データシートにあるように7.6MHzのときには7Hと99Hになります。99H=153dで153/256=0.597なので、799Hで7.597MHzという意味になるのだと思います。しかしながらEXCK_FREQの設定が何かに使われることはないように思えるので、適当でいいのかもしれません。
なお、動作中にPLLの倍率を変更する場合には、レジスタの0x100にあるMODE_SELレジスタに0を一度書き込んでスタンバイにしてから、0x100に1を書き込んで再び動作させないと反映されないようです。
このあたりの仕組みを理解できたので、フレームレートもデータレートも自由自在に設定できるようになりました。
なお、このPLLの設定を理解する上で、RyuzさんのZYBO-Z7 で Raspberry Pi Camera Module V2 (Sony IMX219) を 1000fpsで使うサンプル (復刻記事) がとても参考になりました。この場を借りて御礼申し上げます。
最近のコメント