-
Notifications
You must be signed in to change notification settings - Fork 0
/
hwclock-spi
executable file
·162 lines (135 loc) · 4.85 KB
/
hwclock-spi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/python
# vim:set ts=4 sw=4 et:
import os
import datetime
import sys
import datetime
import fcntl
from ctypes import cdll, c_int, POINTER, c_void_p, Structure
import datetime
from contextlib import contextmanager
RTC_SPI_DEVICE = '/dev/spidev2.0'
SPI_IOC_WR_MODE = 0x40016b01
SPI_IOC_RD_MODE = 0x80016b01
SPI_IOC_WR_BITS_PER_WORD = 0x40016b03
SPI_IOC_RD_BITS_PER_WORD = 0x80016b03
DS3234_REG_SECONDS = 0x00
DS3234_REG_MINUTES = 0x01
DS3234_REG_HOURS = 0x02
DS3234_REG_DAY = 0x03
DS3234_REG_DATE = 0x04
DS3234_REG_MONTH = 0x05
DS3234_REG_YEAR = 0x06
DS3234_REG_CONTROL = 0x0E
DS3234_REG_CONT_STAT = 0x0F
class BcdOutOfRange(Exception):
pass
def bcd2bin(bcd_val):
return (bcd_val & 0xf) + (((bcd_val & 0xf0) >> 4) * 10)
def bin2bcd(bin_val):
if not (bin_val >= 0 and bin_val <= 99):
raise BcdOutOfRange('binary value not between 0 and 99')
return (bin_val % 10) | ((bin_val / 10) << 4)
class Ds3234SpiCommunicationError(Exception):
pass
def _read_data(dev_fp, num_bytes=0):
data = os.read(dev_fp.fileno(), num_bytes + 1)
if data[0] != '\xff':
raise Ds3234SpiCommunicationError('SPI read failed with status ' + \
repr(data[0]))
return data[1:]
def spi_ioctl_write(dev_fp, operation, value):
data = chr(value)
fcntl.ioctl(dev_fp.fileno(), operation, data)
def spi_ioctl_read(dev_fp, operation):
data = chr(0)
data = fcntl.ioctl(dev_fp.fileno(), operation, data)
return ord(data[0])
@contextmanager
def open_spi(dev_fn):
with open(dev_fn, 'r+b') as fp:
yield fp
def init_spi(dev_fp):
spi_ioctl_write(dev_fp, SPI_IOC_WR_MODE, 3)
spi_ioctl_write(dev_fp, SPI_IOC_WR_BITS_PER_WORD, 8)
tmp = _get_reg(dev_fp, DS3234_REG_CONTROL)
_set_reg(dev_fp, DS3234_REG_CONTROL, tmp & 0x1c)
tmp = _get_reg(dev_fp, DS3234_REG_CONT_STAT)
_set_reg(dev_fp, DS3234_REG_CONT_STAT, tmp & 0x88)
def get_time(dev_fp):
dt_buf = _get_regs(dev_fp, 0, 7)
tm_sec = bcd2bin(dt_buf[0])
tm_min = bcd2bin(dt_buf[1])
tm_hour = bcd2bin(dt_buf[2] & 0x3f)
tm_mday = bcd2bin(dt_buf[4])
tm_mon = bcd2bin(dt_buf[5] & 0x1f)
tm_year = bcd2bin(dt_buf[6]) + 2000
try:
return datetime.datetime(tm_year, tm_mon, tm_mday, tm_hour, tm_min,
tm_sec)
except ValueError:
print >>sys.stderr, 'RTC information corrupt: year:{0} month{1}, day:{2} hour:{3} minute:{3} second:{4}'.format(
tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec)
sys.exit(1)
def _get_regs(dev_fp, addr, len):
os.write(dev_fp.fileno(), chr(addr))
return [ord(data) for data in _read_data(dev_fp, len)]
def _get_reg(dev_fp, addr):
return _get_regs(dev_fp, addr, 1)[0]
def _set_reg(dev_fp, addr, val):
data = chr(addr | 0x80)
data += chr(val)
os.write(dev_fp.fileno(), data)
_read_data(dev_fp)
def set_time(dev_fp, dt):
_set_reg(dev_fp, DS3234_REG_SECONDS, bin2bcd(dt.second))
_set_reg(dev_fp, DS3234_REG_MINUTES, bin2bcd(dt.minute))
_set_reg(dev_fp, DS3234_REG_HOURS, (bin2bcd(dt.hour) & 0x3f))
_set_reg(dev_fp, DS3234_REG_DAY, bin2bcd(dt.isoweekday()))
_set_reg(dev_fp, DS3234_REG_DATE, bin2bcd(dt.day))
_set_reg(dev_fp, DS3234_REG_MONTH, bin2bcd(dt.month) & 0x1f)
_set_reg(dev_fp, DS3234_REG_YEAR, bin2bcd(dt.year - 2000))
def _settimeofday(sec_since_epoc):
libc = cdll['libc.so.6']
libc.settimeofday.restype = c_int
class timeval(Structure):
_fields_ = [('tv_sec', c_int), ('tv_usec', c_int)]
libc.settimeofday.argtypes = POINTER(timeval), c_void_p
t = timeval()
t.tv_sec = sec_since_epoc
libc.settimeofday(t, None)
def settimeofday(dt_utc):
sec_since_epoc = (dt_utc - datetime.datetime(1970, 1, 1)).total_seconds()
_settimeofday(int(sec_since_epoc))
def get_dt_from_device():
with open_spi(RTC_SPI_DEVICE) as dev:
init_spi(dev)
return get_time(dev)
def show_usage():
print >>sys.stderr, "Usage: {0} --hctosys|--systohc|--show|--show-delta".format(sys.argv[0])
sys.exit(1)
if len(sys.argv) != 2:
show_usage()
mode = sys.argv[1]
if mode == '--hctosys':
dt = get_dt_from_device()
print "Setting date and time from RTC: {0}".format(dt)
settimeofday(dt)
elif mode == '--show':
dt = get_dt_from_device()
print "Date and time in RTC: {0}".format(dt)
elif mode == '--show-delta':
dt = get_dt_from_device()
now = datetime.datetime.now()
print "Date and time in system: {0}".format(now)
print "Date and time in RTC: {0}".format(dt)
print "Delta between RTC and system: {0}".format((dt - now).total_seconds())
elif mode == '--systohc':
dt = datetime.datetime.now()
print "Writing date and time to RTC: {0}".format(dt)
with open(RTC_SPI_DEVICE, 'r+b') as dev:
init_spi(dev)
set_time(dev, dt)
else:
print >>sys.stderr, "error: unknown mode {0}".format(mode)
show_usage()