マイコン工作実験日記

Microcontroller を用いての工作、実験記録

SDカード・ソケット基板

2009-05-31 21:44:20 | MP3プレーヤ
これまでフラッシュに曲データを書き込むことで、SDカードを使わずにMP3再生の実験を進めてきましたが、やはりここらでSDカードをつなげることにしました。フラッシュの書き込み待ちに耐えられなくなってきたためです。



なにしろ曲データだけで400KBあったので、プログラムを合わせると書き込みサイズは500KBを超えています。JTAGを使って書き込んでいるのですが、(1) ELFファイルの読み込み、(2)フラッシュへの書き込み、(3)ベリファイの3つの段階を合計すると3分ほどかかるようになってしまったのです。曲データを小さくすれば時間かかりませんが、再生時間も短くなってしまい、おもしろくありませんし。



インターフェース6月号というかChaNさんHPの記事のとおりの回路です。手持ちの部品で作ったとはいえ、とてもわたしにはChaNさんように小さくきれいにまとめることはできません。


音量設定とインタフェースの活性化

2009-05-28 23:23:36 | USB
USBスピーカのディスクリプタが読めたので、その情報に基づいて音量設定とPCM出力インタフェースの活性化を行います。本来であれば、ディスクリプタをきちんと解析/解釈すれば、どんなUSBスピーカにも対応することが可能なのですが、試験に使えるスピーカがいくつもあるわけではないし、購入したSONYのスピーカに対応することしか考えません。読み出したディスクリプタからわかったインタフェース番号やエンドポイント番号を半ば決め打ちでコード書いちゃっています。

音量やミュートの制御をおこなうには、Audio Control Interfaceを使いますが、そのインタフェース番号は0番であることが、ディスクリプタからわかっています。そこで、まずはこのインタフェースから現在の音量/ミュート設定を読み出してやります。
> host
Initializing Host Stack
Host Initialized
Connect a Audio device
Port = 1
I  04 00 00 00 01 01 00 00
AI 24 01 00 01 28 00 01 01
AI 24 02 01 01 01 00 02 03 00 00 00
AI 24 03 06 01 03 00 09 00
AI 24 06 09 01 01 01 02 02 00
I  04 01 00 00 01 02 00 00
I  04 01 01 01 01 02 00 00
AI 24 01 01 01 01 00
AI 24 02 01 02 02 10 02 80 bb 00 44 ac 00
E  05 01 09 c8 00 01 00 00
AE 25 01 01 01 01 00
I  04 02 00 01 03 00 00 00
AD 21 00 01 00 01 22 32 00
E  05 83 03 04 00 20
IF #0, alt 0 is audio control.
IF #1, alt 0 is audio streaming.
IF #1, alt 1 is audio streaming.
-- Endpoint 1-OUT (200) is type ISOCHRONOUS
IF #2, alt 0 is HID.
-- Endpoint 3-IN (4) is type INTERRUPT
Audio device connected
> host muteget 0
0
> host volget 1
CUR: 65472 (c0 ff) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
> host volget 2
CUR: 65472 (c0 ff) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
>
引数の0, 1, 2はチャネル番号を表し、マスター、左、右に対応します。ミュートはオフ状態ですので、改めて設定する必要もないようです。音量はCUR, MIN, MAXの3つの値を取得して表示しています。CURが現在の設定値ですが、上記のように電源が入った直後では最大値に近い値に設定されています。ちょっと、どうかしらと思う動作仕様です。そこで、次のように設定して音量を下げてやります。マスターを操作すると左右の両方に影響を与えるのですが、音量設定ではマスターがサポートされていないようなので、左右別々に設定してやります。
> host volset 1 59000
> host volset 2 59000
> host volget 1
CUR: 58944 (40 e6) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
> host volget 2
CUR: 58944 (40 e6) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
>
実際に設定できた値が指定した値と異なっていますが、これはスピーカ側で設定できる値の解像度に制約があり、丸められた値しか設定できないためです。

