PR

Arduino UNO R4のクロック周波数を測る – LCDカウントアップ

前の記事の続き。

この記事では、USB接続の有無でCPUクロックの精度が変わることを確認した。これの補足というか追加試験を実施。

その記事の実験の前半で使っていたPWMで1MHzを出力しつつLEDの点滅に、LCDにカウントアップ表示を追加。これでストップウォッチでの確認をしやすくする。LCD表示なのでUSBケーブル接続も不要。ソースコードは後ほど。

早速、結果。

ストップウォッチでの計測では、やや進む程度(ストップウォッチに対して)。表示の上段が単純なカウントアップ、下段はそれをHH:MM:SS形式で表示したもの。

なお、開始時は、5 4 3 2 1 0 1 2 3 …のように、5からカウントダウンして0からカウントアップする。これでストップウォッチのスタートタイミングを取りやすい。

続いて1MHzの出力を周波数カウンタで測定。USBケーブルは非接続。

結構ふらつくが、まぁ、1000.2kHz程度かな?

念のため、USBケーブルを接続した状態。

予想通り、1000.008kHzくらい。やはり、USBケーブルをつながないと周波数が上がる。+0.2%くらい。

念のため、CPUクロック出力に入れ替えて測定(前の実験のソフト)。48MHz出力。

上はUSBケーブル接続状態。想定通りの周波数。

続いて、USBケーブ非接続。

こちらも前回の実験と同様の結果。

ついでに、LOCO(RTCに使われるクロック)も、USBケーブル接続の有無で測定(前回は測定しなかったので)。

USBケーブル接続。

USBケーブル非接続。

こちらはほぼ変化なし。やはり、LOCOはUSBクロックでの補正はしていないようだ。

最後にソースコード。生成AIに指示して作らせた。

/*
  Arduino UNO R4用
  - D11ピンからPWM信号を出力
  - I2C接続の1602 LCDにカウンタ値とHH:MM:SS形式を表示
  - D8-GND間のボタンスイッチでカウント開始・停止・リセット制御
  - カウント中は内蔵LEDを2:8の比率で点滅
  - 起動時はLCDに"Ready"を表示し、ボタン押下でカウント開始
  - カウント開始時は5から0までダウンカウント、その後はカウントアップ
*/

#include "pwm.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define PWM_FREQ 1000000.0f    // PWMの出力周波数(1MHz)
#define LED 13                 // 内蔵LEDピン番号
#define PWM_PIN 11             // PWM出力ピン番号
#define BUTTON_PIN 8           // ボタン入力ピン番号
#define DEBOUNCE_DELAY 10      // デバウンス対策用待ち時間(ms)
#define START_DOWNCOUNT 5      // ダウンカウント開始値

LiquidCrystal_I2C lcd(0x27, 16, 2); // LCDのI2Cアドレス(必要に応じて変更)

PwmOut pwm(PWM_PIN);

// 動作状態:READY(待機), RUNNING(カウント中), STOPPED(一時停止)
enum State { READY, RUNNING, STOPPED };
State state = READY;           // 初期状態はREADY

unsigned long lastBlinkMillis = 0;   // 最後にLEDを切り替えた時刻
int counter = 0;                     // 現在のカウント値(アップカウント時は増加、ダウン時は減少)
bool ledOn = false;                  // LEDの点灯状態
bool lastButtonState = HIGH;         // 前回のボタン状態
bool debounceLock = false;           // デバウンス制御用フラグ
bool stopToReset = false;            // STOPPED→リセットフラグ
bool isDownCount = false;            // ダウンカウント中かどうか

void setup() {
  pinMode(LED, OUTPUT);              // 内蔵LEDを出力に設定
  pinMode(BUTTON_PIN, INPUT_PULLUP); // ボタンはプルアップ入力
  pwm.begin(PWM_FREQ, 50.0f);        // PWM出力開始(周波数、デューティ比)

  lcd.init();                        // LCD初期化
  lcd.backlight();                   // バックライトON

  // 起動時に"Ready"をLCDに表示(そのまま保持)
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Ready");
}

