2020年9月19日土曜日

STM32でDCC/DDCを作る方向で (62) AK4495でDSD 設計情報(最終回)

告知です.
コミケ99にて当社のDDC/DACを頒布いたします.
  日付   2021年12月31日(金) 東地区 テ-40b  東5ホール
  サークル名    bangflat
コミケにお越しの際はお立ち寄りいただけますとありがたいです.
商品紹介ページを作りました.
ーーーー
STM32でDCC/DDCを作ろう!    AK4495でDSDを聴こう!      INDEXページへ
DSD64/128の音が出たので、今回で最終回にしたいと思います.

走馬灯の如く過去を振り返ります.

一時期ラズパイオーディオをやろうとしたのだけど、あれってDDCの代用品のためにラズパイを活用してるだけじゃんと気づきました.windowsから制御できるUSB HiRes DACが一番使い勝手がイイじゃん.それでDDCを開発し始めました.2019年11月のことでした.
最初はEZ-USBを使って開発して音が出るところまで辿り着いたのだけれど、なんと、feedbackの実装がEZ-USBでは不可能と思われEZ-USBの使用を断念.
世界がコロナでうきゃきゃだった2020年4月からSTM32でDDCを実装するべく活動開始しました.

STM32CubeMXのUSB Audio Class 1.0サンプルをフルに参考にして現在に至ります.

------
以下では設計資料を書きます.
♪なにから伝えればいいのか? わからないまま時は流れて♪

当連載の41~57で説明した時点では、DSDは未実装でした.以下ではDSDを実装したversionを説明します.DSDへの変更点だけを説明します.なので大枠については連載41~57を見てもらうしかないですね.

回路図やcodeへのリンクINDEXページに貼っておきます.わたしは著作権を主張しませんけど、STMのsample codeのlicenseが何かしらあるんだろうね、でもそれがどーなってるかは知らないや.なお、情報の誤りやbugが在ったとしてもわたしは謝罪も賠償も一切しないので自己責任で素直に死ぬなりPCを蹴とばすなり好きにしてくださいね

あと、いずれこのプリント基板+DAC基板を通信販売しますのでその時にはよろしくです.今期中にはECを立ち上げたいなぁ.


【DDC device仕様】
ホストマシン       windows10のみ  (Linuxは未検証)
ホストデバドラ    win10標準のUSB Audio Class 2.0デバイスドライバ
ホストインターフェース      USB 2.0
USBクラス         USB Audio Class 2.0、ただしDSDではDoP
DACインターフェース       I2S
DAC制御インターフェース      I2C(400kHz)
DAC対応         とりあえずAK4495
DAC clock        当基板上のXTALがmaster clock
DAC BITCLK jitter      TBD      (あぁTIA欲しい.....)
対応フォーマット
     PCM     16/24/32bit   44.1/48/88.2/96/176.4/192/352.8/384kHz
     DSD     DSD64=2.8MHz、DSD128=5.6MHz   ※
レイテンシ    TBD
電源             USBバスパワー
消費電流       180mA実測(LEDインジケータ含む/DACを除く)
使用温度       常温

※1:DoPが動く再生アプリと動かない再生アプリがある.TuneBrowserはOKだが、foobar2000 Rev1.6ではNG.その他のアプリは試していない.

※2:WASAPIは動くが、ASIOは動かない


【回路・基板】
おおまかにはこんなモノが基板に乗っかってます.
STM32        USB処理
USB3300     USB2.0 highspeed interface
XC9536        I2S interface
RS232         内部状態モニタのため
PCM5101     モニタ用DAC    32bit/384kHz
ICについて散文的に説明しておきます.

