Raspberry Pi PicoでRTCモジュールDS1302を使いこなす!動作原理とプログラミング解説

目次

はじめに

電子工作をしていると時間を正確に計測、表示したい場合もあると思います。WiFiにつながっていれば時刻情報を取得することも可能ですが、そうでない場合は困ります。今回は正確に時を刻むことのできるリアルタイムクロック(DS1302)をご紹介します。

リアルタイムクロックの作動原理

リアルタイムクロック(RTC)は、内部クロック信号を利用して時間をカウントし続ける仕組みです。

クロック信号の生成

RTCは時間を計測するために安定したクロック信号を必要とします。クロック信号は、以下の部品で生成します

水晶発振子(Quartz Crystal Owcillator)

特定の結晶(石英など)に電気を与えると振動を始める特性を利用しています。この現象は、「圧電効果」と「逆圧電効果」と呼ばれています。結晶に外力(電力)を加えると結晶内部の電荷のバランスが変化し、結晶表面に電位差(電圧)が生じます。その結果、電力で振動した結晶が電気を発生します。この電気は結晶固有の周波数(kHz)で発生します。

RTCでは、32.768kHzの水晶発振子(石英などを利用した電子部品)を用います。数字が細かい理由は、この数字が2の15乗で割り切れる為、2進数処理が容易であるためです。

内部発振回路

水晶発振子振動を利用して、RTC内部でクロック信号を発生する

クロック信号の分周

水晶発振子が生成する32.768kHzの信号は、人間が認識できる時間単位(秒、分、時など)に変換するために分周(frequency division)されます。

分周回路

32.768kHzのクロック信号を、カウンタ回路で1Hz(1秒ごと)に分周します。これにより正確な1秒間隔の信号が得られます。

カウンタ回路

RTCは、時間をカウントするための複数のカウンタを内部に持っています

秒カウンタ

1Hzの信号を受け取り、1秒ごとにカウンタを1つ進めます。

分、時、日付カウンタ

秒カウンタが60になると、分カウンタを1つ進める。
日付や月カウンタも、日数や月数に応じて自動的に進むように設計されています

データの保持

RTCは、現在の時刻や日付を保持するために小規模なレジスタ(メモリ)を内蔵しています

データ形式

BCD(Binary Coded Decimal)形式でデータを格納

バックアップ電源

RTCは低消費電力で動作し、メイン電源が切れてもコイン電池などのバックアップ電源で動作を継続可能。そのため、メイン電源をOFFにしても時刻がリセットされません

日常生活での活用例

・時計機能の提供(デジタル時計、スマートウォッチ)
・タイムスタンプ付きのデータの記録(センサー、ログ記録)
・タイミング制御(アラーム、定期イベント)

配線

RTCを接続します

RaspberryPi Picoリアルタイムクロック
VSYS(39番)VCC
GND(38番)GND
GP14(19番)CLK
GP15(20番)DAT
GP16(21番)RST
()内はピン番号

リアルタイムクロックを使ってみる

詳細は秋月電子さんのHPからデータシートを確認ください。

from machine import Pin
import time

class DS1302:
    def __init__(self, clk_pin, dat_pin, rst_pin):
        self.clk = Pin(clk_pin, Pin.OUT)
        self.dat = Pin(dat_pin, Pin.OUT)
        self.rst = Pin(rst_pin, Pin.OUT)
        self.rst.value(0)
        self.clk.value(0)

    def _write_byte(self, byte):
        for i in range(8):
            self.dat.value((byte >> i) & 1)
            self.clk.value(1)
            time.sleep_us(1)
            self.clk.value(0)
            time.sleep_us(1)

    def _read_byte(self):
        value = 0
        self.dat.init(Pin.IN)
        for i in range(8):
            if self.dat.value():
                value |= (1 << i)
            self.clk.value(1)
            time.sleep_us(1)
            self.clk.value(0)
            time.sleep_us(1)
        self.dat.init(Pin.OUT)
        return value

    def _burst_read(self):
        self.rst.value(1)
        self._write_byte(0xBF)  # Burst read command
        data = []
        for _ in range(8):
            data.append(self._read_byte())
        self.rst.value(0)
        return data

    def _burst_write(self, data):
        self.rst.value(1)
        self._write_byte(0xBE)  # Burst write command
        for byte in data:
            self._write_byte(byte)
        self.rst.value(0)

    def get_time(self):
        data = self._burst_read()
        second = (data[0] >> 4) * 10 + (data[0] & 0x0F)
        minute = (data[1] >> 4) * 10 + (data[1] & 0x0F)
        hour = (data[2] >> 4) * 10 + (data[2] & 0x0F)
        day = (data[3] >> 4) * 10 + (data[3] & 0x0F)
        month = (data[4] >> 4) * 10 + (data[4] & 0x0F)
        year = (data[6] >> 4) * 10 + (data[6] & 0x0F) + 2000
        return year, month, day, hour, minute, second

    def set_time(self, year, month, day, hour, minute, second):
        data = [
            ((second // 10) << 4) | (second % 10),
            ((minute // 10) << 4) | (minute % 10),
            ((hour // 10) << 4) | (hour % 10),
            ((day // 10) << 4) | (day % 10),
            ((month // 10) << 4) | (month % 10),
            0,  # Day of the week (not set)
            ((year % 100) // 10) << 4 | (year % 10),
            0  # Control byte (not used)
        ]
        self._burst_write(data)

# 使用例
rtc = DS1302(clk_pin=14, dat_pin=15, rst_pin=16)
rtc.set_time(2024, 12, 14, 18, 0, 0)  # 時刻を設定

while True:
    print("現在の時刻:", rtc.get_time())
    time.sleep(1)

まとめ

時計はあって当たり前と考えがちですが、時を正確に測る方法を知っておくのはとても大切だと思います。ご自身で、ぜひ一度チャレンジしてみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次