”FPGAで遊んでみる”では、セミナー用に作成したサンプル回路などを整理して少しづつ紹介していきます。


セミナ開催します
手ぶらでOK!実習・1日でわかる!CANプログラミング入門
-- マイコンモジュールでマスタするプロトコルの基本と通信プログラムの作り方 
2024年4月13日(土) CQ出版社セミナ・ルーム
詳しくはこちらへ

ZYBOの本
FPGAパソコンZYBOで作るLinux I/Oミニコンピュータ CQ出版 発売中

Papilioの本
FPGA版Arduino!!Papilioで作るディジタル・ガジェット CQ出版 発売中


第11回 カメラモジュールの画像をシリアル通信でPCへ転送してみる

  今回は第10回の回路(カメラモジュール画像表示)をベースにシリアル通信(UART)経由でPCへ画像を送る回路を追加したので、PC側プログラムと合わせて紹介します。
 画像はFPGAからシリアル通信で出力してUSB-UART変換器を経由してPCへ転送します。
 また、VRAMの書込みや読出しで、画像の同期がズレないように、VRAMを2つの領域に分けて読み書きを行うように変更しています。
 PC側のプログラムの開発は無償利用できるMicrosoft Visual C++2010 Expressを使用しました。

 評価機器
 
 

 左上がSVGAモニタ(RGB)、モニタの左下がカメラモジュール(OV7670)、
 左下がFPGAボード(DE0)、さらにその左側がUSB-UART変換器、
 右側は画像を転送するPCです。
  DEOのシリアル通信端子は、COMポートの直接接続できる電圧レベルで出力されます。
  ただし、DE0にCOMポート用コネクタはついていないのでコネクタを追加する必要があります。

