はじめに
ここでは音声データをSDカードに保存したり読み出したりの検討を行います。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。このページと同じ内容についてのご質問についてはロボット掲示板にてお願いいたします。
※以下の情報は2007年12月現在のものです。ご注意ください。
※このページではC言語の基本的な部分を理解している方向けに書いてあります。また、何回かMPLABの操作を行った方を対象にしています。それ以外の方はまず基本編を読んで下さい。
始めに
音声データは8kHzのPCMデータです。8kHzは時間間隔にすると125uSec毎となります。一方、microSDカードは1ブロック512バイトごとに読み書きするのが最適なのですが、SPIで1ブロック単位となると比較的時間がかかることが予想されます。
ここでは、簡単に今までのプログラム例だけでmicroSDカードに音声を読み書きできるかの検討を行います。
とりあえずのプログラム例
実際のプログラムを次のように作ってみました。ソースは2つに分割しています。(右クリックで対象を保存)
PCM_SD.c
PCM_SD_2.c
適当なプロジェクトを作って、2つのソースファイルを追加してください。(プロジェクトウィンドウが下のようになればOK。)
PCM_SD.cの方がメイン部、PCM_SD_2.cの方がSDカードとDCI関連の関数部です。
今まで行ったのが、SDカードへのブロック書き込み&読み出し、DCIでのPCM音源の入出力ですので、今回は簡単に、512バイトづつ録音して512バイトづつ再生するようなプログラムを作ってみました。
まず、DCI割り込み部です。前回と同じようにLM4930から生成される8KHzのフレーム信号に同期して割り込みが発生します。受信データはRXBUF0に入り、送信データはTXBUF0に入れておきます。この関数では、録音か再生かのフラグにより、データをSdBuffer[]という512バイトの配列に格納したりその配列からデータを出力したりします。SDカードの読み書きは8ビットつづですが、音声データは16ビットなので、SDカードのバッファ2バイトで1つの音声信号の値を保持します。
今回、録音と再生用に作った関数が次の通りです。rec_voice()関数は引数の秒数だけ音声を録音し、SDカードへ書き込みます。SdBlockWrite()関数は、SDカードのところで作ったCMD24関数です。SDカードへのCMD24はブロック書き込みを表します。単に名前をCMD24()からSdBlockWrite()へ変えただけで内容は変わりません。
秒数はキリのよい数値にならないので大体1秒単位で録音します。
再生も同じくCMD17の関数名をSdBlockReadに変えただけです。
今回、SDカードのブロック長は512に固定しています。
メイン部の中心部分です。
このようにrec_voice(3)で3秒間録音する、という動きになるはずです。
実際の結果
マイコン開発を行った方ならば大体よそうは付いていると思いますが、このような単純な方法ですと、SDカードへのアクセスが非常に遅いので、512バイト単位でブツブツと音が切れます。
SDカードへのアクセス関数の実行時間を測定(前後でLEDを変化させ、LED部をオシロで測定)してみたところ、読み込みも書き込みも512バイトで約1.5mSかかっていました。
これは録音(再生)時間にくらべて大きいのでブツブツ切れるはずです。
改良したプログラム
録音や再生はDCIの割り込みで処理するので、バッファを2つ用意し、交互に録音と書き込みを行えばよいのではと考えました。
実際のプログラムが次の通りです。(右クリックで対象を保存)
SD_REC.c
SD_REC_2.c
まず、行30のように512バッファを2つ用意しました。切り替えられるように2次元配列にしてます。SdBuffer[0][?]とSdBuffer[1][?]の2つの512バイト配列です。
割り込み部は次のようになります。交互にバッファを切り替える部分を追加しただけです。
録音部分です。大きく変わったのは、DCI割り込みが関数が終わるまで続いている点です。また、バッファの切り替えの記述も追加されています。
再生部も同様です。再生は、DCI割り込みを発生させる前にはじめのデータをセットしておく必要があります(行448)。ということでループの番号は1つズレます。
と、ここで実行してみて問題が発生しました。今回は常にDCI割り込みが動いているので、SDカードの書き込み時に割り込みが入ってしまうとSDカードの書き込みルーチンがズレて、SPI通信がコケるという事態になりました。
ということでやむをえず、SPI通信をする瞬間だけDCI割り込みを切りました。
行79の部分です。この関数実行中にDCI割り込みが入ると、行82の受信タイミングを外してしまい、行82でずっと止まってしまいます。ということで、行79の冒頭で割り込みを一瞬無効にしています。
当然、この方式だと、DCI割り込みをきっている間は音声が切れてしまうことになりますが、32mS中の1.5mSの中の何回かだけなので前よりは良いと思います。
ということで実行してみましたが、前のプログラムよりも格段に音切れはなくなりました。しかしまだ微妙に切れる感じです。もう少し追求してみると改善できるかもしれません。
2007年12月17日
|