ソフト 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 にも「こぼれて」います。