ありゃ、9月だ.STM32でDCC/DDCを作ろう! INDEXページへ
情報が間違っていても責任とらないし賠償とかしないです.
audio data flowを描写したく思います.始まりはUSB endpointの辺り、終着点はI2Sまで.
【endpoint 1の辺り】
USB EP1がaudio streamの入り口です.当deviceの最高レートは32bit384kHz2chですので、毎秒3.072MbytesがUSBを飛んできます.(4bytes x 2ch x 384k)
一方でUSB2.0のマイクロフレーム周期は125uSec=8kHzです.つまりpacketをなるべくせわしなく送ろうとしても毎秒8000個が上限だと言えます.
USBのpacketを観測すると、8000個のpacketに均等にバラかしてaudio streamが飛んでくるみたいです.だとすると、毎packetのaudio dataはどれだけかを計算できます.
以上から、STM32のcodeはaudio data処理をこのようにする必要があります.
・packet毎に、すなわち125uSec毎に割り込みがかかる
・44.1~384bytesのaudio dataを処理する必要がある @32bit
・このaudio dataはメモリ上のpacketバッファに格納されて引き渡される
・次の割り込みがかかるよりも速くaudio dataを何処かへコピーせにゃいかん
処理内容は何ですか? コピー先は何処ですか? などの疑問が浮かびます.
処理内容について
単純なコピーではなく、Little Endian→Big Endian変換しなくちゃいけません.ダラッとしたcodeだと384bytesの処理に125usecでは時間が足りないのですが、それはcodeの話題なのでここでは語りません.
なぜLEBE変換が必要なのか?
audio fileがwindowsのHDDの中に在る時、32bit整数はLittleEndianが主流です(WAVE formatはLEが主流だと思う).そのLEがUSBを経てSTM32まで辿り着いてもやはりLEのままです.ちなみにSTM32もLEです.
ところがI2SはMSB firstなのでSPIへaudio dataを渡す前にBigEndianに変更しておかないと砂の嵐になってしまう.
それでLE→BE変換がSTM32に必要なのです.
コピー先は何処ですか?
SPIへdataを送る前にリングバッファにaudio dataを蓄積します.そのリングバッファがコピー先です.
以上から、STM32はLEBE変換とリングバッファコピーを手際よく行う必要があります.
手際よくコピーするならDMAにやらせればよいのですが、ここではLEBE変換が伴うのでDMAが使えません.CPUのマシン語loopで処理しなくちゃいけません.
以上が、EndPoint1近傍での処理内容となります.
【リングバッファ】
リングバッファとは言ってもRAM上に確保されたリニア領域に過ぎません.アドレス操作によってリング状に見せかけているだけです.
リングバッファの容量は、現状のcodeでは、512x60=30720bytes確保してあります.512はdescriptorに記述したEP1の最大packetサイズです.
バッファと呼ぶからには何かからprotectするためのダムであるはずで、それはhostの動作が遅くなってレートが384bytes/Secよりも遅くなったりしたケースの緩衝役です.
挙動は、packet毎に384bytesがリングバッファに蓄積してゆきます.リングバッファが30720bytesですから、たかが80packetでリングバッファは満杯になってしまい、過去データを踏みつぶしてしまいます.どうするんだーっ?
その答えはSPIにさっさと読んでいただくのです.
SPIにどう読んでいただくのか?
SPI DMAでaudioサンプリングレートで読んでもらいます.SPIには専用のDMAが連結されています.
SPIの読みレートは何で決まるのか?
STM32の外部から供給される64Fsのbitclkです.bitclkは当device基板上に置かれたXTALです.DACがmaster clockだからです.
SPIが起動されるのはいつ?
再生先頭の蓄積データがリングバッファの半分に達した時にSPI DMAを起動します.こうするとhostのレート変動への耐性が最も強いはずだから.
リングバッファでもprotect出来ないほどhostレートが悪化したらどうするの?
そうならないようにfeedbackがあります.レート調整機構です.feedbackは別の投稿で説明します.ここではレートは概ね良好との仮定で先へ進みます.
【SPIの仕様】
STM32にはI2S peripheralも内蔵されているのですが、192kHzまでしか対応していないのです.384kHzは実現不能なのです.残念ですね.そこでいささか変則的ですが、似た信号仕様のSPIを流用します.
I2Sの信号仕様はこうなっていて、clkとdataだけでなくLRCKがあります.LRCKは32bit data区切りを表す信号です.
しかしSPIの信号仕様はclkとdataのみでLRCKが在りません.
なのでSPIが起動した先頭bitを検出して、そこから32bit毎にLRCKをON/OFFさせる回路を別途設ける必要があります.そのためにXC9536でbitカウントしてLRCKを生成しています.
なお、32bit384kHz2chのaudio伝送のためには、SPIのclkは24.576MHzが必要です.
SPIのバグかな
main()でSPIにこんな処理をやっています.STOP中はSPIをresetしています.
while(HAL_SPI_DeInit(&hspi1)!=HAL_OK);
__HAL_RCC_SPI1_FORCE_RESET();
__HAL_RCC_SPI1_RELEASE_RESET();
MX_SPI1_Init();
なぜresetしているのかというと、再生→停止→再生のように2曲目を再生した時にdataが化けて砂の嵐になってしまうからです.原因はSPI peripheralの中に1曲目の末尾が数bit残存していて、serial dataのword区切りが破綻してしまうんです.hardware的にSPIをresetするしかありませんでした.
以上で、USB EP~I2Sまでのデータの流れを説明できました.
かしこ
0 件のコメント:
コメントを投稿