arduino 24MHZ(25MHz)動作確認用に周波数測定プログラム作成

掲載日: / 更新日:
arduino 24MHZ(25MHz)動作確認用に周波数測定プログラム作成

周波数測定プログラム作成

arduino 24MHZ(25MHz)動作確認を兼ねて、基準クロック発生器のプログラムを改変し、周波数測定プログラムを作成しました。

周波数測定プログラムには、いくつか便利なライブラリーが公開されていますが、ノーマル状態でないと正常に動作しませんし、高精度と書かれているものでもそれなり程度なので、高クロックでの動作確認を兼ねて、周波数測定プログラムを作成しました。

手抜きで、システムクロックを25MHz決め打ちで作っていますが、F_CPUによってパラメータ(定数)を切り替えるようにするといろいろなシステムでも動作可能となります。システムクロックの誤差も補正できるので、測定できる周波数の上限はシステムクロックの1/2.3程度(24MHzで10.5MHz、16MHzで7MHz程度)ですが、….

arduino 24MHZ(25MHz)動作確認用に周波数測定

F_CPUによって以下の表の黄色の部分の定数に差し替えます。

TIMER2_OCR2A、ratio_vctの場所、conversion(補正値)は、10000000倍した整数にします。補正値には必要に応じてシステムクロックの補正分も盛り込みます。

F_CPU TCCR2B(CS2) TCNT2 → ratio        
               
25000000 1024 218 55.7398687214612 217 56 0.99992064629751 100.007936
24999899 1024 217 55.9953299706135 217 56 0.999916606618099 100.008340033694
24000000 1024 220 53.0260180995475 216 54 1.00006400409626 99.9936
24000000 1024 216 54.0034562211982 243 50 0.960553278688525 104.106666666667
20000000 1024 243 40.0230532786885 216 45 1.00006400409626 99.9936
20000000 1024 216 45.0028801843318 251 39 0.993653337403337 100.63872
16000000 1024 251 31.0019841269841 251 31 1.00006400409626 99.9936
16000000 1024 255 30.517578125 251 31 1.00006400409626 99.9936
8000000 256 251 62.0039682539683 251 62 1.00006400409626 99.9936
8000000 1024 216 18.0011520737327 216 18 1.00006400409626 99.9936

プログラム本体は、以下。

#define FAST_MODE と #define ALONE_MODE のコメントアウトするかどうかで、1Sごとの通常測定と高精度測定、外部1pps入力と自前クロック(単独モード) での測定と切替ができます。

  • 計測対象クロックは、5に入力。
  • 外部1ppsは、8に入力。
  • 単独モードの場合は、11と8を結線
  • T0はシステム、T1は周波数測定、T2は自前クロックに使用しています。
  • LCDコントラストは、簡易ソフトウェアPWM