void loop() {
  handleButton();    // ボタン入力処理

  if (state == RUNNING) {
    handleLedBlink();  // LED点滅・カウントアップ/ダウン処理
  } else {
    // カウント停止時はLED消灯
    digitalWrite(LED, LOW);
    ledOn = false;
  }
}

// ボタン入力を監視し、状態遷移を管理
void handleButton() {
  bool buttonState = digitalRead(BUTTON_PIN); // 現在のボタン状態読み取り

  // デバウンス処理
  if (buttonState != lastButtonState) {
    delay(DEBOUNCE_DELAY);
  }

  // ボタンが離された瞬間(立ち上がり検出)で状態遷移
  if (buttonState == HIGH && lastButtonState == LOW && !debounceLock) {
    debounceLock = true;       // 連続検出防止
    switch (state) {
      case READY:
        // READY→RUNNING、ダウンカウント開始、LCD表示をカウンタに切り替え
        counter = START_DOWNCOUNT;
        isDownCount = true;
        state = RUNNING;
        lastBlinkMillis = millis();
        lcd.clear();           // クリアはカウント開始時のみ
        displayCounter();
        stopToReset = false;
        break;
      case RUNNING:
        state = STOPPED;       // 動作中→停止
        stopToReset = true;    // 次回ボタンでリセット
        break;
      case STOPPED:
        if (stopToReset) {
          // 停止中に再度押されたら再びダウンカウントからRUNNING
          counter = START_DOWNCOUNT;
          isDownCount = true;
          state = RUNNING;
          lastBlinkMillis = millis();
          lcd.clear();         // クリアはカウント再開時のみ
          displayCounter();
          stopToReset = false;
        }
        break;
      default:
        // 不定状態はREADYに戻す
        state = READY;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Ready");
        stopToReset = false;
        break;
    }
  }
  // ボタンが押された瞬間(立ち下がり検出)でデバウンス解除
  else if (buttonState == LOW && lastButtonState == HIGH) {
    debounceLock = false;
  }
  lastButtonState = buttonState; // 状態更新
}

// LED点滅とカウントアップ/ダウン処理
void handleLedBlink() {
  unsigned long now = millis();
  if (!ledOn && now - lastBlinkMillis >= 800) { // 消灯800ms後に点灯
    digitalWrite(LED, HIGH);
    ledOn = true;
    lastBlinkMillis = now;
    // LED点灯のタイミングでカウント処理
    if (isDownCount) {
      counter--;
      if (counter < 0) {
        counter = 1;      // 0の次は1からアップカウント
        isDownCount = false;
      }
    } else {
      counter++;
    }
    displayCounter();
  } else if (ledOn && now - lastBlinkMillis >= 200) { // 点灯200ms後に消灯
    digitalWrite(LED, LOW);
    ledOn = false;
    lastBlinkMillis = now;
  }
}

// LCDにカウント値と時分秒形式を表示(lcd.clear()は呼ばない)
void displayCounter() {
  // 1行目:カウンタ値(そのまま表示)
  lcd.setCursor(0, 0);
  lcd.print(counter);

  // 2行目:HH:MM:SS形式に変換して表示
  int absCounter = counter;
  if (absCounter < 0) absCounter = 0; // 負値は0で表示
  int sec = absCounter % 60;
  int min = (absCounter / 60) % 60;
  int hour = absCounter / 3600;
  lcd.setCursor(0, 1);
  char buf[16];
  sprintf(buf, "%02d:%02d:%02d", hour, min, sec);
  lcd.print(buf);
}
自作
この記事のタイトルとURLをコピーする
スポンサーリンク
スポンサーリンク
スポンサーリンク
スポンサーリンク
スポンサーリンク

コメント