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設定編/

 

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

コメントを残す

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