« 22khzステレオ8ビットのPCMオーディオ再生成功 | トップページ | LCDをつけた実装版PCMプレーヤーの設計 »

2009年1月22日 (木)

Mega328の44.1khz非圧縮オーディオ再生

ロジアナの威力を改めて実感(1/18/09)
 やっとのことで綺麗な音の再生ができて、色々調べる余裕が生まれた。ロジックアナライザーのタイミングチャートは、スタート直後はとれているが、再生中の状況は観測できていない。バッファーがどれくらい余っているのか、44khzのCDレベルのオーディオ再生にはあとどれくらいのバッファーサイズが必要なのか知りたいところである。ロジアナを接続しなおし、再生中にトリガーをかけて観測することにした。22khz8stereo_2

 プローブ点をバッファーの切り替え時点に置き(これに早く気がつくべきだった)、SDカードを読む前後にもプローブ点をつけて、トリガーをファイル読み込みのスタートにしてデータを採集する。出た。おお、しっかりファイルを読み込み、余裕でバッファーがスイッチされているのがわかる。おや、ファイル読み込みが瞬間で終わっているところがある。これは何だ。SDカードを読むSPIは何も動いていない。プローブピンに変なパルスが入ったのだろうか。

 あ、あ、あ、分かったぞ。今まで何を考えていたのだろう。SDカードのデータアクセスがセクター単位(このときは512バイト)であることを、すっかり忘れていた。今までのロジックアナライザーのデータは、DACのサンプリング間の極く短期間のファイルアクセスにばかり気を取られて、やれ、区間内に3バイト読んだ、2バイトだと危ないとか一喜一憂していたけれど、これはセクターを一気読みしている間の部分で、全体からみれば、データアクセスはバッファーサイズがセクターより小さければ、間歇的なのだ。まさしく、木を見て森を見ずという諺どおりである。

 セクターを読んだ後の、ファイル読み込みを示す短いパルスは、SRAM内のデータ移動だけの区間だったのである。ノイズでも何でもない。FatFSのソース(ttf.c)の中に、512バイトの構造体があって、これは何だろうと気になっていたが、実際に512バイトのSRAMエリアがあることを恥ずかしながら、ここで始めて理解した。

 お馬鹿な話である。コンパイルの後のSRAMサイズがバッファーを200バイトくらいしかとっていないのに、常に800バイト近くあるのを不思議に思っていた疑問は氷解した。構造体は必ずしも実メモリが存在しなくても定義できるので、てっきり仮想的なメモリ空間だとばかり思っていたのである。

 今になって考えれば、そんな仮想的な空間がこの狭いマイコンのメモリの中であるわけがない。知らないということは恐ろしいことである。それはともかく、このチャートで、今さらながら全体の動きを完全に理解できた。こうなると44.1khzのCDレベルのデコードに必要な要件はこのタイミングチャート上からすぐ計算ができることになる。

 チャートを見ると、ファイルのアクセスにかかっている時間は一目瞭然である。SDカードの1セクター、512バイトを読む区間は、readリクエストを出して、SDカードがACKを返し、実際に読み込みを終了して、読み込み関数f_readがアプリに帰ってくる時間である。これはクロック20Mhzで1300μsくらいだ。バッファーをセクターより大きくしない限り、ファイルアクセス時間がこれ以上長くなることはないだろう。

 一方、DACが送り出すバイト数は、完全にサンプリング周波数とデータ長で決まる。チャートを調べるまでもない。44.1khz16ビットステレオを、BU9480Fは、11.3μsごとに16ビット取っていくので、1300/11.2×2=229.4バイト以上のバッファーサイズがあれば、バッファーのアンダーランはおこらない。

 惜しい、Mega168でも、あと少しだった。思ったより少ない。Mega128の4KBのSRAMまで必要がなさそうだ。このところウェブで話題になっているのMega168の拡張版、Mega328がある。これならSRAMが2KBある。これで十分である。

 早速、ショップ(マイクロファン)にMega328を注文した(¥525)、ここは何度かMega168を買っているところで、事務所に近いので電話で注文し、直接取りに行っていたが、今回は気がせいているので通販にする。

 このあいだはオシロに助けられ、今度はロジアナで全貌が見えた。いやいや道具には金をかけるものである。ただ、実装のイメージがまだ固まらない。SDカードに入れた音楽CDを聴く機会がどれだけあるのかわからない。今の携帯電話で十分なのではないか。これまで作った温度ロガーや、LANによるAC電源制御、メトロノームの間隔を調べるリズムキャプチャー、自分から言うのもなんだが、結構みんな役に立っている。今度の機械はちょっと自信がない。

