ATtiny10 用プログラム (6)

#define によるモード設定の続きです。

#define USE_ADC // PB2 (pin 4) = freq control AD input

でシンボル「USE_ADC」を定義すると、可変周波数のモードになり、PB2 (4 番ピン) に入力されるアナログ電圧を内蔵 8 ビット AD で 変換して得られた 0 〜 255 のディジタル値に対応した周波数のサイン波を出力します。
AD の変換精度 8 ビットを確保するためには、AD クロックを 50 〜 200 kHz の間に選ぶ必要があり、また、AD クロック・プリスケーラは 2 のべき乗分の 1 の分周比に制限されるので、1/64 分周を設定しており、8 MHz 内部/外部クロックで 8 [MHz] / 64 = 125 [kHz]、12 MHz 外部クロックで 12 [MHz] / 64 = 185 [kHz] の AD クロックを得ています。
AD 変換開始のトリガは、Timer0 のオーバーフロー (TOV0) 発生で周期的にかかるモード (オート・トリガ・モード) にしています。
オート・トリガ・モードでは、1 回の AD 変換に 13.5 サイクルかかります。
一方、サイン出力波形のサンプリング周波数 fs = 8 [MHz] / 512 = 15.625 [kHz] であり、1 サンプリング周期内に AD 変換クロックは 8 波しか入りません。
1 発目の TOV0 トリガで AD 変換を開始すると、まだ変換が完了していない 8 クロック目で 2 発目の TOV0 トリガがかかることになりますが、AD では変換途中のトリガは無視される仕様になっています。
つまり、fs (15.625 kHz) のレートで発生する TOV0 のトリガは、2 発に 1 発は無視され、AD 変換のレートは fs/2 = 7.8125 kHz のレートになります。
前に述べたように、AD 変換値の 1 ステップの変化を 1/3 半音の変化に対応させているので、 AD 変換後の値を平均化して安定させる必要があります。

左の図のような「漏れのある積分器」 (leaky integrator) の構成で平均化を実現しています。
1/256 の係数は、8 ビット右シフトで実現しています。 シフト数を 8 ビットとしたのは、バレル・シフタを持つ 32 ビット RISC とは違い、AVR では 1 ビット単位のシフト命令しか持たないためです。
基本的には任意ビット数のシフトは、1 ビットシフトを必要なビット数の回数だけ繰り返す形にコンパイルされることになります。
シフト数が 8 の倍数の場合のみ、8 ビット・レジスタ間の転送としてコンパイルされるので、8 ビットシフトの 1/256 を選んであります。
DC ゲインは 1 であり、時定数、つまり、ステップ応答の最終値の約 63 % まで達する時間は 256 * (2/fs) = 約 33 ms となります。
終値に 1/256 以下の残差で収束するまでの時間は時定数の約 5.5 倍となり、時間にして約 0.2 秒となります。
応答速度としては遅いのですが、手で VR を回して電圧を与える程度の応用には、これで十分だと思います。
遅延要素の初期値はゼロから出発しているので、プログラムの実行開始から、約 0.2 秒かけて周波数ゼロからスイープして目的の周波数まで到達することになります。
プログラム開始時から目的の周波数を発生させたい場合には、初期化の段階で AD 変換値をあらかじめ読み込んでおき、16 ビット変数 ad_ave の上位バイトにその値を設定してから、メインの無限ループに入る必要があります。
ATiny10 では、PB1 (3 番ピン) に、外部クロック入力と、タイマ OC0B 出力とが重なって割り付けられているので、両者の利用は排他的になります。
その他の設定も含め、複数の #define によって PB1 の設定を切り換えています。
下の表にまとめて示します。

PB1 機能 USE_EXTCLK USE_BUSY USE_OC0B USE_FOUT2
外部クロック Ο - - -
ジー信号 × Ο - -
ADC モニタ × × Ο ×
サイン波出力 × × × Ο

ここで「Ο」はシンボルの定義あり、「×」は定義なし、「-」は「どちらでも良い」ことを表しています。
「USE_EXTCLK」は優先度が最も高く、他の設定が何であろうとも、ピン 3 から外部クロックを入力するモードになります。
12 MHz のクリスタル・オシレータ出力をピン 3 に加え、固定周波数、シグマ・デルタ変調ありのモードでのサイン波出力を WaveSpectra で観察した結果を下に示します。

12 MHz クロックなのでサンプリング周波数は 12 [MHz] / 512 = 23.4375 [kHz]、PHASE_INC は 0x20000 を直接指定し、サイン波の周波数は 12 [MHz] / 512 / 128 = 183.11 [Hz] となっています。
参考のため、前回の内部 8 MHz RC オシレータの場合の結果を再掲します。


両者を比べて明らかなように、内部 RC オシレータではジッタ、スプリアスのために、スペクトルのメインローブの幅が大きく広がっていることが分かります。
「ビジー信号出力」モードでは、PB1 は通常の出力ポートに設定され、サイン波発生処理中は「H」を出力し、タイマのオーバーフロー・フラグをチェックしてサンプリング周期と同期を取っているアイドル状態の時には「L」を出力しています。
この出力をデューティー測定可能なテスタで測定すると、おおよその「CPU 負荷率」がデューティーの値として読めます。
前述のように、AD 変換結果が得られるのは 2 サンプリング周期ごとなので、AD 変換結果が得られなかったサンプリング周期のデューティーは 16 % 程度、AD 変換結果が得られたサンプリング周期のデューティーは最大 50 % 程度となり、両者の平均としてテスタで読めるのは 33 % 程度の値となります。
Arduino 本体の「L」と表示されている LED が接続されているディジタル 13 番ピンは ATtiny10 の PB1 に接続されていますから、忙しくてデューティーが大きい場合に LED が明るく、ヒマな場合に暗く光ることになり、感覚とは逆になりますが、テスタでの測定をメインとして、このような設定にしてあります。
「ADC モニタ」モードでは、平均化しない AD 変換結果そのものを OC0B 側の PWM デューティーに設定し、アナログ電圧として「エコーバック」します。
ジー信号の場合と同様に、Arduino 本体の「L」LED が点灯しますが、この場合は、AD 変換値が大きい場合に明るく、小さい場合に暗くなり、感覚と合った動きをします。
「サイン波出力」モードでは、PB1 は、もうひとつの PWM 出力の OC0B に設定され、固定周波数のサイン波を出力します。
その周波数は、PHASE_INC2 の値で設定します。