出力クロックの周波数を、ある程度任意に設定可能な可変周波数クロック発振器を作ってみようと思います。
広く一般に使われる水晶発振器🔮は、発振に使う水晶発振子によって決まった周波数を発振出力可能です。発振周波数を変更するには、水晶発振子を物理的に取り換える必要があります。水晶発振子を使った発振器は、非常に高精度な発振が特長です。
今回作成する可変周波数クロック発振器は、出力するクロックの周波数を、ある程度任意に設定可能な事を特長とします。
8DIPと言う小さな筐体に収まったAVRコアのATtiny85(下図)には、RC発振器(8MHz)と、×8逓倍可能なPLLが搭載されています。

上図のリンク等から辿っていただくと解りますが、「tiny」シリーズでありながらの、相当に高機能なマイコンです。それにもかかわらず、たったの8本しか端子が無く、しかも、その内2本は電源とGNDだというのですから、デバッグもままならず、中々活用の機会が無かったのです。
今回はATtiny85の内蔵RC発振器8MHzを内蔵PLLで×8逓倍したPCK @64MHzを作り出して使います。ATtiny85の8ビットタイマTC1には、カウントクロックとしてPLLで×8逓倍したPCKを使用可能なモードが備わっています。
8ビットタイマTC1でデューティ50%のPWMを生成して出力すると、最高でPCK/2の周波数である、32MHzクロックを出力することができます。以降、TC1のPWM出力をクロック出力CLKOと称します。
CLKOの周波数を決定する要因は、以下の4点です。
ただしPLL出力クロックPCKの最高周波数は、データシートによると85MHz(MIN)で飽和する様です。例えば手元の個体では、RC発振器の周波数を17MHzまで引き上げることができますが、×8逓倍しても136MHzのPCKは得られず、85MHz辺りで飽和するという事です。
これはMIN規定なので、個体によって実力では85MHzを超えた周波数まで発振するかもしれませんが、仕様通りだと仮定すると、最高で85MHz÷2=42.5MHzまでのクロック出力が可能です。
内蔵RC発振器を所望の周波数で発振させるためには、校正データの取得が必要です(下図)。

校正データの取得方法に関しては、こちらの記事「#29-4 ATtiny85の内蔵RC発振器」でご確認ください。
上図グラフは、筆者手元のATtiny85で実測したデータで作図したものです。個体差があるので、皆様お手元のATtiny85で各々計測する必要があります。
取得した校正データから、変換表に相当するC言語の埋め込みデータを生成する必要があります。生成規則を以下に示します。
変換表の生成規則:
以上の規則で変換表を生成するスクリプトを以下にリンクしておきます。
生成スクリプト:ti85_vclkgen_frqcal.olv
※スクリプト(.olv)を実行するには拙作Olive+をWindowsにインストールしてください。
こちらの実験にて【eval】コマンドを実行した時のログを、「ti85_vclkgen_frqcal.txt」と言うファイル名でスクリプトと同じディレクトリに格納して、実行してください。
ログファイルサンプル:ti85_vclkgen_frqcal.txt
eval
0,5045
1,5071
2,5093
3,5118
:
: (中略)
:
252,16537
253,16729
254,16849
255,17071
OK: eval
スクリプトを使って生成したソースリスト(一部抜粋)を以下に示します。
変換表のソースリスト(一部抜粋):
// RC oscillator; frequency - OSCCAL conversion table
PROGMEM const uint16 OSCFRQ[]=
{
0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100,
0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100,
0x1100, 0x1101, 0x1203, 0x1306, 0x1409, 0x150c, 0x160e, 0x1711,
0x1813, 0x1916, 0x1a19, 0x1b1b, 0x1c1d, 0x1d1f, 0x1e21, 0x1f23,
:
: (中略)
:
0xc0f8, 0xc0f7, 0xc3f9, 0xc3f9, 0xc5fa, 0xc5fa, 0xc5fa, 0xc7fb,
0xc7fb, 0xc9fc, 0xc9fc, 0xccfd, 0xccfd, 0xcefe, 0xcefe, 0xcefe,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff,
0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff, 0xd1ff
};
この変換表はATtiny85の個体差があるため、皆様自身で計測して作成する必要があります。
可変周波数クロック発振器としての仕様を策定して行きましょう。基本的にはI2Cで接続して、I2Cマスタデバイスから出力周波数を制御可能な装置に仕上げて行きたいと思います。
可変周波数クロック発振器自体は、I2Cスレーブデバイスとなります。
スレーブアドレスは、コンパイル時に指定した定数値で設定可能とします(以下コード)。
const int i2c_slv_adr=0x0c; // i2c: slave address (7 bits)
もし他のI2Cスレーブデバイスとスレーブアドレスが重複した場合、上の定数を変更して再コンパイル&書き込みすれば良いことなので、何らかの端子を読み取って動的にアドレスを変更するような機構は設けません。
プログラムとしては、ATtiny85のWireライブラリがスレーブアクセスに対応しているので、他のArduinoと同じ様に応用できます。
アクセス仕様は、SMBus準拠のバイト読み出し/バイト書き込みとします。
I2C通信でアクセス可能な、I/Oレジスタの目標仕様を以下に示します。
I/Oレジスタ目標仕様:
出力クロックCLKOの周波数は以下式で表すことができます。
fCLKO=fCPU(OSCCAL)÷PLLCSR[LSM]×8逓倍÷TCCR1[CS1]÷(OCR1C+1)÷2
8ビットタイマTC1の設定(CS1フィールドやOCR1Cレジスタ)で制御できるのは大まかな周波数なので、細かな周波数を制御したい場合は、OSCCAL(やOSCFRQ)レジスタによる設定が有効です。
従って、事前にしっかり校正データを取得しておくことが必須です。
目標仕様策定の途中ですが、一旦切っておきます。まだまだ、先は長そうですので、慌てず進めたいと思います。


