Arduino UNOでリアルタイムクロック(RTC)機能を追加する方法: 実践的ガイドとテクニック

UNO-ATmega328

ATmega328Pに用意されているTOSC1は、外部32.768kHzの水晶振動子(クリスタル)を接続するためのピンで、TOSC1とTOSC2のピンの2つが存在します。これらは主に、リアルタイムクロック(RTC)機能やタイマー/カウンターの低速外部クロックソースとして利用されま、これらのピンを通じて接続された低周波数のクリスタルは、非常に正確な時間計測を可能にします。

TOSCを使った2つの機能の具体的な方法

  1. リアルタイムクロック(RTC)
    ATmega328Pは直接的なRTCモジュールを内蔵していませんが、外部32.768kHzクリスタルをTOSCピンに接続することで、ソフトウェアベースのRTCを実装することが可能です。32.768kHzは2の15乗(32768)であり、1秒ごとのタイミングを生成するのに適した周波数です。
  2. タイマー/カウンターのクロックソース
    特定のアプリケーションでは、マイクロコントローラの内部クロックよりも低速でより精確なタイミングが求められることがあります。このような場合、TOSCピンに接続されたクリスタルをタイマー/カウンターのクロックソースとして使用できます。

電気的仕様と接続方法

TOSC1およびTOSC2のピンは、PDIP、TQFP、およびQFNパッケージのATmega328Pで利用可能です。これらのピンは、通常、ピン配置の端に位置しています。外部クリスタルは、TOSC1とTOSC2の間に接続し、適切なロードキャパシタもクリスタルの両側に接続します。これはクリスタルの仕様に基づいて選択されますが、これらのキャパシタは、クリスタルの起動時間を最適化し、周波数の安定性を高めるために重要な要素になります。

必要な部品

  • 32.768kHzのクリスタルオシレータ
  • 2つのロードキャパシタ(通常、22pFまたはクリスタルのデータシートに推奨されている値)

具体的な接続方法

クリスタルの配置は、 32.768kHzのクリスタルをTOSC1ピンとTOSC2ピンの間に接続します。たとえば、ATmega328PのPDIP-28パッケージでは、これらはピン9(TOSC1)とピン10(TOSC2)に相当します。次に、ロードキャパシタの接続:は、クリスタルの両端に接続します。各キャパシタの一方の端子はクリスタルの各端子に接続し、もう一方の端子はグラウンド(GND)に接続します。これらのキャパシタは、クリスタルの正確な振動を保証し、安定したクロック信号を生成するために必要です。

