前々回で音が出たところまでレポした.
だが問題が2つあった.
1)win10で動かない →win10のデバドラを削除更新したら何故か治った
2)起動→再生→停止→再生 2度目以降の再生がうまくいかない
2番目の問題について、丸3日悩んでやっと治った.
2度目の再生をする直前に、バッファのポインタをゼロにしなくちゃいけない.
これを怠るとbitがズレて音がめちゃくちゃになってしまう.
なので3日間もバッファの挙動を調べていた.
余談だが、EZ-USBはstop/startの繋ぎが楽だった.理由はhardwareが剥き出しなのでbuffer emptyなどのstatus signalが1 clock精度で外部pinへ出てくるからだ.ところがSTM32はそうゆうのが全然出てこない.転送完了割り込みはあるけど、割り込みがかかった時点で数clock進んでしまっているはずなのでアテにならない.かったるい.
------
ポインタをゼロにする必要のあるbufferとre-start時の取り扱い
根元から順に.
1)16bit buffer
書く人: USB IF
re-start時: write pointer=0にする
方法: packet受信準備関数 USBD_LL_PrepareReceive() が用意されており、それでptrを引数渡しできるので無問題
2)32bit buffer
書く人: 16bit buf→32bit bufへコピーするprogram
re-start時: pointer=0
方法: 自分でそのようにprogramすればよいので無問題
3)SPI DMA
作業内容: 32bit bufから読んでSPIへ渡す
re-start時: read pointer=0
方法: DMA start関数 HAL_SPI_Transmit_DMA()はptr=0から開始するようになっているので無問題
4)SPI DMA転送完了割り込み
割り込み条件: 32bit bufのを半分/全部消化したら割り込み
re-start時: read pointer=0
方法: 3番で一蓮托生にptr=0になるので無問題
ここまで検証するためにbuffer dumpとか色々なことをやった.
全部無問題なのだが、2度目の再生で音が破壊される症状は変わらなかった.
1~4は、byte単位のズレの無き事を確認したのだったが、実はその先にbit単位のズレが在ったのだ.
5)SPI が再起動する時に、MSB 1stになってない ←原因はこれ
SPIはMSB 1stでdataを出力してくれるのが大前提.これが担保されないと、bit streamをブツ切りにする役割のLRCKが不正になってしまい音が破壊されてしまう.しかし2度目の再生時には何bit目から出てくるのかが不定なのだ.腰が抜けるぜ.1度目の再生時の末尾のdataがshift registerに残っているからだろう.
不満が募る.DeInit()をcallすればSPIはdisableにされる(EN=0).ならばEN=0にされた時点でSPI内部FlipFlopもclearしてもらいたい.2度目の利用時にはhardware resetを叩かなくちゃいけないっていうのはかなり不気味だ.
もっともSPI Slave TXなんつう珍妙な使い方をするからこういう地雷を踏むのだろうが.
後日考えるに、1度目の再生の末尾でSPI busyのままブチ切っているのが作法に反しているのだろうと思う.
------
5番を修正するための試行錯誤
要はSPIをどうやって初期化=resetするのか?である.
対策1)2度目の再生の先頭でSPIを初期化する.
HAL_SPI_INIT()みたいな関数があるのでそれをcallしてみる. →ダメ
対策2)2度目の再生の先頭でSPIをDeInit()Init()する.
DeInit()は構造体やbufferの解放をケアするためである. →ダメ
対策3)2度目の再生の先頭で、SPIのregisterを強制的にゼロにする. →ダメ
DeInit()Init()と絡めてみたがダメ.
ここまでやって、registerレベルで初期化してもまだSPI hardwareのどこかにresetされていない場所が密かに在って、それが原因でSPIの再起動時にMSB 1stで出てこないのだと推測された.
そこでDeInit()のsourceを見ると、hardware resetは一切やってないのだと判った.
datasheetには、各peripheral独立のresetが可能だと書かれている.
対策4)2度目の再生の先頭で、SPIをhardware resetする. →治った
具体的にはこんなcodeでSPIのresetと初期化を行う.
while(HAL_SPI_DeInit(&hspi3)!=HAL_OK); ←リソース開放
__HAL_RCC_SPI3_FORCE_RESET(); ←hard reset
__HAL_RCC_SPI3_RELEASE_RESET(); ←reset解除
MX_SPI3_Init(); ←通常の初期化
peripheralはブラックボックスで使いにくいな.
かしこ
0 件のコメント:
コメントを投稿