GPS/RTC(DS3234)の1PPS出力を利用した超高精度周波数カウンター
(“データシートの上では”ですが)±10nsの不確かさを誇るGPS受信モジュールGM-5157Aの1PPS出力を使って不確かさ0.01ppm(のはず)の高精度周波数カウンタを作ってみました。つまり100MHzを測定しても最後の1Hzの桁まで正確(なはず)です。水晶発振器の温度係数を調べるというような目的を想定しています。100MHzじゃPICが追いつかないでしょうが...
でもこれはハードウェアを追加すれば対応が可能だと思います。つまり100MHz超えでも1Hz単位で測定できる周波数カウンターは作れると思います(構想はしています)
その後の調べではこの周波数カウンターはそのままでもけっこう高い周波数まで測定が可能です。
「PIC18F26K22のカウンター(タイマー)の測定できる周波数の上限を調べてみた」
「PICのタイマー(T1CKI)の使用可能最大周波数 - あるいはリングオッシレーターの発振周波数」
100MHzが測定できるのは確認できたのでタイトルには100MHzというのを入れました。メーカーが保証する動作ではないと思いますが...
なお上限は未確認です。165MHzあたりが測れないのは確認済みです。
と書いてしまったのですが...
「PICで作った100MHz周波数カウンターの動作検証で困った問題」
タイトルから100MHzというのをはずします (^^;;
ほんとうに0.01ppmの不確かさの測定ができるかはわかりません。しかし「続・GPS受信モジュール GM-5157A vs GE-612T 1PPS対決」では“GM-5157Aの1PPS信号がGE-612Tに対しだいたい0.2μ秒先行して出力されているようだ”ということを書きました。逆に言うとこのときの1/800万分の1秒の分解能で調べても相対的な時間的な関係は保たれているわけで少なくとも0.1ppmつまり10MHzで1Hzくらいのところまで正しく測定できそうに思えます。それ以上の精度で測定できるかどうかは今後の課題です。
ただGPSの特性から言えばサンプル時間を長くすればどんどん精度は上がって行くわけなのでこれは他の方法では得られない大きなメリットです。
GPSの1PPS出力が面倒な方は秋月にもある“超高精度SPIバスRTC(リアルタイムクロック)DS3234S”なんかを使うといいと思います。1ppm以下の不確かさに持っていくのはGPSの1PPS出力との突き合わせが必要ですが、そういうことをしなくても少なくとも2ppm以下にはなると思います。DS3234Sの時計は一週間に1秒違うか違わないかくらいに正確です。
「超高精度SPIバスRTC(リアルタイムクロック)DS3234Sを使った周波数カウンターの実力」
「PICでSPI - 超高精度SPIバスRTC(リアルタイムクロック)DS3234Sの使い方」
=======
具体的な作り方なんですがまずPICは18F26K22を使います。これはこの周波数カウンタを「PIC+SPI+I2C 自記温湿度計+気圧計+8ch電圧計のソース - main()」の中に組み込もうとしているからです。
TIMER1を使うことにします(たぶん3でも5でも同じでしょう)
TIMER1/3/5にはTimer Gateの機能があるのでこれを利用します。
タイマーをカウント状態にしてもゲートが開くまで実際にはカウントされません。ゲートが開いたらカウントを開始しゲートが閉じたところでカウントが終了します。
ゲートをGPS受信モジュールの1PPS信号にします。ただこれだけだと1PPSがHighになったときだけ、つまり0.1秒間だけしかカウントされません。1秒間カウントするためにはTOGGLE MODEを使います。これを使うと最初の1PPS信号でゲートを開け、次の1PPS信号でゲートを閉じるようにできます。つまり1秒間ゲートが開きます。
周波数カウンタを作るのが目的ではなくすでにあるシステムに周波数カウンタの機能を組み込むのが目的なので周波数の測定は任意のタイミングでできるようにしたいです。このためにはSINGLE-PULSE MODEを使います。これを使うと一回カウントが行われると次にゲートが開いてもカウントはされなくなります。
タイマーは16bitなので最大カウント数は65535です。それ以上のカウントをするために割込みを使うことにします。TIMER1のオーバーフロー回数をTIMER3でカウントできる、とかあれば割込みを使う必要はなくなるのですがタイマーのブロックダイアグラムを見る限りそういう機能はなさそうです。この周波数計測中に割込みが発生するということがシングルパルスモードを使う理由でもあります。SDカードの入出力中に割込みが発生したりするとすべてがぶち壊しになります。
まとめると次のようになります。
・タイマーのソースはExternal Clock(T1CKIピン)とし、ここに測定対象の出力をつなぎます。
・タイマーゲートを有効にします。タイマーゲートソースはTimer1/3/5 Gate pin(T1G)とします。
・1秒間での計測を行うためトグルモードを有効にします(そんなに精度は要求しないときはトグルモードを無効にすればいいです。これだと毎秒計測可能です)
・ゲートの極性はGM-5157Aの場合はどちらでもそんなに違いはないと思いますが、1PPSの立ち上がりの極性に合わせていた方が余計な心配をしなくていいでしょう。
・一回限りの測定にしたいのでシングルパルスモードにします。
・タイマーゲートを使った場合ゲートが閉じるとT1GGO_NOT_DONEビットがLowに落ちますのでそれを待ってタイマーのカウント数を調べます。割込みの回数*65536+タイマーのカウント数が周波数ということになります。
(トグルモードをオフにした場合はこの数値に10を掛けたものが周波数です)
-------
この方法だとタイマーソースを周波数がわかっている信号源にし、タイマーゲートソースに未知の周波数を入れると周期がわかります。周期の逆数からタイマーゲートソースに入力された信号の周波数がわかります。
「VFコンバータ(VCO)の製作(1) - 自動周波数特性測定器に向けて」にある周波数測定機能はこれを利用して作るつもりです。
-------
次に回路図になるわけですが面倒なのでどう結線されているかだけ書きます。
01 MCLR ..... プルアップ
08 VSS
10 OSC2 ..... FOSC/4出力
FOSC/4をTimer1ソースの入力にしてテストに使います
ソフト的に切り替えてももちろんOKです。
11 T1CKI .... Timer1ソース、測定対象信号源に接続します。テストのときは隣のピンへ
14 SCL1 ..... I2C/SCL ..... LCD(AM3201)に測定結果を表示します
15 SDA1 ..... I2C/SDA ..... 〃
19 VSS
20 VDD
26 T1G .... Timer1 Gate、GPS受信モジュールまたはRTCの1PPS出力に接続します。
「確度0.0005ppmの周波数測定 - GPSの1PPS出力を使った高精度周波数カウンタ」
「超高精度SPIバスRTC(リアルタイムクロック)DS3234Sを使った周波数カウンターの実力」
「ほんとうに超高精度かもしれないRTC・DS3234Sを使った周波数カウンター」
へ続く
-----
関連記事のリスト
趣味の電子工作
PIC
趣味の気象観測
趣味の実験
-----
ソース
// PIC18F26K22 // 01 RE3/VPP/MCLR .....(1) .... プルアップ // 02 RA0/C12IN0-/AN0 ..... // 03 RA1/C12IN1-/AN1 /..... // 04 RA2/C2IN+/AN2/DACOUT/VREF .... Reserved DACOUT // 05 RA3/C1IN+/AN3/VREF+ .... // 06 RA4/CCP5/C1OUT/SRQ/T0CKI ..... Reserved CCPイベント // 07 RA5/C2OUT/SRNQ/SS1/HLVDIN/AN4 .... // 08 VSS // 09 RA7/CLKI/OSC1 .... // 10 RA6/CLKO/OSC2 ..... FOSC/4出力(テスト用) // 11 RC0/P2B/T3CKI/T3G/T1CKI/SOSCO ..... Timer1ソース // 12 RC1/P2A/CCP2/SOSCI ..... // 13 RC2/CTPLS/P1A/CCP1/T5CKI/AN14 ..... // 14 RC3/SCK1/SCL1/AN15 ..... I2C/SCL ..... AM3201 // 15 RC4/SDI1/SDA1/AN16 ..... I2C/SDA // 16 RC5/SDO1/AN17 .... つかっちゃまずい? // 17 RC6/P3A/CCP3/TX1/CK1/AN18 ..... Reserved TX1 // 18 RC7/P3B/RX1/DT1/AN19 ..... Reserved RX1 // 19 VSS .....(3) // 20 VDD .....(2) // 21 RB0/INT0/CCP4/FLT0/SRI/SS2/AN12 ..... Reserved INT0 // 22 RB1/INT1/P1C/SCK2/SCL2/C12IN3-/AN10 ...... Reserved SPI/SCK // 23 RB2/INT2/CTED1/P1B/SDI2/SDA2/AN8 ..... Reserved SPI/SDI // 24 RB3/CTED2/P2A/CCP2/SDO2/C12IN2-/AN9 ..... Reserved SPI/SDO // 25 RB4/IOC0/P1D/T5G/AN11 .... // 26 RB5/IOC1/P2B/P3A/CCP3/T3CKI/T1G/AN13 .... Timer1 Gate // 27 RB6/IOC2/TX2/CK2/PGC .....(5) // 28 RB7/IOC3/RX2/DT2/PGD .....(4) #include <xc.h>#include <stdio.h>
#include "..\..\include\common\MyDef.h" #include "..\..\include\common\I2CLib.h" #include "..\..\include\common\ACM1602lib.h" // C:\Program Files (x86)\Microchip\xc8\v1.32\docs\chips\18f26k22.html #ifdef _18F26K22 //#pragma config FOSC = INTIO67 #pragma config FOSC = INTIO7 // PIC発振周波数測定テスト用 #pragma config WDTEN = OFF #pragma config MCLRE = INTMCLR #endif // 割込みがあったかどうか確認するためのフラグです。 static int Interrupt=0; // センサーから読み込んだデータをLCDに16進表示するため // sprintf()を使いたくないとき用 void putLCDhex(unsigned char ch) { char hex[]="0123456789ABCDEF"; LCD_Putc(hex[ch>>4]) ; LCD_Putc(hex[ch & 0x0f]) ; } // 割込みがあったときはフラグを立てるだけで // 割込みルーチンの中では何もしていません。 void interrupt InterFunction( void ) { // TIMER1割込み if( TMR1IF == 1 ) { Interrupt = 1; TMR1IF = 0; // I2C関連の割り込み処理 } else { InterI2C() ; } } void main() { unsigned char i,F_High,F_Low; unsigned int count=0; unsigned long frq; char buf[16]; OSCCON = MyOSCCON ; // for I2C ANSELCbits.ANSC3 = 0; ANSELCbits.ANSC4 = 0; // for Timer1 Gate ANSELBbits.ANSB5 = 0; // 1PPS出力がオープンコレクタでも使えるように INTCON2bits.NOT_RBPU = 0; WPUBbits.WPUB5 =1; // それぞれのビットの意味がわかるように書いています。 // 実際にコーディングするときはふつうに書いてください。 // 10 =Timer1/3/5 clock source is pin or oscillator: // If TxSOSCEN = 0: // External clock from TxCKI pin (on the rising edge) // If TxSOSCEN = 1: // Crystal oscillator on SOSCI/SOSCO pins // 01 =Timer1/3/5 clock source is system clock (FOSC) // 00 =Timer1/3/5 clock source is instruction clock (FOSC/4) T1CONbits.TMR1CS = 2; // T1CKI pin // 3 1:8 Prescale value, 2 1:4, 1 1:2, 0 1:1 T1CONbits.T1CKPS = 0; // Secondary Oscillator Enable Control bit T1CONbits.T1SOSCEN = 0; // Timer1/3/5 External Clock Input Synchronization Control bit // TMRxCS<1:0> = 1X // 1 = Do not synchronize external clock input // 0 = Synchronize external clock input with system clock (FOSC) // TMRxCS<1:0> = 0X // This bit is ignored. Timer1/3/5 uses the internal clock when TMRxCS<1:0> = 1X. T1CONbits.T1SYNC = 1; // 16-Bit Read/Write Mode Enable bit T1CONbits.RD16 = 0; // TMRxON: Timer1/3/5 On bit // 1 = Enables Timer1/3/5 // 0 = Stops Timer1/3/5 // Clears Timer1/3/5 Gate flip-flop T1CONbits.TMR1ON = 1; // 割込み許可 TMR1IE = 1; PEIE = 1; GIE = 1; // それぞれのビットの意味がわかるように書いています。 // 実際にコーディングするときはふつうに書いてください。 // TMRxGE: Timer1/3/5 Gate Enable bit // If TMRxON = 0: // This bit is ignored // If TMRxON = 1: // 1 = Timer1/3/5 counting is controlled by the Timer1/3/5 gate function // 0 = Timer1/3/5 counts regardless of Timer1/3/5 gate function T1GCONbits.TMR1GE = 1; // TxGPOL: Timer1/3/5 Gate Polarity bit // 1: active High, 0: active Low T1GCONbits.T1GPOL = 0; // TxGTM: Timer1/3/5 Gate Toggle Mode bit // RTC,GPS等のときはセット、たいていの場合セットした方がいいはずです。 T1GCONbits.T1GTM = 1; //TxGSPM: Timer1/3/5 Gate Single-Pulse Mode bit T1GCONbits.T1GSPM = 1; // TxGGO/DONE: Timer1/3/5 Gate Single-Pulse Acquisition Status bit T1GCONbits.T1GGO_NOT_DONE = 1; // TxGVAL: Timer1/3/5 Gate Current State bit // Indicates the current state of the Timer1/3/5 gate that could be provided to TMRxH:TMRxL. // Unaffected by Timer1/3/5 Gate Enable (TMRxGE). // Read Only // TxGSS<1:0>: Timer1/3/5 Gate Source Select bits // 00 = Timer1/3/5 Gate pin // 01 = Timer2/4/6 Match PR2/4/6 output (See Table 12-6 for proper timer match selection) // 10 = Comparator 1 optionally synchronized output (sync_C1OUT) // 11 = Comparator 2 optionally synchronized output (sync_C2OUT) T1GCONbits.T1GSS = 0; InitI2C_Master() ; LCD_Init() ; LCD_Clear(); LCD_Puts("Start!"); while(1) { // 割込みの処理はさっさと済ませて次の割込みに備えます。 if( Interrupt == 1 ) { count++; Interrupt = 0; continue; } // カウント終了まで待ちます。 if( T1GCONbits.T1GGO_NOT_DONE == 1 ) continue; F_Low = TMR1L; F_High = TMR1H; frq = count; frq = (frq << 8) + F_High; frq = (frq << 8) + F_Low; sprintf(buf,"%12ld",frq); LCD_SetCursor(0,0); LCD_Puts(buf);
// タイマーソースに周波数既知の信号源、タイマーゲートソースに未知の信号源の場合 if( frq != 0 ) frq = 20000000 / frq; sprintf(buf,"%12ld",frq); LCD_SetCursor(0,1); LCD_Puts(buf); for(i=0; i <20; i++ ) __delay_ms(10); // 表示が確認できるように..... // 次の計測に入ります。 TMR1H =0; TMR1L =0; count = 0; T1GCONbits.T1GGO_NOT_DONE=1; }; }
« 続・サーミスタで正確な温度を測るコツ - 基準抵抗(R0)、B定数、熱拡散係数、... | トップページ | VFコンバータ(VCO)の製作(3) - 自動周波数特性測定器に向けて »
「趣味の電子工作」カテゴリの記事
- PICで作る100MHz周波数カウンタ検証用XOR(エクスクルーシブオア)逓倍器(2016.03.08)
- 150MHz(~200MHz?)周波数カウンター用プリスケーラー(1/4分周器)(2016.03.06)
- 測温抵抗体(Pt100、白金薄膜温度センサー)の抵抗値を温度に変換する(平方根を使わない)計算式(2016.03.01)
- GPS/JJY(標準電波)を基準周波数源とするためのPLLの詳細(2016.02.27)
- GPS受信モジュール1PPS対決 - GE-612T vs GM-5157A(2016.02.21)
コメント
この記事へのコメントは終了しました。
« 続・サーミスタで正確な温度を測るコツ - 基準抵抗(R0)、B定数、熱拡散係数、... | トップページ | VFコンバータ(VCO)の製作(3) - 自動周波数特性測定器に向けて »
着々と進んでますね(^-^)
カウンタのアーキテクチャは例の方を参考にされたのでしょうか。
GPSの信号を使えば確かに正確なゲート時間が得られますよね。たしかにこれ以上正確なソースは他では手軽に得られないかも。ますます完成が楽しみです^^。
投稿: ほよほよ | 2015年2月19日 (木) 22時03分
例の方を始めあちこち散策したのですが、結論としてMicrocchipのデータシートがいちばん役にたちました (^^;;
ほよほよさんの正弦波的なブレークスルーを期待していたのですが、やっぱりそういうのはめったにあるものじゃなさそうです (^^)
投稿: セッピーナ | 2015年2月19日 (木) 22時21分