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

今回はオーディオ・データの BMC エンコードと変換テーブルの作成についてです。
16 ビット・オーディオ・データを上位 8 ビットと下位 8 ビットとに分割し、それぞれを BMC 符号化して 2 つの 16 ビット・パターンを得ます。
このエンコードを 1 ビットずつ行っていたのでは効率が悪いので、要素数が 256、要素の値のサイズが 16 ビットの BMC 変換配列 (メモリ容量 512 バイト) をあらかじめ用意しておき、符号化規則に従って値を初期化しておきます。
実際の処理では 8 ビット・オーディオ・データをインデクスとして BMC 変換配列を表引きし、エンコードされた 16 ビット・パターンを得るようにします。
本来のパリティ・ビット部分は固定にしたので、代替のパリティ補正用の 16 ビット・パターンと合わせて、全体ではハーフワード (16 ビット・ワード) 4 つで構成されるサブフレームの中の 3 つのハーフワードを書き換えます。
固定パターンである VUCP ビットとプリアンブル部は「1 始まり」→「0 終わり」の偶数パリティ・パターンとなっていますから、オーディオ・データを符号化した 3 ハーフワードのパターンも「1 始まり」→「0 終わり」の偶数パリティ・パターンとする必要があります。
オーディオ・データの上位バイトをエンコードしたものは一番最後に送信される位置に配置され、VUCP ビットに隣接しています。
VUCP ビットは「1 始まり」なので、オーディオ・データの上位バイトを BMC 符号化したものが「0 終わり」であれば、うまくつながります。
そこで、BMC 変換配列には「0 終わり」のビット・パターンを格納しておきます。
そのビット・パターンには「1 始まり」、「0 始まり」の両方が含まれます。
上位バイトが「1 始まり」のパターンであれば、BMC 変換配列から読み出したオーディオ・データの下位バイトをエンコードしたパターン (「0 終わり」) がそのままつながります。
上位バイトが「0 始まり」のパターンの場合には、配列から読み出した下位バイトの「0 終わり」パターンを論理反転して「1 終わり」パターンに変換します。
同様にして下位バイトを符号化したパターンが「1 始まり」であればパリティ補正部は '00000000' を BMC 符号化したパターンを使い、下位バイトを符号化したパターンが「0 始まり」であればパリティ補正部は '10000000' を BMC 符号化したパターンを使います。
ここで、BMC 変換配列のサイズを 2 倍 (512 要素) にして、「1 終わり」パターンも格納すれば、論理反転動作は必要なくなりますが、配列のためのメモリ消費が増えることになります。 (512 バイト → 1 K バイト)
ここでは 256 要素の配列を使い、論理反転動作を行うプログラムとしています。
BMC 変換行列は初期化の後は内容は不変なので、RAM 上にも、FLASH 上にも置くことができます。
FLASH 上に置く場合には、当然「初期値付き配列」として定義しなければ役に立ちません。
RAM 上に置く場合には、初期値付き配列とするか、初期値を指定していない配列をプログラムで初期化するかの、2 つの方法があります。
512 バイトの RAM 上の配列を初期化するためには、FLASH 上に 512 バイトの初期化パターンを格納する必要があり、一般にプログラムで初期化する場合より FLASH の消費量が大きくなります。
(初期化定数付き) BMC 変換配列と、初期化プログラムを下に示します。

// uncomment following line to use BMC table in FLASH to save RAM
//#define BMC_TAB_IN_ROM 1

