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

今回から、やっとソフトウェアの話に入ります。
まず、ブロック/フレーム/サブフレームの組み立てや BMC 変調の説明の図では、左側のビットが先に送信され、右側のビットが後に送信されるような配置で記されています。
実際にはオーディオ・データとの対応や、サブフレーム内のビット番号の割り振り方は LSB ファーストですが、これを普通に左側を MSB とみなして 16 進数に書き下し、MSB ファーストの設定の SPI/I2S モジュールで送信すれば、結果的に正しい順序でビット列が送信されることになります。
たとえば、プリアンブル「B」のビット列 '11101000' は 0xE8 と表現できます。
オーディオ・データ部分を全てゼロとして、RAM 上の 1 ブロック分の出力バッファを初期化するコードを下に示します。

// S/PDIF frame/block definition
#define FRAMES_PER_BLOCK   192 // frames per block
#define W16_PER_SUBFRAME     4 // 16-bit halfword count per subframe
#define W16_PER_FRAME        8 // 16-bit halfword count per frame 
// 16-bit halfword count per block
#define W16_PER_BLOCK     (W16_PER_FRAME * FRAMES_PER_BLOCK) 

// biphase mark code bitmaps
//
// bitmap for        0 0 0 0
//                   --__--__
#define BMC_0000 (0xCC)

// bitmap for        1 0 0 0
//                   -_--__-- 
#define BMC_1000 (0xB3)

// bitmap for        0 0 1 1
//                   --__-_-_ 
#define BMC_0011 (0xCA)

// bitmap for VUCP bits = 0 0 0 0
#define BMC_VUCP_0000 (BMC_0000 << 8)

// bitmap for VUCP bits = 0 0 1 1
#define BMC_VUCP_0011 (BMC_0011 << 8)

// bitmap for preamble 'B' (frame 0 L-ch indication)
//                         ---_-___
#define BMC_PREAMBLE_B (0xE8)

// bitmap for preamble 'W' (R-ch)
//                         ---__-__
#define BMC_PREAMBLE_W (0xE4)

// bitmap for preamble 'M' (L-ch)
//                         ---___-_
#define BMC_PREAMBLE_M (0xE2)

// bitmap for                0 0 0 0 0 0 0 0
//                           --__--__--__--__
#define BMC_0000_0000 (0xCCCC)

// bitmap for                1 0 0 0 0 0 0 0
//                           -_--__--__--__-- 
#define BMC_1000_0000 (0xB333)

const uint8_t sync_tab[2][2] = {
  { BMC_PREAMBLE_M, BMC_PREAMBLE_W }, // preamble M/W for L/R (not frame 0)
  { BMC_PREAMBLE_B, BMC_PREAMBLE_W }  // preamble B/W for L/R (frame 0 only)
}; // const uint8_t sync_tab[][]

const uint16_t vucp_tab[2] = {
  BMC_VUCP_0000, // bitmap for C=0, P=0
  BMC_VUCP_0011  // bitmap for C=1, P=1
}; // const uint16_t vucp_tab[]

// S/PDIF channel status bits
//
// b0     = 0  : PRO=0 (consumer)
// b1     = 0  : AUDIO#=0 (digital audio)
// b2     = 1  : Copy=1 (permitted)
// b3:4:5 = 0  : Pre-emphasis=0 (none)
// b7:6   = 0  : Mode=0 (defines byte1-3)
//
// b8:9:10:11:12:13:14 = 0  : General
// b15                 = 0  : Generation=0 (no indication)
//
// b16:17:18:19 = 0  : Source  Number=0 (unspecified)
// b20:21:22:23 = 0  : Channel Number=0 (unspecified)
//
// b24:25:26:27 = 4  : fs = 48 kHz
// b28:29       = 0  : Clock Acuracy=0 (Level II, +-1000 ppm, default)
// b30:31       = 0  : Reserved

// first 32 bits of channel status (copy permitted, fs = 48 kHz)
#define CH_STATUS_BITS (0x20000040UL)

