MicroPythonでリアルタイムクロックRTC-8564NBを使う

前回、PICでアバウトにRaspberryPiを間欠動作させましたが、ちゃんとした間欠動作をやろうと思って、リアルタイムクロックモジュール(RTC-8564NB)を買ってきました。(ホントは結構前に買ってたんですけど、やっと手を付けました・・・^^;)

 

リアルタイムクロック(RTC)モジュール ¥500
https://akizukidenshi.com/catalog/g/gI-00233/

 

■ 構成

RTCモジュールの操作にはM5Stack ATOM Lite(ESP32搭載)を使って、MicroPythonでI2C通信することで操作しました。

このRTCモジュールは、半田ショートさせることで、SDL、SDAのプルアップ(2.2kΩ)、INT出力(アクティブロー)時にLED点灯させることができますが、全部(3箇所)半田ショートしました。

ATOM Lite        RTC-module
3.3V/5V -------- 8pin(VDD)
GND ------------ 4pin(VSS)
G21 ------------ 6pin(SCL)
G25 ------------ 5pin(SDA)

 

■ プログラム(MicroPython)

珍しくサブモジュールとして使えそうなところまで書きました。PICの後だと、MicroPython簡単、楽しい。

このRTCには、アラーム機能とタイマー機能があり、それらを使うとこまで書きました。が、その他クロック出力機能とか自分が使う見込みがないものは、実装していません。また、週の設定も実装してません。

 

【参考サイト】

RTC-8564の使い方
https://www.elec-hobbyist.com/MicomMemo/Pgm_Method/Pgm_method_4.html

