マイコン工作実験日記

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

AVRCPによるリモコン機能

2010-07-12 23:35:40 | MP3プレーヤ
中途半端になったままだったBluegigaで久しぶりに作業です。AVRCPの動作確認まではしたものの、それをリモコン機能として生かす段階まで進んでいなかったので、その機能を追加しました。

ヘッドセット側の操作スイッチは、ジョグスイッチになっています。↓押すとPLAY/PAUSE, 廻すとFOW/REW です。



Bluegigaはシリアルでの操作が可能なのですが、これまでは「LPC2388側のUARTポートに空きが無い」と思い、LPC2388とはつないでいませんでした。これが、リモコン機能を追加できていなかった大きな理由です。ところが、良く確認してみるとわたしの思い違いで、未使用端子上にUART3が残っていました。いつも参照している付録基板回路図上では、CN2の1番ピンから8番ピンまでが、AD0_0~AD0_7と記載されています。わたしは、勝手にこれらのピンがADC専用に割り当てられていると思い込んでしまっていたのですが、実際にはTXD3とRXD3が、AD0_2とAD0_3と重なっているので、これらを使うことが可能でした。慣れ親しんだATMELのSAM7にはAD専用に割り当てられたピンがあるので、ついついLPC2388でも専用ピンだと思い込んでしまっていたのでした。

そんなわけで、BluegigaのTXOをLPC2388のRXD3に接続。これで、AVRCPのメッセージを拾えるようになりました。ソフトの方では、WT32からのAVRCPメッセージ出力を監視するタスク(wt32_listener)を追加。ここで、Q-STEERリモコンを使う時と同様のイベントを発生することにしました。これだけの簡単な機能追加で、一応の操作がヘッドフォンからできるようになりました。



動作の様子を説明すると次のようになります。
  • ヘッドセットの電源を入れて、ジョグスイッチを押してしばらくすると、RINGを検出して再生音が流れ始めます。
  • もう一度押すと、PLAYイベントを検出しますが、すでに再生中なので、このイベントは無視します。
  • さらにもう一度押すと、PAUSEイベントが検出され、プレーヤは再生を停止します。
  • 次にスイッチが押されると、PLAYイベントが検出され、再生を再開します。
  • ジョグを廻すと前後の曲へスキップもできますが、ログは省略。


ちょっと気になるのは、始めの部分でジョグを押すと再生中にもかかわらず、PLAYイベントが検出されるところ。AVRCPで接続した時点で、ヘッドセット側とプレーヤ側の状態整合がおこなわれていないため、このような動作になってしまっていると推測されます。AVRCPには状態問い合わせのための手順が定義されていたと思うのですが。。。ヘッドセット側が状態取得要求を出していないのか、出しているけどWT32側がそれに応答できていないのか、このログだけからは判断できずにいます。

それでも、ヘッドセットだけで操作できるのはやはり快適です。ヘッドセットには音量調節のボタンもあるのですが、こちらはヘッドセット単体での制御になっており、AVRCPのメッセージは飛ばないようです。プレーヤ側の対応の有無を考慮すれば、ヘッドセット単体で制御してしまった方が無難な選択だというところでしょうか。実験して遊んでみたい側としては、ちょっともの足りませんが。。


平原綾香にはついていけない

2010-04-07 23:42:00 | MP3プレーヤ
今頃になってAACのデコード処理が、再生の速度に追いついていけない場合があることに気が付きました。いつも同じアルバムしか聞いていなかったため、動作確認が充分でなかったのです。SDカードを2GBから4GBに換えたのを機会にAACのアルバムを追加したところ、ブツブツとおと切れが発生することに気が付きました。何も問題無く再生できる曲もあるのですが、曲によっては音が割れるほどひどくなってしまうこともあります。これまでに調べた中では、平原綾香のJupiterが一番ひどく音割れします。

CODECへのDMA出力用のバッファを大きくして時間稼ぎをしたところ、かなり症状は改善されたのですが、平原綾香はなかなか手強いです。音割れまでには至らないものの、まだ音切れが生じます。バッファの容量にも限りがあるので、タスクの優先度を変更してみたりしましたがやはり音切れが残ってしまいます。

しょうがないので、禁じ手にでることにしました。LPC2388は72MHzで動作させているので、ほんとは MAM (Memory Acceleration Module)では4CCLK分のフェッチサイクルをMAMTIMに与えなければいけないのですが、これを3に変更。問題はきれいになくなりました。処理に時間がかかっている関数をフラッシュからRAMに配置しなおすことでも問題解決できるかもしれませんが、MAMTIMを3にしても今のところ不都合は生じていないようなので、これで様子を見ることにします。

