« 2009年12月20日 - 2009年12月26日 | トップページ | 2010年1月17日 - 2010年1月23日 »

2010年1月3日 - 2010年1月9日の2件の記事

2010年1月 8日 (金)

SDカードプレーヤーの演奏中の時間表示に成功

 昨年初めから取り組んでいた、SDカードを使った8ビットマイコンAVRによるリニアPCMプレーヤーは、ついに、演奏中にLCD(液晶ディスプレイ)へ経過時間を表示できるところまで成長した。

 OSは使っていない。タスクを3段構造にし、SDカード読み込みのバッファー空き待ち時間を利用して、LCDに残り時間と、プログレスバー(もどき)を表示する。

 今年のお正月は近親に不幸があったりして、気分が暗かったが、これですっかり明るさを取り戻した。典型的な躁鬱質なので今は意気軒昂、矢でも鉄砲でも持って来いという爽快な気分である(落ち込むのも早いが)。

OlimexからPOフォームが来た。ドリルサイズの割り増しをとられる(1/6/10)
 年末にボードファイルを送ったOlimexのプリント基板のPO(購入依頼)フォームが届いた。営業日数から言えば4日しかかかっていない。優秀である。Tsvetan君にちょっといやみなことを書いたので心配していたけれど、ちゃんと送られてきた。Olimexholidays

 しかし、POフォームを見ると、規格外のドリルサイズを使ったということで1ユーロの割り増しがついている。何い、そんなわけないぞ。あわててEAGLEを開いて、例のドリルサイズをチェックするコマンドを入れる(run drillcfg)。うむ、大丈夫なはずなのにどうして?

 念のため、ウェブを開いてOlimexがサポートするドリルサイズを再確認。ありゃあ、32milは駄目なんだ。そうだ、EAGLEのライブラリは、大抵の部品のドリルサイズが32milで、パッケージにするとき修正しまくったことを思い出した。

 違反の32milのドリルサイズは予想通り、新しく入れたパスコンのパッケージファイルだった。修正もれだ。やれやれ。ドリルサイズは、発送前にチェックしたが、32は大丈夫だとばっかり勘違いしていた。まあ1ユーロだから\140程度、たいしたペナルティではない。でも何となく腹が立つ。

 PDFファイルになったPOフォームを印刷し、カード番号を記入して早速ファックスした。2回目なので発注作業は、10分もかからない。これは楽だ。最近、ブログのメールにEAGLEを使っている人を対象に、「Olimexより安く基板を作ります。試しませんか」というメールが届いている。しかし、Webサイトもなく、メールだけというのが少し気味が悪い。それと少しでもOlimexに慣れてしまうと他に移りたくない気持ちもある。誰か試した人がいたら状況をWebに上げてもらいたいものだ。

 ファックスの次の日、例のfax receivedの2語メールが届いた。こいつはいつも恐ろしく早い。このまま行くと、1月下旬には基板が送られてくるだろう。そろそろ年末に成功したマルチタスクにしたソフトの仕上げを急がねばなるまい。

リニアPCMプレーヤーは、ついに経過時間を表示するようになった(1/8/10)
 前記事でマルチタスクが出来たと書いたが、出来ているのは読み出したセクターの数を表示しているだけである。このセクターの数から、時間表示にしなければいけない。それにプログレス(進捗)バーのようなものも出したい。

 LCDは、たったの2行16文字のキャラクターディスプレイである。しかも相当遅い。そのうえSRAMがもうぎりぎりだ。いつスタックのオーバーフローが起きるか分からない状況だ。フラッシュの方はまだ余裕があるが、メモリを消費しないコードにする必要がある。

 このミニLCDは、一般の5VドライブのLCDに較べると明らかに表示速度が遅く、コマンドを立て続けに送ると全く表示されなくなってしまう。少なくとも100ms以上間隔をあける必要があり、それでも薄くなる。この待ち時間のロジックも組まなければならない。結構面倒である。

 それに、今度のプレーヤーは、WAVファイルの沢山の形式に対応している(サンプリング周波数44.1、22.05、16.0khz、ステレオ、モノ、データ長16、8ビット)ので、