// channel status data mask
#define CH_STAT_MASK (0x80000000UL)

// S/PDIF TX buffer (1 frame)
uint16_t spdif_txbuf[W16_PER_BLOCK]; 

// setup S/PDIF TX buffer in RAM for 1 block
void setup_spdif_txbuf( void )
{
  int i, j, k, cbit;
  uint32_t chs; // channel status shift register
  
  j = 0; // index of SPDIF TX buffer array
  chs  = CH_STATUS_BITS; // first 32 bits of channel status
  cbit = 0;  // assume C=0 for the very last subframe of prev. block
  for (i = 0; i < (FRAMES_PER_BLOCK); i++) { // i = frame index (0..191)
    for (k = 0; k < 2; k++) { // k = subframe index (0..1)
      spdif_txbuf[j++] = ( vucp_tab[cbit]      // VUCP of prev subframe
                         | sync_tab[0 == i][k] // preamble
                         );
      spdif_txbuf[j++] = BMC_0000_0000; // AUX (4 bit) + sub_LSB (4 bit) 
      spdif_txbuf[j++] = BMC_0000_0000; // LSByte(8 bit) of audio data (0x00)
      spdif_txbuf[j++] = BMC_0000_0000; // MSByte(8 bit) of audio data (0x00)   
      if (0 == k) { // frame boundary for VUCP nibble
        cbit = (0 != (CH_STAT_MASK & chs)); // extract ch status bit
        chs <<= 1 ;             // shift ch status reg for next frame
      } // if (0 == k) { ...
    } // for (k = 0; ...
  } // for (i = 0; ...
} // void setup_spdif_txbuf()

. . . <中略> . . .

int main( void )
{
. . . <中略> . . .
  setup_spdif_txbuf();
. . . <中略> . . .
} // int main()

プリアンブルなどの固定パターンのシンボル定義や、チャネル・ステータスの説明のコメントなどが長いですが、中心は出力バッファ spdif_txbuf 配列を初期化する setup_spdif_txtbuf() 関数です。
呼び出し後の spdif_txbuf
配列の中身は次のようになります・

fno    +0   +1   +2   +3   +4   +5   +6   +7
----  ---- ---- ---- ---- ---- ---- ---- ----
  0:  CCE8 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
  1:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
  2:  CCE2 CCCC CCCC CCCC CAE4 CCCC CCCC CCCC
  3:  CAE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
  4:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
  5:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC

        ... <中略> ...

 23:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
 24:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
 25:  CCE2 CCCC CCCC CCCC CAE4 CCCC CCCC CCCC
 26:  CAE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
 27:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
 28:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC

        ... <中略> ...

189:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
190:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
191:  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC
     ->||<---------------->||<----------------
             subframe 1          subframe 2

左端の 10 進数はフレーム番号 (相当) で、配列のインデクスそのものではありません。
前回説明したように、本当のフレームの区切りとは VUCP ビットの分だけズレています。
1 フレーム当たり 8 ハーフワード (16 ビット・ワード) となりますから、フレーム内のオフセット +0 から +7 までの値を 16 進 4 桁の数字として並べてあります。
フレーム内の最初の 8 ビットは直前のサブフレームの VUCP ビットを BMC 符号化したものです。
0xCC は 4 ビット入力 '0000' を「1 始まり」の BMC 符号に変換したもので、オーディオ・データ部および VUCP = 0000 の部分が 0xCC になっています。
0xE8/0xE2/0xE4 は、それぞれプリアンブル B/M/W です。
ほとんどのフレームの内容が

  CCE2 CCCC CCCC CCCC CCE4 CCCC CCCC CCCC

なので、多くの部分を省略してあります。
4 箇所に 0xCA がありますがこれは C=1 (および P=1) の部分で、チャネル・ステータスの

  • b2 = 1 (Copy permitted)
  • b25 = 1 (fs = 48 kHz)

に対応するものです。
本来のサブフレームの区切りとのズレにより、フレーム 2 とフレーム 25 だけでなくフレーム 3 とフレーム 26 にも「こぼれて」います。