クロックの同期

2010-04-03 19:51:40 | MP3プレーヤ
CODECへの出力に続いて、CODECからの入力についてもI2SのDMA対応を行いました。仕組みは出力の場合と基本的に同じなので、説明省略します。ただし、追加でもうひとつ対処した問題があります。それが、CODECとUSBのクロックの同期です。この問題も、放置したままになっていたので。。

これまで、CODECの動作クロックにはCODECモジュール上に載っている12MHzのクリスタルを使用していました。これを使うのが作業的には一番簡単だったからです。実用上も音楽プレーヤとして使っている場合には、これで何の問題もありません。ところが、FMラジオとして使って、USBスピーカへ出力する場合には問題が生じます。なぜなら、CODECがFMチューナからのアナログ信号をデジタル化する作業は、CODEC上の12MHzクリスタルをもとにして行われるのに対して、USBスピーカへの出力はLPC2388基板上のクリスタルをもとにして生成されたクロックを用いておこなわれるからです。両者が完全に同一周波数になっていれば問題ありませんが、個別のクリスタルがもとになっているのですから、そうはなりません。

実際にFM放送を聞いていると、ちゃんとキレイに聞こえていることもありますが、しばらくすると次第に音がかすれてきて、まるでチューニングがずれたかのように聞こえたりします。そして、しばらくするとまたキレイに聞こえるようになったりします。CODECとUSBの双方のクロックが互いにゆらぐので、こんな症状が発生するのでしょう。普段はヘッドフォンで聞いていることの方が多いので、USBスピーカ出力で問題があることはわかっていたのですが、放置したままになっていました。

問題の原因はCODECとUSBが違うクロックで動いていることですので、同じクロックを使って同期がとれれば、問題は解決できるはずです。そこで、↓のようにLPC2388とCODECの接続を変更しました。



CODECモジュールでは、MCKをモジュール上のクリスタルではなく、外部から入力することができるようになっているので、これを使います。LPC2388は72MHzで動作していますので、それを3分周した24MHzでタイマのMAT出力を反転させてやることで、12MHz出力を作りました。これで、きれいに問題解決できました。

DMA対応

2010-03-28 12:22:04 | MP3プレーヤ
「そのうちにやろう。」と思っていたものの、放置状態だった作業をようやく片づけました。その作業とは、I2SのDMA対応。ATMEL SAM7のとっても簡単に使えるPDCに慣れてしまったわたしには、LPC2388のGPDMAの使い方は恐ろしく複雑に感じられ、ずっと敬遠したままになっていたのです。そのため、I2Sの送受信はずっと割り込みでおこなっていました。

44.1KHzのサンプリングレートでは、毎秒44100回のデータ送受が必要になります。これだけの頻度で割り込みが発生すると72MHzのLPC2388でもかなりの負荷になってしまいます。割り込み負荷を軽減するために、LPC2388では8レベルのFIFOを持っており、割り込みをかけるトリガのレベルを指定できるようになっています。この機能を使うことで割り込み回数を半分程度にできるので実用上は問題無いのですが、それでもDMAを使えばさらに大幅にプロセッサの負荷を軽減できます。他の作業もひと段落ついた状態なので、ようやくとこの作業に着手することにしました。

改めてマニュアルを読んでみると、まぁ概要は理解できたのですが、マニュアルでのI2SのDMAに関する記述はとてもわかりにくく感じました。I2S側ではDMA1とDMA2がありますが、これをGPDMA側ではI2SのChannel 0とChannel 1と呼んでいるようです。用語統一するか、ちゃんと関係を説明して欲しいところです。
実際にコードを書いて、いちおう動くようにはなったのですが、それでもまだちゃんと理解できていないのが、DMACCxControlに設定するDWidth, SWidth, DBsize, SBsizeの値。DWidth, SWidthの方はワード単位でいいでしょうから2を選択。DBsize, SBsizeの選択の仕方がわかりません。I2S側からDMA転送要求を出すFIFOレベルの設定を4にしたので、こちらも転送要求数4に対応する1を選択してあるのですが、これが正しい考え方なのか、適切な値なのかがわかりません。試しにちょっと値を変えてみても違いが区別できませんでした。

DMAで用いるLinked List Itemは次↓のようにしてみました。