Z80に萌えたい ラズパイでRTC設定編
https://www.rikachann.jp/blog/pinecone/2016/09/18/z80に萌えたい ラズパイでrtc設定編/

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import machine
import math
from machine import Pin, SoftI2C
class RTC_8564NB:
def __init__(self,scl,sda,freq=100000,reset=True):
self.connected = False
self.i2c = SoftI2C(scl=Pin(scl), sda=Pin(sda), freq=freq)
ls = self.i2c.scan()
if len(ls) == 0:
print('error: cannot find i2c slave')
return
found = False
for addr in ls:
if addr == 0x51:
found = True
break
if not found:
print('error: cannot find RTC_8564NB')
return
self.connected = True
if reset:
self.reset()
def reset(self):
self.i2c.writeto(0x51,b'\x00\x00\x00')
def set_time(self,years,months,days,hours,minutes,secconds):
if not self.connected:
print('error: disconnect RTC module')
return
if years > 2000:
century = 1
else:
century = 0
years = int2bcd(years % 100)
months = int2bcd(months) & 0b00011111
if century:
months = months | 0b10000000
days = int2bcd(days)
hours = int2bcd(hours)
minutes = int2bcd(minutes)
secconds = int2bcd(secconds)
weekdays = 0b000 #sunday (not used)
values = bytes([2,secconds,minutes,hours,days,
weekdays,months,years])
self.i2c.writeto(0x51,values)
def time(self):
if not self.connected:
print('error: disconnect RTC module')
self.i2c.writeto(0x51, b'\x02')
data = self.i2c.readfrom(0x51, 7)
vol_low = data[0] >> 7
if vol_low == 1:
print('error: voltage low bit is set. must be reset.')
return
secconds = bcd2int(data[0] & 0b01111111)
minutes = bcd2int(data[1] & 0b01111111)
hours = bcd2int(data[2] & 0b00111111)
days = bcd2int(data[3] & 0b00111111)
months = bcd2int(data[5] & 0b00011111)
century = data[5] >> 7
years = bcd2int(data[6])
if century == 1:
years += 2000
else:
years += 1900
return [years,months,days,hours,minutes,secconds]
def set_alarm(self,day=None,hour=None,minute=None):
self.i2c.writeto(0x51,b'\x09\x80\x80\x80') #reset
if day != None:
day = int2bcd(day) & 0b00111111
self.i2c.writeto(0x51,bytes([0x0B,day]))
if hour != None:
hour = int2bcd(hour) & 0b00111111
self.i2c.writeto(0x51,bytes([0x0A,hour]))
if minute != None:
minute = int2bcd(minute) & 0b01111111
self.i2c.writeto(0x51,bytes([0x09,minute]))
def start_alarm(self):
self._alarm_enable(True)
def stop_alarm(self):
self._alarm_enable(False)
def _alarm_enable(self,enable):
self.i2c.writeto(0x51, b'\x01')
data = self.i2c.readfrom(0x51, 1)[0]
if enable:
data = data | 0b00000010 #set AIE
else:
data = data & 0b11111101 #clear AIE
self.i2c.writeto(0x51,bytes([0x01,data]))
def alarm_flag(self,clear=False):
self.i2c.writeto(0x51,b'\x01')
data = self.i2c.readfrom(0x51,1)[0]
r = (data & 0b00001000) >> 3
if clear:
data = data & 0b11111101 #clear
self.i2c.writeto(0x51,bytes([0x01,data]))
return r
def set_timer(self,count,freq_mode=2,pulse_mode=False):
'''
freq_mode: 0 => 4096Hz, 1 => 64Hz, 2 => 1Hz, 3 => 1/60Hz
count: 0~256
pulse_mode: True/False
'''
#write timer control
if freq_mode == 1:
data = 0b01
elif freq_mode == 2:
data = 0b10
elif freq_mode == 3:
data = 0b11
self.i2c.writeto(0x51,bytes([0x0E,data,count]))
#write control 2
self.i2c.writeto(0x51,b'\x01')
data = self.i2c.readfrom(0x51,1)[0]
if pulse_mode:
data = data | 0b00010000 # set TI/TP
else:
data = data & 0b11101111 #clear TI/TP
self.i2c.writeto(0x51,bytes([0x01,data]))
def start_timer(self):
self.timer_flag(clear=True)
self._timer_enable(True)
def stop_timer(self):
self._timer_enable(False)
def _timer_enable(self,enable):
#write TIE
self.i2c.writeto(0x51,b'\x01')
data = self.i2c.readfrom(0x51,1)[0] #control 2
if enable:
data = data | 0b00000001 # set TIE
else:
data = data & 0b11111110 #clear TIE
self.i2c.writeto(0x51,bytes([0x01,data]))
#write TE
self.i2c.writeto(0x51,b'\x0E')
data = self.i2c.readfrom(0x51,1)[0] #timer control
if enable:
data = data | 0b10000000 # set TE
else:
data = data & 0b01111111 #clear TIE
self.i2c.writeto(0x51,bytes([0x0E,data]))
def timer_flag(self,clear=False):
self.i2c.writeto(0x51,b'\x01')
data = self.i2c.readfrom(0x51,1)[0]
r = (data & 0b00000100) >> 2
if clear:
data = data & 0b11111110 #clear
self.i2c.writeto(0x51,bytes([0x01,data]))
return r
def timer_count(self):
self.i2c.writeto(0x51,b'\x0F')
data = self.i2c.readfrom(0x51,1)[0]
return data
def bcd2int(byte):
conv = [1,2,4,8]
dt = 0
for i in range(4):
dt += ((byte >> i+4) & 1)*conv[i]
dt = dt*10
for i in range(4):
dt += ((byte >> i) & 1)*conv[i]
return dt
def int2bcd(number):
ten_dig = int(hex(math.floor(number/10)))
ten_bin = ten_dig.to_bytes(1,'big')
one_dig = number % 10
one_bin = one_dig.to_bytes(1,'big')
value = ten_bin[0] << 4 | one_bin[0]
return value.to_bytes(1,'big')[0]
def alarm_test():
import utime
rtc = RTC_8564NB(scl=21,sda=25)
rtc.set_time(2023,3,3,12,59,55)
rtc.set_alarm(hour=13,minute=0)
rtc.start_alarm()
print('start alarm test')
while True:
print(rtc.time())
if rtc.alarm_flag():
print('its time!')
break
utime.sleep(1)
utime.sleep(1)
rtc.alarm_flag(clear=True)
rtc.stop_alarm()
def timer_test():
import utime
rtc = RTC_8564NB(scl=21,sda=25)
rtc.set_timer(5,freq_mode=2)
rtc.timer_flag()
rtc.start_timer()
print('start timer test')
while True:
print(rtc.timer_count())
if rtc.timer_flag():
print('its time!')
break
utime.sleep(1)
utime.sleep(1)
rtc.timer_flag(clear=True)
rtc.stop_timer()
if __name__ == '__main__':
#alarm_test()
timer_test()
import machine import math from machine import Pin, SoftI2C class RTC_8564NB: def __init__(self,scl,sda,freq=100000,reset=True): self.connected = False self.i2c = SoftI2C(scl=Pin(scl), sda=Pin(sda), freq=freq) ls = self.i2c.scan() if len(ls) == 0: print('error: cannot find i2c slave') return found = False for addr in ls: if addr == 0x51: found = True break if not found: print('error: cannot find RTC_8564NB') return self.connected = True if reset: self.reset() def reset(self): self.i2c.writeto(0x51,b'\x00\x00\x00') def set_time(self,years,months,days,hours,minutes,secconds): if not self.connected: print('error: disconnect RTC module') return if years > 2000: century = 1 else: century = 0 years = int2bcd(years % 100) months = int2bcd(months) & 0b00011111 if century: months = months | 0b10000000 days = int2bcd(days) hours = int2bcd(hours) minutes = int2bcd(minutes) secconds = int2bcd(secconds) weekdays = 0b000 #sunday (not used) values = bytes([2,secconds,minutes,hours,days, weekdays,months,years]) self.i2c.writeto(0x51,values) def time(self): if not self.connected: print('error: disconnect RTC module') self.i2c.writeto(0x51, b'\x02') data = self.i2c.readfrom(0x51, 7) vol_low = data[0] >> 7 if vol_low == 1: print('error: voltage low bit is set. must be reset.') return secconds = bcd2int(data[0] & 0b01111111) minutes = bcd2int(data[1] & 0b01111111) hours = bcd2int(data[2] & 0b00111111) days = bcd2int(data[3] & 0b00111111) months = bcd2int(data[5] & 0b00011111) century = data[5] >> 7 years = bcd2int(data[6]) if century == 1: years += 2000 else: years += 1900 return [years,months,days,hours,minutes,secconds] def set_alarm(self,day=None,hour=None,minute=None): self.i2c.writeto(0x51,b'\x09\x80\x80\x80') #reset if day != None: day = int2bcd(day) & 0b00111111 self.i2c.writeto(0x51,bytes([0x0B,day])) if hour != None: hour = int2bcd(hour) & 0b00111111 self.i2c.writeto(0x51,bytes([0x0A,hour])) if minute != None: minute = int2bcd(minute) & 0b01111111 self.i2c.writeto(0x51,bytes([0x09,minute])) def start_alarm(self): self._alarm_enable(True) def stop_alarm(self): self._alarm_enable(False) def _alarm_enable(self,enable): self.i2c.writeto(0x51, b'\x01') data = self.i2c.readfrom(0x51, 1)[0] if enable: data = data | 0b00000010 #set AIE else: data = data & 0b11111101 #clear AIE self.i2c.writeto(0x51,bytes([0x01,data])) def alarm_flag(self,clear=False): self.i2c.writeto(0x51,b'\x01') data = self.i2c.readfrom(0x51,1)[0] r = (data & 0b00001000) >> 3 if clear: data = data & 0b11111101 #clear self.i2c.writeto(0x51,bytes([0x01,data])) return r def set_timer(self,count,freq_mode=2,pulse_mode=False): ''' freq_mode: 0 => 4096Hz, 1 => 64Hz, 2 => 1Hz, 3 => 1/60Hz count: 0~256 pulse_mode: True/False ''' #write timer control if freq_mode == 1: data = 0b01 elif freq_mode == 2: data = 0b10 elif freq_mode == 3: data = 0b11 self.i2c.writeto(0x51,bytes([0x0E,data,count])) #write control 2 self.i2c.writeto(0x51,b'\x01') data = self.i2c.readfrom(0x51,1)[0] if pulse_mode: data = data | 0b00010000 # set TI/TP else: data = data & 0b11101111 #clear TI/TP self.i2c.writeto(0x51,bytes([0x01,data])) def start_timer(self): self.timer_flag(clear=True) self._timer_enable(True) def stop_timer(self): self._timer_enable(False) def _timer_enable(self,enable): #write TIE self.i2c.writeto(0x51,b'\x01') data = self.i2c.readfrom(0x51,1)[0] #control 2 if enable: data = data | 0b00000001 # set TIE else: data = data & 0b11111110 #clear TIE self.i2c.writeto(0x51,bytes([0x01,data])) #write TE self.i2c.writeto(0x51,b'\x0E') data = self.i2c.readfrom(0x51,1)[0] #timer control if enable: data = data | 0b10000000 # set TE else: data = data & 0b01111111 #clear TIE self.i2c.writeto(0x51,bytes([0x0E,data])) def timer_flag(self,clear=False): self.i2c.writeto(0x51,b'\x01') data = self.i2c.readfrom(0x51,1)[0] r = (data & 0b00000100) >> 2 if clear: data = data & 0b11111110 #clear self.i2c.writeto(0x51,bytes([0x01,data])) return r def timer_count(self): self.i2c.writeto(0x51,b'\x0F') data = self.i2c.readfrom(0x51,1)[0] return data def bcd2int(byte): conv = [1,2,4,8] dt = 0 for i in range(4): dt += ((byte >> i+4) & 1)*conv[i] dt = dt*10 for i in range(4): dt += ((byte >> i) & 1)*conv[i] return dt def int2bcd(number): ten_dig = int(hex(math.floor(number/10))) ten_bin = ten_dig.to_bytes(1,'big') one_dig = number % 10 one_bin = one_dig.to_bytes(1,'big') value = ten_bin[0] << 4 | one_bin[0] return value.to_bytes(1,'big')[0] def alarm_test(): import utime rtc = RTC_8564NB(scl=21,sda=25) rtc.set_time(2023,3,3,12,59,55) rtc.set_alarm(hour=13,minute=0) rtc.start_alarm() print('start alarm test') while True: print(rtc.time()) if rtc.alarm_flag(): print('its time!') break utime.sleep(1) utime.sleep(1) rtc.alarm_flag(clear=True) rtc.stop_alarm() def timer_test(): import utime rtc = RTC_8564NB(scl=21,sda=25) rtc.set_timer(5,freq_mode=2) rtc.timer_flag() rtc.start_timer() print('start timer test') while True: print(rtc.timer_count()) if rtc.timer_flag(): print('its time!') break utime.sleep(1) utime.sleep(1) rtc.timer_flag(clear=True) rtc.stop_timer() if __name__ == '__main__': #alarm_test() timer_test()
import machine
import math

