DS3231 RTC (for Raspberry PI)

RTC DS3231をRaspbrry PIで使ってみました。このディバイスのインターフェースはI2Cです。I2Cのセットアップに関しては、”I2Cを使う”参照下さい。

配線

下記の様に配線しています。信号線は、”I2Cを使う”と同じです。DS3231のSQWピンはアラーム出力。今回はRaspberry PIで割り込みの検証を行う為に、SQWをRaspberry PIの7番ピン(GPIO04)につないでいます。

DS3231 Raspberry Pi
VCC
GND
SCL
SDA
SQW

制御用プログラム

DS3231に関しては以前ここで、”RTC DS3231” 説明していますが、この時はESP32を使用してDS3231を制御していました。そこでRaspberry PI用に制御用のプログラムを書いて見ました。使用言語は、”Python” です。このデバイスは設定する項目が多く、下記の様に多くの関数となりました。

 

関数 引数1 引数2 引数3 内容
set_all(str_data,mode): 文字列 0:時計
1:アラーム1
2:アラーム2
  時計、アラーム1,2の設定をモードに合わせて行う
set_sec(data,mode) 秒データ
(0-59)
0:時計
1:アラーム1
  対象への秒の設定
set_min(data,mode) 分データ
(0-59)
0:時計
1:アラーム1
2:アラーム2
  対象への分の設定
set_hour(data,mode,fl) 時データ
(0-23)
 (1-12)
0:時計
1:アラーム1
2:アラーム2
0:24H
1:12H
対象への時の設定
12時間制の午後はマイナス表示
 → 午後2時:−2時
set_day(data) 曜日データ
(1-7)
    対象への曜日の設定 1:日曜 7:土曜
set_date(data,mode) 日データ
(1-31)
0:時計
1:アラーム1
2:アラーム2
  対象への日の設定
アラーム設定でのマイナス値は曜日設定となる
set_month(data) 月データ
(1-12)
    月の設定
set_year(data) 年データ
(0-99)
    年の設定。20xxの最後の2桁を指定
         
set_data(data,addr,mode) 書き込む
データ
アドレス 0:時計
1:アラーム1
2:アラーム2
DS3231のアドレスを指定してモードに合わせてデータを書き込む
set_alarm_reg(bit_data,mode) マスク
データ
1:アラーム1
2:アラーム2
  マスクデータの書き込み
set_alarm_flg(data) データ     アラームフラグビットへの書き込み
stop_alarm(mode) 1:アラーム1
2:アラーム2
3:両方
    アラームタイマー停止
start_alarm(mode) 1:アラーム1
2:アラーム2
3:両方
    アラームタイマー開始
関数 引数1 引数2 戻り値 内容
get_all(para,mode) 読込用配列
ポインター
0:時計
1:アラーム1
2:アラーム2
データ モードに合わせてデータを読み込む
get_sec(mode) 0:時計
1:アラーム1
2:アラーム2
  モードに合わせた秒の読み込み
get_min(mode) 0:時計
1:アラーム1
2:アラーム2
  モードに合わせた分の読み込み
get_hour(mode) 0:時計
1:アラーム1
2:アラーム2
  モードに合わせた時の読み込み
12時間制の午後はマイナス表示
get_day()     曜日 曜日の読み込み
get_date(mode) 0:時計
1:アラーム1
2:アラーム2
  モードに合わせた日の読み込み
アラームでマイナス表示は曜日を表す
get_month()     月の読み込み
get_year()     年の読み込み
get_data(addr,mode) アドレス 0:時計
1:アラーム1
2:アラーム2
データ モードに合わせた時間データ
chk_ampm(mode) 0:時計
1:アラーム1
2:アラーム2
  0:24h
1:12h
24/12のチェック
get_alarm_reg(mode) 1:アラーム1
2:アラーム2
  下位4/3
ビット
マスクデータの読み込み
get_alarm():     下位3ビット イネーブルビットの状態
get_alarm_flg()     0−3 フラグビットの状態
         

プログラムと動作の確認

3231.py

import RPi.GPIO as GPIO
import smbus
import time

