Skip to content

Commit fa2e0f3

Browse files
Merge branch 'main' into grid
2 parents 98b3b4e + be8931b commit fa2e0f3

File tree

4 files changed

+159
-84
lines changed

4 files changed

+159
-84
lines changed

adafruit_is31fl3741/__init__.py

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
2525
"""
2626

27-
import adafruit_bus_device.i2c_device as i2c_device
27+
from adafruit_bus_device import i2c_device
2828
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
2929
from adafruit_register.i2c_bit import RWBit
3030

@@ -43,6 +43,11 @@
4343
_IS3741_FUNCREG_GCURRENT = 0x01
4444
_IS3741_FUNCREG_RESET = 0x3F
4545

46+
# Buffer allocation behaviors passed to constructor
47+
NO_BUFFER = 0x00 # DO NOT buffer pixel data, write pixels as needed
48+
PREFER_BUFFER = 0x01 # OPTIONALLY buffer pixel data, RAM permitting
49+
MUST_BUFFER = 0x02 # MUST buffer pixel data, else throw MemoryError
50+
4651

4752
class IS31FL3741:
4853
"""
@@ -51,6 +56,12 @@ class IS31FL3741:
5156
5257
:param ~adafruit_bus_device.i2c_device i2c_device: the connected i2c bus i2c_device
5358
:param address: the device address; defaults to 0x30
59+
:param allocate: buffer allocation strategy: NO_BUFFER = pixels are always
60+
sent to device as they're set. PREFER_BUFFER = RAM
61+
permitting, buffer pixels in RAM, updating device only
62+
when show() is called, but fall back on NO_BUFFER
63+
behavior. MUST_BUFFER = buffer pixels in RAM, throw
64+
MemoryError if allocation fails.
5465
"""
5566

5667
width = 13
@@ -63,8 +74,19 @@ class IS31FL3741:
6374
_gcurrent_reg = UnaryStruct(_IS3741_FUNCREG_GCURRENT, "<B")
6475
_reset_reg = UnaryStruct(_IS3741_FUNCREG_RESET, "<B")
6576
_shutdown_bit = RWBit(_IS3741_FUNCREG_CONFIG, 0)
66-
67-
def __init__(self, i2c, address=_IS3741_ADDR_DEFAULT):
77+
_pixel_buffer = None
78+
79+
def __init__(self, i2c, address=_IS3741_ADDR_DEFAULT, allocate=NO_BUFFER):
80+
if allocate >= PREFER_BUFFER:
81+
try:
82+
# Pixel buffer intentionally has an extra item at the start
83+
# (value of 0) so we can i2c.write() from the buffer directly
84+
# (don't need a temp/copy buffer to pre-pend the register
85+
# address).
86+
self._pixel_buffer = bytearray(352)
87+
except MemoryError:
88+
if allocate == MUST_BUFFER:
89+
raise
6890
self.i2c_device = i2c_device.I2CDevice(i2c, address)
6991
if self._id_reg != 2 * address:
7092
raise AttributeError("Cannot find a IS31FL3741 at address 0x", address)
@@ -135,6 +157,8 @@ def page(self, page_value):
135157
def __getitem__(self, led):
136158
if not 0 <= led <= 350:
137159
raise ValueError("LED must be 0 ~ 350")
160+
if self._pixel_buffer:
161+
return self._pixel_buffer[1 + led]
138162
if led < 180:
139163
self.page = 0
140164
self._buf[0] = led
@@ -154,16 +178,18 @@ def __setitem__(self, led, pwm):
154178
if not 0 <= pwm <= 255:
155179
raise ValueError("PWM must be 0 ~ 255")
156180
# print(led, pwm)
157-
158-
if led < 180:
159-
self.page = 0
160-
self._buf[0] = led
181+
if self._pixel_buffer:
182+
self._pixel_buffer[1 + led] = pwm
161183
else:
162-
self.page = 1
163-
self._buf[0] = led - 180
164-
self._buf[1] = pwm
165-
with self.i2c_device as i2c:
166-
i2c.write(self._buf)
184+
if led < 180:
185+
self.page = 0
186+
self._buf[0] = led
187+
else:
188+
self.page = 1
189+
self._buf[0] = led - 180
190+
self._buf[1] = pwm
191+
with self.i2c_device as i2c:
192+
i2c.write(self._buf)
167193

168194
# This function must be replaced for each board
169195
@staticmethod
@@ -223,3 +249,27 @@ def image(self, img):
223249
for x in range(self.width): # yes this double loop is slow,
224250
for y in range(self.height): # but these displays are small!
225251
self.pixel(x, y, pixels[(x, y)])
252+
253+
def show(self):
254+
"""Issue in-RAM pixel data to device. No effect if pixels are
255+
unbuffered.
256+
"""
257+
if self._pixel_buffer:
258+
self.page = 0
259+
with self.i2c_device as i2c:
260+
# _pixel_buffer[0] is always 0! (First register addr)
261+
i2c.write(self._pixel_buffer, start=0, end=181)
262+
self.page = 1
263+
with self.i2c_device as i2c:
264+
# In order to write from pixel buffer directly (without a
265+
# whole extra temp buffer), element 180 is saved in a temp var
266+
# and replaced with 0 (representing the first regisyer addr on
267+
# page 1), then we can i2c.write() directly from that position
268+
# in the buffer. Element 180 is restored afterward. This is
269+
# the same strategy as used in the Arduino library.
270+
# 'end' below is 352 (not 351) because of the extra byte at
271+
# the start of the pixel buffer.
272+
save = self._pixel_buffer[180]
273+
self._pixel_buffer[180] = 0
274+
i2c.write(self._pixel_buffer, start=180, end=352)
275+
self._pixel_buffer[180] = save

adafruit_is31fl3741/adafruit_ledglasses.py

Lines changed: 76 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,92 +22,111 @@
2222
https://github.com/adafruit/circuitpython/releases
2323
2424
"""
25+
import adafruit_is31fl3741
2526
from . import IS31FL3741
2627