STM32
STM32ラインナップではlow endな部類のSTM32F205を使っています.USB2.0が動くSTM32で一番下のランクじゃないかな.そんなんでDDCが動くのか?が開発のkey pointでしたけど、読者のご協力を得て結果的に動かせました.
ほとんどの処理をperipheralとDMAと割り込みにお任せなのが組み込みCPUのパターンなので、CPUはシーケンサかっつうぐらいの軽い仕事しかしません、一つの例外を除いて.
例外とは、Little Endian→Big Endian変換です.これだけはCPUに働いてもらっています.32bit384kHzではCPUの1/3ぐらいの労力を割いています.

USB Audio Class 2.0のIFをやってもらっています.sample codeのおかげで楽してます.sample codeはUAC1.0だったので、UAC2.0への変更はわたしがやりました.UAC2.0 descriptorの解説はこちらです.

DACをmaster clockで動かすので、USB streamはisochronousの非同期転送です.それゆえfeedbackが必須です.sample codeにはfeedbackが無かったのでそれもわたしが追記しました.1ms毎にレートの上げ下げをホストへ送信しています.→こちらで解説

STM32F205にはRAM容量が64/96/128kbytesの3種あります.いま使ってるのは96kbytesのものです.audio stream bufferにドカッと割り振って60kBぐらい使ってるかんじ.つまるところfeedbackの安定度が高ければもっと浅いbufferでも良いわけで、浅くしても大丈夫だと思っています.やってないだけ.
FLASHは70kbytesぐらいしか消費してません.

audio streamを出すのはSPI1/SPI3を使っています.PCMではSPI1のみ.DSDではSPI1をLchにSPI3をRchに使います.
SPIのサブ機能でそのものズバリのI2SもSTM32に在るのですが使っていません.事情は少々複雑で、SPI1にはI2Sが無いです.SPI2SPI3にはI2Sがありますがbitrate的に32bit192kHzまでしか出せません.なのでSTM32F205のI2Sはボツ.
SPIを使うとLRCKを自分で生成しなくちゃいけなくなります.そのためXC9536を使っています.

DSD対応のためにはDACに「DSDである」「PCMである」と教えてやらねばなりません.DSD対応DACとしてAK4495をひとまず使っています.AK4495のレジスタ設定をするためにI2Cを使います.
PCM5101のような、I2Sを繋げば動くDACならI2CによるDAC制御は不要ですが、AK4495の機能を使いこなすにはI2Cによる制御をしたくなります.

USB3300
STM32のプロセスはUSB2.0の480MHzを増幅できるanalogアンプを含まないのだと想像します.そのためかSTM32はハイエンドCPUに至るまでPhyを内蔵してないんです.仕方なくUSB3300を外付けです.USB3300の価格は中華通販で¥90ぐらいと安いのですがプリント基板はめんどくさいです.

XC9536
ロジック回路です.3つの機能を持ちます.
1)master clock及びbitclkを生成します.源発振は2つのXTALで、44.1kHz系には45.1564MHz、48kHz系には49.152MHzです.STM32に制御されてXTAL選択、分周比選択を行います.
2)LRCKを生成します.SPIの最初のbitから32bit毎にLRCKを反転します.
3)STM32に制御されてDSD/PCMによってRchの信号線をDSD/LRCKの切り替えをします.これはAK4495のDSD信号線仕様がそうなっている事に起因します.

最終的にXC9536の制御線は増えて8本になってしまいました.こんなに増える予定じゃなかったんですがね.全てSTM32が出力する信号です.
PC9_reset       STM32がロジックをresetするための信号
PC8_restart     STM32がSPI-DMAを起動した後に、SPIへSCKを供給させる信号
PB2_clksel       44.1kHz系か48kHz系かのXTALを選択する信号
PA12_clkbothon    再生開始したら不要なXTALをOFFさせる信号
PB14_FS0, PB15_FS1     bitclk等の分周比選択信号(4種類)
PC7_DSD        DSD/PCMの選択信号
DSDR             DSD Rch bit stream.DSD時はLRCK線にこれを出す.

