Skip to content

Commit ec1afdf

Browse files
committed
Stop using memoryviews and start using I2CDevice to manage transaction related state.
1 parent bef500c commit ec1afdf

File tree

4 files changed

+122
-64
lines changed

4 files changed

+122
-64
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
dist
44
*.pyc
55
_build/
6+
*.mpy

README.md

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ SPI register based device. Data descriptors act like basic attributes from the
55
outside which makes using them really easy.
66

77
## Creating a driver
8-
Creating a driver with the register library is really easy. First, import the register modules you need from the [available modules](adafruit_register/index.html):
8+
Creating a driver with the register library is really easy. First, import the
9+
register modules you need from the [available modules](adafruit_register/index.html):
910

1011
from adafruit_register import i2c_bit
12+
from adafruit_bus_device import i2c_device
1113

1214
Next, define where the bit is located in the device's memory map:
1315

@@ -20,14 +22,13 @@ Next, define where the bit is located in the device's memory map:
2022
world = i2c_bit.RWBit(0x1, 0x0)
2123
"""Bit to indicate if world is lit."""
2224

23-
Lastly, we need to add two instance members `i2c` and `device_address` so that
24-
the register classes know how to talk to the device. Make sure their names are
25-
exact, otherwise the registers will not be able to find them. Also, make sure
26-
that the i2c device implements the `nativeio.I2C` interface.
25+
Lastly, we need to add an `i2c_device` member that manages sharing the I2C bus
26+
for us. Make sure the name is exact, otherwise the registers will not be able to
27+
find it. Also, make sure that the i2c device implements the `nativeio.I2C`
28+
interface.
2729

2830
def __init__(self, i2c, device_address=0x0):
29-
self.i2c = i2c
30-
self.device_address = device_address
31+
self.i2c_device = i2c_device.I2CDevice(i2c, device_address)
3132

3233
Thats it! Now we have a class we can use to talk to those registers:
3334

@@ -40,9 +41,14 @@ Thats it! Now we have a class we can use to talk to those registers:
4041
device.world = True
4142

4243
## Adding register types
43-
Adding a new register type is a little more complicated because you need to be careful and minimize the amount of memory the class will take. If you don't, then a driver with five registers of your type could take up five times more extra memory.
44+
Adding a new register type is a little more complicated because you need to be
45+
careful and minimize the amount of memory the class will take. If you don't,
46+
then a driver with five registers of your type could take up five times more
47+
extra memory.
4448

45-
First, determine whether the new register class should go in an existing module or not. When in doubt choose a new module. The more finer grained the modules are, the fewer extra classes a driver needs to load in.
49+
First, determine whether the new register class should go in an existing module
50+
or not. When in doubt choose a new module. The more finer grained the modules
51+
are, the fewer extra classes a driver needs to load in.
4652

4753
Here is the start of the `RWBit` class:
4854

@@ -70,7 +76,7 @@ register classes will be shared.
7076