2728

2829
class Right_Ring:
2930
"""The right eye ring of the LED glasses"""
3031

32+
ledmap = (
33+
(287, 31, 30), # 0
34+
(278, 1, 0), # 1
35+
(273, 274, 275), # 2
36+
(282, 283, 284), # 3
37+
(270, 271, 272), # 4
38+
(27, 28, 29), # 5
39+
(23, 24, 25), # 6
40+
(276, 277, 22), # 7
41+
(20, 21, 26), # 8
42+
(50, 51, 56), # 9
43+
(80, 81, 86), # 10
44+
(110, 111, 116), # 11
45+
(140, 141, 146), # 12
46+
(170, 171, 176), # 13
47+
(200, 201, 206), # 14
48+
(230, 231, 236), # 15
49+
(260, 261, 266), # 16
50+
(348, 349, 262), # 17
51+
(233, 234, 235), # 18
52+
(237, 238, 239), # 19
53+
(339, 340, 232), # 20
54+
(327, 328, 329), # 21
55+
(305, 91, 90), # 22
56+
(296, 61, 60), # 23
57+
)
58+
3159
def __init__(self, is31_controller):
3260
self._is31 = is31_controller
3361

3462
def __setitem__(self, led, color):
3563
if not 0 <= led <= 23:
3664
raise ValueError("led must be 0~23")
3765

38-
ledmap = (
39-
(287, 31, 30), # 0
40-
(278, 1, 0), # 1
41-
(273, 274, 275), # 2
42-
(282, 283, 284), # 3
43-
(270, 271, 272), # 4
44-
(27, 28, 29), # 5
45-
(23, 24, 25), # 6
46-
(276, 277, 22), # 7
47-
(20, 21, 26), # 8
48-
(50, 51, 56), # 9
49-
(80, 81, 86), # 10
50-
(110, 111, 116), # 11
51-
(140, 141, 146), # 12
52-
(170, 171, 176), # 13
53-
(200, 201, 206), # 14
54-
(230, 231, 236), # 15
55-
(260, 261, 266), # 16
56-
(348, 349, 262), # 17
57-
(233, 234, 235), # 18
58-
(237, 238, 239), # 19
59-
(339, 340, 232), # 20
60-
(327, 328, 329), # 21
61-
(305, 91, 90), # 22
62-
(296, 61, 60), # 23
63-
)
64-
rgb = ledmap[led]
66+
rgb = Right_Ring.ledmap[led]
6567
self._is31[rgb[0]] = (color >> 16) & 0xFF
6668
self._is31[rgb[1]] = (color >> 8) & 0xFF
6769
self._is31[rgb[2]] = color & 0xFF
6870

71+
def __getitem__(self, led):
72+
if not 0 <= led <= 23:
73+
raise ValueError("led must be 0~23")
74+
rgb = Right_Ring.ledmap[led]
75+
return (
76+
(self._is31[rgb[0]] << 16) | (self._is31[rgb[1]] << 8) | self._is31[rgb[2]]
77+
)
78+
6979

7080
class Left_Ring:
7181
"""The left eye ring of the LED glasses"""
7282

83+
ledmap = (
84+
(341, 211, 210), # 0
85+
(332, 181, 180), # 1
86+
(323, 151, 150), # 2
87+
(127, 126, 125), # 3
88+
(154, 153, 152), # 4
89+
(163, 162, 161), # 5
90+
(166, 165, 164), # 6
91+
(244, 243, 242), # 7
92+
(259, 258, 257), # 8
93+
(169, 168, 167), # 9
94+
(139, 138, 137), # 10
95+
(109, 108, 107), # 11
96+
(79, 78, 77), # 12
97+
(49, 48, 47), # 13
98+
(199, 198, 197), # 14
99+
(229, 228, 227), # 15
100+
(19, 18, 17), # 16
101+
(4, 3, 2), # 17
102+
(16, 15, 14), # 18
103+
(13, 12, 11), # 19
104+
(10, 9, 8), # 20
105+
(217, 216, 215), # 21
106+
(7, 6, 5), # 22
107+
(350, 241, 240), # 23
108+
)
109+
73110
def __init__(self, is31_controller):
74111
self._is31 = is31_controller
75112