XC9536の主要な出力線です.
I2Sの仕様は32bit LR interleavedです.
I2S信号ではないMCKは何かというと、DACが要求するsys clockなんです.44.1kHz系再生時には22.5782MHz、48kHz系再生時には24.576MHzを出力します.
SCK         SPIへ与えるmaster clock
BCK         DACへ与えるbit clock
LRCK       DACへ与えるLRCK
MCK        DACへ与える22.5782MHzまたは24.576MHz

clk生成するロジック回路は、SPIとDACにとっての最初のbitの諸事情によりBCK=SCKではダメで、timing合わせだの極性反転だのをグダグダとやっています.連載51回でも解説しましたがドロドロしていてマジメに解説する気が起きません.放置プレイ.

RS232、FT232RL
STM32のステートをCOM portへ出力させています.こんなものがterminalに表示されます.
----------F205 USB HS start------------
80 06 0100 0000 0040 -->device ctlsenddata len=18
00 05 001b 0000 0000 -->setaddress
80 06 0200 0000 0109 -->config HS ctlsenddata len=265
80 06 0302 0409 00ff -->string PRODUCT "STM32 Audio Class" 
80 06 0200 0000 0109 -->config HS ctlsenddata len=265
00 09 0001 0000 0000 -->setconfig
01 0b 0000 0001 0000 -->Set Alt0 STOP
a1 02 0100 1200 0100 get clock list
01 0b 0003 0001 0000 -->Set Alt3 32bit STOP
01 0b 0000 0001 0000 -->Set Alt0 STOP
21 01 0100 1200 0004 SET clock frequency Fs=384000 Hz STOP
01 0b 0003 0001 0000 -->Set Alt3 32bit
STOP PKTSTART WAIT0 DACPCM WAIT1 PLAYPCM PLAYING PLAYING
01 0b 0000 0001 0000 -->Set Alt0 STOP

【割り込みについて】
stm32f2xx_it.cに割り込みルーチンが記述されています.

HalfTransfer_CallBack_HS()
TransferComplete_CallBack_HS()
SPI-DMAの割り込みです.半分消化時点で生じる割り込みと、全部消化時点で生じる割り込みです.
消化物は、SPIで出力するaudio streamを積んだリングバッファです.
このルーチンでやっているのは2つ.
1)packetが来ないチェック
2)feedbackのため、リングバッファwrite pointerをsaveしておく

TIM3_IRQHandler()
1mSec周期割り込みです.
用途はひとつだけ、feedbackです.

TIM8_UP_TIM13_IRQHandler()
1Sec周期割り込みです.
UARTへ内部ステータスを出力するお仕事をしています.USB event発生時に逐次UARTへ出力するとUART業務に手間がかかってHOSTがtimeoutすることがあるので、event発生時はメモリに積むだけに止め、postprocess的に1Sec毎にUARTへ出力させています.

【DSDの音声波形と音質】
DDCはI2Sを出力するまでが仕事ですから、DSDの音声波形がどうなっているのかはDDCには無関係です.AK4495の責任区分です.AK4495出力波形を以下に示します.

↓まずは32bit 384kHz PCMの音楽波形、上がLch、下がRch
↓DSD64の音楽波形
↓DSD128の音楽波形
DSD64だけ波形がnoisyです.これは一体どうしたことか?
どうやらこれは、アンチエリアシングフィルタの設定がDSD64にマッチしていないのが原因らしい.DSD64のアンチエリアシングLPFには、cut-off 50kHzで、-20dB@100kHzぐらいが推奨されているんだってさ.ところが現状では100kHzで-2dBぐらいのLPFをAK4495の出力にかませているのでブロード過ぎるのです.DSD128ではエリアシングノイズが高域にシフトするのでナイスな波形になっている.そんな風に推測しています.

聴感ではどうか?
わたしは聴き慣れたDSD音源を持ってないので次の4つを比較しました.
1)CD rippingの原曲flac      16bit 44.1kHz PCM
2)1をwindowsがリアルタイムで変換したstream        32bit  384kHz PCM
3)1をDSD64に変換したstream        1bit  2.8MHz DSD
4)1をDSD128に変換したstream      1bit   5.6MHz DSD