現在のオーディオ出力はalt #0 になっており、PCM出力のエンドポイントと関連づけられていません。そこで、alt #1に変更してやります。
> host ifget 1
0
> host ifset 1 1
> host ifget 1
1
>
これでスピーカ側ではPCM出力動作が開始されているハズ。あとはアイソクロナス転送でPCMデータを送りつけてやれば、スピーカが鳴ってくれるでしょう。

上記の設定変更は、全てUSBのEP0を使うコントロール転送で制御しています。コントロール転送での書き込みや読み出しの基本ルーチンはUSB Host Liteで用意されているので、これを使っているのですが、当初正しく書き出し読み出しができなくて悩みました。音量を設定変更したつもりでも、読み出してみるとちっとも値が変わっていなかったのです。
USB_INT32S Host_CtrlRecv(USB_INT08U bm_request_type,
                         USB_INT08U b_request,
                         USB_INT16U w_value,
                         USB_INT16U w_index,
                         USB_INT16U w_length,
                volatile USB_INT08U *buffer)

USB_INT32S Host_CtrlSend(USB_INT08U bm_request_type,
                         USB_INT08U b_request,
                         USB_INT16U w_value,
                         USB_INT16U w_index,
                         USB_INT16U w_length,
                volatile USB_INT08U *buffer)

使っているのは上記ふたつの関数で、引数bufferで読み書きするデータを保持するバッファを指定します。ところが、USB Host Liteのコードを調べれてみると、これらふたつの関数の実装では引数bufferは全く参照されておらず(!!)、決め打ちのバッファを使うようになっていたのです。こんなのアリ?! サンプル以外の用途には簡単には流用できないようにワザと罠をしかけてあったんでしょうか。

USB Host Liteを使ってみようとお考えの方は、ご注意ください。ちなみに、インターフェース6月号記事のサンプルもこういうコードになっていますが、筆者の方の落ち度ではなく、もともとNXPが配布しているUSB Liteのコードからして、こうなっています。

ディスクリプタの読み込み

2009-05-26 00:39:29 | USB
LPC2388のUSBホスト機能を使って、USBスピーカのディスクリプタの読み込みと簡単な解析を試してみました。



NXPの提供するUSB LiteのサンプルはMass Storage Device用にできていますが、Enumerationの手順は同じですから、基本ロジックはそのまま拝借。Config Descriptorを読み込んだところで、その解析だけAudio Device用に書き換えてみたようなものです。もうひとつの変更点は、割り込み待ちの処理。USB Liteは割り込みは使っているものの、RTOSは必要としないように作られています。そのため、ディスクリプタの読み込みに使われるControl転送が終了するのを、フラグ変数のポーリングによって待つようになっていました。これでは他のタスクが動かないので、TOPPERS/JSPのSemaphore機能を使って待ち合わせるように変更してあります。