USB RAM上に用意したリングバッファに対応するように、LLIも循環リストとしてあります。このままだと、バッファ中のデータを繰り返し送信するだけですが、各LLIの転送が終了すると割り込みをかけることができますので、これを契機にバッファ中のデータを更新してやることで、音楽を連続して再生できるというわけです。つまり、LLIはいったん設定した後は更新しなくてもいいというわけです。

I2SはFMチューナからの受信の際にも入力として利用していますが、そちらについては次回の記事に書くことにします。

フォトフレーム機能の改善

2010-01-31 17:17:52 | MP3プレーヤ
これまでJPEG画像のフォトフレーム表示機能は、単純なスライドショー表示だけだったのですが、ちょっと手を加えてマニュアルでの画像送りと、スライドショー表示を切り替えられるようにしてみました。



操作のために画像表示の下に操作アイコンをもつバーを表示しています。右側の矢印をタッチすることで、次の画像を表示します。左側のプレーボタンを押すと、スライドショー表示に切り替わりボタン表示が一時停止(Pause)に変化します。



バーを常時表示しているのも邪魔なので、画像表示をタッチすることで、バーの表示をオン/オフできるようにしてみました。



動画ではわかりにくいのですが、再生している3種類のアルバムは、それぞれコーデックが異なっています。最初のStart WarsがMP3で、その次は M4A (AAC), そして最後の陽水はWAV非圧縮となっています。画面には映っていませんが、再生音はLPC2388につなげたUSBスピーカから出しています。

アルバム画像JPEG対応

2010-01-23 20:16:38 | MP3プレーヤ
そもそもアルバム画像を表示するためにJPEG伸長の再調査を始めたのですが、ついつい簡易フォトフレーム機能の追加に走ってしまいました。はい、ようやくとMP3の方のアルバム画像もJPEGファイルをもとに表示するように変更しました。見た目では何も変わっていないように見えるわけですが、↓の例ではJPEGを展開して75x72ドットの画像を表示しています。



WMPを使ってアルバム画像を取得すると、JPEGファイルが複数作成されます。実際には大小2種類の画像なのですが、なぜか違う名前でも用意されています。LCDに表示するには、小さい方のAlbumArtSmall.jpg を使えばいいのですが、この名前11文字には収まりません。



LFNを使えばファイル名で区別するのに何も問題が無いのですが、FatFsでLFNを使うとコード変換表のためにコードサイズが大きくなるので、これを避けるためにLFNを使わずに済ますことにしました。解決法は単純で、JPGファイルのうち、ファイルサイズの小さいものを使うというだけです。

さて、JPEG入れたらフラッシュの使用サイズが400KB超えてしまいました。libjpegはv7になってn/8 のスケール機能が使えるようになったのはいいのですが、その処理のためのコードがサイズ喰っているようです。



LPC2388はフラッシュ512KBありますからまだまだ残りはあるのですが、書き込みに時間かかるのがつらくなってきました。

フォトフレーム機能の追加

2010-01-19 00:08:17 | MP3プレーヤ
JPEG展開ルーチンはすんなり移植できたのですが、時計表示モードにフォトフレーム機能を追加するにはいくつか作業が必要だっとので、ちょっと時間を要しました。

まず考えなくてはいけないのは、画像の伸長/表示をどのタスクで処理させるかです。現在 main_taskで時計表示をおこなっていますが、このタスクに画像の伸長/表示処理を追加したのでは、その処理時間が問題となります。VGAサイズでも500msとかかかるので、その間 時計表示の更新ができないと時計の秒表示の進み方が不自然になってしまいます。そこで、伸長/表示はplayer_taskで処理させることにし、main_taskはplayer_taskに対して定期的に次の画像を表示する指示を発行することとしました。

次の問題は、表示の排他処理です。画像の展開/表示に時間がかかるので、その途中に時計表示の更新の要求が入ります。main_taskとplayer_taskの2つのタスクがLCDに対しての書き込みをおこなうことになりますが、表示処理の途中でタスク切替えが発生したのでは、表示がおかしくなってしまいます。そこでLCDへの描画処理の入り口と出口にセマフォの獲得/解放を追加することで、描画の途中で別のタスクがLCDにアクセスすることのないように排他処理をおこなうことにしました。JPEG画像の展開/表示は1ラスタ単位で処理されるので、この単位でLCDに対してのアクセスを占有しています。

他にもスタックサイズを調整したり、不要なルーチンをけずったりしてなんとか動くようになりました。



ポートレートの画像は、↓のように表示します。



動画で示した方がいいのですが、アルバム画像の表示までJPEG対応してから動画を撮影してみようかと思います。

