前回、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()