1.はじめに FujiFilmのFinePixシリーズのデジタルカメラの一部の機種には、PCカメラ(Webカメラ)機能が備わっているものがある。これらのカメラは、Windowsパソコンに専用のドライバーソフトをインストールし、パソコンとカメラを付属のUSBケーブルで接続すれば、Webカメラとして使える。製品に添付されているWindows用のドライバーだけでなく、Linux専用のドライバーも、有志の手により既に開発されており、Linuxでも使える。 ちなみに、ジャンク品のF401とF410の2個のデジタルカメラを入手し、Linuxに接続してみた所、簡単に接続できてしまった。カメラメーカーのデジタルカメラによるWebカメラなので、さぞかし画像品質は良いだろうと期待していたのだが、そこはそれ、おまけの機能という扱いのようで今ひとつである。 画像サイズは、320x240ピクセルのQVGAサイズ固定であり、露出やホワイトバランスの画質調整は一切できない。ズーム機能も働かない。出力される画像フォーマットは、JPEGのみである。出力されてくるファイルのサイズは20Kバイト程度と小さく高圧縮しているので画質は今ひとつだ。ただし、自動露出、ホワイトバランス自動調整なので画質の点に目をつぶれば、使い易いともいえる。 入手したカメラを自宅のLinuxサーバーに接続して暫くライブカメラとして使ってみたのだが、USB接続だけにサーバーからUSBケーブルの届く範囲でしかカメラを設置することが出来ない。そこで、ライブカメラとしての使用は諦めて別の使い道を模索し、コマ撮り用のカメラ(微速度カメラ)としての応用を考えて見た。 USBインタフェースとSDカードを持ったマイコンシステムにこのカメラを接続し、一分間隔あるいは、数分間隔毎に画像データを取得しSDカードに記録する。一定の時間動作させた後、SDカードに記録された画像データをパソコンで読み取って、動画処理を施せば、微速度カメラが出来上がる。 2. 回路設計 USBインタフェース付きのPICマイコンとして、「PIC24FJ64GB002」を採用した。このマイコンのUSBインタフェースは、USBホスト、USBデバイスのどちらにも対応している。そこで、カメラを接続している時は、USBホストとして動作させ、パソコンと接続する時は、USBデバイス(パソコンからは、SDカードリーダーに見える)として動作させるようにした。こうすれば、SDカードの抜き差しのわずらわしさを避けることが出来る。また、USBデバイスとして動作する時は、パソコンからのUSBバスパワーのみで動作できるようにした。 ACアダプタを接続するDC-Jackを2個設けているが、一つは、デジタルカメラ用の電源出力である。FinePixのデジタルカメラは、USBのバスパワーでは動作出来ず、ACアダプタによる+5Vが必要となる。本装置のACアダプタとカメラ専用のACアダプタでACコンセントを2個占有するのはもったいなくてこのような構造とした。なお、F410は、ACアダプタの仕様が、+3V仕様となっており、この方法を使うことはできない。+5V仕様のF401で運用をすることにしている。 カメラとPCの接続切り替えは、USBケーブルの抜き差しをソフトウェアーで検出し自動で行うようにしているが、強制的に切り替えることもできるようにスィッチを設けた。またホストモード、デバイスモードの動作モードを示すLEDをコネクタの傍に設置した。 SDカード用のSDカードスロットとマイコンの接続は、SPIインタフェース接続とした。SDカードは、運用中取り外すことも無いので、カードスロットのWriteEnable端子やCardDetect端子は、接続しなくても構わないのだが、一応セオリー通りに接続しておいた。 その他、PICマイコンのファーム書き換え用端子とデバッグ用のRS-232出力端子を9ピンのピンヘッダーに設けておいた。
3. ソフトウェアの作成 3-1 FinePixデジカメの制御方法 FinePixデジカメの制御方法が判らなければどうにもならない。また、非力なPICで制御できるのかどうか。これらを見極めるためにLinuxのfinpix用のドライバーを調べて見た。 ドライバーソフトは、gspca のメインドライバと finepix のサブドライバーから構成されているが、今回の目的に必要なのは、finepixだけである。このソースファイルを読む限り、FinePixの制御に必要なのは、次の赤字で示す部分のみのようである。 static int command(struct gspca_dev *gspca_dev, int order) /* 0: reset, 1: frame request */ { static u8 order_values[2][12] = { {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */ {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* frame request */ }; memcpy(gspca_dev->usb_buf, order_values[order], 12); return usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), USB_REQ_GET_STATUS, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, 12, FPIX_TIMEOUT); } static void dostream(struct work_struct *work) { struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; struct urb *urb = gspca_dev->urb[0]; u8 *data = urb->transfer_buffer; int ret = 0; int len; /* synchronize with the main driver */ mutex_lock(&gspca_dev->usb_lock); mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "dostream started"); /* loop reading a frame */ again: while (gspca_dev->present && gspca_dev->streaming) { /* request a frame */ mutex_lock(&gspca_dev->usb_lock); ret = command(gspca_dev, 1); mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) break; if (!gspca_dev->present || !gspca_dev->streaming) break; /* the frame comes in parts */ for (;;) { ret = usb_bulk_msg(gspca_dev->dev, urb->pipe, data, FPIX_MAX_TRANSFER, &len, FPIX_TIMEOUT); if (ret < 0) { /* Most of the time we get a timeout * error. Just restart. */ goto again; } if (!gspca_dev->present || !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && data[len - 1] == 0xd9)) { /* If the result is less than what was asked * for, then it's the end of the * frame. Sometimes the jpeg is not complete, * but there's nothing we can do. We also end * here if the the jpeg ends right at the end * of the frame. */ gspca_frame_add(gspca_dev, LAST_PACKET, data, len); break; } /* got a partial image */ gspca_frame_add(gspca_dev, gspca_dev->last_packet_type == LAST_PACKET ? FIRST_PACKET: NTER_PACKET, data, len); } /* We must wait before trying reading the next * frame. If we don't, or if the delay is too short, * the camera will disconnect. */ msleep(NEXT_FRAME_DELAY); } out: PDEBUG(D_STREAM, "dostream stopped"); } 上記のソースファイルを読み解くと、カメラの制御は次のようになる。
・ 続いて、バルク転送用のエンドポイントを使って画像データを受信用バッファに受信する。受信データの大きさがバッファの大きさよりも小さい場合や、画像データの末尾がjpegファイルの終了マーク(0xffd9)に一致していれば、受信完了となる。 非常に簡単な手順でカメラが制御できるわけで、これなら非力なPICでも十分にカメラから画像を読みだすことができる。jpeg形式で送信してくるので、後処理も不要で、そのまま画像データを一つのファイルとしてSDカードに書き込めば良い。 また、バルク転送を使っていることから、画像データの読み出し要求を出すタイミングはシビアに考える必要もなく、SDカードへのデータ書き込み時間も気にする必要はなさそうである。 3-2 PICのソフトウェア PICのUSBソフトウエアの作成に関しては、Microchip社からアプリケーションフレームワークとしてサンプルプログラムが提供されている。USBホストモードとして必要なソースファイルは、 usb_host.c と usb_host_generic.c だ。usb_host_generic.c は、 f inepix の制御用に少し修正が必要だ。 USBデバイスモードとして必要なソースファイルは、usb_device.c , usb_function_msd.c, SD-SPI.c だ。これでSDカードをPC側から、メモリディスクとして読み取ることが出来るようになる。その他、 usb_config.c や usb_descriptors.c などの関連ファイルも環境に合わせて適宜修正をしておく。 カメラを接続してホストモードで動作している時は、 カメラから取得した画像データをSDカードに書き込むために、ファイルシステムソフトウエアが必要であるが、これもMicrochip社から提供されている FSIO.c を使った。FSIO.c は FATファイルシステムを提供するもので、4GB以上のSDカードを使いたくて、config オプションで FAT32のサポートを有りに設定したが、ロングファイルネームはサポート無しにした。このため、SDカードに書き込むファイル名は、8.3形式のファイル名に限定され、「IMGXXXXX.JPG」のファイル名を使うことにした。 4 動作概要
本装置の動作は次のようになる。 SDカードは、4GBのSDHCカードを採用した。予めSDカードリーダー等を使ってPCに接続し、FAT32でフォーマットをしておく。このフォーマット済みのSDカードをマイコンシステムに組み込んでから、「4-2 デバイスモード動作」で示したような手順でPCと接続する。 PCからは、SDカードがメモリディスクとして見えるので、ディスクフォルダを開いて、ルートディレクトリに、CAMERA.INI ファイルと、DATAフォルダを作成する。 CAMERA.INIファイルはテキストファイルであり、先頭の一行に、現在日時と、画像取得間隔を次のように書いておく。 2012/07/16 20:12:35 300 (現在日時が、2012年7月16日20時12分35秒で、5分間隔で画像を取得する場合)この後、PCとの接続を切って、カメラを接続すると、5分間隔で、画像を取得し、DATAフォルダ内に IMGXXXX.JPG のファイル名で画像データの記録を行う。CAMERA.INIファイルに書いておいた日時は、マイコンシステム内の時計の初期値となり、以降この時刻を元にファイルの作成時刻が生成される。 6 ソースファイル main.c finepixカメラの制御と画像取得 Descriptor.txt finepixカメラのUSBディスクリプタ MPLAB Project File MPLAB C30 プロジェクトファイル一式 7 使ってみて |