class DS3231:
    bus = smbus.SMBus(1)

    def set_all(self,str_data,mode):
        para=[1,2,3,4,5,6,7]
        c = 7
        offset = 0

        if mode == 1: 
            c = 4
            offset = 7
        if mode == 2:
            c = 3
            offset = 11

        b = 0
        for a in range(c):
            cmd = ""
            while str_data[b] != ',' :
                cmd += str_data[b]
                b += 1

            para[c-a-1] = int(cmd)
            b += 1

        a = 0
        if str_data[b] == '1': a = 40

        b = 2
        if a != 0:
            if mode == 2: b = 1
            if para[b] < 0: 
                para[b] *= -1 
                a = 60
            para[b] += a

        for a in range(c):
            self.set_data(para[a],a,mode)
           
    def set_sec(self,data,mode):
        self.set_data(data,0,mode)

    def set_min(self,data,mode):
        self.set_data(data,1,mode)

    def set_hour(self,data,mode,fl):
        a = 0
        if fl == 1: 
            a = 40
            if data < 0: 
                data *= -1 
                a = 60
        data += a
        self.set_data(data,2,mode)

    def set_day(self,data):
        self.bus.write_byte_data(0x68, 3, data)

    def set_date(self,data,mode):
        addr = 4
        if mode == 1: addr = 10
        if mode == 2: addr = 13
        if mode != 0:        
            if data < 0:
                data *= -1
                data += 40
        self.set_data(data,addr,0)

    def set_month(self,data):
        self.set_data(data,5,0)

    def set_year(self,data):
        self.set_data(data,6,0)

    def set_data(self,data,addr,mode):
        data = ((data // 10) << 4) + ( data % 10)
        if mode == 1: addr += 7
        if mode == 2: addr += 10
        self.bus.write_byte_data(0x68, addr, data)

    def get_all(self,para,mode):
        data = 7
        if mode == 1: data = 4
        if mode == 2: data = 3

        for a in range(data):
            flg = 0
            if mode == 2:
                if a == 1: flg = 1
                if a == 2: flg = 2
            if mode == 1:
                if a == 2: flg = 1
                if a == 3: flg = 2
            if mode == 0:
                if a == 2: flg = 1

            if flg == 0: para[a] = self.get_data(a,mode)
            if flg == 1: para[a] = self.get_hour(mode)
            if flg == 2: para[a] = self.get_date(mode)


    def get_sec(self,mode):
        return(self.get_data(0,mode))

    def get_min(self,mode):
        return(self.get_data(1,mode))

    def get_hour(self,mode):
        addr = 2
        if mode == 1: addr = 9
        if mode == 2: addr = 12
        a = self.bus.read_byte_data(0x68, addr)

        if mode != 0: a &= 0x7f

        flg = 0
        if (a & 0x40) != 0:
            if (a & 0x20) != 0:
                flg = 1
            a &= 0x1f

        a = (a >> 4) * 10 + ( a & 0xf)

        if flg == 1:
            a *= -1

        return(a)

    def get_day(self):
        return(self.bus.read_byte_data(0x68, 3))

    def get_date(self,mode):
        addr = 4
        if mode == 1: addr = 10
        if mode == 2: addr = 13
        a = self.bus.read_byte_data(0x68, addr)

        flg = 0
        if mode != 0:
            if (a & 0x40) != 0: flg = 1
            a &= 0x3f

        a = (a >> 4) * 10 + ( a & 0xf)

        if flg == 1: a *= -1

        return(a)

    def get_month(self):
        return(self.get_data(5,0))

    def get_year(self):
        return(self.get_data(6,0))

    def get_data(self,addr,mode):
        if mode == 1: addr += 7
        if mode == 2: addr += 11
        a = self.bus.read_byte_data(0x68, addr)
        if mode != 0: a &= 0x7f
        a = (a >> 4) * 10 + ( a & 0xf)
        return(a)

    def chk_ampm(self,mode):
        addr = 2
        if mode == 1: addr = 9
        if mode == 2: addr = 12
        flg = 0
        a = self.bus.read_byte_data(0x68, addr)
        if (a & 0x40) != 0: flg = 1
        return(flg)

    def set_alarm_reg(self,bit_data,mode):
        addr = 7
        num = 4
        if mode == 2: 
            addr = 11
            num = 3

        flg = 1
        for a in range(num):
            data = self.bus.read_byte_data(0x68, addr + a)
            if (bit_data & flg) != 0: data |= 0x80
            else: data &= 0x7f
            self.bus.write_byte_data(0x68, addr + a, data)
            flg = flg << 1

    def set_alarm_flg(self,data):
        a = self.bus.read_byte_data(0x68, 0x0f) & 0xfc
        a |= data
        self.bus.write_byte_data(0x68, 0x0f, a)

    def get_alarm_reg(self,mode):
        addr = 6
        num = 4
        if mode == 2: 
            addr = 10
            num = 3

        flg = 0
        for a in range(num,0,-1):
            flg = flg << 1
            data = self.bus.read_byte_data(0x68, addr + a)
            if (data & 0x80) != 0: flg |= 1
            else: flg &= 0x7f

        return(flg)

    def get_alarm(self):
        return(self.bus.read_byte_data(0x68, 0x0e) & 0x7)

    def get_alarm_flg(self):
        return(self.bus.read_byte_data(0x68, 0x0f) & 0x3)

    def stop_alarm(self,mode):
        data = self.bus.read_byte_data(0x68, 0x0e)
        data &= 0xf8
        data |= 0x04
        mode = ~mode
        data &= mode
        self.bus.write_byte_data(0x68, 0x0e, data)

    def start_alarm(self,mode):
        data = self.bus.read_byte_data(0x68, 0x0e)
        data &= 0xf8
        data |= 0x04
        data |= mode
        self.bus.write_byte_data(0x68, 0x0e, data)

    def __init__(self):

        # 4番pinを入力、プルアップに設定
        pin = 4
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(pin, GPIO.IN, GPIO.PUD_UP) 

        # 割り込みイベント設定
        GPIO.add_event_detect(pin, GPIO.FALLING, callback=self.callback_one)
                    
    def callback_one(self, channel):
        b = a = self.get_sec(0)
        a += 3;
        if a > 59: a -= 60
        self.set_sec(a,1)
        print("Callback IN: Now: %d Next: %d" % (b,a))
        self.set_alarm_flg(0)

print("start")

test = DS3231()
test.set_all("21,12,31,1,23,59,56,0",0)

test.set_alarm_flg(0)
test.set_alarm_reg(0x0e,1)
a = test.get_sec(0)
print("sec: %d" % a)
a += 3;
test.set_sec(a,1)

test.start_alarm(1)

para = [1,2,3,4,5,6,7]
for a in range(10):
    test.get_all(para,0)
    print("20" + str(para[6]) + '/' + str(f'{para[5]:02}') + '/' + str(f'{para[4]:02}') + ' '\
          + str(f'{para[2]:02}') + ':' + str(f'{para[1]:02}') + ':' + str(f'{para[0]:02}'))
    time.sleep(1)

test.stop_alarm(1)
print("end")

プログラムの251行目(print(“start”))から下が確認プログラムです。時間設定後、毎秒時間を表示するプログラムです。

まとめて時間を設定するには、”set_all()”関数を使います。引数は、文字列で、”21,12,31,1,23,59,56,0″とした場合

  • 1つ目: 20XXのXX部分。
  • 2つ目: 月を指定
  • 3つ目: 日を指定
  • 4つ目: 曜日指定。1が日曜日  7が土曜日
  • 5つ目: 時を指定。24時制(0−23) 12時制(1−12)午後はマイナスで指定
  • 6つ目: 分を指定
  • 7つ目: 秒を指定
  • 8つ目: 時間制を指定。 0:24時間制  1:12時間制 

から、2021年12月31日23時59分56秒。となります。実行すると

2021年12月31日23時59分56秒から始まって毎秒毎に時間を表示しています。ちゃんと年を越しています。

割り込み確認

上記のプログラムで、print(“start”)以下を下記の様に書き換えると、タイマーのアラーム信号でRaspberry PIに割り込みが掛けられます。


print("start")
test = DS3231()
test.set_all("21,12,31,1,23,59,56,0",0)
test.set_alarm_flg(0)
test.set_alarm_reg(0x0e,1)
a = test.get_sec(0)
print("sec: %d" % a)
a += 3;
test.set_sec(a,1)
test.start_alarm(1)
para = [1,2,3,4,5,6,7]
for a in range(10):
    test.get_all(para,0)
    print("20" + str(para[6]) + '/' + str(f'{para[5]:02}') + '/' + str(f'{para[4]:02}') + ' '\
          + str(f'{para[2]:02}') + ':' + str(f'{para[1]:02}') + ':' + str(f'{para[0]:02}'))
    time.sleep(1)
test.stop_alarm(1)
print("end")
  • test.set_alarm_reg(0x0e,1): アラームマスクを秒に設定
  • test.set_sec(a,1): アラーム1発動の時間を現在の時間から3秒後に設定。
  • test.start_alarm(1): アラーム1タイマーカウント開始

割り込み処理関数は、def callback_one(self, channel):です。ここでは、print(“Callback IN: Now: %d Next: %d” % (b,a))で割り込みが起こった時間と次の割り込み時間を表示します。また、次の割り込み時間の設定とフラグをクリアしています。実行すると下記の様になります。

startの下に表示されている、sec: 56 はプログラムの開始の時間です。この3秒後(59秒)にアラームが発生するよう設定しています。プログラムは、毎秒毎に時間を表示していますが、59秒で、”Callback IN: Now: 59 Next: 2”と割り込み表示が行われます。次の割り込みは2秒です。その通りに割り込みが発生していることが分かります。