ソフト S/PDIF トランスミッタ (7)

前回の話に関する補足です。
1 ブロックの内容を繰り返し送信する場合、 1 ブロックは 192 オーディオ・サンプルで構成されるので fs = 48 kHz では、4 ms 周期 (周波数 250 Hz) で同じ波形が繰り返されることになります。
ここで、送信バッファの 1 ブロックにちょうど整数周期分のオーディオ信号波形が書き込んであれば、それは 250 Hz の整数倍の周波数の信号波形として再生されることになります。
それを利用して、前回示した setup_spdif_txbuf() 関数のオーディオ・データの上位 8 ビット (MSByte) をゼロに設定している部分、

      spdif_txbuf[j++] = BMC_0000_0000; // MSByte(8 bit) of audio data (0x00)   

を下のように変更すれば、送信バッファの初期化後その内容を全くいじらなくても、左右 ch 間で 90° 位相の異なる 750 Hz 方形波を発生させることができます。

      if (1 & ((i + (k << 4)) >> 5)) { // 750 Hz square wave       
        spdif_txbuf[j++] = 0xB334; // MSByte(8 bit) of audio data (0x41:00 =  16640)
      } else {
        spdif_txbuf[j++] = 0xCCCA; // MSByte(8 bit) of audio data (0xC0:00 = -16384)
      } // if (1 & (i >> 5)) { ...

MS バイト内で偶数パリティにするために、再生される信号の正側の値を 0x4100 = 16640、負側の値を 0xC000 = -16384 と設定しており、正負対称ではありません。
STM32CubeMX を利用して適切にコンフィギュレーションすれば、必要な初期化コードを自動生成してくれて、DMA による SPI/I2S 送信を開始するためにユーザが追加すべきコードは次のような 1 行のみとなります。

// start I2S transmission by DMA (automatic repeat by 'circular mode')  
  HAL_I2S_Transmit_DMA(&hi2s3,         // handle of SPI/I2S for S/PDIF
                       spdif_txbuf,    // TX buffer (array of uint16_t)
                       W16_PER_BLOCK); // buffer size in 16-bit halfword

HAL_I2S_Transmit_DMA() 関数は DMA によって I2S 送信を開始するための関数で、その第一引数には SPI/I2S モジュール制御のためのデータ構造のハンドルを指定します。
これには STM32CubeMX で生成/初期化のコードが作られる hi2s3 (I2S3 用のハンドル) を指定します。
第二引数は送信データ・バッファへのポインタで、もちろんその内容は関数を呼ぶ前に初期化しておく必要があります。
第三引数はハーフワード (16 ビット・ワード) 単位で数えた転送回数です。
I2S モジュールを 32 ビット・データ・ワードのモードとして設定した場合は 32 ビット・ワード単位での転送回数となります。
SPI/I2S モジュールのデータ・レジスタ幅は 16 ビットなので、32 ビット・データ・ワードと設定した場合でも実際には 16 ビット転送が 2 回行われます。
HAL_I2S_Transmit_DMA() 関数は指定のデータ量の DMA 転送を 1 回行うための関数ですが、DMA コントローラに「circular mode」を設定してあれば、1 回の転送終了後に (ソフトウェアの介入なくハードウェアが勝手に) メモリ・ポインタの値および転送カウンタをもとの状態に戻してバッファの先頭から DMA 転送を再開します。
したがって、明示的に DMA 転送の一時停止/終了を指示するまでは連続的に DMA 転送を続けます。
そのため、いったん DMA による SPI/I2S 送信を開始すれば、送信動作自体についてはソフトウェアで面倒をみる必要はありません。
もちろん、「抜け」や「ダブり」なしにオーディオ・サンプルを出力し続けるには、ハードウェアの送信動作とソフトウェアの動作が協調する必要がありますが、その点については後の記事で述べます。