カメラモジュール画像PC転送回路のソース(zip圧縮してあります)
 camera_ap_de0_uart_files.zip
  内容
  camera_ap.v 周辺回路も含めたFPGAの最上階層
  read_mem.v SDRAMからPC出力用データ読出し
  sdram_rd_abt.v SDRAM読出しリクエスト調停
  sdram_ctrl.v 簡易SDRAMコントローラ
  line_buf_in.v 書込み用ラインバッファ
  line_buf_out.v 読出し用ラインバッファ
  camera_if.v カメラモジュールインターフェース回路
  vram_ctrl.v 入力画像のメモリ保存、出力画像のメモリ読出し回路
  ov7670_cmd_genv ov7670の設定情報の生成
  i2c_ov7670_ctrl.v ov7670の設定情報からi2cモジュール制御情報生成
  svga_if.v SVGA画像出力回路(第4回で紹介)
  layer_a.v 移動する箱のグラフィック生成(第4回で紹介
  layer_b.v カラーパタンのグラフィック生成(第4回で紹介
  msg_buf.v メッセージバッファ回路(第8回で紹介)
  fifo.v メッセージ保持用FIFO(第8回で紹介)
  ram.v メッセージ保持用FIFO用RAM(第8回で紹介)
  rs232c_txrx.v シリアル通信(RS232C)インターフェースモジュール(第3回で紹介)
  camera_ap_test.v camera_apのテストベンチ
  camera_ap.qsf FPGAボードにDE0を使用した場合のPIN配置指定
  camera_ap.mif OV7670設定用のROMデータファイル
  vcpp_program/test_vc_p1.exe PC側画像転送プログラム
  vcpp_program/test_vc_p1_src.txt  PC側画像転送プログラムのソースコードの一部
 
 ※camera_ap.vをトップ回路してQuartusUのプロジェクトを作成してください
 ※test_vc_p1_src.txt はソースの一部です。VC++のプログラムを作る場合の参考にしてください
 ※全てのファイルが更新しれていますので、このページ圧縮データに入っているvrilogソースを使用してください
 ※この回路を使用する場合は、自己責任でお願いします。


FPGAでの動作内容
 FPGAトップモジュール(camera_ap.v)
  第10回の回路にread_mem,sdram_abt,msg_bufを追加しています。他のモジュールも変更が入っています。基本動作は第10回、第9回(その1,その2)を参照してください。
  追加の機能は以下の2点です。
  1.VRAMの書込み読出しで画像ズレ防止
  第10回までは、VRAMを1画面しか持っていなかったので、書込みと読出しで画像のズレが生じていました。今回はVRAMに
  2画面分の領域を使い、書込みと読出しで同じ領域にアクセスしないようにして画面のズレを防止しました。
  2.画像転送機能
  シリアル通信で'M'+CR+LFを受信すると、その時点で読出しに使っているVRAM領域をデータをシリアル通信で転送します。
  モニタ表示用の読込みとシリアル通信用の読込みが競合するのでsdram_rd_abtでアクセスの調停をしています。

  以下はブロック図です。グレーのモジュールが今回追加したモジュールです。

FPGA追加機能の動作詳細

 VRAMの書込み読出しで画像ズレ防止
  シリアル通信による画像転送を行ってない通常状態では、カメラモジュールからのデータをvram0領域とvram1領域に交互に書込みを行う。
 モニタ出力用読出しでは、読出し開始時にデータ書込み中でないvram領域を選択して読出しを行う。モニタ用読出し周期の方が書込みより
 短時間であるので、書き込みを追い越して読出しをすることがなくなり、画像のズレを防止できる。


   シリアル通信による画像転送を行っている状態では、シリアル通信用データ読出しは、モニタ出力用読出しを行っているvram領域から読出しを行う。
 シリアル通信用データ読出し周期は、画像の書込み周期より長いので、シリアル通信用データ読出し中の画像データ書込みは、読出しに使ってないvram領域に固定する。
 lこれによりシリアル通信で転送する画像のズレを防止する。このときモニタ出力用読出しの領域も固定されるので、転送中のモニタ表示は転送中の画像の静止画になる。



 画像転送機能
  'M'+CR+LFをシリアル通信で受信すると、画像を書込み中でないVRAM領域からデータを読出してシリアル通信で転送します。
 シリアル通信はボーレート=921.6kHz、データ8bit、パリティbit=なし、ストップbit=1bitで動作します。
 以下の図はRGBデータのデータフォーマットになります。
 


PCの画像取り込みソフト

  画像取り込みソフトは、無償で利用できるMicrosoft Visual C++2010 Express(以下VC++)で作りました。サンプルデータに入っているプログラムは、VC++のランタイムライブラリ等を使用するので
 実行にはVC++のインストールが必要です。 VC++の詳細やインストール方法につきましてはマイクロソフト社のサイトや参考書をご参考ください。
 また、サンプルのプログラムは各種設定やエラー処理など盛り込んでいません。あくまでも参考用とお考えください。
  下の図はVC++のFormのデザインです。sampleボタンを押すと、シリアル通信で'M'+CR+LFを送信します。その後にFPGAから送られてくるデータを受信を取り込みpictureBox1に画像を表示します。
 saveボタンを押すと、画像データをファイルに保存します。コンポーネントトレイには、シリアル通信用コンポーネントとしてserialPort1、ファイルセーブ用コンポーネントとしてsaveFileDialog1を配置しています。




以下はソースファイルの抜粋(test_vc_p1_src.txt)です。VC++のプログラムを作る際に参考にしてください。
シリアルポートの設定はソース内で指定しています。COMポート番号はPCにより違うので使用するPCに合わせて変更の必要があります。
ソースコードを変更して再ビルドしてください。

//----------------------------------------------------------------
// sampleボタン
//----------------------------------------------------------------
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
char tmp_v[3];
array<Byte>^ rcv_ary = gcnew array<Byte>(460800);//画面の受信データ
array<Byte>^ r_ary = gcnew array<Byte>(307200);//赤色データ
array<Byte>^ g_ary = gcnew array<Byte>(307200);//緑色データ
array<Byte>^ b_ary = gcnew array<Byte>(307200);//青色データ
Color color_src,color_dest;
Byte r,g,b;
//==============================================================
//シリアル通信のポートの設定
serialPort1->PortName = "COM10";
//シリアル通信のボーレートの設定
serialPort1->BaudRate = 921600;
//label1へ転送中と表示
label1->Text = L"転送中";
label1->Update();
//==============================================================
//ポートを開く
serialPort1->Open();
//“M+CR+LR”(転送コマンド)を送信する
serialPort1->Write("M\r\n");
// "M+CR+LF"の戻りを読み捨てる
do{} while(serialPort1->ReadBufferSize == 0) ;
tmp_v[0] = serialPort1->ReadByte();
do{} while(serialPort1->ReadBufferSize == 0) ;
tmp_v[1] = serialPort1->ReadByte();
do{} while(serialPort1->ReadBufferSize == 0) ;
tmp_v[2] = serialPort1->ReadByte();
//転送画像の読み込み
for(int j=0;j<480;j++){
//1ライン受信まで待つ
do{} while(serialPort1->BytesToRead < 960) ;
//1ラインのデータをバッファへ移動
serialPort1->Read(rcv_ary,(j*960),960);
}
//色別バッファに保存
for(int j=0;j<480;j++){
for(int i=0;i<320;i++){
r_ary[j*640 + i*2 ] = (rcv_ary[j*960 + i*3] & 0xf0)>>4;
g_ary[j*640 + i*2 ] = rcv_ary[j*960 + i*3] & 0xf;
b_ary[j*640 + i*2 ] = (rcv_ary[j*960 + i*3 +1] & 0xf0)>>4;
r_ary[j*640 + i*2 +1] = rcv_ary[j*960 + i*3 +1] & 0xf;
g_ary[j*640 + i*2 +1] = (rcv_ary[j*960 + i*3 +2] & 0xf0)>>4;
b_ary[j*640 + i*2 +1] = rcv_ary[j*960 + i*3+2] & 0xf;
}
}
//ポートをクローズ
serialPort1->Close();
//
//==============================================================
//データを描画する
//==============================================================
pictureBox1->SizeMode = PictureBoxSizeMode::Zoom;
//bitmapオブジェクトの作成
Bitmap^ bmap=gcnew Bitmap(640,480);
color_dest=Color::FromArgb(255,255,255);
for(int j=0;j<480;j++){
for(int i=0;i<640;i++){
//座標i,jの各色を取り出し
r = r_ary[j*640 + i]*16 ;
g = g_ary[j*640 + i]*16 ;
b = b_ary[j*640 + i]*16 ;
//色情報作成
color_dest=Color::FromArgb(r,g,b);
//i,jの座標へ色データセット
bmap->SetPixel(i,j,color_dest);
}
}
//bitmapを描画する
pictureBox1->Image = bmap;
label1->Text = L"↑を押すと転送";
}

//----------------------------------------------------------------
// saveボタン
//----------------------------------------------------------------
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
//ファイルをセーブ
SaveFileDialog^ myDLG = gcnew SaveFileDialog();
myDLG->AddExtension = true;
myDLG->DefaultExt = "bmp";
myDLG->FileName = "*.bmp";
myDLG->Filter = "ビットマップ(*.bmp) | *.bmp | jpeg(*.jpg;*.jpeg) | *.jpg;*.jpeg | すべてのファイル(*.*) | *.*";
if(myDLG->ShowDialog() != System::Windows::Forms::DialogResult::OK){
return;
}
pictureBox1->Image->Save(myDLG->FileName);

Invalidate();
}


 動作の様子
  FPGAボードとカメラモジュール、FPGAボードの回路ダウンロード用USB端子とPCのUSB端子、USB-UART変換器経由でFPGAのシリアル通信端子とPCのUSB端子をそれぞれ接続します。
 電源を入れてFPGAへ回路データをダウンロードするとディスプレイの中央にFPGAのカメラの画像が表示されます。次にPC側の画像取り込みソフトのsampleボタンを押すと、
 シリアル通信でFPGAからPCへ画像情報の転送が行われます。転送中はモニタの画像は停止します。転送には11秒かかります。
 転送が完了するとPCの画像取り込みプログラムのウィンドに画像が表示されます。



紹介した回路を試す場合は、自己責任でお願いします。

リンクフリーです。
リンクされた場合はご連絡をいただけると嬉しいです。
メール:


変更履歴
(2012/8/22) 初版


TOPへ戻る