割込み駆動でアドレスの比較をしては駄目(1/19/09)Pcm_cons
 公開に向けてソースコードの整理を始めた。今はUARTがモニターで、PCの端末から再生するWAVファイルの指定をするようになっている。実用的とはいえないが、DACチップの動作例として少しは参考になるだろう。Mega328が来れば、CD音質の44.1khz16ビットステレオの再生が出来そうだ。これならネットに公開しても恥ずかしくない。22khzでも結構音は良いが、このあいだPCで音楽CDを22khz(正確には22.05khz)8ビットステレオにダウンコンバートしSDカードで再生してみたら、やっぱり見劣りがする。それはそうだデータ量は1/4になるのだから。思い込みかもしれないが、音の輪郭がぼける感じがするのだ。

 それはともかく、いたるところにデバッグコードが入り、いろいろ試行錯誤をしたソースコードである。整理をしなければ自分でも読みづらい。一番無駄なところは、ダブルバッファーの処理である。余計なフラグを使ってややこしくなっている。リングバッファーと違ってデータが入るファイルの読み込みポイントは2箇所しかないし、データを取り出すDACポインターのアドレスの比較をすれば、フラグを設けたりフラグのスイッチする必要がないはずだ。

 フラグをやめて、ポインターのアドレスの比較だけにする。プログラムがすっきりし、フラッシュサイズも数十バイト減った。意気揚々とファームを書き込み、動かしてみる。これが、うまく動かない。ハングアップか「ブー」というノイズだけになってしまう。他の部分は何も変えていない。ロジックは間違っていないはずだ。何度も紙にチャートを描いて確かめる。間違いない。

 コードを一旦、正常に動いていたときに戻し、少しづつ変更しておかしくなるところをつきとめていく。その結果、犯人は次のバッファー読み込みのため、DACポインターがそのブロックを去るのを待っているコードであることがわかった。ステートメントは
   while(ptr > &Buff[ダブルバッファーの起点]);
だけである。調べようもない簡単なステートメントである。頭を抱えた。

 次の朝、AVRのデータシートを眺めていてひらめいた。I/Oレジスターの16ビットデータの取り出しのところである。LowバイトとHighバイトは、必ず割込みを禁止にしてから取り出せとある。いわゆるアトミックオペレーション(非分割操作)というやつだ。そうだ、これに違いない。ポインターはアドレスなのでAVRでは2バイトだ。ここは頻々と割り込みが入り、DACのポインターであるptrが動いて、それを比較する部分である。確かめるまでもない。比較を1バイトのフラグに戻しテストしてみる。

 直った!ちゃんと元通り音を再生するようになった。やれやれこんなところに落とし穴があったとは。C言語なので割込み禁止命令を挿入するわけにもいかないし、ステートメント全体でかければ、このステップの意味がない(割り込んでくれてはじめてwaitが解ける)。ここでも真犯人は別のところにいた。