76113
def __setitem__(self, led, color):
77114
if not 0 <= led <= 23:
78115
raise ValueError("led must be 0~23")
79116

80-
ledmap = (
81-
(341, 211, 210), # 0
82-
(332, 181, 180), # 1
83-
(323, 151, 150), # 2
84-
(127, 126, 125), # 3
85-
(154, 153, 152), # 4
86-
(163, 162, 161), # 5
87-
(166, 165, 164), # 6
88-
(244, 243, 242), # 7
89-
(259, 258, 257), # 8
90-
(169, 168, 167), # 9
91-
(139, 138, 137), # 10
92-
(109, 108, 107), # 11
93-
(79, 78, 77), # 12
94-
(49, 48, 47), # 13
95-
(199, 198, 197), # 14
96-
(229, 228, 227), # 15
97-
(19, 18, 17), # 16
98-
(4, 3, 2), # 17
99-
(16, 15, 14), # 18
100-
(13, 12, 11), # 19
101-
(10, 9, 8), # 20
102-
(217, 216, 215), # 21
103-
(7, 6, 5), # 22
104-
(350, 241, 240), # 23
105-
)
106-
rgb = ledmap[led]
117+
rgb = Left_Ring.ledmap[led]
107118
self._is31[rgb[0]] = (color >> 16) & 0xFF
108119
self._is31[rgb[1]] = (color >> 8) & 0xFF
109120
self._is31[rgb[2]] = color & 0xFF
110121

122+
def __getitem__(self, led):
123+
if not 0 <= led <= 23:
124+
raise ValueError("led must be 0~23")
125+
rgb = Left_Ring.ledmap[led]
126+
return (
127+
(self._is31[rgb[0]] << 16) | (self._is31[rgb[1]] << 8) | self._is31[rgb[2]]
128+
)
129+
111130

112131
class LED_Glasses(IS31FL3741):
113132
"""Class representing LED Glasses"""
@@ -209,8 +228,9 @@ class LED_Glasses(IS31FL3741):
209228
# These pixels aren't connected to anything and are safe to use for pixels that aren't visible
210229
unused_pixels = (120, 121, 314)
211230

212-
def __init__(self, i2c):
213-
super().__init__(i2c)
231+
def __init__(self, i2c, allocate=adafruit_is31fl3741.NO_BUFFER):
232+
super().__init__(i2c, allocate=allocate)
233+
214234
self.set_led_scaling(0xFF) # turn on LEDs all the way
215235
self.global_current = 0xFE # set current to max
216236
self.enable = True # enable!

examples/is31fl3741_glassesrings.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
2-
# SPDX-License-Identifier: MIT
3-
4-
import board
5-
from rainbowio import colorwheel
6-
from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses
7-
8-
glasses = LED_Glasses(board.I2C())
9-
10-
wheeloffset = 0
11-
while True:
12-
for i in range(24):
13-
glasses.right_ring[i] = colorwheel(i / 24 * 255 + wheeloffset)
14-
glasses.left_ring[23 - i] = colorwheel(i / 24 * 255 + wheeloffset)
15-
wheeloffset += 10
1+
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
import board
5+
from rainbowio import colorwheel
6+
from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses
7+
import adafruit_is31fl3741
8+
9+
glasses = LED_Glasses(board.I2C(), allocate=adafruit_is31fl3741.MUST_BUFFER)
10+
11+
wheeloffset = 0
12+
while True:
13+
for i in range(24):
14+
hue = colorwheel(i * 256 // 24 + wheeloffset)
15+
glasses.right_ring[i] = hue
16+
glasses.left_ring[23 - i] = hue
17+
glasses.show()
18+
wheeloffset += 10

examples/is31fl3741_rgbswirl.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
from rainbowio import colorwheel
66

77
from adafruit_is31fl3741.adafruit_rgbmatrixqt import Adafruit_RGBMatrixQT
8+
import adafruit_is31fl3741
89

9-
is31 = Adafruit_RGBMatrixQT(board.I2C())
10+
is31 = Adafruit_RGBMatrixQT(board.I2C(), allocate=adafruit_is31fl3741.PREFER_BUFFER)
1011
is31.set_led_scaling(0xFF)
1112
is31.global_current = 0xFF
1213
# print("Global current is: ", is31.global_current)
@@ -19,3 +20,4 @@
1920
for x in range(13):
2021
is31.pixel(x, y, colorwheel((y * 13 + x) * 2 + wheeloffset))
2122
wheeloffset += 1
23+
is31.show()

0 commit comments

Comments
 (0)