DSD変換はこちらのアプリでやりました.

際立って音が異なるのは1でした.高音域が低レベルでおとなしい再生音です.
234は同じかなぁ.
DSD変換ソフトのフィルタ特性によって高音のピーキングが全然変わってしまうと思われますが、そこまでは試していません.


【STM32 ステート遷移図】
main()にステートの処理codeが記述されています.こんなステート遷移で動きます.

最初はSTOP状態です.packetが来始めます、ある程度バッファに溜まったらDACを設定して、SPI-DMAを起動して、再生開始になります.HOSTから再生開始の合図が来るのではなくて、packet drivenなわけですね.packetが来なくなったりFsが変更されたりしたら再生停止します.
ただし1ヶ所だけ、ゲロが出そうになるのを堪え切れない挙動があります.
再生開始してからDSD markerを検知してバッファをご破算、DAC設定をやり直し、というひでぶな遷移があるんです.PLAY→DSD→DACDSDのルートです.なんでこんな無茶な事をしているのでしょうか?
それは、windows HOSTがDoP markerを最初のpacketから出さず、数mSecぐらい後になってからDoP markerを出してくるんです.なんでそんな後出しじゃんけんをするのか、さっぱり訳が分かりません.それに対処するべく、再生開始から20mSecほどはDoP markerを監視し続け、DoP markerを検知したらご破算のやり直しDSDへ突入させるルートを設けてあるんです.
こんな死亡遊戯をしているので、DSDの再生先頭でプツッと音声にノイズが出ます.DACをmuteするなど、改善の余地があります.


【USBD_AUDIO_DataOut()】
stream packet処理の中心地である関数を説明します.
packet毎(125uSec)にここに飛んできます.
static uint8_t  USBD_AUDIO_DataOut()
{
  if (epnum == AUDIO_OUT_EP)      stream packetのEP1であるか?
  {
何bytesのstreamを受信したか?
  rcvcnt = USBD_GetRxCount(pdev, AUDIO_OUT_EP);

DoP markerをチェックする.
最初に、Alt=1 16bitならPCMです.Alt=3 32bitならPCMです.DSDである可能性があるのはAlt=2 24bitの場合です.
DSDであれば L[0],L[1],marker,R[0],R[1],marker という6bytes単位でstreamが入ってきます.3bytes毎にいるmarkerを監視します.markerは05,05,FA,FAのパターンで並びます.下記ではpacket先頭の連続する6つのmarkerをチェックしています.2連続packetをチェックします.
  if( Alt==0 ) { dsd=0; dsd_old=0; }
  else if( Alt==1 ) { dsd=0; dsd_old=0; } // 16bit
  else if( Alt==3 ) { dsd=0; dsd_old=0; } // 32bit
  else { // 24bit
  uint8_t x0,x1,x2,x3,x4,x5;
  if(haudio->buffer_writing==0) {
  x0 =  haudio->buffer_packet0[2];    // L(0)
  x1 =  haudio->buffer_packet0[2+3];  // R(0)
  x2 = ~haudio->buffer_packet0[2+6];  // L(1)
  x3 = ~haudio->buffer_packet0[2+9];  // R(1)
  x4 =  haudio->buffer_packet0[2+12]; // L(2)
  x5 =  haudio->buffer_packet0[2+15]; // R(2)
  }
  else {
  x0 =  haudio->buffer_packet1[2];    // L(0)
  x1 =  haudio->buffer_packet1[2+3];  // R(0)
  x2 = ~haudio->buffer_packet1[2+6];  // L(1)
  x3 = ~haudio->buffer_packet1[2+9];  // R(1)
  x4 =  haudio->buffer_packet1[2+12]; // L(2)
  x5 =  haudio->buffer_packet1[2+15]; // R(2)
  }
  if( ( x0==5 && x1==5 && x2==5 && x3==5 && x4==5 && x5==5 )
  || ( x0==FA && x1==FA && x2==FA && x3==FA && x4==FA &&
   x5==FA ) ) {
  if(dsd_old==1) dsd=1; // comfirm 2 sequential packets.
  else dsd_old=1;
  }
  }

packetをバッファへコピーします.
DSDの場合.markerは捨てて音声のみをコピーします.その際、コピー先バッファはL/Rchで別々にします.pbufDSD16LとpbufDSD16Rがコピー先です.
バッファ深さを越えたら先頭へ戻る.
  if(dsd==1) {
    中略
  while(i++<size) {
  lp[1] = *psrc8++; // L lower
  lp[0] = *psrc8++; // L higher
  psrc8++;
  rp[1] = *psrc8++; // R lower
  rp[0] = *psrc8++; // R higher
  psrc8++;
  *pdst16L++ = l;
  *pdst16R++ = r;
  if(pdst16L>=&pbufDSD16L[AUDIO_DSD_BUF_SIZE]) 
   pdst16L=pbufDSD16L; // destination buf full rollback
  if(pdst16R>=&pbufDSD16R[AUDIO_DSD_BUF_SIZE])
   pdst16R=pbufDSD16R; // destination buf full rollback
  }
  }

packetをバッファへコピーします.
PCMではL/Rchを1本のバッファへコピーします.pbufPCM32がコピー先です.
ただし、16/24/32bitによってコピーのやり方が異なります.
16bitでは、2bytesを4bytesへコピーします.下位2bytesはゼロ埋めします.
24bitでは、3bytesを4bytesへコピーします.下位1bytesはゼロ埋めします.
32bitでは、4bytesを4bytesへコピーします.
バッファ深さを越えたら先頭へ戻る.
  else {
  switch(Alt) {
    case 1: //16bit
    中略
    while(i++<size) {
    p[1] = *psrc8++;
    p[0] = *psrc8++;
    *pdst32++ = t; // LE to BE convert
    if(pdst32>=&pbufPCM32[ AUDIO_PCM_BUF_SIZE ])
pdst32 = pbufPCM32; // destination buf full rollback
    }
    break;
     case 2: // 24bit
    中略
     while(i++<size) {
     p[2] = *psrc8++;
     p[1] = *psrc8++;
     p[0] = *psrc8++;
     *pdst32++ = t; // LE to BE convert
     if(pdst32>=&pbufPCM32[ AUDIO_PCM_BUF_SIZE ]) 
   pdst32 = pbufPCM32; // destination buf full rollback
    }
    break;
     case 3: // 32bit
    中略
     while(i++<size) {
     *pdst32++ = __REV(*psrc32++); // LE to BE convert
     if(pdst32>=&pbufPCM32[ AUDIO_PCM_BUF_SIZE ]) 
   pdst32 = pbufPCM32; // destination buf full rollback
    }
    break;
     default:;
  }
  }

コピー先バッファポインタを更新する.
DSDでは、packet byte/3 進む.単位はbytes.
バッファ深さを越えたら先頭へ戻る.
  if(dsd==1) { // DSD
  haudio->wr_ptr_DSD_L += rcvcnt/3;
  haudio->wr_ptr_DSD_R += rcvcnt/3;
  if(haudio->wr_ptr_DSD_L >= AUDIO_DSD_BUF_SIZE) { // overflow
  haudio->wr_ptr_DSD_L -= AUDIO_DSD_BUF_SIZE; // rollback
  HAL_GPIO_TogglePin(BUF_WRTOP_PRT, BUF_WRTOP_BIT);
  }
  if(haudio->wr_ptr_DSD_R >= AUDIO_DSD_BUF_SIZE) { // overflow
  haudio->wr_ptr_DSD_R -= AUDIO_DSD_BUF_SIZE; // rollback
  }
  }

コピー先バッファポインタを更新する.
PCMでは、16bit/24bit/32bitによって進むbyte数が異なります.
バッファ深さを越えたら先頭へ戻る.
  else { // PCM
  switch(Alt) {
    case 1: //16bit
    haudio->wr_ptr += rcvcnt*2; // 16bit -> 32bit
    break;
    case 2: // 24bit
    haudio->wr_ptr += rcvcnt*4/3; // 24bit -> 32bit
    break;
    case 3: // 32bit
    haudio->wr_ptr += rcvcnt; // 32bit -> 32bit
    break;
    default:;
  }
  if(haudio->wr_ptr >= AUDIO_PCM_BUF_SIZE) { // overflow
    haudio->wr_ptr -= AUDIO_PCM_BUF_SIZE; // rollback
    HAL_GPIO_TogglePin(BUF_WRTOP_PRT, BUF_WRTOP_BIT);
    }
  }

再生先頭で、かつバッファ半分までstream dataが溜まったらSPI-DMAを起動する.
DSDでは、SPI1とSPI3のDMAを起動する.
ロジック回路をstartし、SCKがSPIへ与えられるようにする.
  if(dsd==1) { // DSD
if (haudio->wr_ptr_DSD_L >= AUDIO_DSD_BUF_SIZE/2) {
   if (audio_cmd == AUDIO_CMD_WAIT1) { // 1st time ?
// off no-use clock
HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 0);
// reset logic
HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 1);
// SPI start DSD
HAL_SPI_Transmit_DMA(&hspi1, pbufDSD16L, AUDIO_DSD_BUF_SIZE);
HAL_SPI_Transmit_DMA(&hspi3, pbufDSD16R, AUDIO_DSD_BUF_SIZE);
// start logic
HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 0);
audio_cmd = AUDIO_CMD_PLAYING; // PLAY
   }
}
  }