// Biphase Mark Code (BMC) table for byte (8-bit) data
// ('0' ending pattern)
#if defined(BMC_TAB_IN_ROM)
const uint16_t bmc8b[256] = {     // [ index ]
  0xCCCC, 0x4CCC, 0x2CCC, 0xACCC, // 0x00..0x03
  0x34CC, 0xB4CC, 0xD4CC, 0x54CC, // 0x04..0x07
  0x32CC, 0xB2CC, 0xD2CC, 0x52CC, // 0x08..0x0B
  0xCACC, 0x4ACC, 0x2ACC, 0xAACC, // 0x0C..0x0F
  0x334C, 0xB34C, 0xD34C, 0x534C, // 0x10..0x13
  0xCB4C, 0x4B4C, 0x2B4C, 0xAB4C, // 0x14..0x17
  0xCD4C, 0x4D4C, 0x2D4C, 0xAD4C, // 0x18..0x1B
  0x354C, 0xB54C, 0xD54C, 0x554C, // 0x1C..0x1F
  0x332C, 0xB32C, 0xD32C, 0x532C, // 0x20..0x23
  0xCB2C, 0x4B2C, 0x2B2C, 0xAB2C, // 0x24..0x27
  0xCD2C, 0x4D2C, 0x2D2C, 0xAD2C, // 0x28..0x2B
  0x352C, 0xB52C, 0xD52C, 0x552C, // 0x2C..0x2F
  0xCCAC, 0x4CAC, 0x2CAC, 0xACAC, // 0x30..0x33
  0x34AC, 0xB4AC, 0xD4AC, 0x54AC, // 0x34..0x37
  0x32AC, 0xB2AC, 0xD2AC, 0x52AC, // 0x38..0x3B
  0xCAAC, 0x4AAC, 0x2AAC, 0xAAAC, // 0x3C..0x3F
  0x3334, 0xB334, 0xD334, 0x5334, // 0x40..0x43
  0xCB34, 0x4B34, 0x2B34, 0xAB34, // 0x44..0x47
  0xCD34, 0x4D34, 0x2D34, 0xAD34, // 0x48..0x4B
  0x3534, 0xB534, 0xD534, 0x5534, // 0x4C..0x4F
  0xCCB4, 0x4CB4, 0x2CB4, 0xACB4, // 0x50..0x53
  0x34B4, 0xB4B4, 0xD4B4, 0x54B4, // 0x54..0x57
  0x32B4, 0xB2B4, 0xD2B4, 0x52B4, // 0x58..0x5B
  0xCAB4, 0x4AB4, 0x2AB4, 0xAAB4, // 0x5C..0x5F
  0xCCD4, 0x4CD4, 0x2CD4, 0xACD4, // 0x60..0x63
  0x34D4, 0xB4D4, 0xD4D4, 0x54D4, // 0x64..0x67
  0x32D4, 0xB2D4, 0xD2D4, 0x52D4, // 0x68..0x6B
  0xCAD4, 0x4AD4, 0x2AD4, 0xAAD4, // 0x6C..0x6F
  0x3354, 0xB354, 0xD354, 0x5354, // 0x70..0x73
  0xCB54, 0x4B54, 0x2B54, 0xAB54, // 0x74..0x77
  0xCD54, 0x4D54, 0x2D54, 0xAD54, // 0x78..0x7B
  0x3554, 0xB554, 0xD554, 0x5554, // 0x7C..0x7F
  0x3332, 0xB332, 0xD332, 0x5332, // 0x80..0x83
  0xCB32, 0x4B32, 0x2B32, 0xAB32, // 0x84..0x87
  0xCD32, 0x4D32, 0x2D32, 0xAD32, // 0x88..0x8B
  0x3532, 0xB532, 0xD532, 0x5532, // 0x8C..0x8F
  0xCCB2, 0x4CB2, 0x2CB2, 0xACB2, // 0x90..0x93
  0x34B2, 0xB4B2, 0xD4B2, 0x54B2, // 0x94..0x97
  0x32B2, 0xB2B2, 0xD2B2, 0x52B2, // 0x98..0x9B
  0xCAB2, 0x4AB2, 0x2AB2, 0xAAB2, // 0x9C..0x9F
  0xCCD2, 0x4CD2, 0x2CD2, 0xACD2, // 0xA0..0xA3
  0x34D2, 0xB4D2, 0xD4D2, 0x54D2, // 0xA4..0xA7
  0x32D2, 0xB2D2, 0xD2D2, 0x52D2, // 0xA8..0xAB
  0xCAD2, 0x4AD2, 0x2AD2, 0xAAD2, // 0xAC..0xAF
  0x3352, 0xB352, 0xD352, 0x5352, // 0xB0..0xB3
  0xCB52, 0x4B52, 0x2B52, 0xAB52, // 0xB4..0xB7
  0xCD52, 0x4D52, 0x2D52, 0xAD52, // 0xB8..0xBB
  0x3552, 0xB552, 0xD552, 0x5552, // 0xBC..0xBF
  0xCCCA, 0x4CCA, 0x2CCA, 0xACCA, // 0xC0..0xC3
  0x34CA, 0xB4CA, 0xD4CA, 0x54CA, // 0xC4..0xC7
  0x32CA, 0xB2CA, 0xD2CA, 0x52CA, // 0xC8..0xCB
  0xCACA, 0x4ACA, 0x2ACA, 0xAACA, // 0xCC..0xCF
  0x334A, 0xB34A, 0xD34A, 0x534A, // 0xD0..0xD3
  0xCB4A, 0x4B4A, 0x2B4A, 0xAB4A, // 0xD4..0xD7
  0xCD4A, 0x4D4A, 0x2D4A, 0xAD4A, // 0xD8..0xDB
  0x354A, 0xB54A, 0xD54A, 0x554A, // 0xDC..0xDF
  0x332A, 0xB32A, 0xD32A, 0x532A, // 0xE0..0xE3
  0xCB2A, 0x4B2A, 0x2B2A, 0xAB2A, // 0xE4..0xE7
  0xCD2A, 0x4D2A, 0x2D2A, 0xAD2A, // 0xE8..0xEB
  0x352A, 0xB52A, 0xD52A, 0x552A, // 0xEC..0xEF
  0xCCAA, 0x4CAA, 0x2CAA, 0xACAA, // 0xF0..0xF3
  0x34AA, 0xB4AA, 0xD4AA, 0x54AA, // 0xF4..0xF7
  0x32AA, 0xB2AA, 0xD2AA, 0x52AA, // 0xF8..0xFB
  0xCAAA, 0x4AAA, 0x2AAA, 0xAAAA  // 0xFC..0xFF
}; // const uint16_t bmc8b[]
#else
uint16_t bmc8b[256];  // BMC table in RAM
#endif // #if defined(BMC_TAB_IN_ROM)