JPEG伸長とフォトフレーム機能のイメージ

2010-01-11 22:17:53 | MP3プレーヤ
Linux上での動作確認でJPEG伸長に要するメモリ量は把握できたので、今度は実際にLPC2388上にコードを移植してみました。昨年、AT91SAM9260にCMOSカメラをつなげた際に同様の作業をしていますので、その時のインタフェース用コードを流用すればいいだけなので比較的スムーズに作業は終了。もともとlibjpegは移植性を重視した作りになっていますので、移植作業はわりと簡単です。主な作業としては、

  • jconfig.txtをjconfig.hにrenameして、編集。以下の行を追加。
    #define NO_GETENV
    #define MAX_ALLOC_CHUNK 20000
    #define ALLIGN_TYPE long
  • 浮動小数演算を使わないようにjmorecfg.hを編集して、DCT_FLOAT_SUPPORTEDの定義をコメント・アウトする。
  • stdio用のインタフェースであるjdatasrc.cを参考にして、FatFs用のI/Oインタフェースを作成する。
  • vmunix用のメモリ管理インタフェースであるjmemnobs.cを参考にして、メモリプールからの割り当てルーチンを用意する。
  • jerror.cを編集し、エラー出力をTOPPERSのsyslogに変更する。

といったところでしょうか。適宜、必要に応じてヘッダファイル部分を変更したりして、コンパイル、リンクを通します。まずは、デバックコンソールからの操作で展開処理が動くことを確認し、処理時間を測定してみました。まだ、画面への表示はおこなっていません。



アルバム画像のサイズであれば50msかかっていませんから、ほとんど処理時間は気になりません。VGAサイズだとちょっと待つ感じ。最後の1600x1200だと、ハングしたのかと心配になるくらいに長く感じられます。どうやら、フォトフレーム機能での表示にはVGAくらいの大きさが適していそうです。しかし、いずれの場合も画面サイズに合わせてスケーリングした結果、メモリは30KBも使わないことが確認できました。これなら、問題なさそうです。

表示サイズのスケーリングですが、上の処理例からもわかるように表示サイズを240x180に制限することにしました。実際のLCDの画面サイズは 320x240ですが、すでに画面を縦置き(portrait)で使うことにしてしまっているので、landscapeのVGAデジカメ画像を1/2にスケールしたのでは、表示が90度回転してしまうことになります。90度回転して表示させる処理は、LCDの表示モードを設定することで簡単に実現できるのですが、やはり見にくくていけません。そこで、画像幅を240ピクセルとすることで、縦置き状態で正常に画像を表示させることにします。当然、縦方向が余ってしまうわけですが、時計表示のモードにフォトフレーム機能を持たせることにすれば、余りの部分に時計表示を入れることができ、ちょうど良いと思われます。目標とする表示イメージはこんな↓感じです。


JPEG再考

2010-01-08 00:49:15 | MP3プレーヤ
年末に3つのデコーダの記事を書いて、3つのデコーダを担当するタスクは同時に走ることはないので、スタックを含むRAM空間を共有できることを説明しました。この記事を書いて、自分でももう一度確認してみようと思い立った事があります。それが、アルバム画像の表示についてです。MP3再生では、WMPがダウンロードしてくれた画像データを表示していますが、昨年の実装の際にはJPEG画像の表示にはメモリが必要となりそうなので、これを断念していったんBMPに変換したものを表示する仕様としていました。しかし、良く考えてみれば楽曲の再生中に画像表示が必要なわけではなく、アルバム画像を表示してから再生を開始しているので、デコーダが使うメモリ領域は画像表示に利用できます。つまり、JPEG展開に要するメモリ量がMP3再生に要するメモリ量と大差なければ実装可能なわけです。

さっそくJPEG展開に必要なメモリ量を確認してみました。今回は、libjpegの最新版であるrelease 7を使うことにします。まずはドキュメントの確認です。libjpeg.txtにはMemory usageという節があり、それによるとまずは静的におよそ24Kバイトを必要とし、それに加えて画像の幅に比例したバッファ領域を必要とするようです。おぉ、24KBというサイズは、ほぼMP3のデコーダが必要とするメモリ領域と同等です。あとは画像の幅に比例したバッファ領域ですが、アルバム画像は幅80ピクセル程度ですからこれも大して喰わないはずです。これならなんとかなりそうじゃないですか。

