-
Notifications
You must be signed in to change notification settings - Fork 7
Initial FRAM I2C #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
1b63072
a2536df
d9673bd
c80ec10
b97d359
6e12b3b
1251ebb
5546608
3deda9f
0ebf851
fbd6a0e
4c5a58b
8166bbf
2544b4f
5666a1b
8180928
f58f172
59a826f
3005592
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,9 +65,10 @@ class FRAM: | |
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: | ||
import digitalio | ||
self._wp_pin = digitalio.DigitalInOut(wp_pin) | ||
#import digitalio | ||
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 | ||
|
@@ -81,6 +82,20 @@ def max_size(self): | |
""" | ||
return self._max_size | ||
|
||
@property | ||
def write_wraparound(self): | ||
""" Determines if sequential writes will wrapaound the ``FRAM.max_size`` | ||
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 | ||
|
@@ -93,103 +108,58 @@ def write_protected(self): | |
When no ``WP`` pin is supplied, protection is only at the software | ||
level in this library. | ||
""" | ||
if not self._wp_pin is None: | ||
status = self._wp_pin.value | ||
else: | ||
status = self._wp | ||
return status | ||
return self._wp if self._wp_pin is None else self._wp_pin.value | ||
|
||
@write_protected.setter | ||
def write_protected(self, value): | ||
self._wp = value | ||
if not self._wp_pin is None: | ||
self._wp_pin.value = value | ||
|
||
def write_protect_pin(self, wp_pin, write_protect=False): | ||
""" Assigns the write protection (``WP``) pin. | ||
def __getitem__(self, key): | ||
if isinstance(key, int): | ||
if key > self._max_size: | ||
raise ValueError("Register '{0}' greater than maximum FRAM size." | ||
" ({1})".format(key, self._max_size)) | ||
return self._read_byte(key) | ||
elif isinstance(key, slice): | ||
registers = list(range(key.start if not key.start is None else 0, | ||
key.stop if not key.stop is None else self._max_size, | ||
key.step if not key.step is None else 1)) | ||
if (registers[0] + len(registers)) > self._max_size: | ||
raise ValueError("Register + Length greater than maximum FRAM size." | ||
" ({0})".format(self._max_size)) | ||
|
||
read_buffer = bytearray(len(registers)) | ||
for i, register in enumerate(registers): | ||
read_buffer[i] = self._read_byte(register)[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to read more than one byte at a time? That will be much faster. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point, I don't remember why I chose to read one byte at a time. May have been an attempt to minimize memory use. Just ran some comparisons; memory allocation is tripped at the same length of reads both ways. I'll switch it. (it is much faster) |
||
return read_buffer | ||
|
||
def __setitem__(self, key, value): | ||
if self.write_protected: | ||
raise RuntimeError("FRAM currently write protected.") | ||
|
||
:param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin | ||
on the breakout board/chip. To remove a previously | ||
set ``WP`` pin, set this value to ``None``. | ||
:param: bool write_protect: Turn on/off write protection immediately | ||
when setting the pin. Default is ``False`` | ||
if isinstance(key, int): | ||
if not isinstance(value, int): | ||
raise ValueError("Data must be an integer.") | ||
if key.start > self._max_size: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like slice code? Trying to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm...swear I fixed that on the |
||
raise ValueError("Requested register '{0}' greater than maximum" | ||
" FRAM size. ({1})".format(key.start, | ||
self._max_size)) | ||
|
||
""" | ||
if not wp_pin is None: | ||
import digitalio | ||
self._wp_pin = digitalio.DigitalInOut(wp_pin) | ||
# Make sure wp_pin is set to switch_to_output | ||
self._wp_pin.switch_to_output() | ||
self._wp_pin.value = write_protect | ||
else: | ||
if not self._wp_pin is None: | ||
# Deinit the pin to release it | ||
self._wp_pin.deinit() | ||
self._wp_pin = None | ||
|
||
def read(self, register, length=1): | ||
""" Reads the data stored on the FRAM. | ||
|
||
:param: int register: Register location to start reading. Range is: | ||
``0`` to ``max_size``. | ||
:param: int length: Length of registers to read from starting register. | ||
This function will create a buffer the size of | ||
``length``; larger buffers can cause memory | ||
allocation problems on some platforms. | ||
Range is ``1`` (default) to ``max_size``. | ||
However, ``register`` + ``length`` cannot be | ||
greater than ``max_size``. | ||
""" | ||
if length < 1: | ||
raise ValueError("Length must be '1' or greater.") | ||
if length > self._max_size: | ||
raise ValueError("Length '{0}' greater than maximum FRAM size." | ||
" ({1})".format(length, self._max_size)) | ||
if (register + length) > self._max_size: | ||
raise ValueError("Register + Length greater than maximum FRAM size." | ||
" ({0})".format(self._max_size)) | ||
read_buffer = bytearray(length) | ||
for i in range(length): | ||
read_buffer[i] = self._read_byte(register + i)[0] | ||
return read_buffer | ||
self._write_register(key.start, value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here - trying to use |
||
|
||
def write_single(self, register, data): | ||
""" Writes a single byte to the FRAM. | ||
elif isinstance(key, slice): | ||
if not isinstance(value, (bytearray, list, tuple)): | ||
raise ValueError("Data must be either a bytearray, list, or tuple.") | ||
if (key.start > self._max_size): | ||
raise ValueError("Requested register '{0}' greater than maximum" | ||
" FRAM size. ({1})".format(key.start, | ||
self._max_size)) | ||
if not key.step is None: | ||
raise ValueError("Slice steps are not allowed during write operations.") | ||
|
||
:param: int register: Register location to write the byte data. | ||
:param: int data: The data to write. | ||
""" | ||
if not isinstance(data, int): | ||
raise ValueError("Data must be an integer.") | ||
if self.write_protected: | ||
raise RuntimeError("FRAM currently write protected.") | ||
if register > self._max_size: | ||
raise ValueError("Requested register '{0}' greater than maximum" | ||
" FRAM size. ({1})".format(register, | ||
self._max_size)) | ||
self._write_register(register, data) | ||
|
||
def write_sequence(self, start_register, data, wraparound=False): | ||
""" Writes sequential data to the FRAM. | ||
|
||
:param: int start_register: Register location to start writing the | ||
data. | ||
:param: data: The data to write. Must be an iterable type of either | ||
``bytearray``, ``list``, or ``tuple``. | ||
:param: bool wraparound: Controls if sequential writes can wraparound | ||
beyond the ``max_size`` to zero, when | ||
``start_register`` + ``data length`` is | ||
greater than ``max_size``. | ||
""" | ||
if not isinstance(data, (bytearray, list, tuple)): | ||
raise ValueError("Data must be either a bytearray, list, or tuple.") | ||
if self.write_protected: | ||
raise RuntimeError("FRAM currently write protected.") | ||
if start_register > self._max_size: | ||
raise ValueError("Requested register '{0}' greater than maximum" | ||
" FRAM size. ({1})".format(start_register, | ||
self._max_size)) | ||
self._write_page(start_register, data, wraparound) | ||
self._write_page(key.start, value, self._wraparound) | ||
|
||
def _read_byte(self, register): | ||
return self._read_register(register) | ||
|
@@ -209,19 +179,16 @@ def _write_page(self, start_register, data, wraparound): | |
class FRAM_I2C(FRAM): | ||
""" I2C class for FRAM. | ||
|
||
:param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. | ||
:param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. | ||
:param: ~busio.I2C i2c_bus: The I2C bus the FRAM is connected to. | ||
:param: int address: I2C address of FRAM. Default address is ``0x50``. | ||
:param: bool write_protect: Turns on/off initial write protection. | ||
Default is ``False``. | ||
:param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` | ||
object. | ||
:param: wp_pin: Physical pin connected to the ``WP`` breakout pin. | ||
Must be a ``digitalio.DigitalInOut`` object. | ||
""" | ||
#pylint: disable=too-many-arguments | ||
def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, | ||
def __init__(self, i2c_bus, address=0x50, write_protect=False, | ||
wp_pin=None): | ||
from busio import I2C as i2c | ||
i2c_bus = i2c(i2c_SCL, i2c_SDA) | ||
i2c_bus.try_lock() | ||
i2c_bus.writeto((0xF8 >> 1), bytearray([(address << 1)]), stop=False) | ||
read_buf = bytearray(3) | ||
|
@@ -266,7 +233,7 @@ def _write_page(self, start_register, data, wraparound=False): | |
pass | ||
else: | ||
raise ValueError("Starting register + data length extends beyond" | ||
" FRAM maximum size. Use 'wraparound=True' to" | ||
" FRAM maximum size. Use ``write_wraparound`` to" | ||
" override this warning.") | ||
with self._i2c as i2c: | ||
for i in range(0, data_length): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,38 @@ | ||
## Simple Example For CircuitPython/Python I2C FRAM Library | ||
|
||
import board | ||
import busio | ||
import adafruit_fram | ||
|
||
## Create a FRAM object (default address used). | ||
|
||
fram = adafruit_fram.FRAM_I2C(board.SCL, board.SDA) | ||
i2c = busio.I2C(board.SCL, board.SDA) | ||
fram = adafruit_fram.FRAM_I2C(i2c) | ||
|
||
## Optional FRAM object with a different I2C address, as well | ||
## as a pin to control the hardware write protection ('WP' | ||
## pin on breakout). 'write_protected()' can be used | ||
## independent of the hardware pin. | ||
|
||
#fram = adafruit_fram.FRAM_I2C(board.SCL, | ||
# board.SDA, | ||
#import digitalio | ||
#wp = digitalio.DigitalInOut(board.D10) | ||
#fram = adafruit_fram.FRAM_I2C(i2c, | ||
# address=0x53, | ||
# wp_pin=board.D4) | ||
# wp_pin=wp) | ||
|
||
## Write a single-byte value to register address '0' | ||
|
||
fram.write_single(0, 1) | ||
fram[0] = 1 | ||
|
||
## Read that byte to ensure a proper write. | ||
## Note: 'read()' returns a bytearray | ||
## Note: reads return a bytearray | ||
|
||
print(fram.read(0)[1]) | ||
print(fram[0]) | ||
|
||
## Or write a sequential value, then read the values back. | ||
## Note: 'read()' returns a bytearray. It also allocates | ||
## a buffer the size of 'length', which may cause | ||
## Note: reads return a bytearray. Reads also allocate | ||
## a buffer the size of slice, which may cause | ||
## problems on memory-constrained platforms. | ||
|
||
#values = list(range(100)) # or bytearray or tuple | ||
#fram.write_sequence(0, values) | ||
#fram.read(0, length=100) | ||
#fram[0:100] = values | ||
#print(fram[0:100]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe have this be:
so you just get back the value instead of a single element bytearray with the value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meant to comment on this last night. With the change to
__getitem__
, I wanted to make the return value standard. I'm not completely opposed to sending back the value. Just felt that reduced API confusion was more beneficial.