-
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
Conversation
Annnddd, Travis needs to be setup on this repo. Also...needs librarians added to admins. (will this guy ever stop talking? 😆) |
Added Librarians to the repo. Activated the repo on Travis, however it's not seeing the PR. I'll look more into it tomorrow. |
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.
General approach looks good. See comments about changes to interface.
adafruit_fram.py
Outdated
status = self._wp_pin.value | ||
else: | ||
status = self._wp | ||
return status |
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.
Could replace this entire function with:
return self._wp if self._wp_pin is None else self._wp_pin.value
adafruit_fram.py
Outdated
if not self._wp_pin is None: | ||
self._wp_pin.value = value | ||
|
||
def write_protect_pin(self, wp_pin, write_protect=False): |
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.
Are there scenarios where this would need to be set or changed after creating the object? If not, remove this and require passing it in through __init__
.
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.
I originally wrote it where the pin was only passed through during __init__
. I added this later, as a "nicety" for the user. No specific scenario in mind... I am fine with removing it.
adafruit_fram.py
Outdated
""" | ||
if not wp_pin is None: | ||
import digitalio | ||
self._wp_pin = digitalio.DigitalInOut(wp_pin) |
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.
Same comment here about pin object vs. designation.
adafruit_fram.py
Outdated
read_buffer[i] = self._read_byte(register + i)[0] | ||
return read_buffer | ||
|
||
def write_single(self, register, data): |
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.
Use __setitem__
for this to allow for fram[address] = value
.
fram[0x23] = 0x42
adafruit_fram.py
Outdated
self._max_size)) | ||
self._write_register(register, data) | ||
|
||
def write_sequence(self, start_register, data, wraparound=False): |
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.
This can also be done through __setitem__
.
fram[start_address] = data
adafruit_fram.py
Outdated
prod_id = (((read_buf[1] & 0x0F) << 8) + read_buf[2]) | ||
if (manf_id != _I2C_MANF_ID) and (prod_id != _I2C_PROD_ID): | ||
raise OSError("FRAM I2C device not found.") | ||
i2c_bus.unlock() |
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.
I2C bus creation should be done by the calling program and passed in. See how other I2C device drivers are doing this.
The MID/PID check to see if the device is connected and found is good though. Just change to doing that through the I2CDevice.
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.
I can change to, and agree with, having the I2C bus passed in. However, I'll still need to use the raw bus to get the MID/PID due to the following:
- The Device ID (MID/PID) is at specific address, which is different from the I2C address space that is passed in.
- Reading the Device ID requires a certain order of writes/reads, which I2CDevice would not allow. When I2CDevice is initialized, it writes an empty buffer to the stored address, which would (did) interfere with the specified steps.
examples/fram_i2c_simpletest.py
Outdated
## Read that byte to ensure a proper write. | ||
## Note: 'read()' returns a bytearray | ||
|
||
print(fram.read(0)[1]) |
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.
Off by one. fram.read(0)[0]
But this will become
print(fram[0])
Just documenting my testing of the basic functionality of current version. Adafruit CircuitPython 3.0.3 on 2018-10-10; Adafruit ItsyBitsy M4 Express with samd51g19
>>> import board
>>> import adafruit_fram
>>> fram = adafruit_fram.FRAM_I2C(board.SCL, board.SDA)
>>> fram.max_size
32767
>>> fram.write_single(0, 1)
>>> print(fram.read(0)[0])
1
>>> fram.write_protected
False
>>> fram.write_protected = True
>>> fram.write_protected
True
>>> fram.write_single(1, 23)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "adafruit_fram.py", line 165, in write_single
RuntimeError: FRAM currently write protected.
>>> |
adafruit_fram.py
Outdated
self._wp_pin.deinit() | ||
self._wp_pin = None | ||
|
||
def read(self, register, length=1): |
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.
Use__getitem__
for this.
value = fram[0]
values = fram[23:42]
Example of slice handling. Its rudimentary...doesn't support negative values, but aligns with CPython >>> fram[::1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./circuitpython_libs/Adafruit_CircuitPython_FRAM/adafruit_fram.py", line 128, in __getitem__
MemoryError: memory allocation failed, allocating 4096 bytes
>>> fram[:100]
bytearray(b'\x00\x01\x1elohello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
>>> fram[1:10:10]
bytearray(b'\x01')
>>> fram[1]
bytearray(b'\x01')
>>> fram[1:10]
bytearray(b'\x01\x1elohello')
>>> fram[:32760]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./circuitpython_libs/Adafruit_CircuitPython_FRAM/adafruit_fram.py", line 128, in __getitem__
MemoryError: memory allocation failed, allocating 4096 bytes
>>> fram[32760:]
bytearray(b'\x00\x00\x00\x00\x00\x00\x00')
>>> fram[0:1:1] = [0,1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./circuitpython_libs/Adafruit_CircuitPython_FRAM/adafruit_fram.py", line 160, in __setitem__
ValueError: Slice steps are not allowed during write operations.
>>> fram[0:5] = [0,1,2,3,4]
>>> fram[0:5]
bytearray(b'\x00\x01\x02\x03\x04') |
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.
New item access looks good. Found a few more tweaks.
adafruit_fram.py
Outdated
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) |
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:
return self._read_byte(key)[0]
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.
adafruit_fram.py
Outdated
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like slice code? Trying to use .start
on an int
type.
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.
hmm...swear I fixed that on the fix the fixes
commit.
adafruit_fram.py
Outdated
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Same here - trying to use .start
on an int
type.
#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 I2CDevice as i2cdev |
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.
Do you have this here so it only gets imported when the I2C class is used?
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.
Correct.
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.
OK. We may end up refactoring the various classes into other files. It's been done in other libraries, but I don't think we're being too consistent about it. This will become more important as this grows with the addition of the code for the SPI class.
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.
A couple comments but looks good otherwise!
adafruit_fram.py
Outdated
|
||
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 comment
The 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 comment
The 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)
adafruit_fram.py
Outdated
self._wp_pin = wp_pin | ||
|
||
@property | ||
def max_size(self): |
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.
Should this be the standard __len__
instead? That way you can use range on it in a loop.
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.
I have no issue with changing to __len__
, if that is more Pythonic. Since __getitem__
and __setitem__
are already documented as special members, I can just add this too.
Don't merge yet... I need to fix
|
@sommersoft Ping me when you need another review. |
@tannewt ready for another look. note: dropped slicing altogether for |
@tannewt ping. |
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.
Looks great! Thanks!
Here it be. Fixes Part 1 of CircuitPython Issue #665
I assume RTD sub-project will need to be created, but that can wait until I finish the SPI version (separate PR).
Also, I'm going to put in an issue to update the FRAM Learn Guide with CircuitPython. I'm happy to do it, just need to be added to the guide.