RTC DS3231をRaspbrry PIで使ってみました。このディバイスのインターフェースはI2Cです。I2Cのセットアップに関しては、”I2Cを使う”参照下さい。
配線
下記の様に配線しています。信号線は、”I2Cを使う”と同じです。DS3231のSQWピンはアラーム出力。今回はRaspberry PIで割り込みの検証を行う為に、SQWをRaspberry PIの7番ピン(GPIO04)につないでいます。
DS3231 | Raspberry Pi |
VCC | 1 |
GND | 6 |
SCL | 5 |
SDA | 3 |
SQW | 7 |
制御用プログラム
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秒です。その通りに割り込みが発生していることが分かります。