libjpeg.txtのメモリ必要量の記述はv6bの頃の数字のようですし、画像幅に応じたメモリ量を確認するためにも、実際に使ってみて消費されるメモリ量を確認してみました。まずはLinux上での確認です。djpegのソースとメモリ管理するjmemnobs.cにちょっとprintf追加してみました。



矢印の左側の数字が追加獲得したメモリ量、右側は合計量です。jpeg_start_decompressで、画像幅に応じた領域まで割り当てられているようですが、アルバム画像ファイル程度の大きさなら30KBもあれば充分なようです。それじゃ、ちょっと画像ファイルを大きくしてみましょう。



今度↑はVGA(640x480)のファイルですが、確かにメモリ使用量増えてます。さすがに64KBのうちの半分以上を使われるのはツライ。せめて半分の32KB以下に抑えたいところです。そこで....



LCDはQVGAですのでVGAの画像はそのままでは表示できません。そこで、展開時にスケーリングして半分にしています。縮小することでメモリ消費量も抑えられるのですが、アルバム画像の時と同量のメモリしか割り当てられていません。この理由については調べていませんが、元のJPEG画像のカラーモデルに違いがあったりするのかもしれません。それじゃ、さらに大きい元画像を試してみましょう。



djpegで指定できるスケーリングはn/8となっていますので、この大きさになると1/8にしないとLCDに収まりません。結果、表示画像サイズは200x150となり、QVGAを下回ります。その影響もあってか、メモリ使用量も減ってます。

大雑把に見て30KB~32KBのメモリ・プールを用意しておけば、アルバム画像だけでなく、デジカメ画像の表示もできそうです。こうなったら、簡易フォトフレーム機能も追加するしかないですね。

3つのデコーダ

2009-12-31 21:59:15 | MP3プレーヤ
MP3, AAC, WAVの3種類のファイルが再生できるようになったので、その処理方法について書いておくことにします。それぞれのファイル形式に対しては、それぞれのデコーダを用意しています。ファイルを再生する際に、そのファイルの拡張子から対応するデコーダを判断し、それを処理するタスクを起動するようにしています。例えば、MP3ファイルを再生中のタスク状態はこんな感じ。



AACファイル再生中は、こうなります。



もちろんwavdecoder_taskはWAVファイルの再生を担当するタスクです。それぞれのタスクは、デコード結果として44.1KHz, 16bit, 2チャンネルのPCMデータを出力しますので、それをCODECあるいはUSBスピーカに出力してやることで音が出ます。この出力を担当するのがpcmsender_taskです。

MP3やAACのデコーダは、それぞれその動作に20KB以上のRAM領域を必要とします。しかしながら、合計で40KB以上のメモリを必要とするというわけでもありません。なぜなら、3種類のデコーダは同時に動作することはないので、それぞれのタスクが使用するバッファやメモリを共通のメモリプールから動的に獲得/解放することができるからです。

同じ理屈で、3つのデコーダ・タスクのスタック領域を共通化させることも可能ですが、残念ながらTOPPERS/JSPのコンフィギュレータには、そのようなタスク間でのスタック共用を表現する能力は用意されていないようです。LPC2388にはRAMがたくさんあるものの、こうしてタスク数が増えてくるとスタックで消費されるメモリ量の割合が大きくなってくるので、これら同時に動くことのないタスクに対して独立した充分なスタック領域を割り当ててしまうのもモッタイナイです。そこで、ちょっとした dirty trickを施してみました。

上記のデコーダ実行時のps コマンド出力のStack表示はスタック残量/スタック割り当て量を示しています。aacdecoder_taskとmp3decoder_taskのふたつのタスクのスタック残量はゼロになっており、スタック残量が不足していることを示しています。本来であればこのような事態はマズイことなのですが、wavdecoder_task, aacdecoder_task, mp3decoder_taskの3つのタスクは同時に実行されることはないうえに、これらのタスクのスタック領域は隣接して割り当てられています。そのため、mp3decoder_taskのスタックは、割り当て量で足りなくなるとaacdecoder_taskのスタック領域を侵食し、さらにそれでも足りなくてwavdecoder_taskのスタック領域まで浸食しています。aacdecoder_taskも同様に自分のタスクに割り当てられたスタック領域では足りずにwavdecoder_taskのスタック領域を侵食しています。このような侵食が生じても、3つのタスクが同時に実行されるわけではないので、実害は無いというわけです。



現在のメモリ使用状況はこんな↑です。AACとWAV入れたらtextサイズが100KBほど増えました。

今年の作業は、ここまでとします。それでは、皆さん、良いお年を。