Skip to content

Commit 62b83b8

Browse files
committed
feature/unit/rtc: HYM8563 low-power CMOS real-time clock
1 parent b8c3316 commit 62b83b8

File tree

1 file changed

+324
-0
lines changed

1 file changed

+324
-0
lines changed

m5stack/libs/unit/rtc8563.py

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
'''
2+
MIT License
3+
4+
Copyright (c) 2019 lewis he
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
24+
pcf8563.py - MicroPython library for NXP PCF8563 Real-time clock/calendar
25+
Created by Lewis he on September 17, 2019.
26+
github:https://github.com/lewisxhe/PCF8563_PythonLibrary
27+
'''
28+
29+
from machine import I2C
30+
from time import localtime, sleep
31+
from gc import collect
32+
from driver.timezone import TZONE
33+
from micropython import const
34+
35+
from .pahub import PAHUBUnit
36+
from .unit_helper import UnitError
37+
38+
import sys
39+
40+
if sys.platform != "esp32":
41+
from typing import Union
42+
43+
PCF8563_SLAVE_ADDRESS = const(0x51)
44+
PCF8563_STAT1_REG = const(0x00)
45+
PCF8563_STAT2_REG = const(0x01)
46+
PCF8563_SEC_REG = const(0x02)
47+
PCF8563_MIN_REG = const(0x03)
48+
PCF8563_HR_REG = const(0x04)
49+
PCF8563_DAY_REG = const(0x05)
50+
PCF8563_WEEKDAY_REG = const(0x06)
51+
PCF8563_MONTH_REG = const(0x07)
52+
PCF8563_YEAR_REG = const(0x08)
53+
PCF8563_SQW_REG = const(0x0D)
54+
PCF8563_TIMER1_REG = const(0x0E)
55+
PCF8563_TIMER2_REG = const(0x0F)
56+
PCF8563_VOL_LOW_MASK = const(0x80)
57+
PCF8563_minuteS_MASK = const(0x7F)
58+
PCF8563_HOUR_MASK = const(0x3F)
59+
PCF8563_WEEKDAY_MASK = const(0x07)
60+
PCF8563_CENTURY_MASK = const(0x80)
61+
PCF8563_DAY_MASK = const(0x3F)
62+
PCF8563_MONTH_MASK = const(0x1F)
63+
PCF8563_TIMER_CTL_MASK = const(0x03)
64+
PCF8563_ALARM_AF = const(0x08)
65+
PCF8563_TIMER_TF = const(0x04)
66+
PCF8563_ALARM_AIE = const(0x02)
67+
PCF8563_TIMER_TIE = const(0x01)
68+
PCF8563_TIMER_TE = const(0x80)
69+
PCF8563_TIMER_TD10 = const(0x03)
70+
PCF8563_NO_ALARM = const(0xFF)
71+
PCF8563_ALARM_ENABLE = const(0x80)
72+
PCF8563_CLK_ENABLE = const(0x80)
73+
PCF8563_ALARM_MINUTES = const(0x09)
74+
PCF8563_ALARM_HOURS = const(0x0A)
75+
PCF8563_ALARM_DAY = const(0x0B)
76+
PCF8563_ALARM_WEEKDAY = const(0x0C)
77+
78+
CLOCK_CLK_OUT_FREQ_32_DOT_768KHZ = const(0x80)
79+
CLOCK_CLK_OUT_FREQ_1_DOT_024KHZ = const(0x81)
80+
CLOCK_CLK_OUT_FREQ_32_KHZ = const(0x82)
81+
CLOCK_CLK_OUT_FREQ_1_HZ = const(0x83)
82+
CLOCK_CLK_HIGH_IMPEDANCE = const(0x0)
83+
84+
SECONDS = 0
85+
MINUTES = 1
86+
HOURS = 2
87+
DAY = 3
88+
DATE = 4
89+
MONTH = 5
90+
YEAR = 6
91+
92+
class RTC8563Unit:
93+
def __init__(self, i2c: Union[I2C, PAHUBUnit], address=PCF8563_SLAVE_ADDRESS):
94+
"""Initialization needs to be given an initialized I2C port
95+
"""
96+
self.i2c = i2c
97+
self.i2c_addr = address
98+
self.buffer = bytearray(16)
99+
self.bytebuf = memoryview(self.buffer[0:1])
100+
self._available()
101+
self.clear_alarm_flag()
102+
self.clear_timer_flag()
103+
self.turn_off_alarm()
104+
self.turn_off_timer()
105+
106+
def _available(self):
107+
if not (self.i2c_addr in self.i2c.scan()):
108+
raise UnitError("RTC unit maybe not connect")
109+
110+
def __write_byte(self, reg, val):
111+
self.bytebuf[0] = val
112+
self.i2c.writeto_mem(self.i2c_addr, reg, self.bytebuf)
113+
114+
def __read_byte(self, reg):
115+
self.i2c.readfrom_mem_into(self.i2c_addr, reg, self.bytebuf)
116+
return self.bytebuf[0]
117+
118+
def __bcd2dec(self, bcd):
119+
return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f))
120+
121+
def __dec2bcd(self, dec):
122+
tens, units = divmod(dec, 10)
123+
return (tens << 4) + units
124+
125+
def get_date_time(self, select=0):
126+
if select == SECONDS:
127+
return self.__bcd2dec(self.__read_byte(PCF8563_SEC_REG) & 0x7F)
128+
129+
elif select == MINUTES:
130+
return self.__bcd2dec(self.__read_byte(PCF8563_MIN_REG) & 0x7F)
131+
132+
elif select == HOURS:
133+
d = self.__read_byte(PCF8563_HR_REG) & 0x3F
134+
return self.__bcd2dec(d & 0x3F)
135+
136+
elif select == DAY:
137+
return self.__bcd2dec(self.__read_byte(PCF8563_WEEKDAY_REG) & 0x07)
138+
139+
elif select == DATE:
140+
return self.__bcd2dec(self.__read_byte(PCF8563_DAY_REG) & 0x3F)
141+
142+
elif select == MONTH:
143+
return self.__bcd2dec(self.__read_byte(PCF8563_MONTH_REG) & 0x1F)
144+
145+
elif select == YEAR:
146+
return self.__bcd2dec(self.__read_byte(PCF8563_YEAR_REG))
147+
148+
def set_date_time(self, seconds=None, minutes=None, hours=None, day=None,
149+
date=None, month=None, year=None):
150+
"""Direct write un-none value.
151+
Range: seconds [0,59], minutes [0,59], hours [0,23],
152+
day [0,7], date [1-31], month [1-12], year [0-99].
153+
"""
154+
if seconds is not None:
155+
if seconds < 0 or seconds > 59:
156+
raise ValueError('Seconds is out of range [0,59].')
157+
seconds_reg = self.__dec2bcd(seconds)
158+
self.__write_byte(PCF8563_SEC_REG, seconds_reg)
159+
160+
if minutes is not None:
161+
if minutes < 0 or minutes > 59:
162+
raise ValueError('Minutes is out of range [0,59].')
163+
self.__write_byte(PCF8563_MIN_REG, self.__dec2bcd(minutes))
164+
165+
if hours is not None:
166+
if hours < 0 or hours > 23:
167+
raise ValueError('Hours is out of range [0,23].')
168+
# no 12 hour mode
169+
self.__write_byte(PCF8563_HR_REG, self.__dec2bcd(hours))
170+
171+
if year is not None:
172+
if year < 0 or year > 99:
173+
raise ValueError('Years is out of range [0,99].')
174+
self.__write_byte(PCF8563_YEAR_REG, self.__dec2bcd(year))
175+
176+
if month is not None:
177+
if month < 1 or month > 12:
178+
raise ValueError('Month is out of range [1,12].')
179+
self.__write_byte(PCF8563_MONTH_REG, self.__dec2bcd(month))
180+
181+
if date is not None:
182+
if date < 1 or date > 31:
183+
raise ValueError('Date is out of range [1,31].')
184+
self.__write_byte(PCF8563_DAY_REG, self.__dec2bcd(date))
185+
186+
if day is not None:
187+
if day < 0 or day > 6:
188+
raise ValueError('Day is out of range [0,6].')
189+
self.__write_byte(PCF8563_WEEKDAY_REG, self.__dec2bcd(day))
190+
191+
def datetime(self, dt):
192+
"""Input a tuple such as (year, month, date, day, hours, minutes,
193+
seconds).
194+
"""
195+
self.set_date_time(dt[5], dt[4], dt[3],
196+
dt[6], dt[2], dt[1], dt[0] % 100)
197+
198+
def write_now(self):
199+
"""Write the current system time to PCF8563
200+
"""
201+
self.datetime(localtime())
202+
203+
def set_internet_time(self, source='ntp', host='cn.pool.ntp.org', tzone=0):
204+
"""Set the time from the NTP server
205+
"""
206+
if source == 'ntp':
207+
self.tzone = TZONE(tzone)
208+
for i in range(5):
209+
ntp = self.tzone.getntp(host)
210+
if ntp != 0:
211+
break
212+
sleep(5)
213+
# z = self.tzone.adj_tzone(localtime(ntp))
214+
tzone = int(3600 * (int(tzone) + ((tzone - int(tzone)) * 100/60)))
215+
utc = localtime(ntp + tzone)
216+
217+
(yy, MM, mday, hh, mm, ss, wday, yday) = utc
218+
self.datetime((yy - 2000, MM, mday, hh, mm, ss, wday))
219+
220+
def set_clk_out_frequency(self, frequency=CLOCK_CLK_OUT_FREQ_1_HZ):
221+
"""Set the clock output pin frequency
222+
"""
223+
self.__write_byte(PCF8563_SQW_REG, frequency)
224+
225+
def check_if_alarm_on(self):
226+
"""Read the register to get the alarm enabled
227+
"""
228+
return bool(self.__read_byte(PCF8563_STAT2_REG) & PCF8563_ALARM_AF)
229+
230+
def turn_off_alarm(self):
231+
"""Should not affect the alarm interrupt state.
232+
"""
233+
alarm_state = self.__read_byte(PCF8563_STAT2_REG)
234+
self.__write_byte(PCF8563_STAT2_REG, alarm_state & 0xf7)
235+
236+
def clear_alarm_flag(self):
237+
"""Clear status register.
238+
"""
239+
alarm_state = self.__read_byte(PCF8563_STAT2_REG)
240+
alarm_state &= ~(PCF8563_ALARM_AF)
241+
alarm_state |= PCF8563_TIMER_TF
242+
self.__write_byte(PCF8563_STAT2_REG, alarm_state)
243+
244+
self.__write_byte(PCF8563_ALARM_MINUTES, 0x80)
245+
self.__write_byte(PCF8563_ALARM_HOURS, 0x80)
246+
self.__write_byte(PCF8563_ALARM_DAY, 0x80)
247+
self.__write_byte(PCF8563_ALARM_WEEKDAY, 0x80)
248+
249+
def set_daily_alarm(self, hours=None, minutes=None, date=None, weekday=None):
250+
"""Set alarm match, allow sometimes, minute, day, week
251+
"""
252+
if minutes is None:
253+
minutes = PCF8563_ALARM_ENABLE
254+
self.__write_byte(PCF8563_ALARM_MINUTES, minutes)
255+
else:
256+
if minutes < 0 or minutes > 59:
257+
raise ValueError('Minutes is out of range [0,59].')
258+
self.__write_byte(PCF8563_ALARM_MINUTES,
259+
self.__dec2bcd(minutes) & 0x7f)
260+
261+
if hours is None:
262+
hours = PCF8563_ALARM_ENABLE
263+
self.__write_byte(PCF8563_ALARM_HOURS, hours)
264+
else:
265+
if hours < 0 or hours > 23:
266+
raise ValueError('Hours is out of range [0,23].')
267+
self.__write_byte(PCF8563_ALARM_HOURS, self.__dec2bcd(
268+
hours) & 0x7f)
269+
270+
if date is None:
271+
date = PCF8563_ALARM_ENABLE
272+
self.__write_byte(PCF8563_ALARM_DAY, date)
273+
else:
274+
if date < 1 or date > 31:
275+
raise ValueError('date is out of range [1,31].')
276+
self.__write_byte(PCF8563_ALARM_DAY, self.__dec2bcd(
277+
date) & 0x7f)
278+
279+
if weekday is None:
280+
weekday = PCF8563_ALARM_ENABLE
281+
self.__write_byte(PCF8563_ALARM_WEEKDAY, weekday)
282+
else:
283+
if weekday < 0 or weekday > 6:
284+
raise ValueError('weekday is out of range [0,6].')
285+
self.__write_byte(PCF8563_ALARM_WEEKDAY, self.__dec2bcd(
286+
weekday) & 0x7f)
287+
288+
def set_timer_mode(self, mode=0, value=0):
289+
"""
290+
Set the timer mode.
291+
"""
292+
self.__write_byte(PCF8563_TIMER2_REG, value)
293+
timer_state = (PCF8563_TIMER_TE | 0x02 | mode)
294+
self.__write_byte(PCF8563_TIMER1_REG, timer_state)
295+
296+
def get_timer_value(self):
297+
"""
298+
get the timer value.
299+
"""
300+
return self.__read_byte(PCF8563_TIMER2_REG)
301+
302+
def check_if_timer_on(self):
303+
"""
304+
Read the register to get the alarm status
305+
"""
306+
return bool(self.__read_byte(PCF8563_STAT2_REG) & PCF8563_TIMER_TF)
307+
308+
def turn_off_timer(self):
309+
"""
310+
clear the timer flag and disable timer.
311+
"""
312+
self.__write_byte(PCF8563_TIMER1_REG, PCF8563_TIMER_TD10)
313+
self.__write_byte(PCF8563_TIMER2_REG, 0x00)
314+
315+
timer_state = self.__read_byte(PCF8563_STAT2_REG)
316+
timer_state &= ~(PCF8563_TIMER_TF)
317+
self.__write_byte(PCF8563_STAT2_REG, timer_state)
318+
319+
def clear_timer_flag(self):
320+
"""
321+
clear the timer flag.
322+
"""
323+
timer_state = self.__read_byte(PCF8563_STAT2_REG)
324+
self.__write_byte(PCF8563_STAT2_REG, (timer_state & 0xfb))

0 commit comments

Comments
 (0)