11年前(2011年2月)のことです。秋月電子通商製の発振器キット(XR-2206使用ファンクションジェネレーターキット)を購入・組み立てたことがあります。その時の完成写真が次の写真です。
このキットでは、周波数レンジの切替を基板に接続するコンデンサを繋ぎかえて行います。レンジ毎の周波数の設定は、100KΩのボリュームを使って行います。抵抗(R)とコンデンサ(C)によるアナログ発振ですので調整次第では希望する周波数にピタリの周波数の信号を出力出来ます。 ただし、任意の希望する周波数を出力するためには、別途オシロスコープや周波数カウンターを見ながらの調整となります。固定周波数の出力であれば、抵抗やコンデンサを予め調整しておけば良いので使用時にはオシロスコープや周波数カウンタは必要ありません。 この発振器キットと同様のキットがAmazonでも販売されています。製作記事もネットにたくさんアップされています。キットの性能に関しては、その評価は様々なようです。 製作したキットがまだ手元に残っていましたので、性能評価の是非は別として、このキットの周波数調整をマイコンで自動制御できれば面白いのではと思い、検討ならびに実験を行ってみました。 2.マイコンでの制御方法 希望する周波数の信号をこの発振器キットから出力させるには、どのようにマイコンで制御すればよいのでしょうか。それについて考えてみます。 2−1.周波数の設定
キットの心臓部であるIC(XR-2206)は、そのデータシートを見ると周波数設定を電圧制御で行えると書かれています。
式からVcが3Vの時は、f=1/RCとなり、これが最低周波数となります。Vcが0Vの時は f=1/RC * (1+R/Rc)となり、これは最高周波数となります。 R=100KΩ, Rc=1KΩとすると最高周波数は最低周波数の101倍になります。R=1MΩ,Rc=1KΩなら1001倍になります。つまり選ぶ抵抗RやRcによって、ある一つのCに対して周波数の設定範囲を100倍にしたり1,000倍にしたりすることが出来る訳です。 この制御電圧をマイコンで出力するのは簡単です。マイコンに内蔵の出力比較モジュールを使えば PWMにより、0から3.3V(電源電圧3.3Vのマイコンの場合)までの任意の電圧を出力出来ます。ただし、その精度はPWMの分解能に依存します。 2−2.周波数の確認 制御電圧により任意の周波数に設定できるとはいえ実際に発振している周波数が確認できないことにはうまく制御できません。XR-2206では、11番ピンからパルス信号が出力されます。発振器キットではこの出力は5.1KΩの抵抗で電源にプルアップされています。 このパルス信号の周波数をマイコンのタイマーモジュールを使って計測することにします。制御電圧を出力した際に周波数を計測し計測結果に応じて制御電圧を微調整(増減)することで希望する目的の周波数に設定することが出来ます。 2−3.周波数レンジの切替 マイコンで制御する周波数の設定範囲は、10Hz から 100KHz までとします。RやRcの選定によって、一つのCで、この範囲をカバーしようとするとRとRcの比は10,000ということになります。制御電圧が0Vの時にRcに流れる電流は3mA以内という制約からRcは1KΩ未満には出来ません。よって、Rは、最小でも10MΩ以上となります。 また10,000倍の範囲をカバーするには、制御電圧を生成するPWMも10,000以上の分解能が必要ということになります。これは現実的ではありません。そこで発振器キットと同様、XR-2206の5番,6番ピンに接続するタイミングコンデンサを切り替えて複数のレンジでカバーすることにします。 コンデンサの切替についてもマイコンで制御することにします。切替方法はリレーではなくアナログの電子スィッチを使うことにします。 3.回 路
マイコンには、3.3V動作のPIC32MX220F032Bを採用しました。回路電源は、+5V出力のACアダプタを使うことにしましたので、3.3Vのレギュレータ(LP2950)を搭載しています。 任意の周波数の設定用にロータリエンコーダを使います。ロータリーエンコーダーの出力ピンには、2.7KΩのプルアップ抵抗と1μFのセラミックコンデンサを接続しています。コンデンサはエンコーダ内部のスィッチのチャタリングを除去するためのものです。 ロータリーエンコーダーで設定する周波数の表示確認用にI2C接続のOLED(128dotx32dot)を接続しています。SDAやSCLラインの外付けプルアップ抵抗は、LCDモジュール内でプルアップされているので不要です。 制御電圧は、出力比較モジュール1(OC1)にてRB7から出力される5KHzのPWM信号をローパスフィルタを通し、オペアンプ(NJM2732D)の電圧バッファーで受けてから出力しています。データシートに記載の電圧制御回路のRとRcは、金属皮膜抵抗の100KΩと1KΩとしました。 発振周波数測定用のパルス信号は、6.8KΩと3KΩの抵抗分圧回路を経てRB13に接続しています。RB13は、INT2割込みやT4CK(タイマー4の外部クロック入力)になっています。発振器キットの電源は、+16Vになっています。キット側の5.1KΩのプルアップ抵抗を含めるとRB13へのパルス信号の入力電圧は3Vになります。 周波数レンジ切替のためのコンデンサ切替用のアナログスィッチには、NECのμPD4066BCを使いました。スィッチの電源は、発振器キットから取りました。スィッチのドライブには、抵抗内蔵のトランジスタ(RN1206)を使いました。 μPD4066BCは、4回路のスィッチ構成となっています。そこで、10μFから0.01μFまでの4個のコンデンサを使って4レンジの周波数切替としました。次のようにレンジを使い分けています。ただし、各レンジにおける発振可能な最低周波数や最高周波数はこの範囲よりももっと広いです。
回路をブレッドボードを使って組み立ててみました。 ![]() 4.マイコンのファームウェア
マイコンのファームウェアは、MPLAB IDE にて XC32 コンパイラにて作成しました。主なソースコードは main.cです。 発振器キットの電源をオンにしてからマイコン制御回路に+5VのACアダプタを接続します。この時点で、周波数レンジは、FREQ_VERY_LOW(10Hz〜100Hz)、 周波数制御の制御電圧出力は、約1.6Vになっています。キットの発振周波数は、約57Hz(実測です)となっています。 OLEDの表示器の上段行には、order: xxxxHzの表示が出ます。ロータリエンコーダーを右回転すると表示周波数が高くなり、左回転すると表示周波数が低くなります。ゆっくり回転すると1Hz単位で増減します。早く回転すると大きく増減します。設定可能な周波数は10Hzから100,000Hzまでです。 希望する周波数が表示された所で、エンコーダーを押し込みスィッチをオンにすると、キットの発振周波数がその周波数になるように自動制御が始まります。OLEDの下段行には、** tunning... の文字が表示されます。制御が完了するとdone (xxx.xHz)の表示に変わります。周波数表示の部分は、低周波では、0.1Hz単位、高周波数では、1Hz単位での表示になります。 エンコーダーのスィッチが押されると、まず最初に指定された周波数が含まれるレンジになるようにアナログスィッチを操作してコンデンサ切替を行います。その後、予め調べておいたレンジ毎の最低周波数、最高周波数になる制御電圧の値から希望する周波数になる制御電圧を計算で求めて出力します。 続いて発振周波数を計測し、周波数が高ければ制御電圧を上げ、周波数が低ければ制御電圧を下げて発振周波数を調整します。制御電圧を出力する毎に周波数を計測し、結果に応じて調整を繰り返します。低周波数のレンジでは、希望周波数に対して0.1Hz未満の誤差、高周波数のレンジでは、10数Hz未満の誤差になれば調整完了とします。 UARTからはデバッグ情報として、周波数調整中の様子を出力しています。パソコンのテラタームなどの画面で調整中の発振周波数とその時の制御電圧(PWMの値)を確認できます。
4−2 PWMによる制御電圧の生成 周波数設定用の制御電圧はタイマー2(TIMER2)と出力比較モジュール1(OC1)を次のように設定し、RB7からPWM信号として出力しています。ローパスフィルタを経ることでPWMのパルス幅に比例した直流電圧を得ることができます。 // *** controlle voltage out put ********************** // setup 16bit Timer2 for OC1 T2CON = 0; T2CONbits.TCKPS = 0b000; // 1:1 40MHz // setup OC1 to PWM range 0 to 8000 RPB7R = 0b0101; // RPB7 --> OC1 OC1CON = 0; OC1CONbits.OCTSEL=0; // use Timer2 PR2 = 7999; // 40MHz/8000=5KHz OC1R = 4000; OC1RS = 4000; OC1CONbits.OCM = 0b110; // PWM // Go output OC1CONbits.ON = 1; T2CONbits.ON = 1; PWMの分解能は8,000です。0で出力電圧が0V, 8,000で出力電圧 3.3Vとなります。3.3V/8,000=0.41mV ステップで出力電圧を可変出来ることになります。任意のタイミングでOC1RSに値を設定することでそれに応じた電圧が出力されます。 周波数レンジが10Hz〜100Hzでは、1Hz変更するのに約80ステップが必要です。100Hz〜1,000Hzでは、1Hzの変更に8ステップ必要です。 1,000Hzから10,000Hzでは、逆に1ステップの変更で周波数は約1.2倍動きます。更に10,000Hz以上では、1ステップで周波数は約12倍動くことになります。 以上から、高周波レンジでは、周波数を1Hz単位で設定することは出来ません。ある程度の設定誤差を許容することにします。 4−3 周波数の計測XR-2206から出力されるパルス信号の周波数の計測は、低周波レンジでは、パルス信号の周期を測定し、高周波レンジでは、パルス信号の数をカウントすることにします。 低周波レンジでは、0.1Hz単位で発振周波数を制御します。0.1Hz単位の測定をパルス数のカウントで行うと1回の測定に10秒かかります。これでは、周波数の調整に時間がかかってしまいます。そこで、周期を測定することで調整時間の短縮を図っています。 Timer4とTimer5を組み合わせた32ビットのタイマーを使います。周期測定の際には、20MHzのシステムクロックをタイマーで計数します。計数の開始と終了は、INT2(RB13)の割込みで行います。 // // return value frequency * 10 (unit 0.1Hz) // int measure_frequency_by_period(void) { // clear measuring parameter MeasDone=0; PrevPeriod=0; PeriodVal=0; MeasTime = 4; // setup Timer4 and Timer5 32bit system clock counter T4CONbits.TCS = 0; // clock in from peripheral: clock is 40MHz/TCKPS T4CONbits.TCKPS = 0b001; // prescale 1:2 40MHz/2=20MHz TMR4=0; T4CONbits.TON=1; // Go 20MHz clock count // INT2 connected XR-2206 pulse out INT2R = 0b0011; // INT2R asigned to RB13(XR-2206 pulse) IFS0bits.INT2IF = 0; IEC0bits.INT2IE = 1; while(MeasDone==0); IEC0bits.INT2IE = 0; PeriodVal >>= 2; // avarage MeasTime(4) times FreqVal = ((400000000/PeriodVal)+1)/2; return FreqVal; // freq*10 } void __attribute__ (( interrupt(ipl4soft), vector(_EXTERNAL_2_VECTOR) )) _INT2Interrupt(void) { unsigned int val; val = TMR4; if(PORTBbits.RB13){ // rise edge if(PrevPeriod){ PeriodVal+=(int)(val-PrevPeriod); if(--MeasTime==0){ MeasDone=1; } } PrevPeriod = val; } IFS0bits.INT2IF = 0; return; } 高周波レンジでは、ある程度の設定誤差を許容することにしましたので 0.5秒間のパルス数のカウントとしました。 パルス数の計数は、T4CK(RB13)から入力されるパルスの数をタイマーでカウントします。カウントの開始終了のタイミングはTimer3による10msec割込みで行っています。 // // return value Frequency // meas_time <= 100 1sec measure int measure_frequency(int meas_time) { // set measuring parameter MeasTime = meas_time; MeasDone=0; // setup Timer3 for measure timing // setup Timer4 and Timer5 32bit counter T4CKR = 0b0011; // RB13 is T4CK, Timer4 clock in from RB13 T4CONbits.TCS = 1; // clock in from T4CK T4CONbits.TCKPS = 0; // prescale 1:1 T4CONbits.TON=1; // Go frequency count T3CON = 0; T3CONbits.TCKPS = 0b100; // prescale 1:16 clock: 40MHz/16=2.5MHz PR3 = 24999; // 2.5MHz/100Hz(10msec)=25000 TMR3=0; IFS0bits.T3IF = 0; IEC0bits.T3IE = 1; TMR4 = 0; T3CONbits.TON = 1; while(MeasDone==0); T3CONbits.TON = 0; IEC0bits.T3IE = 0; return FreqVal * (100/meas_time); } void __attribute__ (( interrupt(ipl4soft), vector(_TIMER_3_VECTOR) )) _T3Interrupt(void) { FreqVal = TMR4; // read Timer4+Timer5 if(--MeasTime ==0){ MeasDone=1; } IFS0bits.T3IF = 0; }4−4 アナログスィッチのオン抵抗の影響は? コンデンサ切替に使ったアナログスィッチ(μPD4066BC)ですが、スィッチオン時のオン抵抗が実測で約80数Ω ありました。 この抵抗が発振周波数に影響があるのではと心配したのですが、スィッチを通した場合と通さなかった場合で比較してみました所、発振周波数に違いはありませんでした。 タイミングコンデンサは定電流で充電されているということのようです。 |