from machine import Pin, SoftI2C

class RTC_8564NB:
    def __init__(self,scl,sda,freq=100000,reset=True):
        self.connected = False
        self.i2c = SoftI2C(scl=Pin(scl), sda=Pin(sda), freq=freq)
        ls = self.i2c.scan()
        if len(ls) == 0:
            print('error: cannot find i2c slave')
            return
        found = False
        for addr in ls:
            if addr == 0x51:
                found = True
                break
        if not found:
            print('error: cannot find RTC_8564NB')
            return
        self.connected = True
        if reset:
            self.reset()
        
    def reset(self):
        self.i2c.writeto(0x51,b'\x00\x00\x00')
    
    def set_time(self,years,months,days,hours,minutes,secconds):
        if not self.connected:
            print('error: disconnect RTC module')
            return
        if years > 2000:
            century = 1
        else:
            century = 0
        years = int2bcd(years % 100)
        months = int2bcd(months) & 0b00011111
        if century:
            months = months | 0b10000000
        days = int2bcd(days)
        hours = int2bcd(hours)
        minutes = int2bcd(minutes)
        secconds = int2bcd(secconds)
        weekdays = 0b000 #sunday (not used)
        
        values = bytes([2,secconds,minutes,hours,days,
                          weekdays,months,years])
        self.i2c.writeto(0x51,values)    

    def time(self):
        if not self.connected:
            print('error: disconnect RTC module')
            
        self.i2c.writeto(0x51, b'\x02')
        data = self.i2c.readfrom(0x51, 7)
        
        vol_low = data[0] >> 7
        if vol_low == 1:
            print('error: voltage low bit is set. must be reset.')
            return
        secconds = bcd2int(data[0] & 0b01111111)
        minutes  = bcd2int(data[1] & 0b01111111)
        hours    = bcd2int(data[2] & 0b00111111)
        days     = bcd2int(data[3] & 0b00111111)
        months   = bcd2int(data[5] & 0b00011111)
        century  = data[5] >> 7
        years    = bcd2int(data[6])
        if century == 1:
            years += 2000
        else:
            years += 1900
        return [years,months,days,hours,minutes,secconds]

    def set_alarm(self,day=None,hour=None,minute=None):
        self.i2c.writeto(0x51,b'\x09\x80\x80\x80') #reset
        if day != None:
            day = int2bcd(day) & 0b00111111
            self.i2c.writeto(0x51,bytes([0x0B,day]))
        if hour != None:
            hour = int2bcd(hour) & 0b00111111
            self.i2c.writeto(0x51,bytes([0x0A,hour]))
        if minute != None:
            minute = int2bcd(minute) & 0b01111111
            self.i2c.writeto(0x51,bytes([0x09,minute]))

    def start_alarm(self):
        self._alarm_enable(True)
        
    def stop_alarm(self):
        self._alarm_enable(False)

    def _alarm_enable(self,enable):
        self.i2c.writeto(0x51, b'\x01')
        data = self.i2c.readfrom(0x51, 1)[0]
        if enable:
            data = data | 0b00000010 #set AIE
        else:
            data = data & 0b11111101 #clear AIE
        self.i2c.writeto(0x51,bytes([0x01,data]))

    def alarm_flag(self,clear=False):
        self.i2c.writeto(0x51,b'\x01')
        data = self.i2c.readfrom(0x51,1)[0]
        r = (data & 0b00001000) >> 3
        if clear:
            data = data & 0b11111101 #clear
            self.i2c.writeto(0x51,bytes([0x01,data]))
        return r

    def set_timer(self,count,freq_mode=2,pulse_mode=False):
        '''
        freq_mode: 0 => 4096Hz, 1 => 64Hz, 2 => 1Hz, 3 => 1/60Hz
        count: 0~256
        pulse_mode: True/False
        '''
        #write timer control
        if freq_mode == 1:
            data = 0b01
        elif freq_mode == 2:
            data = 0b10
        elif freq_mode == 3:
            data = 0b11
        self.i2c.writeto(0x51,bytes([0x0E,data,count]))
        #write control 2
        self.i2c.writeto(0x51,b'\x01')
        data = self.i2c.readfrom(0x51,1)[0]
        if pulse_mode:
            data = data | 0b00010000 # set TI/TP
        else:
            data = data & 0b11101111 #clear TI/TP
        self.i2c.writeto(0x51,bytes([0x01,data]))
            
    def start_timer(self):
        self.timer_flag(clear=True)
        self._timer_enable(True)
        
    def stop_timer(self):
        self._timer_enable(False)
    
    def _timer_enable(self,enable):
        #write TIE
        self.i2c.writeto(0x51,b'\x01') 
        data = self.i2c.readfrom(0x51,1)[0] #control 2
        if enable:
            data = data | 0b00000001 # set TIE
        else:
            data = data & 0b11111110 #clear TIE
        self.i2c.writeto(0x51,bytes([0x01,data]))
        #write TE
        self.i2c.writeto(0x51,b'\x0E')
        data = self.i2c.readfrom(0x51,1)[0] #timer control
        if enable:
            data = data | 0b10000000 # set TE
        else:
            data = data & 0b01111111 #clear TIE
        self.i2c.writeto(0x51,bytes([0x0E,data]))
        
    def timer_flag(self,clear=False):
        self.i2c.writeto(0x51,b'\x01')
        data = self.i2c.readfrom(0x51,1)[0]
        r = (data & 0b00000100) >> 2
        if clear:
            data = data & 0b11111110 #clear
            self.i2c.writeto(0x51,bytes([0x01,data]))
        return r
    
    def timer_count(self):
        self.i2c.writeto(0x51,b'\x0F')
        data = self.i2c.readfrom(0x51,1)[0]
        return data
            