セクター数だけで時間が決まらない。あらかじめ計算しておかないといけない。

 SRAMを増やさないコードにするため、慎重にロジックを考える。プログレスバーは、時間を表示したあとの10キャラクター分を#や>などの文字で表示していけば、それらしい感じになるだろう。全体の演奏時間は、ファイルをオープンしたときにファイルサイズを取得しているので、ここから換算できる。10で割った数を基準にして1つづつ増やしていけば進捗が表せる。

 問題は、表示するタイミングである。バックグラウンドタスクは、ファイルがEOFになるまでループしつづける。的確なタイミングで表示間隔を明けていかないと、LCDは真っ白になってしまう。

 さらに、ファイルサイズはバイト数なので、下手をすると8桁台(数百万バイト)である。一方、セクター(512バイト)をDACが処理する時間はミリセカンドオーダー、この両極端な数字を使って、正確な分、秒にもどすのは神経を遣う。4バイトのデータは8ビットマイコンには荷が重い。うっかりすると有効数字が消えてしまうのは周波数カウンターのとき経験済みである。

 色々考えているうち、ふとアイデアを思いついた。進捗の単位をすべて表示する秒まで求めてこれに統一し、この数値が変わったときのみ表示すれば、表示間隔を気にする必要がない。そのためには一旦、表示した時の値はセーブしておいて、これが変わったときのみ次のデータを表示する。これだと表示は秒1回、または進捗が10%増えた時だけしか表示しない。これはうまいぞ。A1082601

 このアイデアを思いついてからは早かった。タイマーなどは必要ない。念入りにロジックを検討したお陰で、実際のコーディングはすぐ済んだ。表示部だけなら、わずか7行、初期設定を入れて全体でも10行余りの追加におさまった。

 秒まで出てくれば、分に直すのは60で割るだけである。最初、経過時間を表示しようとしたが、残りの演奏時間を表示する方が情報量が多いので、こちらにする。 A1082604

 わくわくしながらファームに書き込み、早速テスト。おおお、出た出た。市販のCDプレーヤーと同じように時間がカウントダウンしていき、プログレスバーの文字が少しづつ増えていく。嬉しい。単位を、#にしたり>にしたりしてチェックする。いやあ、もうこれは完全なプレーヤーだ。今の表示ディバイス(キャラクタLCD)ではこれ以上のことは望めない。

 22khz、8bitのWAVファイルや、短いファイルを入れてテストする。22khzは問題なかったが、短いファイルはプログレスバーが止まらない。暴走する。考えてみたら、演奏が終わっても表示の処理が残り、際限なくバーが延びていくためである。対症療法だが、ストッパーをつけて止める。

 昔から「段取り8分」という。計画を万全に立てたら80%は出来たも同じという意味だ。慎重に検討したお陰でプログラムはあっという間に動いてしまい、意気込んでいた肩の力がすっかり抜けてしまった。力が余ってしまって、ついでに前の5VのLCDを入れた1号機まで改修してしまう。これも問題なく動いた。いやいや今回はあまり楽しめなかったな。A1082603

 2号機の一台目は、娘がユーザーだ。こいつもバージョンアップしてやる。娘が喜んでくれた。こちらの苦労も知らないで今度は、「早送り」の機能もつけろと言われる。うーむ、出来ないことはないけれど、もっと他にやりたいこともあるからなあ。

ここに、演奏の経過表示をするリニアPCMプレーヤー2号機(ストロベリーリナックスのミニLCD使用)と、1号機(秋月などの定番LCDを使用)のソースコードを、AVRstudioのフォルダーをzipにして置きます。
1号機は、SDPCM328V4.zip  2号機は、mLPCM328V4.zip です。
ソフトのバージョンはV4です。
回路図等は、以前の記事を参照してください。なお、1号機の回路図には音量調整のVRがついていません。フォンジャックの直前につけてください。

「mLPCM328V4.zip」をダウンロード

「SDPCM328V4.zip」をダウンロード

| | コメント (6) | トラックバック (0)

2010年1月 4日 (月)

マルチタスクにしたプレーヤー3号機の開発状況