再生先頭で、かつバッファ半分までstream dataが溜まったらSPI-DMAを起動する.
PCMでは、SPI1のDMAを起動する.
ロジック回路をstartし、SCKがSPIへ与えられるようにする.
  else { // PCM
if (haudio->wr_ptr >= AUDIO_PCM_BUF_SIZE/2) {
   if (audio_cmd == AUDIO_CMD_WAIT1) { // 1st time ?
HAL_GPIO_TogglePin(BUF_RDTOP_PRT, BUF_RDTOP_BIT);
// off no-use clock
HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 0);
// reset logic
HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 1);
// SPI start PCM
HAL_SPI_Transmit_DMA(&hspi1, pbufPCM32, AUDIO_PCM_BUF_SIZE);
// start logic
HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 0);
audio_cmd = AUDIO_CMD_PLAY; // PLAY
  }
     }
}

次回のpacketを読む準備をする.
packetバッファは0と1の2本ある.書き込み中と読み出し中で操作を分けるため.
  if(haudio->buffer_writing==0)   {
  haudio->buffer_writing = 1;
  USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, 
haudio->buffer_packet1, AUDIO_OUT_PACKET);
  }
  else  {
  haudio->buffer_writing = 0;
  USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, 
haudio->buffer_packet0, AUDIO_OUT_PACKET);
  }

ステートを遷移させる.
最初のpacketが来たらSTOP→PKTSTARTへ遷移させます.
  if( audio_cmd == AUDIO_CMD_STOP) 
  audio_cmd = AUDIO_CMD_PKTSTART;
再生先頭でバッファが1/4埋まったらDACPCMまたはDACDSDへ遷移させる.
  else if( audio_cmd == AUDIO_CMD_WAIT0 ) {
  if( haudio->wr_ptr > AUDIO_PCM_BUF_SIZE/4 ) 
audio_cmd = AUDIO_CMD_DACPCM;
  else if( haudio->wr_ptr_DSD_L >= AUDIO_DSD_BUF_SIZE/4 ) 
audio_cmd = AUDIO_CMD_DACDSD;
  }