def bcd2int(byte):
    conv = [1,2,4,8]
    dt = 0
    for i in range(4):
        dt += ((byte >> i+4) & 1)*conv[i]
    dt = dt*10
    for i in range(4):
        dt += ((byte >> i) & 1)*conv[i]
    return dt

def int2bcd(number):
    ten_dig = int(hex(math.floor(number/10)))
    ten_bin = ten_dig.to_bytes(1,'big')
    one_dig = number % 10
    one_bin = one_dig.to_bytes(1,'big')
    value = ten_bin[0] << 4 | one_bin[0]
    return value.to_bytes(1,'big')[0]

def alarm_test():
    import utime
    rtc = RTC_8564NB(scl=21,sda=25)
    rtc.set_time(2023,3,3,12,59,55)
    rtc.set_alarm(hour=13,minute=0)
    rtc.start_alarm()
    print('start alarm test')
    while True:
        print(rtc.time())
        if rtc.alarm_flag():
            print('its time!')
            break
        utime.sleep(1)
    utime.sleep(1)
    rtc.alarm_flag(clear=True)
    rtc.stop_alarm()
    
def timer_test():
    import utime
    rtc = RTC_8564NB(scl=21,sda=25)
    rtc.set_timer(5,freq_mode=2)
    rtc.timer_flag()
    rtc.start_timer()
    print('start timer test')
    while True:
        print(rtc.timer_count())
        if rtc.timer_flag():
            print('its time!')
            break
        utime.sleep(1)
    utime.sleep(1)
    rtc.timer_flag(clear=True)
    rtc.stop_timer()

if __name__ == '__main__':
    #alarm_test()
    timer_test()
Updated: 2023年3月29日 — 07:19

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です