Configurationの設定まで動きましたので、すでにデバイス自体は有効になっているのですが、実際にスピーカを鳴らすには
  1. 必要であれば、ミュートのオフ、再生ボリュームの設定
  2. エンドポイントを持たないインタフェース(IF #1, alt 0)から、実際にPCMを出力できるエンドポイントを持つインタフェース(IF #1, alt 1)への切り替え。
  3. 再生するPCMデータの送出開始
という手順がまだ必要です。

USBスピーカ

2009-05-24 16:52:36 | Weblog
LPC2388につなげるUSBスピーカを用意しました。当初は数年前に買った手持ちの安物USBスピーカを使うつもりだったのですが、調べてみるとサンプリング周波数として48KHzしかサポートしていないという困ったチャンでした。これでは、CDから取り込んだMP3データを再生する際には44.1KHzから48KHzへのレート変換が必要になってしまいます。そこまでMCUにやらせるのも酷だと思うので、もう少しまっとうなスピーカを用意することにした次第です。



安物に手を出して痛いメにあうのはイヤなので、ちょっと値は張るけどSONYのSRS-T10PCを選択しました。もう生産終了になっている、古い製品のようですけど。どの製品もそうですが、お店でパッケージに表示されている製品仕様を確認してもサポートするサンプリング周波数なんて書いてありません。普通、そんなことまで確認する人なんていないわけですね。このスピーカも、製品マニュアルの仕様記述を読んでも、サンプリング周波数には言及していませんでした。ちょっとドキドキしながらPCにつなげてusbviewを使ってディスクリプタを調べてみました。
Device Descriptor:
bcdUSB:             0x0110
bDeviceClass:         0x00
bDeviceSubClass:      0x00
bDeviceProtocol:      0x00
bMaxPacketSize0:      0x40 (64)
idVendor:           0x0D8C
idProduct:          0x000C
bcdDevice:          0x0100
iManufacturer:        0x00
iProduct:             0x01
0x0409: "C-Media USB Headphone Set  "
iSerialNumber:        0x00
bNumConfigurations:   0x01

ConnectionStatus: DeviceConnected
Current Config Value: 0x01
Device Bus Speed:     Full
Device Address:       0x02
Open Pipes:              1

Endpoint Descriptor:
bEndpointAddress:     0x83
Transfer Type:   Interrupt
wMaxPacketSize:     0x0004 (4)
bInterval:            0x20

Configuration Descriptor:
wTotalLength:       0x008A
bNumInterfaces:       0x03
bConfigurationValue:  0x01
iConfiguration:       0x00
bmAttributes:         0xA0 (Bus Powered Remote Wakeup)
MaxPower:             0xFA (500 Ma)

Interface Descriptor:
bInterfaceNumber:     0x00
bAlternateSetting:    0x00
bNumEndpoints:        0x00
bInterfaceClass:      0x01 (Audio)
bInterfaceSubClass:   0x01 (Audio Control)
bInterfaceProtocol:   0x00
iInterface:           0x00

Audio Control Interface Header Descriptor:
bLength:              0x09
bDescriptorType:      0x24
bDescriptorSubtype:   0x01
bcdADC:             0x0100
wTotalLength:       0x0028
bInCollection:        0x01
baInterfaceNr[1]:     0x01

Audio Control Input Terminal Descriptor:
bLength:              0x0C
bDescriptorType:      0x24
bDescriptorSubtype:   0x02
bTerminalID:          0x01
wTerminalType:      0x0101 (USB streaming)
bAssocTerminal:       0x00
bNrChannels:          0x02
wChannelConfig:     0x0003
iChannelNames:        0x00
iTerminal:            0x00

Audio Control Output Terminal Descriptor:
bLength:              0x09
bDescriptorType:      0x24
bDescriptorSubtype:   0x03
bTerminalID:          0x06
wTerminalType:      0x0301 (Speaker)
bAssocTerminal:       0x00
bSoruceID:            0x09
iTerminal:            0x00

Audio Control Feature Unit Descriptor:
bLength:              0x0A
bDescriptorType:      0x24
bDescriptorSubtype:   0x06
bUnitID:              0x09
bSourceID:            0x01
bControlSize:         0x01
bmaControls[0]:
01 
bmaControls[1]:
02 
bmaControls[2]:
02 
iFeature:             0x00

Interface Descriptor:
bInterfaceNumber:     0x01
bAlternateSetting:    0x00
bNumEndpoints:        0x00
bInterfaceClass:      0x01 (Audio)
bInterfaceSubClass:   0x02 (Audio Streaming)
bInterfaceProtocol:   0x00
iInterface:           0x00

Interface Descriptor:
bInterfaceNumber:     0x01
bAlternateSetting:    0x01
bNumEndpoints:        0x01
bInterfaceClass:      0x01 (Audio)
bInterfaceSubClass:   0x02 (Audio Streaming)
bInterfaceProtocol:   0x00
iInterface:           0x00

Audio Streaming Class Specific Interface Descriptor:
bLength:              0x07
bDescriptorType:      0x24
bDescriptorSubtype:   0x01
bTerminalLink:        0x01
bDelay:               0x01
wFormatTag:         0x0001 (PCM)

Audio Streaming Format Type Descriptor:
bLength:              0x0E
bDescriptorType:      0x24
bDescriptorSubtype:   0x02
bFormatType:          0x01
bNrChannels:          0x02
bSubframeSize:        0x02
bBitResolution:       0x10
bSamFreqType:         0x02
tSamFreq[1]:      0x00BB80 (48000 Hz)
tSamFreq[2]:      0x00AC44 (44100 Hz)

Endpoint Descriptor:
bEndpointAddress:     0x01
Transfer Type: Isochronous
wMaxPacketSize:     0x00C8 (200)
wInterval:          0x0001
bSyncAddress:         0x00

Audio Streaming Class Specific Audio Data Endpoint Descriptor:
bLength:              0x07
bDescriptorType:      0x25
bDescriptorSubtype:   0x01
bmAttributes:         0x01
bLockDelayUnits:      0x01
wLockDelay:         0x0001

Interface Descriptor:
bInterfaceNumber:     0x02
bAlternateSetting:    0x00
bNumEndpoints:        0x01
bInterfaceClass:      0x03 (HID)
bInterfaceSubClass:   0x00
bInterfaceProtocol:   0x00
iInterface:           0x00

HID Descriptor:
bcdHID:             0x0100
bCountryCode:         0x00
bNumDescriptors:      0x01
bDescriptorType:      0x22
wDescriptorLength:  0x0032

Endpoint Descriptor:
bEndpointAddress:     0x83
Transfer Type:   Interrupt
wMaxPacketSize:     0x0004 (4)
bInterval:            0x20

ちゃんと44.1KHzと48KHzの両方がサポートされていますね。天下のソニーブランドであっても、USBスピーカなんかはOEMで全く手をかけていないんでしょうか。使っているチップのベンダー名であるC-MediaがiProductに表れていますし、idVendorの 0x0D8Cも調べてみるとC-Mediaに割り当てられた番号です。

このスピーカ USB ACTIVE SPEAKER SYSTEMと呼んで売られている製品ですが、使用しているC-MediaのチップはもともとUSBヘッドフォン用に開発されたチップであり、そのヘッドフォンアンプを使ってスピーカを鳴らしているということでしょうかね。

今日は、MTM03も見てきました。chikakiさんのTimpyは、イメージしていたよりも実物の方がさらに小さく感じられ、ホントに驚きでした。基板の実物も展示されていましたが、老眼のわたしには顔を近づけるとかえって見えなくなってしまいました。やはり、Webの方が良く見えます。掲示板で話題のカメラVS6724も展示されており、興味深く拝見させていただきました。Makeやニコ動でおなじみのものの現物が色々見れる素晴らしいイベントですね。個人的には、Rokuroがおもしろかったです。実物に自分で触れることによる生じる変化は、動画ではわかりませんし。

OHCIの勉強中

2009-05-23 13:36:32 | Weblog
LPC2388にMMcodec01をつなげてのMP3再生ができたので、次のステップとして、再生/休止の制御あるいはSDカードからの再生機能の実装をするつもりだったのですが、ちょっと考えが変わりました。

いずれは、これらの機能追加はするつもりですが、その前にUSBホスト機能であるOHCIを使ってみようではないかと。USBスピーカをつなげてやれば、MMcodec01のようなコーデックが無くてもMP3プレーヤになるじゃないかと考えたわけです。そんなことだったらMMcodec01買わなくても良かったかもという気もしないではありませんが、まだ入力側は使っていませんので、気が向いたらそちら側も試すことにしましょう。

そういうわけで、OHCIの勉強中です。OHCIはAT91SAM9260でもサポートされているので以前から興味はあったのですが、ちゃんと資料を読んだことがありませんでした。FR60でもサポートされているので説明記事を期待していましたが、昨年はほとんど満足な説明がありませんでした。今年はインターフェース6月号で簡単に説明がありましたが、使い方の詳細は別途資料とサンプルプログラムを読まないとわかりそうもありません。

6月号の記事はNXPが提供しているUSB Host Liteという簡易USBホスト・スタックを利用しています。実際にソースを見てみるとファイル数も少なくて、とても小さくできています。ちょっとコードを読んでみると、デバイスはひとつしかつなげないことを前提としていますし、リスト処理も簡単化して複数ED/TDをリンクしないとか最低限の処理に限定してあるようです。しかし、これでも限定的な使い方しかしない組み込み用途ではそれなりに使えるでしょう。しかし、このHost Liteではコントロール転送とバルク転送しかサポートしていません。サンプルのようにUSBメモリをつなぐには これでも用が足りるのですが、USBスピーカをつなぐにはアイソクロナス転送を追加サポートしてやらねばなりません。

LPC2388のマニュアルを読んでも、OHCIの詳しい説明についてはコンパックのサイトにある仕様書を参照しろと書いてあるだけです(あるいはココ)。そんなこんなで、やはり真面目にOHCIの仕様書を読むことになりました。この仕様書ですが、3回ほど斜め&飛ばし読みしてなんとか雰囲気がつかめてきました。最初読みにくく感じるのは、この仕様書の独特の構成の仕方に原因があります。実際のところ、仕様書の Introductionの章でも断り書きがあるのですが、この仕様書ではOHCIの動作仕様をドライバとコントローラの両方の観点から説明する独立した章を持っています。また、両者が共通して扱うデータ構造やレジスタについては別建ての章で説明されています。その結果、類似した説明が繰り返しでてくるのです。また、説明に関連するデータ構造やレジスタを参照するために、別の章やページを行き来して読む必要も生じます。PDFファイルには、しおりがついていないので、読みたいページへジャンプすることもできません。

OHCIの仕様書には、リスト制御の手順を説明するためのデータ構造やコードも例示されているので、これをベースにしてスタックを作成することもできそうです。しかし、スピーカを鳴らすだけの限定的な使い方であればUSB Liteのコードにアイソクロナス転送機能の処理を追加するだけで用が足りそうなので、その方向で作業する予定です。

LPC2388の I2C と I2Sを使う

2009-05-20 23:40:29 | MP3プレーヤ
MMcodec01をつなげるハードの準備(と、言ってもコネクタだけですが)ができたので、ソフトの作成です。MMcodec01に載っているTLV320AIC23Bを使うには、レジスタの設定に使用するI2CとPCMデータ出力に使用するI2Sのふたつのインタフェースのドライバを用意してやる必要があります。

まずはI2Cから。LPC2388のI2Cの使い方は、AVRとほぼ同じだったのですぐに理解できました。TLV320AIC23Bのレジスタへの書き込みは、16bitでレジスタ番号と書き込むデータを指定するのですが、レジスタ番号を7ビット、データを9ビットで指定します。ついつい、8ビットづつだと勝手に思い込んでしまい、しばらくハマッテいました。

いつもの手順だと、まずI2Cを動かしてレジスタの読み書きができることを確認して一安心するのですが、TLV320AIC23Bのレジスタは書き込み専用で読み出しができません。そこでI2Sマスターに設定した後、Sample Rate Controlレジスタを書き込んでクロックを生成させてみることで動作確認しました。Sample Rateを44.1Kに設定すると、BLCKから12MHz, LRCIN/LRCOUTから44.1KHzが出てくることが確認できて、一安心です。

続いてI2S。こちらは44.1KHzとか48KHzというクロックに同期してPCMデータを送出するインタフェースです。したがって、このレートに間に合うようにデータを次々と書き込んでやらねばなりません。割り込みで処理するにしてもこのペースはつらいので、LPC2388では8レベルのFIFOが用意されています。FIFOに溜められたデータが順次送出されていき、FIFOの残りが指定されたレベル以下に達すると割り込みをかけることができます。FIFOの残りレベル数もレジスタで確認できるので、割り込み処理ではFIFOがいっぱいになるまで送出データを書き込んでやります。送出するデータがなくなったら、割り込みを禁止して演奏終了となります。今回は、レベルが3になったら割り込みをかけるように設定してみました。

MP3デコーダとつなげてやって、再生実験です。本来であればMP3のデコード結果から検出したサンプリングデータに基づいてTLV320AIC23Bの設定をする必要があるのですが、今のところCDから取り込んだ固定曲データですので、44.1Kで決め打ちにしています。以下、実行の様子。最初のmp3コマンドは、前回と同じダミー再生です。その後、codec init によりTLV320AIC23Bを初期化。playコマンドでデコードの結果生成された16bit PCMをI2SでTLV320AIC23Bに送っています。



MMcodec01につなげたヘッドホンからきれいに音が出てきました。嬉しい! 再生時間も元データと同じ16秒程度になり、割り込み処理も問題なく間に合っているようです。

CODEC実験準備

2009-05-18 09:22:58 | MP3プレーヤ
MMcodec01をLPC2388で使ってみるべく、14Pの接続コネクタを準備。



フラットケーブルで接続して、こんな↓感じ。ちょっとケーブル長すぎなんですが、秋月にちょうど14Pのケーブルがあったので、実験はこれで進めることにします。



両者の間のは次のように端子をつなぐだけ。モジュール使ったのでお気楽工作になってます。LPC2388のI2Cには、I2C0/1/2の3つのポートがありますが、I2C2を使っています。


LPC2388とTLV320AIC23Bは、両者ともI2Sのマスター/スレーブのどちらにも設定して利用することができます。MMcodec01にはクロック生成のためのXTALも載っているので、今回は上図の矢印で示したようにTLV320AIC23B側をI2Sマスターとして使用することにします。LPC2388をマスターにすると適切なクロックとWS信号を供給するための設定が必要になりますが、TLV320AIC23Bをマスターにした場合には必要な設定パラメータをI2Cで送ってやるだけですし、そのパラメータもデータシートに書かれているのでソフトの作成も簡単なのです。

じつはこの準備作業、すでにしばらく前に終えていたもの。現在、I2Sのコード作成中。次回は、その報告をするつもりです。

MP3デコーダ

2009-05-16 18:03:29 | MP3プレーヤ
ついついカメラの方に手が出てしまったので、今度はLPC2388の方でも少しまとまった作業を。。。

MMcodec01を実際に使うに先立って、MP3デコーダ・ソフト部分を用意しておく必要があります。デコーダ・ソフトとしてはATMELのAPノート等の作例に倣って、HelixのMP3デコーダを使うことにしました。このデコーダは、整数演算だけで済むコードも用意されているうえに、ARM用への最適化作業も施されており、ARM用のアセンブラコードも含まれているようです。APノートの作例を参考に必用なコードをダウンロードしてコンパイル。ダミーのmalloc/freeルーチンを用意してやる程度の移植作業で、TOPPERS/JSP上で動くデコーダを用意することができました。

次は再生に使うMP3データの用意です。適当なCDからWMP(Windows Media Player)を使って1曲取り込み。フォーマットとして mp3, ビットレートとして192Kを明示的に指定して取り込んでみました。192Kの再生ができれば、実用的なのではないかと。こうして用意したMP3データをSDカードに入れて、LPC2388で読ませればいいのですが、まだSDカードのハードは作成してありません。すでにインターフェース6月号でChaNさんがSDカードのためのハード/ソフトの解説記事を書かれていますので、このとおりのものを作成すれば良いでしょう。しかし、わたしがいま興味があるのはMP3のデコーダやI2Sの動作確認の方なので、とりあえずSDカードの準備は省略。LPC2388のフラッシュが512KBとデカイことをいいことに、MP3データはフラッシュ内に保持させて実験を進めることにします。

いくらフラッシュがデカイとはいえ、MB単位の曲データをすべてフラッシュ内に保持することはできません。Linuxのheadコマンドを使って、最初の400KBだけを無理やり切り出して使うことにします。
$ head --bytes 400k sample.mp3 > test.mp3

こうして切り出したファイルにも、必要なヘッダ情報は含まれているようで、Windows上では普通のMP3ファイルに見えますし、再生することもできました。400KBでおよそ16秒相当になっています。こうして用意したデータをダンプして、const unsigned char[ ] の配列に格納することで、フラッシュの領域にもっていきます。

曲データも用意できたので、こいつをMP3デコーダに喰わせてダミー再生する実験を実施してみました。つまり、MP3のデコードはするものの、その結果生成されるPCMデータは再生することなく、読み捨てるだけというプログラムです。以下、その実行の様子です。



  • 曲データの最初の方にはID3タグが含まれているらしく、デコーダは最初の4352バイトを読み飛ばしています。
  • 実際の曲データのフレームを見つけた時点で、レートやサンプル数等の情報が表示されています。
  • 以後、フレームを見つける毎にダミー再生をおこない、残りのデータ長を表示させています。
  • 残り226バイトになったところで400KBの境目に到達したためデコードが異常終了しています。

このように、およそ16秒の曲データを再生するのに、デコーダの実行には10秒弱しか要しないことがわかりました。これなら、残りのCPUを使ってじゅうぶんに再生処理が行えそうですね。なお、この実験は60MHzのクロックでおこないました。72MHzにすれば、さらに速く処理できるのかもしれません。

SXGAの画像とJPEG圧縮

2009-05-14 00:26:30 | CMOSカメラ
VGAで撮影した画像の確認が正常にできたので、続いてSXGA (1280*1024)で撮影した画像を確認することにしました。カメラのCOM7レジスタを0x00に設定してやることで、撮影画素数がSXGAに設定されます。プレビュー機能を使って、QVGAに縮小してのLCD表示はすでに問題なく動作しています。そこで、Codec path機能を使って取得したカメラの画像データをPC側に転送。今度は 1280 * 1024 * 2 = 2,621,440バイトのデータ量です。改めてデカイもんだと認識を新たにさせられます。

こうして取得した画像データを、JPEGへ変換してみたのですが、できた画像はこんな↓になってしまいました。



斜めに黒い線が入っていることから、水平方向の画素数が1280になっていないことは容易に推察されます。 COM7以外のレジスタの設定も調節しないと、カメラから正しく映像出力信号が出てこないということなのでしょうか? LCDでプレビューしていた時には何の問題もなかったのですが、ISIのプレビュー機能には指定された画素数よりも多くの画素データが与えられた場合に、自動的にクリッピングをおこなって表示データを生成するという、じつに賢い機能が用意されています。どうやら、このクリッピング機能が働いたために、LCD表示では何の問題もなく撮像できているように見えていたようです。

カメラのレジスタ設定をいろいろといじってみれば、正しく水平1280画素を出力する方法もわかるかもしれませんが、とりあえずJPEGへの変換処理を調整することで画像を修正することにしました。カメラからの出力は YCbCr 4:2:2形式ですので、出力される水平方向画素数は必ず偶数になっているはずです。1280よりも多いということで、試しに1282ドットと推測して変換してみたところ、ビンゴでした。↓



libjpegでは、JPEG圧縮する際に圧縮度と画質のトレードオフを調節するために、qualityというパラメータを指定することができるようになっています。前回の記事では書き忘れてしまいましたが、これまでJPEG化の際にはqualityを100に設定していました。上の画像もそうで、サイズは739Kもあります。これは、できるだけ画像の劣化を抑止して撮影画像を確認したかったためですが、反面圧縮率は悪くなっており実用的ではありません。試しに、qualityを75にして変換してみたのが次のふたつの画像です。





ファイルサイズを比較してみると、次の表のようになります。JPEGのありがたみが実感できました。

画像名画素数BMP形式quality=100quality=75
きなこチャンVGA900K224K35.4K
うりクンSXGA3.8MB739K106K


ようやくとカメラ本来の能力であるSXGA画像の確認ができて、自分ではけっこう満足しています。

画像の確認

2009-05-12 00:44:32 | CMOSカメラ
きょうはCMOSカメラのネタです。そういえば、トラ技来月号(2009年7月号)はCMOSイメージ・センサの特集のようですね。

これまでカメラで撮影した画像をLCDで確認することを中心に実験をしてきました。しかしながら、LCDの画素数がQVGA(320X240)であることから、カメラが本来撮影できる画像を縮小して表示しているというものでした。そこで、今度はカメラが撮影した元画像をPCに持ってきて確認する実験をしてみました。

AT91SAM9260のISIにはCodec pathと呼ばれる機能が用意されており、ISI_CR1レジスタを操作するだけで、ISI_CDBA (ISI Codec DMA Base)レジスタで指定されるメモリアドレス以降にカメラで撮像したデータを自動的に格納することができます。したがって、このデータを吸い出してやれば元画像を確認することができます。あいからわらずSDカードが使えないままなので、いったんボードをリセットしてSAM-BAを使ってSDRAMからデータを読み出してPCにもってくる方法でVGAで撮影された画像を確認することにしました。

カメラから送出される画像データの形式はYCbCr 4:2:2の形式を使っています。現在使っているカメラの設定だと、メモリ上には次のように画像データが展開されて入ってきます。

n番地Cr(i)
n+1番地Y(i)
n+2番地Cb(i)
n+3番地Y(i+1)


この形式では4バイト分の領域を使って2つの画素のデータを表現しています。Y(i)とY(i+1)が2つの画素それぞれの輝度を表現していますが、色差情報であるCb(i)とCr(i)については2つの画素に共通のデータを適用して、データ量を減らしています。VGA画像であれば、640*480*2 = 614,400バイトのデータとなります。

このままの形式では、パソコン上では表示できないので、変換が必要です。まず、それぞれの画素のデータを明示的に表現してYCbCrの順番に並び換えてやれば、次のように各画素には24bitのデータが使われ、2画素では6バイトのデータとなります。

n番地Y(i)
n+1番地Cb(i)
n+2番地Cr(i)
n+3番地Y(i+1)
n+4番地Cb(i)
n+5番地Cr(i)


このデータを色空間の変換式を適用してRGBデータに変換してやり、適切なヘッダを付与してやれば、RGB24bitのBMPファイルを作成できます。まずは試しに色空間の変換をおこなわずに無理やりYCbCrをRGBに読み替えてBMPファイルを作成してみたのが、この↓画像です。(ブログにアップするために、BMPからさらにPNGに変換してあります。)



オォッ! 色がオカシイのはあたりまえなのですが、どんな画像かはそれなりにわかるもんなんですねぇ。

あとは色空間の変換をおこなえばまっとうな色になるわけですが、BMPファイルを作成してもデータ量としては画素あたり24bit(3バイト)必要なので640*480*3=921,600バイトも必要になってしまいます。そこで、どうせならJPEGに変換してみようと考え、Linux上でJPEGファイルに変換するプログラムを作成することにしました。JPEGファイルを作成するにはIJGのlibjpegが使えます。そうは言っても、名前は知っていても実際には使ったこともないライブラリなので、ドキュメントにざっと目をとおすとともにこのサイトの使い方説明を参考にさせていただきました。みなさんRGBのビットマップデータをJPEGに圧縮する場合の例を説明されているようなのですが、libjpegでは入力データの色空間形式を指定することができるので、YCbCr形式のデータからJPEGファイルを作成することも可能です。具体的には、
input_components = 3;
in_color_space = JCS_YCbCr;

と指定し、上述のように画素あたり3バイトに並び換えたデータを jpeg_write_scanlines() に渡してやればいいだけでした。こうして変換してできたJPEG画像がこれ↓です。サイズは229,817バイトになりました。



今回の画像形式変換処理は、全てLinux PC上でおこなっています。libjpegの変換処理は整数演算でおこなえるようにコードが書かれているようなので、この変換処理をARM9ボード上にもっていくこともできそうです。そうすれば、撮影した画像をJPEGファイルとして保存でき、デジカメごっこができることになります。ぜひとも試してみたいのですが、そのためにはSDカードが使えなくなってしまっている問題をなんとか解決しないと。。