再生開始からおよそ20mSec経過したらPLAY→PLAYINGに遷移させる.
再生開始からおよそ20mSec以前にDSDを検知したらPLAY→DSDに遷移させる.
  else if( audio_cmd == AUDIO_CMD_PLAY ) {
  sumrcvcnt += rcvcnt;
  if(sumrcvcnt > AUDIO_PCM_BUF_SIZE*16) 
  audio_cmd = AUDIO_CMD_PLAYING;
  else if(sumrcvcnt > AUDIO_DSD_BUF_SIZE*16) 
  audio_cmd = AUDIO_CMD_PLAYING;
  else if(dsd==1) audio_cmd = AUDIO_CMD_DSD;
  }


【main()のwhile loop】
main()のwhile loopではステート遷移をしています.
  
switch(audio_cmd)     ステートで分岐
  {
   case AUDIO_CMD_CHANGEALT:
   audio_cmd = AUDIO_CMD_STOP;
break;
   case AUDIO_CMD_CHANGEFS:
   audio_cmd = AUDIO_CMD_STOP;
break;
Packet来ない監視は別の場所で---SPI-DMAのcallback関数---で監視しています.現状はバッファ半周の間にpacketが来なければアウトォ!という監視アルゴリズムなのですが、これだと判定が厳格すぎるかもしれません.HOSTのUSB portに別のdeviceを挿した時などに一瞬audio streamが途切れてアウトォ!と判定されてしまいがちです.
   case AUDIO_CMD_NOPACKET:
    audio_cmd = AUDIO_CMD_STOP;
    break;
   case AUDIO_CMD_STOP:
各種クリア
  packetcnt=0;
        dsd=0; dsd_old=0;
        haudio->buffer_writing = 0U;
        haudio->wr_ptr = 0U;
        haudio->wr_ptr_DSD_L = 0U;
        haudio->wr_ptr_DSD_R = 0U;
        sumrcvcnt = 0;
SPI1とSPI3のDMAを強制停止
    while(HAL_SPI_DeInit(&hspi1)!=HAL_OK); // reset SPI1
    __HAL_RCC_SPI1_FORCE_RESET();
    __HAL_RCC_SPI1_RELEASE_RESET();
    MX_SPI1_Init();
     while(HAL_SPI_DeInit(&hspi3)!=HAL_OK); // reset SPI3
     __HAL_RCC_SPI3_FORCE_RESET();
     __HAL_RCC_SPI3_RELEASE_RESET();
     MX_SPI3_Init();
logic回路制御
     // FPGA clock both on to avoid XTAL unstability
    HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 1);
     // stop FPGA
    HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 1);
     // FPGA PCM mode (not DSD)
    HAL_GPIO_WritePin(FPGA_DSD_PRT, FPGA_DSD_BIT, 0);
Fs制御
    switch(Fs){
     case 44100  : fbc=AUDIO_Fs_44_FB; Fs_ctl=AUDIO_Fs_44;  break;
     case 48000  : fbc=AUDIO_Fs_48_FB; Fs_ctl=AUDIO_Fs_48;  break;
     case 44100*2: fbc=AUDIO_Fs_88_FB; Fs_ctl=AUDIO_Fs_88;  break;
     case 48000*2: fbc=AUDIO_Fs_96_FB; Fs_ctl=AUDIO_Fs_96;  break;
     case 44100*4: fbc=AUDIO_Fs176_FB; Fs_ctl=AUDIO_Fs_176; break;
     case 48000*4: fbc=AUDIO_Fs192_FB; Fs_ctl=AUDIO_Fs_192; break;
     case 44100*8: fbc=AUDIO_Fs352_FB; Fs_ctl=AUDIO_Fs_352; break;
     case 48000*8: fbc=AUDIO_Fs384_FB; Fs_ctl=AUDIO_Fs_384; break;
     default:      fbc=AUDIO_Fs_48_FB; Fs_ctl = AUDIO_Fs_48;  break;
    }
     // reset logic
    HAL_GPIO_WritePin(FPGA_RESET_PRT, FPGA_RESET_BIT, 1);
     // logic Fs select