あけましておめでとうございます。
 がた老AVR研究所も発足以来2年が経過し、おかげさまでアクセスが月に1万アクセスを超える、そこそこのブログに育ちました。組み込みコンピューターを中心とした電子工作の気ままな制作の過程をなるべく詳しくご紹介することをモットーにしています。

 これは、所長が現役時代に大規模システムの開発を職業としていた経験から、自分のやっている試行錯誤は、たとえどんなくだらないことでも必ず誰かの何かの参考になるという自信にもとづくものです。

 このブログを知る知人からは、「レベルが高いのか低いのかわからない」「文章が長すぎる」などのご批評をいただいていますが、技術的な情報を提供するブログをもともと目指しているわけではありません。少し偉そうに言えば、所長のかっての専門分野である「問題解決」のさまざまなアプローチを、電子工作を通じて楽しみながら紹介できれば望外の幸せというものです。

 これからもよろしくお願い申し上げます。

さて、本記ですが、現在のプロジェクト、リニアPCMプレーヤー第三版の制作は、年末に身内に不幸があったりして目だった進展はありません。備忘録的にこれまでの出来事をご紹介しておきます。

2回目のOlimex基板発注(12/21/09)
 リニアPCMプレーヤーの2つ目の基板は、12/21にEAGLEのボードファイルを添付してOlimexに注文申請メールを発送した。すると親切にも例のTsvetan君から、

Did you read about our Holidays?
Tsvetan / Olimex

と返事が来た。
あわててOlimexのトップページを読み直す。

お休みは12/24から1/4までだよ、12/17以降の発注(PO)は、出荷が1月になるよ。というメッセージしかない。
(We are closed 24-th of December to 4th of January.  PO forms sent after December 17th will be shipped in January)

別に年内に基板が欲しいわけではない。
「お宅の休みが24日からなのは知っている。17日以降のPOが来月送りになるのもね。
何か言うことある?(Do you have any other suggestion?)」とちょっといやみな返事を出す。

それきり返事はなかった。もしかすると怒らせてPOformが送られてこないかもしれない。少し心配する。

 とにかく、これでLPCMプレーヤー3号機のハード工程は一段落した。電池基板ケースも2個分の切り出しが終わった。ミニサーキュラソーは快調だが、標準の丸鋸の刃でプラスティックを切ると荒らすぎて切り口がきたなくなり整形が面倒だ。あさりのない細い刃の方が綺麗に切れそうなので、入手するまでそれ以上の作業は中止することにした。しかし、お正月の準備が忙しく、なかなか買い物にいけない。