// build biphase mark code table for byte data
#if !defined(BMC_TAB_IN_ROM)
void setup_bmc8b( void )
{
  int i, j, b, d;
  uint16_t m; // bit pattern of biphase mark coded data
  
  for (i = 0; i < 256; i++) { // loop for 8-bit number
    m = 0; // init biphase mark code bit pattern
    b = 0; // output bit data (assume 1 start)
    d = i; // input data
    for (j = 0; j < 8; j++) { // scan for 8-bit
// bit boundary edge (always flip)     
      m <<= 1; // shift left bit patten
      b  ^= 1; // flip always for bit boundary
      m  |= b; // bit boudary edge
// data bit edge (flip for (data_bit == 1))
      m <<= 1; // shift left bit pattern
      b  ^= (1 & d); // flip / not flip output bit
      m  |= b; // data bit edge
//      
      d >>= 1; // shift right input data
    } // for (j = 0; ...
    if (0x01 & m) { // test for "1 end"
      m ^= 0xFFFF;  // invert bits to force "0 end"
    } // if (0x01 & m)
    bmc8b[i] = m; // store result in table
  } // for (i = 0; ...
} // void setup_bmc8b()
#endif

「BMC_TAB_IN_ROM」というシンボルが定義されていると、FLASH 上に初期値付き配列として bmc8b[] を確保し、そうでなければ RAM 上に確保します。
Nucleo F401RE/F411RE 用プログラムでは、RAM 上に配列を確保したほうが、ごくわずかに速くなりました。 (RAM: CPU 負荷率 6.5 %、FLASH: CPU 負荷率 6.8%)
RAM 上に配列を取る場合の初期化のための setup_bmc8b() 関数は、当初は「1 始まり」パターンを作成するプログラムとして書いたものですが、それをそのまま利用しています。
最後に「0 終わり」パターンかどうかを検査し、そうでなければ論理反転しています。
最初から「0 終わり」パターンだけを発生させるプログラムを書くこともできますが、その場合は入力ビットの検査や出力ビットの操作の対象が LSB ではなくなり、少し面倒なので、このようにしました。
オーディオ・データのエンコード・プログラム自体については次回に述べます。