SSブログ

RP2040で音の再生実験 その2 [DCCデコーダ]

 今日は箱根駅伝でした。沿道での感染はお控えくださいと主催者が案内していたような気がするので、見に行きませんでしたが、テレビで見ると、観戦者がいっぱいですね。まあ、オミクロン株って重症化しないようですし、リスクは低いってことですかね。
 で、本題ですが、年末に、やあさんさんから、以下のメールが・・・。面倒なので、ほぼ原文を書いてしまいますが、
ーーーーーーーーーーー
RP2040となり機能が増えているので、以下の2つの対応を順次、やっていければと思ってます。
【DMA伝送】
(1)PWMレジスタにDMA伝送する機能の追加。
(2)DMA用のバッファ(0.5秒分x2?)の定義の追加、バッファに書き出す処理の追加(今の割込みごとを、0.5秒分まとめてガツンと書くなど)
→割込みごとにPWMを書いているとCPUが食われるので、それを自動化する。DMA化することで同時発音数をさらに増やせるのではと。
あとのちのちのサウンドシーケンス(LokProgrammerのブロック図の状態遷移の実行)を実装するには必須になります。
【ファイルシステム対応】
(3)LittleFSでのサウンドデータアクセス
(4)LittleFSのファイルシステムに置くバイナリデータの作成ツールDSwav2の修正、バイナリ出力機能の追加(やあさんさんの仕事です)
ーーーーーーーーーーー

で、音声ファイルを16MByteも入れられる、Nano RP2040も送っていただいたので、さっそく実験です。
DSC03567.jpg

 (1)は難しいので、(3)、(2)の順に進めています。
 まず、(3)「LittleFSでのサウンドデータアクセス」ですが、1音だと、事前にファイルをオープンして、割込み中にファイルアクセスしてもとりあえずは音が出ましたが、複数音だと、うまくできませんでした。ということで、LittleFSを使ってのファイルアクセスはできましたが、現状のPWM割込み部分にすべて詰め込もうとすると、全然ダメです。
 次に(2)「DMA用のバッファ(0.5秒分x2?)の定義の追加、バッファに書き出す処理の追加(今の割込みごとを、0.5秒分まとめてガツンと書くなど)」ですが、最初リングバッファ作って・・・と半日ほど考えましたが、なんか良い案が思いつかずに眠気だけが出てきてたので、それはやめて、字面通りの方法をとることにしました。ただし、現状は以下のいくつか制約付きです。
・もちろんDMA非対応です。
・バッファの最初からしか音は鳴らせない(将来的には、オフセットをつけて、バッファの途中からならすことも考えようかとは思う)
・バッファの途中でファイルが終わったらあとは無音(そのうちリピートモードを実装しようかとは思う)
。音のchは6chとする(For文を追加するだけで処理時間が間に合えば音の数は増やせるが・・・)
 スケッチです。
https://desktopstation.net/wiki/lib/exe/fetch.php/timer_test75.zip
 まあなんとなく作れましたが、音の周波数やバッファの大きさによって、制御がバグったりしたので、PWMの割込みと、バッファ制御計算部分にどのくらいの時間かかっているのかオシロで確認しました。
 ところで、PWMの制御は、bool TimerHandler1(struct repeating_timer *t)
で行っており、中身はバッファA、バッファBの切り替えと1byteずつ音を出していくという作業をしています。
 また、バッファ制御計算部分は、void manage_wave()
で、行っており、この関数はLoop()から呼ばれ、バッファが切り替わっているフラグをつかんだら動きます。各chについて音を鳴らすフラグが立っていたら、音をコピーし始めます。バッファ分だけファイルからPWMのバッファへコピーします。次に呼ばれたときに音の再生が終わっていなければ、またバッファ分だけコピーします。

 で、まずはPWMの制御がどのくらいの時間かかっているのかオシロで確認しました。
DSC03569.jpg

 デバッグ用に出力ピンを設定して、黄色い波形のパルスが立っているところがPWMの制御を行っているところで、1.13us程度で終わっています。ただし、このインターバルに問題がありました。
 なんか、踏切の音は低いし、車内放送の声が低くて暗いなと思っていたら、出力周波数が微妙に低かったです。
 Setup()のITimer1.attachInterruptInterval(TIMER1_INTERVAL_us, TimerHandler1);
の、TIMER1_INTERVAL_usで割り込みのインターバルを設定しているのですが、実出力からみると、+5usのインターバルになるようです。インターバルは下記で設定しているのですが、-5usするように直しておきました。これで、車掌さんの声もやる気がみなぎるようになりました。(最初の方の#define)
//44k 22->17
#define TIMER1_INTERVAL_us 17
//32k 31 -> 26
//#define TIMER1_INTERVAL_us 26
//16k 62 -> 57
//#define TIMER1_INTERVAL_us 57
//8k 125 -> 120
//#define TIMER1_INTERVAL_us 120

 次に、バッファ制御計算部分ですが、こちらは、
void manage_wave()で行っています。
 こちらもデバッグ用に出力ピンを設定して、オシロで青いパルスが立っているところが処理中です。
 バッファは8KHzの時に1024Byteとして、一秒に7回ぐらいは反応してくれるようにしております。
 バッファ数は下記で定義しています。(最初の方の#define)
//#define BUF_LENGTH 32768
//#define BUF_LENGTH 16384
//#define BUF_LENGTH 8192
#define BUF_LENGTH 4096
//#define BUF_LENGTH 2048
//#define BUF_LENGTH 1024

また、周波数によって、読み出すファイルを変更しています。(Setup()内)
//String dir = "/8kHz_8/";
//String dir = "/16kHz_8/";
//String dir = "/32kHz_8/";
String dir = "/";
 ルートフォルダに44KHz版を入れています。

 この時、同時発音数を振って、処理時間のパルス幅を確認した表が以下になります。
データ数と処理時間.png

 44KHz、バッファサイズ4096で6音同時に出したとき、35.1ms処理にかかり、インターバルに対する処理の占有率は32%程度となります。(ただし、この後に、PWM制御の割込みインターバルがおかしいことを見つけたため、もう少し占有率は高いかも)
 まあ、まだバッファ制御計算部分は適当に書いていて、速くできそうな感じはあるので、あまり気になるところではありません。

 あと、波形を見ていて気になったのが、音が揺れることがあるのですが、PWM制御の割込みを見てみると不安定です。黄色いパルスで真ん中のパルスでトリガーをかけてそろえているが、右のパルスが二重に見えている→パルスが揺れている。
クロックがふらつく.jpg

 なんか、バックグランドでこそこそ他の処理が動いている感があります。
 例えば、今回のスケッチに入れているvoid serialEvent()
 をトリガーに確認してみると、その前後で、PWMの割込みが数クロック消えるのが見えました。
 また、Core1は使っていないからいいかなあと思って、Setup1(),loop1()にスケッチを移してみてみたら、PWM制御の1.13usが大きく間延びすることもあるなど、やばそうな現象が見られました。
 まあ普通に考えて、割込み内では、ほかの割込み禁止!の命令を入れるべきだよなあとそういえば思い出しました。なんて命令だろう・・・。
 PWMのところはPIO化か?ちょっと敷居が高そうだけど、そのうち。
 
 今後ですが、16bitのWav対応、音の切れ目ないリピート、二つの音をシームレスにつなげる方法などをやっていこうかと思います。

コメント(0) 

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。