マルチタスク化はジッターが心配(12/24/09)
 演奏中に経過時間などを表示するマルチタスク化は、実現の見通しがたったが、いざ実際の開発に入ろうとして、マルチタスクのTick割り込みによって起きる、DACの割込みのゆらぎが気になってきた。いわゆるジッター(サンプリング周波数のゆらぎ)である。

 少なくとも、これまでのリニアPCMプレーヤーにはDACの割り込みを妨げる処理はない。理論上はCPUクロックのゆらぎ以外にジッターは起きない理屈である。音質が売り物の今度のプレーヤーだ。いくら発生頻度が低くても、マルチタスクにして経過時間を表示した結果、折角の音質が損なわれてしまうのでは改善の意味がない。

 CDプレーヤーから直接聞くより、一旦PCにリッピングしてディスクからWAVファイルを聞くほうが音が良いというのは、CDプレーヤーのドライブでエラーなどでジッターが起きるからだと言われている。高級な音楽CDドライブは必ずバッファーを設けてクロックを揃えたりする。

 しかし、今度のマルチタスク化によって、DACの割り込みは、SDカード読み込みの割込みで少ない確率ながら遅れる可能性が出てきた。これが音質を左右するほどの遅れになるのかが問題だ。

 ウェブの情報を漁る。まずジッターの単位が良く分からない。UIというのはUnit Intervalの略で、サンプリング時間を1としたときの、ゆらぎの時間だという。一方、psという単位もある。ピコセカンドという秒の単位だ。これは絶対値で、サンプリング周波数が変われば当然変化するので同じサンプリング周波数でないと意味がない。

 デジタルオーディオの世界で有名な、シーラスロジックのCS8416というトランシーバーICのジッターは、業界最高水準という200psを謳っている。この石のサンプリング周波数は192khzなので、UIに換算すれば、0.00004UIということになる。

 一方、現在のリニアPCMプレーヤーのクロックの水晶発振子の精度は数百ppm(百万分の1)で、DAC(BU9480F)のサンプリング周波数、88.2khz(2倍オーバーサンプリング)も、これに比例して変動し、UIでいうと0.0002(200ppmとする)、psで言えば、1/(88.2*1000)*200/100000=22000psである。かなり大きい。

 現在既にこの程度のジッターは出ている。従ってこれより少ないジッターに押さえることが出来れば、今回のマルチタスク化による割込み遅れは無視できると言えるだろう。業界最高水準と争うのは無意味だ。

 恐る恐る計算を始める。まず、割込みによって遅れる時間の見積もりである。WINAVRの説明によれば、割込みが起きて割込みプログラムにジャンプするとき、CPUはハードウエアでSREGレジスターをリセットし、ここで割込みが禁止になる。このあとレジスターなどをセーブし実際のユーザーの割込みプログラムに制御が渡るが、ISR_NOBLOCKオプションではジャンプの直後にSEI命令を置いて、すぐに割込みを許可にする。つまりジャンプ1命令とSEI命令の2命令分、クロック20Mhzで100ns遅れる計算である。

 DACのサンプリング間隔、11.33μsからみると、100nsは、UIで0.01となる。相当大きい。しかし、この遅れは常に起きるわけではない。ファイルアクセスが割込みによって2.9msごとに512バイトのセクターを読む時と、その前のバッファーが空くまで空振りする割込みの数回しかおきない。これが11.33μs間隔のDAC割込みとぶつかる確率は、相当低いはずだ。Dack_tick_2

 このぶつかる確率を正しく計算しようとして、このところずっと悩んでいた。まず、ファイルアクセスの開始を監視するTick割り込みは、これまでのチャートから見て、300から500μs間隔で十分余裕があると考えられる。クロック20Mhzの一命令の時間は50nsで、割込みの頻度は、Tickを300μsとして、300/0.05=6000命令に1回である。とても少ない頻度に見えるが、DACの割込みは11.33μsで226命令に一回、この割込みに先ほどのTick割込みが、どれくらいの頻度でぶつかるのかが計算できない。

 単純に考えれば、DAC割込みの26.54回に1回(6000/226=26.54)、Tick割込みがやってくるが、必ずしも常にDAC割込みと重複するわけではない。楽観的に考えれば、DAC割り込みは226命令に1回の頻度なので、1/(26.54×226)のような気もするが、この頻度は、Tick割込みの頻度と同じで、何か0=0を計算している感じだ。

 間違いないところでは、最悪で26.54回に1回(Tick割込みの度に必ずDACとぶつかる)、なので、この頻度をUIに適用するとそのUIは、0.01/26.54 = 0.0003で、水晶発振子のジッターとほぼ同じだ。Tick割込みの度に必ずDACの割込みと被るというのは、ちょっと考えられないので実際にはもっと低い。さっきの計算では、その1/226まで下がる。

 歯切れは悪いが、一応、割込みによるジッターは殆ど無視できるレベルにあると考えることにした。あとは実際に動かして聞いて見るしかない。ジッターの音に対する影響も明確な基準があるわけでもない。あまりこれにこだわることは止めることにする。