Mega328届く。苦もなく44.1khzを再生(1/20/09)
 SDカードのWAVデータを再生するプロジェクトもブレッドボード上のUARTインタフェース試作版では完成に近づきつつある。プログラムの改修で、こんなのは自分には関係がないと思っていた、アトミックオペレーションのトラブルを解決し何か急に自信がついた感じがする(乗りやすいタイプである)。勢いに乗って、周辺の機能を書き込んだ。

 まず、曲の再生中の中止である。ファイルを読むメインループにUARTのread監視関数を入れ、キーが押されたらループを抜けるようにする。簡単に出来た。調子に乗って中断機能を追加する。これも同じ関数をforループでまわし特定のキー(スペース)でこのループを抜ける。おお、これは便利だ。段々プレーヤーらしくなっていく。

 次はいよいよアナログ部だ。トランジスタのエミッタフォロワーをオペアンプのボルテージフォロワーにとりかえる。LPFはどうも今のPCスピーカー程度では、余り効果がわからない(耳が悪くなって高周波ノイズが気にならなくなったか)ので今のところ省略し、オペアンプに前に買ってあったオーディオ用のNJM4580を使う。

 最初は音が濁り、オシロで見ると明らかに発振を起こしている。ボルテージフォロワーは発振しやすいと言うし、具体的な対策は最近の位相補償がされているオペアンプでは難しい。アナログは一筋縄では行かない。色々試しているうち、DACの出力とオペアンプの間に直列に抵抗を入れると発振が弱くなることに気づいた。ここはハイインピーダンスなので相当高い抵抗を入れても問題ない。ためしに10kΩにしてみると見事に発振がとまったのである。理由はわからない。もしかしたら発振ではなく、DACからの干渉かもしれない。

 発振が止まったとなると音は、確かにトランジスタに比べてはっきりした音になったように思う。しかし何事も確認しないと気がすまない性格である。ウェブあたりでは酷評されている汎用のオペアンプLM358に替えてみる。なんだ、なんだ。ノイズもなくオーディオ用のNJM4580と変わらないじゃないか。増幅すれば変わってくるのだろうが、ボルテージフォロワーで使う限りは何の問題もない。オペアンプは2回路あるのでステレオにするのが楽だ。ヘッドフォンで聞くには少しゲインがありすぎるので、PCスピーカーに入れて色々な音を聞き比べる。Mega328

 夕方、郵便受けを覗くと、Mega328Pを入れたメール便が届いていた。予想より早い。夕食後、早速、換装にとりかかる。AVRSPは感心なことにちゃんとMega328Pを認識し、フューズビットの書き換えも全く問題ない。勢いにまかせてAVRstudioに持ち込む。328は168とピン互換なので何も替えないで良い筈だ。バッファーサイズを512バイトに上げ、サンプリングルーチンの22khz用をコメントアウトし、元の44.1khz用にもどし、コンパイルしてみる。

 あれえ、エラーが出る。何がエラーだ。何と、PD4とかPB0という汎用のピン名がエラーになっている。これはどうしたことだ。328用のヘッダーファイルを確認する。ない!全部、PORTD4、PORTB0という定義しかない。何でこんな変える必要もない(単に数字におきかわるだけ)基本的なところを換えるんだ、と悪態をつきつつ、定義し直し、コンパイル出来たので動かしてみる。ところが出来たバイナリはスタートメッセージをだすだけで先に進まない。リセットされてしまう。

 やれやれ、ピン互換じゃないのか。もういちどコンパイルメッセージを詳しく見ると警告の形で、割込みラベルが未定義というのを見つけた。ありゃりゃあ、ソフトUARTに使っているピンチェンジ割込みのエントリの名前が換わっている。何でこんなのを換えるんだ。それに警告はないだろう。これでバイナリを作られても困る。

 ぶつぶつ言いながらもう一度コンパイル。今度は警告メッセージは出ない。はやる心を抑えてPCから44.1khzのWAVデータをもう一枚のSDカードに移し、テストする。出た!これまで何度も挑戦して果たせなかった、44.1khz16ビットステレオの音である。やはりこれまでの22khz8ビットとは違うきめ細かな音で、シーッという量子化ノイズも全く聞こえない。やはり音の格が違う。素晴らしい。ロジアナでタイミングチャートを見てみた。バッファーをセクターと同じ512バイトにとったので、バッファーの切り替えの度にファイルを読んでいるのがわかる。うむ、想定どおりだ。満足、満足である。Pcm44_ok

 遂に、このプロジェクトも大詰めを迎えた。次のテーマをどうするか。こいつの実装版か。それも良いが、STARMを少しやりだしたし、H8のTOPPERSもある。AVGAもある。いやいや、やりたいことが多すぎる。

|

« 22khzステレオ8ビットのPCMオーディオ再生成功 | トップページ | LCDをつけた実装版PCMプレーヤーの設計 »

AVR」カテゴリの記事

コメント

ありがとうございます。
説明が不足していたかもしれません。Cで割り込み制御ができないと言うことではありません。あのステートメントは2バイトのアドレスを比較して、ぐるぐる回っているのですが、このときDACの割り込みがかかって、そのアドレスが変更されたときLowバイトと、Highバイトの処理の間は割り込み禁止にしておかないと、正しいアドレスにならないわけです。whileの前後に、cli(),sei()をはさめば、永遠にループは抜けられませんよね。アセンブラーなら何とかなりますが、今度はサンプリング周期が変わってジッターの原因になります。

投稿: がた老 | 2009年1月22日 (木) 15時41分

44.1kHzPCM再生おめでとうございます。

C言語からの割込み制御は出来ますよ。

avr/interrupt.hをインクルードして、cli()で割込み禁止、sei()で割込み許可です。


投稿: そら。 | 2009年1月22日 (木) 08時45分

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック


この記事へのトラックバック一覧です: Mega328の44.1khz非圧縮オーディオ再生:

« 22khzステレオ8ビットのPCMオーディオ再生成功 | トップページ | LCDをつけた実装版PCMプレーヤーの設計 »