HAL_GPIO_WritePin(FPGA_CKSEL_PRT, FPGA_CKSEL_BIT, (Fs_ctl&4)>>2 );
     // FPGA Fs select
HAL_GPIO_WritePin(FPGA_FS1_PRT,   FPGA_FS1_BIT,   (Fs_ctl&2)>>1 );
HAL_GPIO_WritePin(FPGA_FS0_PRT,   FPGA_FS0_BIT,    Fs_ctl&1 );
       // restart logic
HAL_GPIO_WritePin(FPGA_RESET_PRT, FPGA_RESET_BIT, 0);
    break;
    case AUDIO_CMD_PKTSTART:
     audio_cmd = AUDIO_CMD_WAIT0;
    break;
     case AUDIO_CMD_WAIT0:
    break;
DACをPCMモードに設定する
     case AUDIO_CMD_DACPCM:
    setAK4495(0,0);
    audio_cmd = AUDIO_CMD_WAIT1;
    break;
DACをDSDモードに設定する
     case AUDIO_CMD_DACDSD:
logic回路のFsをDSD用に変更する
if(Fs==176400) Fs_ctl = AUDIO_Fs_44;  
if(Fs==352800) Fs_ctl = AUDIO_Fs_88;
     // reset FPGA
    HAL_GPIO_WritePin(FPGA_RESET_PRT, FPGA_RESET_BIT, 1);
       // FPGA Fs select
HAL_GPIO_WritePin(FPGA_CKSEL_PRT, FPGA_CKSEL_BIT, (Fs_ctl&4)>>2 );
HAL_GPIO_WritePin(FPGA_FS1_PRT,   FPGA_FS1_BIT,   (Fs_ctl&2)>>1 );
HAL_GPIO_WritePin(FPGA_FS0_PRT,   FPGA_FS0_BIT,    Fs_ctl&1 );
     // FPGA DSD mode
    HAL_GPIO_WritePin(FPGA_DSD_PRT,   FPGA_DSD_BIT, 1);
setAK4495( 1, Fs==44100*8 ? 1 : 0); 
// restart logic
HAL_GPIO_WritePin(FPGA_RESET_PRT, FPGA_RESET_BIT, 0);
    audio_cmd = AUDIO_CMD_WAIT1;
break;
     case AUDIO_CMD_WAIT1:
break;
     case AUDIO_CMD_PLAY:
    break;
     case AUDIO_CMD_PLAYING:
    break;
再生開始してからDSDだと判ったとき、バッファをご破算してDSDでrestartする.
     case AUDIO_CMD_DSD: 
    HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 1);
    HAL_GPIO_WritePin(FPGA_RESTART_PRT, FPGA_RESTART_BIT, 1);
SPI-DMA強制停止
     while(HAL_SPI_DeInit(&hspi1)!=HAL_OK); // reset SPI1
     __HAL_RCC_SPI1_FORCE_RESET();
     __HAL_RCC_SPI1_RELEASE_RESET();
     MX_SPI1_Init();
     while(HAL_SPI_DeInit(&hspi3)!=HAL_OK); // reset SPI3
     __HAL_RCC_SPI3_FORCE_RESET();
     __HAL_RCC_SPI3_RELEASE_RESET();
     MX_SPI3_Init();
各種初期化、バッファご破算
        haudio->buffer_writing = 0U;
        haudio->wr_ptr = 0U;
        haudio->wr_ptr_DSD_L = 0U;
        haudio->wr_ptr_DSD_R = 0U;
DACをDSDモードに設定するステートへ飛ぶ
    audio_cmd = AUDIO_CMD_DACDSD;
    break;
     default:;
  }


かしこ

0 件のコメント:

コメントを投稿