バックグラウンドタスクが動いた(12/30/09)
 ジッターの問題があって、実際のコーディングには、なかなか踏み切れなかった。折角苦労して作ってもジッターで音質が台無しになるかもしれない。うまく動いているのをいじりたくない気持ちも邪魔をしている。

 しかし、ハードの方は、Olimexに基板を発注してしまったので、やることがない。少しづつメモに書き出しながら、ロジックを練った。鉛筆で何度も書き直す。細かいフラグまで擬似コーディングし、満を持してAVRStudioに新プロジェクトを定義し、ソースコードを移す。

 バックグラウンドタスクの時間表示は手間がかかるので当面はアクセス回数を表示することにする。擬似コーディングを入念にやったお陰で、ソースの変更は簡単にすんだ。しかし、すぐ音を出す勇気がない。ぐずぐずしていたが意を決してファームを書き込み、祈る気持ちで電源を入れた。

 「LPCM PlayerV4」というWelcomeメッセージが出る。ソフトのバージョンはV4だ。ここまでは何も変えていないから動くのは当然だ。演奏開始ボタンを押す。鳴り出さない。心臓が鼓動を打ってきた。演奏中断を押し、再開ボタンを押す。おおお、SDカードのアクセスLEDが点き、演奏を開始した。

 しかし音は歪みだらけである。この前のアンダーランと同じ現象だ。全く動かないのならともかく、歪むと言うのはどういうことだ。Tickインターバルが想定どおりの時間ではないのか。考えたとおりに動いていないのか。

 時間的な要因なのでUARTで調べるわけにはいかない。ロジアナで実際のタイミングチャートを出した方が早い。I/Oポートに状態を出力するステートメントを要所に入れ、調べることにする。

 久しぶりにロジアナに火を入れる。プローブを割込みルーチンとファイルアクセス部分に入れてタイミングチャートを出す。出た出た。おやあ、ちゃんと想定どおりの動きをしているぞ。正確に300us単位に割り込みがあがり、バッファーが空くとファイルのアクセスを始めている。考えたとおりのタイミングチャートだ。何の問題もない。バッファーのアンダーランもない。何だ何だこれは。Lpcm4

 これで歪むというなら、この読んだデータが正しくないということだ。もう一度ソースを見直す。あっあっあ、割込みルーチンの書き出すバッファーがテレコになっている。これではDACが動いているときに、そこへ書き出してしまうことになる。こりゃ音が歪むのは当たり前だ。あわてて修正し、再度電源ON。おおお、綺麗な音に戻った。音質も全く前と変わらない。

 喜んでいたのも束の間、突然、演奏中の音楽が別の曲に変わる。何だ何だ、これは。こんな器用なことを誰がやる。SDカードを変えても同じように起きる。しかも同じところでは起きない。SDカードのアクセスがおかしい。  読み込むセクター位置が突然変わる感じである。これはどうもスタックのオーバーフローの疑いが強い。

 SRAMの使用バイト数は、1890バイトを越えて、90%を軽く越えている。うーむ、やっぱりこれだ。割込みプログラムは大量のスタックを消費する。こんどのSDカードアクセスの関数は、割込みプログラム上だ。ここのレジスターが破壊されれば、簡単に別のセクターを読む可能性はある。うむ、これに違いない。

 SRAMを減らす作業を開始する。これがなかなか減らない。1バイト変数を節約してもらちがあかない。結局、10ヶあるディレクトリリストを6ヶまで減らして、やっとこの現象は解消された。SRAMの使用率が90%を越えるとスタックオーバーフローが起きるようだ。

 曲リストをスクロールするときディレクトリの読み込みの頻度が多くなるが実用的には全く問題がない。定数ひとつの変更だけでリストの大きさを変えられるようにしておいて助かった。最初は面倒だがこういうときには威力を発揮する。

 演奏は正しくなった。しかし肝腎のバックグラウンドが動いていない。ファイルアクセス数は0のままである。試しに*を移動させるコードに変えてみる。少し待ち時間を入れてLCDが見えるようにする。

 おう、これは動いた。いや素晴らしい。バックグラウンドタスクをこなしながら演奏が続く。しかしコードをカウンター表示に戻すと0のままだ。xprintfがおかしいのか。そんなわけはない。気を落ち着けて調べ直す。変数をグローバル変数に替えている。

 あああ、なーんだ。わかった。ローカル変数にも同じ名前を定義している。これだこれだ。ファイルアクセス部分を割込みプログラムにして、変数をグローバル化したとき、ローカル変数の定義を移すのを忘れていた。良くあるバグのひとつである。A1042600

 ローカル変数の定義を削除してテスト。うむ、音楽を演奏しながら読み込んだセクター数が順調に増えていく。良いぞ。このLCDは表示時間が遅いので余り早くデータを送ると表示コマンドが立て続けに送られて結局何も映らない状態になる。ある程度、表示間隔を長めに(最低50ms)とる必要があるようだ。

 マルチタスク化は、これで峠を越した。あとは経過時間表示の計算と、プログレスバーなどの表示のロジックを考えるだけだ。このあたりはソフト開発でも最も楽で面白いところである。良い年を迎えられそうだ。

| | コメント (0) | トラックバック (0)

« 2009年12月20日 - 2009年12月26日 | トップページ | 2010年1月17日 - 2010年1月23日 »