ルビジウムオシレーターの周波数調整(2022年、その1)
Arduino のタイマー1 インプットキャプチャでパルス幅を精密測定
// ATmega328Pを使ってパルス幅を測定 20220514_PulseWidthMeasure // D8に信号を入力 Timer1 インプットキャプチャで測定。オーバーフローのミスカウントをソフトで対策 // 2022/05/15 ラジオペンチ http://radiopench.blog96.fc2.com/ volatile int irqFlag = 0; // タイマー1インプットキャプチャ割り込みフラグ volatile uint16_t ovfCount = 0; // オーバーフローカウンタ volatile boolean execRising = true; // 波形測定方向フラグ volatile uint16_t t1, t2, t3; volatile uint16_t t2MSB, t3MSB; void setup() { Serial.begin(115200); // タイマー1をインプットキャプチャに設定 // (参照:http://igarage.cocolog-nifty.com/blog/2020/08/post-ba853b.html) TIMSK1 = 0b00100001; // | ||+--- TOIE1 オーバーフロー割込on // | |+---- OCIE1A // | +----- OCIE1B // +-------- ICIE1 キャプチャー割込on TCCR1A = 0b00000000; // |||| ++--- WGM 標準動作 // ||++------- COM1B // ++--------- COM1A TCCR1B = 0b00000001; // || ||+++--- CS 16MHz入力(下位3ビットでプリスケーラー設定) // || ++------ WGM // |+--------- ICES1 ICP1入力↓エッジ(プログラム内で動的に変更) // |---------- ICNC1 ノイズキャンセルなし startUp(); } void loop() { uint32_t tw, tp; // クロック数で表したパルス幅、周期 uint32_t last_tw, last_tp; float t_tw, t_tp; // パルス幅、周期 while (irqFlag == 0) { // IRQフラグが立つまで待つ } irqFlag = 0; tw = ((uint32_t)t2MSB << 16) + (uint32_t)t2 - (uint32_t)t1; // パルス幅の計算 tp = ((uint32_t)t3MSB << 16) + (uint32_t)t3 - (uint32_t)t1; // パルス周期の計算 if((tw - last_tw) > 30000) tw -= 65536; // TC1オーバーフローミスカウント対策の暫定対策 if((last_tw - tw) > 30000) tw += 65536; if((tp - last_tp) > 30000) tp -= 65536; if((last_tp - tp) > 30000) tp += 65536; t_tw = tw * 62.5E-9; t_tp = tp * 62.5E-9; Serial.print(t_tw, 9); Serial.print(", "); Serial.print(t_tp, 9); Serial.println(); t1 = t3; // 次回の測定のためにパルスの先頭位置を記録 last_tw = tw; last_tp = tp; } void startUp() { Serial.println(); Serial.println("Pulse measurement start"); Serial.println("width, period"); for (int i = 0; i < 3; i++) { // 変数が落ち着くまで空廻し while (irqFlag == 0) { // IRQフラグが立つまで待つ } irqFlag = 0; t1 = t3; } } ISR(TIMER1_OVF_vect) { // Timer1オーバーフロー割り込み処理 ovfCount ++; } ISR(TIMER1_CAPT_vect) { // Timer1 インプットキャプチャ割り込み処理 uint16_t t; // sei(); // 途中でovfCountの値が動くと困るので割り込み禁止で処理(これは逆効果) t = ICR1; if (execRising == true) { // 立ち上がりの処理だったら、 t3 = t; // パルス周期測定のための立ち上がり時刻として記録 t3MSB = ovfCount; ovfCount = 0; // オーバーフローカウンタをクリア TCCR1B &= 0b10111111; // 次回はネガエッジでキャプチャ(ICES1=0) execRising = false; // 次回は立下りの処理 irqFlag = 1; // フラグセットしてメインに通知 } else { // 立ち下がりの処理だったら t2 = t; // 立下り時刻として記録 t2MSB = ovfCount; TCCR1B |= 0b01000000; // 次回はポジエッジでキャプチャ(ICES1=1) execRising = true; // 次回は立ち上がりの処理 } // cli(); }上の方でリンクを貼った居酒屋ガレージ日記さんの記事中にリンクがある、duty_checker1.c のコードを参考にさせていただきました。動作説明も詳しく書かれているのでそちらを見て頂くのが宜しいかと思います。
Arduino (ATmega328)のTimer1インプットキャプチャー使って時間の精密測定をやってるんだけど、GPSの1PPSの周期が20μsもばらついて見える。
— ラジオペンチ (@radiopench1) May 10, 2022
周波数カウンターで測定すると実際の変動は100ns以下だったのでGPSのせいじゃなかった。
ということは私のおつむの性能限界か、、 pic.twitter.com/Yo0kg2np6V
ちなみに、ボードはNANOなので水晶オシレーター(セラロックでは無い)。この基板をビニール袋に2重に入れ、小さな段ボール箱に布切れと一緒に閉じ込めて温度変化の影響を受け難くしてるんだけどダメ。 pic.twitter.com/tB86naC5IC
— ラジオペンチ (@radiopench1) May 10, 2022
ArduinoのTimer1でGPSの1PPSの周期を測定した時にバラツキが大きかったのは、ボードのクロック品質が悪かったのが原因みたい。
— ラジオペンチ (@radiopench1) May 11, 2022
グラフの1枚目は良好な測定結果、2枚目はダメだったグラフ(再掲)
写真は特性の良い基板(UNOの互換品)とダメな基板(NANOの互換品) pic.twitter.com/sXNhX7VyWk
Arduinoを使ってパルス幅を精密測定する話、CPUクロックの分解能で0.1秒程度のパルス幅の測定が出来るようになった。
— ラジオペンチ (@radiopench1) May 13, 2022
最終的にはGPSとルビジウムオシレーターの位相変化を観察したいんだけど、これで一山越えた気がする。 pic.twitter.com/IRvmqTNDc4