・イメージ図

    使用上の注意

    • クリスタルの接続距離はできるだけ短く保つことが重要です。長いリード線やトレースは、クリスタルの振動に影響を与え、周波数の安定性を損なう可能性があります。
    • 推奨されるロードキャパシタの値はクリスタルによって異なるため、クリスタルのデータシートを確認して適切な値を選択してください。
    • クリスタルとマイクロコントローラの間には、他の信号線やノイズ源がないように配慮してください。これにより、クリスタルの性能に悪影響を及ぼす可能性があるノイズの影響を最小限に抑えることができます。

    ソフトウエア実装例

    1.1秒間隔でLEDを光らせる

    Arduinoの環境でRTCを使用するためには、タイマー2を非同期モードで動作させ、外部クリスタルからのクロックを使用する設定が必要です。以下に、RTCを設定し、特定の時間間隔でLEDを点滅させるサンプルコードを示します。

    #include <avr/sleep.h>
    #include <avr/power.h>
    
    // LED接続ピン
    const int ledPin = 13;
    
    void setup() {
      pinMode(ledPin, OUTPUT);
      // タイマー2を非同期モードに設定
      ASSR |= (1 << AS2); // 外部クリスタルをタイマー2のクロックソースとして使用
      // タイマー2の設定
      TCCR2A = 0; // 通常モード
      TCCR2B = (1 << CS22) | (1 << CS20); // プリスケーラを128に設定
      // タイマー2の割り込みを有効化
      TIMSK2 |= (1 << TOIE2); // オーバーフロー割り込みを有効化
      // 割り込みが安定するまで待つ
      while (ASSR & ((1<<TCN2UB) | (1<<TCR2AUB) | (1<<TCR2BUB)));
      // 割り込みが設定されたらスリープモードを設定
      set_sleep_mode(SLEEP_MODE_PWR_SAVE);
      sleep_enable();
    }
    
    ISR(TIMER2_OVF_vect) {
      static int ledState = LOW;
      static unsigned long ticks = 0;
      // 1秒ごとにLEDの状態を切り替え
      if (++ticks >= 16) {
        ledState = !ledState;
        digitalWrite(ledPin, ledState);
        ticks = 0;
      }
    }
    
    void loop() {
      sleep_mode(); // 割り込みまでCPUをスリープさせる
    }

    コード解説

    • タイマー2の非同期設定: ASSRレジスタのAS2ビットを設定して、タイマー2が外部クリスタルからのクロックを使用するようにします。
    • プリスケーラの設定: プリスケーラを128に設定することで、オーバーフロー割り込みの頻度が約1秒ごとになります(32.768 kHz / 128 = 256 Hz、256カウントで1秒)。
    • スリープモード: マイクロコントローラの消費電力を削減するために、可能な限りスリープモードを使用します。
    • 割り込みハンドラ: タイマー2のオーバーフロー割り込みハンドラ内でLEDの状態を切り替え、約1秒ごとに点滅させます。

    このセットアップにより、Arduinoで正確なRTC機能を実装し、低消費電力で長期間動作させることが可能です。

    2.24時間ごとにLEDを光らせる

    ATmega328Pのアシンクロナスタイマー機能(タイマー2を使ったRTC機能)を利用して、24時間ごとに特定のアクション(例えばLEDを点灯するなど)を行います。

    #include <avr/sleep.h>
    #include <avr/power.h>
    
    // LED接続ピン
    const int ledPin = 13;
    
    void setup() {
      pinMode(ledPin, OUTPUT); // ピンを出力として設定
      // タイマー2をアシンクロナスモードに設定
      ASSR |= (1 << AS2);
      // タイマー2の設定
      TCCR2A = 0; // 通常モード
      TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // プリスケーラを1024に設定
      // タイマー2のオーバーフロー割り込みを有効化
      TIMSK2 |= (1 << TOIE2);
      // 割り込みが安定するまで待つ
      while (ASSR & ((1<<TCN2UB) | (1<<TCR2AUB) | (1<<TCR2BUB)));
      // スリープモードを設定(省電力モード)
      set_sleep_mode(SLEEP_MODE_PWR_SAVE);
      sleep_enable();
    }
    
    ISR(TIMER2_OVF_vect) {
      static unsigned long ticks = 0;
      // 24時間カウントする(32.768 kHz / 1024 のプリスケーラ = 約32 Hz、約32,000カウントで1日)
      if (++ticks >= 86400L) {
        digitalWrite(ledPin, HIGH); // LEDを点灯
        delay(1000); // 1秒間LEDを点灯状態に保つ
        digitalWrite(ledPin, LOW); // LEDを消灯
        ticks = 0; // カウンタをリセット
      }
    }
    
    void loop() {
      sleep_mode(); // 割り込みまでCPUをスリープさせる
    }

    コードの解説

    • タイマー設定: タイマー2をアシンクロナスモードに設定し、外部32.768 kHzクリスタルからクロックを供給します。プリスケーラを1024に設定して、オーバーフロー割り込みの発生頻度を下げ、24時間ごとのカウントを可能にします。
    • 割り込みハンドラ: タイマー2のオーバーフローで発生する割り込みを捕捉し、指定した時間が経過したらLEDを点灯させます。
    • スリープモード: デバイスの電力消費を最小限に抑えるために、割り込みが発生するまでCPUをスリープ状態にします。

    この設定により、Arduino Unoは非常に正確な24時間周期で動作するタイマーとして機能し、指定した時間になったらLEDを点灯させることができます。

    3.LCDを接続した時計サンプル

    Arduino UnoとLCD(液晶ディスプレイ)を使用して、外部32.768 kHzクリスタルを用いたリアルタイムクロック(RTC)機能で時計を作るプロジェクトの構築について説明します。このプロジェクトでは、ArduinoにLCDを接続して現在の時刻を表示します。

    接続図イメージ
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>
    #include <avr/sleep.h>
    #include <avr/power.h>
    
    // LCDのアドレスとサイズ設定
    LiquidCrystal_I2C lcd(0x27, 16, 2);
    
    void setup() {
      lcd.init(); // LCDの初期化
      lcd.backlight(); // バックライトのオン
      // タイマー2をアシンクロナスモードに設定
      ASSR |= (1 << AS2);
      TCCR2A = 0; // 通常モード
      TCCR2B = (1 << CS22) | (1 << CS20); // プリスケーラを128に設定
      while (ASSR & ((1<<TCN2UB) | (1<<TCR2AUB) | (1<<TCR2BUB)));
      TIMSK2 |= (1 << TOIE2); // タイマー2の割り込みを有効化
      set_sleep_mode(SLEEP_MODE_PWR_SAVE);
      sleep_enable();
    }
    
    ISR(TIMER2_OVF_vect) {
      static unsigned long ticks = 0;
      static unsigned int seconds = 0;
      static unsigned int minutes = 0;
      static unsigned int hours = 0;
      if (++ticks >= 32) { // 約1秒ごと
        ticks = 0;
        if (++seconds >= 60) {
          seconds = 0;
          if (++minutes >= 60) {
            minutes = 0;
            if (++hours >= 24) {
              hours = 0;
            } 
         }
       }
       // 時間の更新
       char buffer[9];
       sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
       lcd.setCursor(0, 0);
       lcd.print(buffer);
      }
    }
    
    void loop() {
      sleep_mode(); // スリープモード
    }

    説明

    • LCDの設定: 16×2のI2Cインターフェース付きLCDディスプレイを使用しています。LiquidCrystal_I2C ライブラリを使って初期化し、時刻を表示します。
    • タイマー2の設定: タイマー2をアシンクロナスモードで設定し、1秒ごとに割り込みを発生させて時刻を更新します。

     

    コメント

    タイトルとURLをコピーしました