-
Notifications
You must be signed in to change notification settings - Fork 6
/
adafruit_24lc32.py
256 lines (217 loc) · 9.47 KB
/
adafruit_24lc32.py
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# SPDX-FileCopyrightText: Copyright (c) 2021 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_24lc32`
================================================================================
CircuitPython driver for Adafruit 24LC32 I2C EEPROM Breakout
* Author(s): Tim Cocks
Implementation Notes
--------------------
**Hardware:**
* `Adafruit 24LC32 I2C EEPROM Breakout <https://www.adafruit.com/product/5146>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
"""
# imports
import time
from micropython import const
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_24LC32.git"
_MAX_SIZE_I2C = const(0x1000)
class EEPROM:
"""
Driver base for the EEPROM Breakout.
"""
def __init__(self, max_size, write_protect=False, wp_pin=None):
self._max_size = max_size
self._wp = write_protect
self._wraparound = False
if not wp_pin is None:
self._wp_pin = wp_pin
# Make sure write_prot is set to output
self._wp_pin.switch_to_output()
self._wp_pin.value = self._wp
else:
self._wp_pin = wp_pin
@property
def write_wraparound(self):
"""Determines if sequential writes will wrapaound highest memory address
(``len(EEPROM) - 1``) address. If ``False``, and a requested write will
extend beyond the maximum size, an exception is raised.
"""
return self._wraparound
@write_wraparound.setter
def write_wraparound(self, value):
if not value in (True, False):
raise ValueError("Write wraparound must be 'True' or 'False'.")
self._wraparound = value
@property
def write_protected(self):
"""The status of write protection. Default value on initialization is
``False``.
When a ``WP`` pin is supplied during initialization, or using
``write_protect_pin``, the status is tied to that pin and enables
hardware-level protection.
When no ``WP`` pin is supplied, protection is only at the software
level in this library.
"""
return self._wp if self._wp_pin is None else self._wp_pin.value
def __len__(self):
"""The size of the current EEPROM chip. This is one more than the highest
address location that can be read or written to.
.. code-block:: python
fram = adafruit_fram.FRAM_xxx() # xxx = 'I2C' or 'SPI'
# size returned by len()
len(fram)
# can be used with range
for i in range(0, len(fram))
"""
return self._max_size
def __getitem__(self, address):
"""Read the value at the given index, or values in a slice.
.. code-block:: python
# read single index
fram[0]
# read values 0 thru 9 with a slice
fram[0:9]
"""
if isinstance(address, int):
if not 0 <= address < self._max_size:
raise ValueError(
"Address '{0}' out of range. It must be 0 <= address < {1}.".format(
address, self._max_size
)
)
buffer = bytearray(1)
read_buffer = self._read_address(address, buffer)
elif isinstance(address, slice):
if address.step is not None:
raise ValueError("Slice stepping is not currently available.")
regs = list(
range(
address.start if address.start is not None else 0,
address.stop if address.stop is not None else self._max_size,
)
)
if regs[0] < 0 or (regs[0] + len(regs)) > self._max_size:
raise ValueError(
"Address slice out of range. It must be 0 <= [starting address"
":stopping address] < {0}.".format(self._max_size)
)
buffer = bytearray(len(regs))
read_buffer = self._read_address(regs[0], buffer)
return read_buffer
def __setitem__(self, address, value):
"""Write the value at the given starting index.
.. code-block:: python
# write single index
fram[0] = 1
# write values 0 thru 4 with a list
fram[0:4] = [0,1,2,3]
"""
if self.write_protected:
raise RuntimeError("FRAM currently write protected.")
if isinstance(address, int):
if not isinstance(value, int):
raise ValueError("Data stored in an address must be an integer 0-255")
if not 0 <= address < self._max_size:
raise ValueError(
"Address '{0}' out of range. It must be 0 <= address < {1}.".format(
address, self._max_size
)
)
self._write(address, value, self._wraparound)
elif isinstance(address, slice):
if not isinstance(value, (bytes, bytearray, list, tuple)):
raise ValueError(
"Data must be bytes, bytearray, list, "
"or tuple for multiple addresses"
)
if (address.start is None) or (address.stop is None):
raise ValueError("Boundless slices are not supported")
if (address.step is not None) and (address.step != 1):
raise ValueError("Slice stepping is not currently available.")
if (address.start < 0) or (address.stop > self._max_size):
raise ValueError(
"Slice '{0}:{1}' out of range. All addresses must be 0 <= address < {2}.".format( # pylint: disable=line-too-long
address.start, address.stop, self._max_size
)
)
if len(value) < (len(range(address.start, address.stop))):
raise ValueError(
"Cannot set values with a list smaller than the number of indexes"
)
self._write(address.start, value, self._wraparound)
def _read_address(self, address, read_buffer):
# Implemented by subclass
raise NotImplementedError
def _write(self, start_address, data, wraparound):
# Implemened by subclass
raise NotImplementedError
class EEPROM_I2C(EEPROM):
"""I2C class for EEPROM.
:param: ~busio.I2C i2c_bus: The I2C bus the EEPROM is connected to.
:param: int address: I2C address of EEPROM. Default address is ``0x50``.
:param: bool write_protect: Turns on/off initial write protection.
Default is ``False``.
:param: wp_pin: (Optional) Physical pin connected to the ``WP`` breakout pin.
Must be a ``digitalio.DigitalInOut`` object.
"""
# pylint: disable=too-many-arguments
def __init__(self, i2c_bus, address=0x50, write_protect=False, wp_pin=None):
from adafruit_bus_device.i2c_device import ( # pylint: disable=import-outside-toplevel
I2CDevice as i2cdev,
)
self._i2c = i2cdev(i2c_bus, address)
super().__init__(_MAX_SIZE_I2C, write_protect, wp_pin)
def _read_address(self, address, read_buffer):
write_buffer = bytearray(2)
write_buffer[0] = address >> 8
write_buffer[1] = address & 0xFF
with self._i2c as i2c:
i2c.write_then_readinto(write_buffer, read_buffer)
return read_buffer
def _write(self, start_address, data, wraparound=False):
# Decided against using the chip's "Page Write", since that would require
# doubling the memory usage by creating a buffer that includes the passed
# in data so that it can be sent all in one `i2c.write`. The single-write
# method is slower, and forces us to handle wraparound, but I feel this
# is a better tradeoff for limiting the memory required for large writes.
buffer = bytearray(3)
if not isinstance(data, int):
data_length = len(data)
else:
data_length = 1
data = [data]
if (start_address + data_length) > self._max_size:
if wraparound:
pass
else:
raise ValueError(
"Starting address + data length extends beyond"
" FRAM maximum address. Use ``write_wraparound`` to"
" override this warning."
)
with self._i2c as i2c:
for i in range(0, data_length):
if not (start_address + i) > self._max_size - 1:
buffer[0] = (start_address + i) >> 8
buffer[1] = (start_address + i) & 0xFF
else:
buffer[0] = ((start_address + i) - self._max_size + 1) >> 8
buffer[1] = ((start_address + i) - self._max_size + 1) & 0xFF
buffer[2] = data[i]
i2c.write(buffer)
time.sleep(0.005)
# pylint: disable=no-member
@EEPROM.write_protected.setter
def write_protected(self, value):
if value not in (True, False):
raise ValueError("Write protected value must be 'True' or 'False'.")
self._wp = value
if not self._wp_pin is None:
self._wp_pin.value = value