Skip to content

A couple of speedups #48

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

Merged
merged 3 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions adafruit_is31fl3731/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ class IS31FL3731:
width = 16
height = 9

def __init__(self, i2c, address=0x74):
def __init__(self, i2c, address=0x74, frames=None):
self.i2c = i2c
self.address = address
self._frame = None
self._init()
self._init(frames=frames)

def _i2c_read_reg(self, reg, result):
# Read a buffer of data from the specified 8-bit I2C register address.
Expand All @@ -111,19 +111,21 @@ def _i2c_read_reg(self, reg, result):
self.i2c.unlock()
return None

def _i2c_write_reg(self, reg, data):
# Write a buffer of data (byte array) to the specified I2C register
# address.
def _i2c_write_block(self, data):
# Writes a contiguous block of data (bytearray) where the first byte
# is the starting I2C register address (register is not an argument).
while not self.i2c.try_lock():
pass
try:
buf = bytearray(1)
buf[0] = reg
buf.extend(data)
self.i2c.writeto(self.address, buf)
self.i2c.writeto(self.address, data)
finally:
self.i2c.unlock()

def _i2c_write_reg(self, reg, data):
# Write a contiguous block of data (bytearray) starting at the
# specified I2C register address (register passed as argument).
self._i2c_write_block(bytes([reg]) + data)

def _bank(self, bank=None):
if bank is None:
result = bytearray(1)
Expand All @@ -142,16 +144,21 @@ def _register(self, bank, register, value=None):
def _mode(self, mode=None):
return self._register(_CONFIG_BANK, _MODE_REGISTER, mode)

def _init(self):
def _init(self, frames=None):
self.sleep(True)
time.sleep(0.01) # 10 MS pause to reset.
self._mode(_PICTURE_MODE)
self.frame(0)
for frame in range(8):
self.fill(0, False, frame=frame)
for col in range(18):
self._register(frame, _ENABLE_OFFSET + col, 0xFF)
self.audio_sync(False)
# Clear config; sets to Picture Mode, no audio sync, maintains sleep
self._bank(_CONFIG_BANK)
self._i2c_write_block(bytes([0] * 14))
enable_data = bytes([_ENABLE_OFFSET] + [255] * 18)
fill_data = bytearray([0] * 25)
# Initialize requested frames, or all 8 if unspecified
for frame in frames if frames else range(8):
self._bank(frame)
self._i2c_write_block(enable_data) # Set all enable bits
for row in range(6): # Barebones quick fill() w/0
fill_data[0] = _COLOR_OFFSET + row * 24
self._i2c_write_block(fill_data)
self._frame = 0 # To match config bytes above
self.sleep(False)

def reset(self):
Expand Down
37 changes: 37 additions & 0 deletions adafruit_is31fl3731/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,40 @@ class Matrix(IS31FL3731):
def pixel_addr(x, y):
"""Calulate the offset into the device array for x,y pixel"""
return x + y * 16

# This takes precedence over image() in __init__ and is tuned for the
# Matrix class. Some shortcuts can be taken because matrix layout is
# very straightforward, and a few large write operations are used
# rather than pixel-by-pixel writes, yielding significant speed gains
# for animation. Buffering the full matrix for a quick write is not a
# memory concern here, as by definition this method is used with PIL
# images; we're not running on a RAM-constrained microcontroller.
def image(self, img, blink=None, frame=None):
"""Set buffer to value of Python Imaging Library image.
The image should be in 8-bit mode (L) and a size equal to the
display size.

:param img: Python Imaging Library image
:param blink: True to blink
:param frame: the frame to set the image
"""
if img.mode != "L":
raise ValueError("Image must be in mode L.")
if img.size[0] != self.width or img.size[1] != self.height:
raise ValueError(
"Image must be same dimensions as display ({0}x{1}).".format(
self.width, self.height
)
)

# Frame-select and then write pixel data in one big operation
if frame is not None:
self._bank(frame)
# We can safely reduce the image to a "flat" byte sequence because
# the matrix layout is known linear; no need to go through a 2D
# pixel array or invoke pixel_addr().
self._i2c_write_block(bytes([0x24]) + img.tobytes())
# Set or clear blink state if requested, for all pixels at once
if blink is not None:
# 0x12 is _BLINK_OFFSET in __init__.py
self._i2c_write_block(bytes([0x12] + [1 if blink else 0] * 18))