/*
   周波数カウンター
   カウンタ変数をULL化
   CPUを8MHzから24MHzに、それに伴いダイレクト計測周波数も2.5MHzから10MHzへ
   (※現状、テスト評価用の25MHzの設定。ALONE部分はクロックに合わせて変更が必要)
  ・割り込み処理ルーチンを最低限の処理にする。
  ・周波数測定方法を1000sを最長に100s間隔で周波数を表示
  最終的に1000sでの平均周波数を表示。
   +----------------------------+
   |Frequency/stability/accuracy|
   +----------------------------+
*/
//#include <Wire.h>
#include <LiquidCrystal.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#define rs 10
#define enable 9
#define rw 7
#define d4 6
#define d5 4
#define d6 3
#define d7 2
#define v0 12 //固定 ソフトウェアPWMの関係。変更する場合はPWMの部分も修正
#define bl 13
LiquidCrystal lcd(rs, rw, enable, d4, d5, d6, d7);
const int lcd_cont = 20; //LCDコントラスト ソフトウェアPWM(%)
#define ocxo 5       //OCXO 入力ピン
#define gps1ppm 8    //GPS 1Hz 入力ピン
#define FAST_MODE     //高精度測定時は、コメントアウト
#define ALONE_MODE    //GPS 1pps使用時は、コメントアウト。単独モード。 11と8を結線
const uint8_t debug = 0; // 1: debug code output
uint32_t debug_f = 0 ;
#define TIMER2_OCR2A_VAL  217                               // F_CPUによりratioと組み合わせ決定:224
#define TIMER2_TCCR2B_VAL (1<<CS22) | (1<<CS21) | (1<<CS20) // F_CPUによりratioと組み合わせ決定:1024
#ifdef ALONE_MODE
#define TIMER2_TCCR2A_VAL (1<<COM2A0) | (1<<WGM21) // 比較一致でOC2Aピン トグル(交互)出力&CTC動作
const uint32_t conversion = 9999166;   // 補正値(1/10000000)、計算値+OSC誤差の吸収
const uint8_t ratio = 1;
const uint16_t ratio_vct = 56 * ratio; // 分周比を考慮したカウント単位,F_CPUにより決定
const uint8_t accuracy_max = 5; /* ratioの整数n倍、時間をかけて正確に周波数を計測... */
volatile uint16_t carry_chk = 3000; /* キャリー追い越しチェック幅 -65535 */
#else
#define TIMER2_TCCR2A_VAL 0 // OC2Aピン 切断&標準動作
const uint32_t conversion = 10000000; // 補正値(1/10000000)
volatile uint16_t carry_chk = 6000; /* キャリー追い越しチェック幅 -65535 */
#ifdef FAST_MODE
const uint8_t ratio = 1; // 分周比を考慮したカウント単位
const uint8_t ratio_vct = ratio;
const uint8_t accuracy_max = 1; /* ratioの整数n倍、時間をかけて正確に周波数を計測... */
#else
const uint8_t ratio = 100; // 分周比を考慮したカウント単位
const uint8_t ratio_vct = ratio;
const uint8_t accuracy_max = 10; /* ratioの整数n倍、時間をかけて正確に周波数を計測... */
#endif
#endif
/* 精度設定 */
uint8_t accuracy = 0; /* 単位の周波数測定のカウンター */
volatile uint16_t intcarry;
volatile uint16_t carry;    /* 割り込みルーチン内で割り込み追い越しを配慮した溢れ回数を保存*/
volatile uint32_t start_cnt;
volatile uint32_t end_cnt;
volatile uint16_t gps_loop;
uint64_t  freq1000;
uint8_t ignore;
uint32_t count[accuracy_max] = {};
uint64_t count_chk;
volatile uint8_t f_check; //周波数処理確認フラグ 1:処理が必要な状態、0:処理完了 >2:処理追い越しが発生!
void timer_start(void)
{
  TCCR2A = TIMER2_TCCR2A_VAL;
  OCR2A = TIMER2_OCR2A_VAL;
  GTCCR = (1 << PSRASY);
  TCCR2B = TIMER2_TCCR2B_VAL;
  //  TIMSK2 = (1<<OCIE2A);
  DDRB |= (1 << PB3);
  //  PORTB &= ~_BV(PB3);
}
ISR(TIMER1_CAPT_vect) {
  /*
    WDT Reset
    if(カウンターフラグ無効)
      WDT起動
      ICR1に保存された値を保存
      カウンターフラグ有効に
    else
      ICR1に保存された値を保存
      溢れ割り込み追い越しをチェック
      桁溢れ回数を保存(intcarry -> carry + 1?)
      周波数処理確認フラグ += 1
    if(周波数処理確認フラグ > 1)
      発生回数・オーバーフロー回数を初期化
      カウンターフラグ無効
  */

  wdt_reset();
  ++ gps_loop;

  if (ignore > 0) {
    /* 最初はICR1値が不安定なので無視 */
    -- ignore ;
    /* GPS 1ppm 監視 WDT start */
    {
      MCUSR = 0;
      WDTCSR |= 0b00011000; //WDCE WDE set
      WDTCSR =  0b01000000 | 0b000111; /* /WDIE set 2s WDT */
    }
    start_cnt = (word)ICR1;
    /* キャり追い越し補正する */
    if ((start_cnt < carry_chk) && (TIFR1 & 0x01)) { /* CPUの割り込み処理が6サイクル+実行中コマンドだが実測から... */
      TIFR1 = 0b00000001; //Timer/Counter Overflow Flag clear(TOV1)
    }
    intcarry = 0;
    gps_loop = 0;

  } else {
    if (gps_loop == ratio_vct) {
      end_cnt = (word)ICR1;
      /* キャり追い越し補正する */
      if ((end_cnt < carry_chk) && (TIFR1 & 0x01)) { /* CPUの割り込み処理が6サイクル+実行中コマンドだが実測から3160... */
        ++ intcarry;
        TIFR1 = 0b00000001; //Timer/Counter Overflow Flag clear(TOV1)
      }
      carry = intcarry;
      ++ f_check;
      gps_loop = 0;
      intcarry = 0;
    }
  }
  if (f_check > 1) {
    ignore = 2;
    f_check = 0;
  }
}
ISR(TIMER1_OVF_vect) {
  /* オーバーフロー回数を加算 */
  if (!ignore) ++ intcarry;
}
/*
  WDT設定(2s)は、WDTCSRを47hにセットして、wdt_reset();
*/
ISR(WDT_vect) {
  wdt_reset();
  /*
      カウンターフラグを無効にする
      発生回数・オーバーフロー回数を初期化
  */
  start_cnt = 0;
  gps_loop = 0;
  ignore = 2;
  f_check = 0;
  {
    MCUSR = 0;
    WDTCSR |= 0b00011000; /* WDCE WDE set */
    WDTCSR =  0b00000000; /* status clear */
  }
}
void setup() {
  lcd.begin(20, 4);
  lcd.setCursor(0, 0);
  lcd.print("FreqCounter 10MHz");
  lcd.setCursor(0, 1);
  lcd.print(" 2018.12 by Drg.Jr. ");
  pinMode(v0, OUTPUT);
  //  digitalWrite(v0, LOW);     // LCD コントラスト
  //  PORTB &= ~_BV(4); //LOW    // SW PWMの為、PINコントロールを高速化
  //  PORTB |= _BV(4); //HIGH

  pinMode(bl, OUTPUT);
  digitalWrite(bl, HIGH);    // LCD バックライト ON

  if (debug) {
    Serial.begin(115200);
    Serial.println("Hello World!");
    Serial.println("");
  }


  //  lcd.clear();


  intcarry = 0;
  start_cnt = 0;
  end_cnt = 0;
  freq1000 = 0;
  ignore = 2;
  gps_loop = 0;
  f_check = 0;

  /*カウンターセットアップ*/
  timer_start(); //Stand alone用 基準クロック発生PB3/pin11

  cli();
  TCCR1A = 0;   /* TCCR1A タイマモード */
  TCCR1B = 0x46;      /*ICP, T1入力、立ち上がり,立下りを検知 */
  TCNT1 = 0x0000;  /* タイマ1の初期値設定   */
  TIMSK1 = 0x21;  /* 入力キャプチャ1オーバーフロー割り込みの許可 */
  //  TIFR1 = 0x21;  /* 入力キャプチャ1オーバーフロー割り込み要求クリア */
  sei(); /* 全体の割り込み許可 */
}
void loop() {
  /*----------Freq Counter & VOCXO Control----------*/
  if (f_check == 1) {

    count_chk = ((carry * 65536) + end_cnt) - start_cnt;
    start_cnt = end_cnt ;

    f_check = 0;

    if (!ignore) {
      count_chk = count_chk * conversion / 10000000; // 既知誤差の補正
      count[accuracy] = count_chk;

      if (accuracy < (accuracy_max - 1))accuracy += 1; else accuracy = 0;

      byte n = 0;
      uint64_t count_sum = 0;
      for (int i = 0 ; i < accuracy_max ; ++i) {
        if (count[i] > 0) {
          ++n;
          count_sum = count_sum + count[i];
        }
      }
      if (n > 0) {
        freq1000 = count_sum;
      } else freq1000 = 0;

      char msg15[15];
      char s8[9];
      if ((ratio * n) >= 1000) {
        uint16_t freq1000_surplus = (freq1000 % (ratio * n)) * 1000 / (ratio * n);
        sprintf(msg15, " %s.%03dHz ", dtostrf(uint32_t(freq1000 / (ratio * n)), 8, 0, s8), freq1000_surplus);
      } else if ((ratio * n) >= 100) {
        uint16_t freq1000_surplus = (freq1000 % (ratio * n)) * 100 / (ratio * n);
        sprintf(msg15, " %s.%02dHz ", dtostrf(uint32_t(freq1000 / (ratio * n)), 8, 0, s8), freq1000_surplus);
      } else if ((ratio * n) >= 10) {
        uint16_t freq1000_surplus = (freq1000 % (ratio * n)) * 10 / (ratio * n);
        sprintf(msg15, " %s.%01dHz ", dtostrf(uint32_t(freq1000 / (ratio * n)), 8, 0, s8), freq1000_surplus);
      } else {
        sprintf(msg15, " %sHz ", dtostrf(uint32_t(freq1000 / (ratio * n)), 8, 0, s8));
      }

      lcd.setCursor(0, 2);
      lcd.print("                    ");
      lcd.setCursor(0, 2);
      lcd.print(msg15);

      if (debug) {
        char msg4[4];
        sprintf(msg4, "%04d", uint16_t(freq1000 % 10000));
        Serial.print(uint32_t(freq1000 ));  Serial.print("," );
        Serial.print(msg4 );  Serial.print("," );
        Serial.print(long(count_chk ) ); Serial.print("," );
        Serial.print(n ); Serial.print("," );
        Serial.print(accuracy ); Serial.println("" );
      }

    } else {
      count[accuracy_max] = {};
      accuracy = 0;
    }

  }
  //--------------------------------------------
  /*
      LCDコントラスト調整
      高速化の為に直接レジスタを操作
      LCDコントラストの制御ピンを変更する場合は注意
  */
  int pwb_time = (micros() % 1000) / 10;
  if (pwb_time > lcd_cont) PORTB &= ~_BV(4); /*LOW*/ else PORTB |= _BV(4); /*HIGH*/
}

最後に、基準クロック発生器とツーショット

基準クロック発生器 と オーバークロック動作確認中のArduino 25MHz

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA