From bc5f43e1d93d507c15ba9b9979f18f054f4401a6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 Jun 2024 00:43:31 +0100 Subject: [PATCH] Keybow 2040: drop double-buffering, simplify buffer output. Remove hot path conversion to bytes and drop double-buffering which had no visible impact. Reduce the size of the output buffer to remove leading 17 and trailing 4 bytes, and prime the buffer with the first LED address. Update time is now ~4ms and much more stable. Signed-off-by: Phil Howard --- .../pimoroni/keybow_2040/keybow_2040_rgb.py | 66 ++++++++----------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/boards/pimoroni/keybow_2040/keybow_2040_rgb.py b/boards/pimoroni/keybow_2040/keybow_2040_rgb.py index 3133202c5..a28d66a93 100644 --- a/boards/pimoroni/keybow_2040/keybow_2040_rgb.py +++ b/boards/pimoroni/keybow_2040/keybow_2040_rgb.py @@ -5,7 +5,6 @@ from adafruit_bus_device.i2c_device import I2CDevice from adafruit_pixelbuf import PixelBuf -_FRAME_REGISTER = const(0x01) _SHUTDOWN_REGISTER = const(0x0A) _CONFIG_BANK = const(0x0B) _BANK_ADDRESS = const(0xFD) @@ -19,43 +18,46 @@ class Keybow2040Leds(PixelBuf): width = 16 height = 3 + + # Pixel addresses starting from LED 17, + # then offset by 1 to fit register addr _pixel_addr = [ - (120, 88, 104), # 0, 0 - (136, 40, 72), # 1, 0 - (112, 80, 96), # 2, 0 - (128, 32, 64), # 3, 0 - (121, 89, 105), # 0, 1 - (137, 41, 73), # 1, 1 - (113, 81, 97), # 2, 1 - (129, 33, 65), # 3, 1 - (122, 90, 106), # 0, 2 - (138, 25, 74), # 1, 2 - (114, 82, 98), # 2, 2 - (130, 17, 66), # 3, 2 - (123, 91, 107), # 0, 3 - (139, 26, 75), # 1, 3 - (115, 83, 99), # 2, 3 - (131, 18, 67), # 3, 3 + (104, 72, 88), # 0, 0 + (120, 24, 56), # 1, 0 + (96, 64, 80), # 2, 0 + (112, 16, 48), # 3, 0 + (105, 73, 89), # 0, 1 + (121, 25, 57), # 1, 1 + (97, 65, 81), # 2, 1 + (113, 17, 49), # 3, 1 + (106, 74, 90), # 0, 2 + (122, 9, 58), # 1, 2 + (98, 66, 82), # 2, 2 + (114, 1, 50), # 3, 2 + (107, 75, 91), # 0, 3 + (123, 10, 59), # 1, 3 + (99, 67, 83), # 2, 3 + (115, 2, 51), # 3, 3 ] - def __init__(self, size: int = 16): # Kept for backward compatibility + def __init__(self, size: int = 16): # size kept for backward compatibility self.i2c = busio.I2C(board.SCL, board.SDA, frequency=400_000) self.i2c_device = I2CDevice(self.i2c, 0x74) - self.out_buffer = bytearray(144) + self.out_buffer = bytearray(124) self._pixels = 16 - self._frame = 0 super().__init__(self._pixels, byteorder='RGB') with self.i2c_device as i2c: - for frame in range(2): - i2c.write(bytes([_BANK_ADDRESS, frame])) - i2c.write(bytes([_ENABLE_OFFSET] + [0xFF] * 18)) # Enable all LEDs - i2c.write(bytes([_COLOR_OFFSET] + [0x00] * 144)) # Init all to 0 + i2c.write(bytes([_BANK_ADDRESS, 0])) + i2c.write(bytes([_ENABLE_OFFSET] + [0xFF] * 18)) # Enable all LEDs + i2c.write(bytes([_COLOR_OFFSET] + [0x00] * 144)) # Init all to 0 i2c.write(bytes([_BANK_ADDRESS, _CONFIG_BANK])) i2c.write(bytes([0x00] * 14)) # Clear Mode, Frame, ... etc i2c.write(bytes([_SHUTDOWN_REGISTER, 0x01])) # 0 == shutdown, 1 == normal + i2c.write(bytes([_BANK_ADDRESS, 0])) + def _transmit(self, buffer): # Bring these into local scope for a tiny perf improvement ~.7ms pixel_addr = self._pixel_addr @@ -69,16 +71,6 @@ def _transmit(self, buffer): out[b] = buffer[x * 3 + 2] with self.i2c_device as i2c: - # Switch to our new (not currently visible) frame - i2c.write(bytes([_BANK_ADDRESS, self._frame])) - - # We only actually use 16 * 3 = 48 LEDs out of the 144 total - # but at 400KHz I2C it's cheaper just to write the whole lot - i2c.write(bytes([_COLOR_OFFSET + 17]) + self.out_buffer[17:140]) - - # Set the newly written frame as the visible one - i2c.write(bytes([_BANK_ADDRESS, _CONFIG_BANK])) - i2c.write(bytes([_FRAME_REGISTER, self._frame])) - - # Switch to our other buffer - self._frame = not self._frame + # Write our 124 byte buffer + # byte 0 is prefilled with the register address + i2c.write(self.out_buffer)