ATtiny10 用プログラム (5)

「sin_tn10.c」プログラムの最初の部分にある #define で定義されるシンボルで設定される各種のモードについて説明します。
まず、

#define OSCCAL_ADJ (0)

は、アドレス 0x39 にある OSCCAL レジスタに設定されるデフォルト値に足しこむ補正値の定義です。
ゼロならばデフォルト値のままです。
サイン波の周波数が固定のモードでは、フェーズ・アキュムレータに対する増分 (PHASE_INC) の値を調整することにより周波数の微調整ができますが、周波数可変のモードでは、「f-number」テーブルは固定なので、周波数を微調整するには OSCCAL の値をいじる必要があります。
その周波数固定モードの場合の PHASE_INC の値の定義部分は次のようになっています。

// phase increment for 440 Hz @ fs = 15.625 kHz
//
// PHASE_INC = 440.0 * (2 ** 24) / fs
//           = 440.0 * (2 ** 24) * 512 / F_CPU
//           = 440.0 * (2 ** 24) / 15.625e3
//           = 440.0 * 1073.742
//           = 472446.4
//
#define CALC_PHASE_INC(f) ((uint32_t)(0.5+((f)*0x1000000UL*512.0/F_CPU)))

#if defined(F_CPU)
  #define PHASE_INC  CALC_PHASE_INC(440.0)
  #define PHASE_INC2 CALC_PHASE_INC(440.0)
#endif // #if defined(F_CPU)

#if !defined(PHASE_INC)
#define PHASE_INC  (472446UL) // for F_CPU = 8 MHz
#define PHASE_INC2 (472446UL) // for F_CPU = 8 MHz
#endif

CPU クロック周波数を Hz 単位の値で表したシンボル「F_CPU」が定義されていると、増分値の定数を計算するためのマクロ「CALC_PHASE_INC()」が使われます。
「F_CPU」は AVR Studio のメニューの「Project / Configuratio Options」を選択して開くダイアログの「General」ページの「Frequency」のフィールドで設定します。
「F_CPU」の定義がない場合には、F_CPU = 8000000 を仮定してあらかじめ計算しておいた整数値を設定します。
マクロ「CALC_PHASE_INC()」の定義中には実数演算が現れていますが、これはコンパイル時に計算される「定数式」であり、最終的には整数の定数に変換されるので、実際の ATtiny10 のプログラム中に実数演算ライブラリがリンクされるようなことはありません。
マクロ「CALC_PHASE_INC()」の引数として、希望の周波数を (小数点を含む) 実数値として Hz 単位で指定します。
デフォルトでは、サイン波 2 出力ともに 440.0 Hz の指定になっているので、必要があれば、その値を変更してください。
周波数可変モードでは、前回述べたように 8 ビット AD の変換値 0 〜 255 で 88 鍵ピアノの音域程度をカバーするような設定にしてあります。
AD 変換値が 1 変化すると、音程は 1/3 半音変化し、実際にカバーする範囲は 88 鍵には少し足りなくて約 84 鍵分となります。
f-number テーブルは、メモリが少ないこともあって、1/6 半音ステップ、つまり、1 オクターブで 72 ステップ刻みにしてあります。
AD 変換値 0 〜 255 を f-number テーブルのインデクスに変換する際のオフセットが、

#define PITCH_OFS  (27)

です。
AD 変換値は 2 倍され、このオフセットが加算された上で 1/3 半音ステップでテーブルをアクセスすることになります。
この値を 72 増やせば、1 オクターブ上の音程で発音することになります。
f-number テーブルは CPU クロック周波数に合わせて専用のテーブルを作成するわけではなく、どの CPU クロック周波数に対しても共通の固定のテーブルを使うようになっているので、出力周波数は 1/6 半音ステップでしか変化できません。
1 半音は約 5.9 % の変化ですから、1/6 半音は約 1 % となり、周波数誤差は最大 ±0.5 % となります。 (±8.3 セント)
誤差ゼロで、ぴったりと合わせたい場合には CPU クロック周波数の方を調整する必要があります。
下のように、「USE_SIGMADM」というシンボルを定義すると、ピン 1 からは 1 次シグマ・デルタ変調されたサイン波が出力されるようになります。

#define USE_SIGMADM // 1st order Sigma-DM PWM

ピン 3 からの 2 波目のサイン波には影響しません。
スプリアスの影響を少なくするため、固定周波数モードで、128 ポイントで 1 周期となるように PHASE_INC を 0x20000 に設定した場合の波形を WaveSpectra で観測した FFT 結果を下に示します。
WaveSpectra の設定は

  • 16 ビット/96 kHz サンプリング
  • 128 K (131072) ポイント FFT
  • Blackmann-Harris 7 term 窓
  • FFT 300 回の平均

です。
まずは、シグマ・デルタ変調なし、つまり、普通の PWM のみの場合です。

サイン波の周波数は (公称) 8 [MHz] / 512 / 128 = 122.1 [Hz] です。
3 倍、4 倍、5 倍、7 倍波までは -80 dB より大きなレベルとなっています。
次は、シグマ・デルタ変調ありの場合です。

周波数が 2 kHz 程度以下の高調波成分は -80 dB 以下に抑えられています。
その分、2 kHz 程度以上のノイズ成分は、シグマ・デルタ変調なしの場合に比べて多くなっています。
グラフの左側の [THD,+N] の欄の 1 行目の青色の数字は、FFT 結果の高調波成分を拾い集めて計算した結果の高調波ひずみ率 (THD: Total Harmonic Distortion) で 、シグマ・デルタ変調なしでは約 0.088 %、ありでは約 0.035 % となっています。
2 行目の青い数字はノイズ成分も含むひずみ率 (THD+N) で、シグマ・デルタ変調なし、あり、両者ともに 2.9 % 程度の数値で、あまり差がありません。
他の設定の説明については次回に回します。