7177
In `__init__` we only use two member variable because each costs 8 bytes of
7278
memory plus the memory for the value. And remember this gets multiplied by the
73-
number of registers of this type in a device! Thats why we pack both the
79+
number of registers of this type in a driver! Thats why we pack both the
7480
register address and data byte into one bytearray. We could use two byte arrays
7581
of size one but each MicroPython object is 16 bytes minimum due to the garbage
7682
collector. So, by sharing a byte array we keep it to the 16 byte minimum instead
@@ -85,24 +91,29 @@ Ok, onward. To make a [data descriptor](https://docs.python.org/3/howto/descript
8591
we must implement `__get__` and `__set__`.
8692

8793
def __get__(self, obj, objtype=None):
88-
obj.i2c.writeto(obj.device_address, self.buffer, end=1, stop=False)
89-
obj.i2c.readfrom_into(obj.device_address, self.buffer, start=1)
94+
with obj.i2c_device:
95+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
96+
obj.i2c_device.readfrom_into(self.buffer, start=1)
9097
return bool(self.buffer[1] & self.bit_mask)
9198

9299
def __set__(self, obj, value):
93-
obj.i2c.writeto(obj.device_address, self.buffer, end=1, stop=False)
94-
obj.i2c.readfrom_into(obj.device_address, self.buffer, start=1)
95-
if value:
96-
self.buffer[1] |= self.bit_mask
97-
else:
98-
self.buffer[1] &= ~self.bit_mask
99-
obj.i2c.writeto(obj.device_address, self.buffer)
100+
with obj.i2c_device:
101+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
102+
obj.i2c_device.readfrom_into(self.buffer, start=1)
103+
if value:
104+
self.buffer[1] |= self.bit_mask
105+
else:
106+
self.buffer[1] &= ~self.bit_mask
107+
obj.i2c_device.writeto(self.buffer)
100108

101109
As you can see, we have two places to get state from. First, `self` stores the
102110
register class members which locate the register within the device memory map.
103-
Second, `obj` is the device class that uses the register class which must by
104-
definition provide a `nativeio.I2C` compatible object as ``i2c`` and the 7-bit
105-
device address as ``device_address``.
111+
Second, `obj` is the driver class that uses the register class which must by
112+
definition provide a `adafruit_bus_device.I2CDevice` compatible object as
113+
``i2c_device``. This object does two thing for us:
114+
115+
1. Waits for the bus to free, locks it as we use it and frees it after.
116+
2. Saves the device address and other settings so we don't have to.
106117

107118
Note that we take heavy advantage of the ``start`` and ``end`` parameters to the
108119
i2c functions to slice the buffer without actually allocating anything extra.

adafruit_register/i2c_bcd_datetime.py

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
123
try:
224
import ucollections as collections
325
except:
@@ -55,32 +77,32 @@ class BCDDateTimeRegister:
5577
def __init__(self, register_address):
5678
self.buffer = bytearray(8)
5779
self.buffer[0] = register_address
58-
self.register_address = memoryview(self.buffer)[:1]
59-
self.datetime = memoryview(self.buffer)[1:]
6080

6181
def __get__(self, obj, objtype=None):
6282
# Read and return the date and time.
63-
obj.i2c.writeto(obj.device_address, self.register_address, stop=False)
64-
obj.i2c.readfrom_into(obj.device_address, self.datetime)
83+
with obj.i2c_device:
84+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
85+
obj.i2c_device.readfrom_into(self.buffer, start=1)
6586
return datetime_tuple(
66-
year=_bcd2bin(self.datetime[6]) + 2000,
67-
month=_bcd2bin(self.datetime[5]),
68-
day=_bcd2bin(self.datetime[4]),
69-
weekday=_bcd2bin(self.datetime[3]),
70-
hour=_bcd2bin(self.datetime[2]),
71-
minute=_bcd2bin(self.datetime[1]),
72-
second=_bcd2bin(self.datetime[0]),
87+
year=_bcd2bin(self.buffer[7]) + 2000,
88+
month=_bcd2bin(self.buffer[6]),
89+
day=_bcd2bin(self.buffer[5]),
90+
weekday=_bcd2bin(self.buffer[4]),
91+
hour=_bcd2bin(self.buffer[3]),
92+
minute=_bcd2bin(self.buffer[2]),
93+
second=_bcd2bin(self.buffer[1]),
7394
)
7495

7596
def __set__(self, obj, value):
76-
self.datetime[0] = _bin2bcd(value.second) # format conversions
77-
self.datetime[1] = _bin2bcd(value.minute)
78-
self.datetime[2] = _bin2bcd(value.hour)
79-
self.datetime[3] = _bin2bcd(value.weekday)
80-
self.datetime[4] = _bin2bcd(value.day)
81-
self.datetime[5] = _bin2bcd(value.month)
82-
self.datetime[6] = _bin2bcd(value.year - 2000)
83-
obj.i2c.writeto(obj.device_address, self.buffer)
97+
self.buffer[1] = _bin2bcd(value.second) # format conversions
98+
self.buffer[2] = _bin2bcd(value.minute)
99+
self.buffer[3] = _bin2bcd(value.hour)
100+
self.buffer[4] = _bin2bcd(value.weekday)
101+
self.buffer[5] = _bin2bcd(value.day)
102+
self.buffer[6] = _bin2bcd(value.month)
103+
self.buffer[7] = _bin2bcd(value.year - 2000)
104+
with obj.i2c_device:
105+
obj.i2c_device.writeto(self.buffer)
84106

85107
class BCDAlarmTimeRegister:
86108
"""
@@ -95,25 +117,25 @@ class BCDAlarmTimeRegister:
95117
def __init__(self, register_address):
96118
self.buffer = bytearray(5)
97119
self.buffer[0] = register_address
98-
self.register_address = memoryview(self.buffer)[:1]
99-
self.alarm_time = memoryview(self.buffer)[1:]
100120

101121
def __get__(self, obj, objtype=None):
102122
# Read the alarm register.
103-
obj.i2c.writeto(obj.device_address, self.register_address, stop=False)
104-
obj.i2c.readfrom_into(obj.device_address, self.alarm_time)
105-
if not self.alarm_time[0] & 0x80:
123+
with obj.i2c_device:
124+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
125+
obj.i2c_device.readfrom_into(self.buffer, start=1)
126+
if not self.buffer[1] & 0x80:
106127
return None
107128
return datetime_tuple(
108-
weekday=_bcd2bin(self.alarm_time[3] & 0x7f),
109-
day=_bcd2bin(self.alarm_time[2] & 0x7f),
110-
hour=_bcd2bin(self.alarm_time[1] & 0x7f),
111-
minute=_bcd2bin(self.alarm_time[0] & 0x7f),
129+
weekday=_bcd2bin(self.buffer[4] & 0x7f),
130+
day=_bcd2bin(self.buffer[3] & 0x7f),
131+
hour=_bcd2bin(self.buffer[2] & 0x7f),
132+
minute=_bcd2bin(self.buffer[1] & 0x7f),
112133
)
113134

114135
def __set__(self, obj, value):
115-
self.alarm_time[1] = (_bin2bcd(value.minute) if value.minute is not None else 0x80)
116-
self.alarm_time[2] = (_bin2bcd(value.hour) if value.hour is not None else 0x80)
117-
self.alarm_time[3] = (_bin2bcd(value.day) if value.day is not None else 0x80)
118-
self.alarm_time[4] = (_bin2bcd(value.weekday) | 0b01000000 if value.weekday is not None else 0x80)
119-
obj.i2c.writeto(obj.device_address, self.buffer)
136+
self.buffer[1] = (_bin2bcd(value.minute) if value.minute is not None else 0x80)
137+
self.buffer[2] = (_bin2bcd(value.hour) if value.hour is not None else 0x80)
138+
self.buffer[3] = (_bin2bcd(value.day) if value.day is not None else 0x80)
139+
self.buffer[4] = (_bin2bcd(value.weekday) | 0b01000000 if value.weekday is not None else 0x80)
140+
with obj.i2c_device:
141+
obj.i2c_device.writeto(self.buffer)

adafruit_register/i2c_bit.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
123
class RWBit:
224
"""
325
Single bit register that is readable and writeable.
@@ -13,18 +35,20 @@ def __init__(self, register_address, bit):
1335
self.buffer[0] = register_address
1436

1537
def __get__(self, obj, objtype=None):
16-
obj.i2c.writeto(obj.device_address, self.buffer, end=1, stop=False)
17-
obj.i2c.readfrom_into(obj.device_address, self.buffer, start=1)
38+
with obj.i2c_device:
39+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
40+
obj.i2c_device.readfrom_into(self.buffer, start=1)
1841
return bool(self.buffer[1] & self.bit_mask)
1942

2043
def __set__(self, obj, value):
21-
obj.i2c.writeto(obj.device_address, self.buffer, end=1, stop=False)
22-
obj.i2c.readfrom_into(obj.device_address, self.buffer, start=1)
23-
if value:
24-
self.buffer[1] |= self.bit_mask
25-
else:
26-
self.buffer[1] &= ~self.bit_mask
27-
obj.i2c.writeto(obj.device_address, self.buffer)
44+
with obj.i2c_device:
45+
obj.i2c.writeto(self.buffer, end=1, stop=False)
46+
obj.i2c.readfrom_into(self.buffer, start=1)
47+
if value:
48+
self.buffer[1] |= self.bit_mask
49+
else:
50+
self.buffer[1] &= ~self.bit_mask
51+
obj.i2c_device.writeto(self.buffer)
2852

2953
class ROBit(RWBit):
3054
"""Single bit register that is read only. Subclass of `RWBit`.

0 commit comments

Comments
 (0)