Skip to content

Commit 9b9a580

Browse files
committed
Add current_control, restore software brightness functionality
1 parent 4836032 commit 9b9a580

File tree

1 file changed

+81
-15
lines changed

1 file changed

+81
-15
lines changed

adafruit_tm1814.py

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,68 @@
6060
"""
6161
)
6262

63+
TM1814_MIN_CURRENT = 6.5
64+
TM1814_MAX_CURRENT = 38
65+
TM1814_CURRENT_SCALE = 2
6366

64-
def _convert_brightness(x):
65-
x = int(x * 63) + 13
66-
x |= x << 8
67-
return x | (x << 16)
67+
68+
def _convert_one_current(value):
69+
if value < TM1814_MIN_CURRENT or value > TM1814_MAX_CURRENT:
70+
raise ValueError("Current control out of range")
71+
return round((value - TM1814_MIN_CURRENT) * TM1814_CURRENT_SCALE)
72+
73+
74+
def _current_control_word(arg):
75+
if isinstance(arg, (int, float)):
76+
arg = arg, arg, arg, arg
77+
result = [_convert_one_current(value) for value in arg]
78+
result += [value ^ 0xFF for value in result]
79+
return result
6880

6981

7082
class TM1814PixelBackground( # pylint: disable=too-few-public-methods
7183
adafruit_pixelbuf.PixelBuf
7284
):
73-
def __init__(self, pin, n, *, brightness=1.0, pixel_order="WRGB"):
85+
"""
86+
A sequence of TM1814 addressable pixels
87+
88+
Except as noted, provides all the functionality of
89+
`adafruit_pixelbuf.PixelBuf`, particularly
90+
`adafruit_pixelbuf.PixelBuf.fill` and
91+
`adafruit_pixelbuf.PixelBuf.__setitem__`.
92+
93+
As the strip always auto-written, there is no need to call the `show` method.
94+
95+
:param ~microcontroller.Pin pin: The pin to output neopixel data on.
96+
:param int n: The number of neopixels in the chain
97+
:param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full
98+
brightness. This brightness value is software-multiplied with raw pixel values.
99+
:param float|tuple[float,float,float] current_control: TM1814 current
100+
control register. See documentation of the ``current_control`` property
101+
below.
102+
:param str pixel_order: Set the pixel color channel order. WRGB is set by
103+
default. Only 4-bytes-per-pixel formats are supported.
104+
"""
105+
106+
def __init__( # noqa: PLR0913
107+
self,
108+
pin,
109+
n: int,
110+
*,
111+
brightness: float = 1.0,
112+
pixel_order: str = "WRGB",
113+
current_control: float | tuple[float, float, float, float] = 38.0,
114+
):
115+
if len(pixel_order) != 4:
116+
raise ValueError("Invalid pixel_order")
117+
74118
byte_count = 4 * n
75119
bit_count = byte_count * 8 + 64 # count the 64 brightness bits
76120

77-
self._brightness = brightness
78-
raw_brightness = _convert_brightness(brightness)
121+
self._current_control = current_control
79122

80123
# backwards, so that dma byteswap corrects it!
81-
header = struct.pack(">LLL", bit_count - 1, raw_brightness, raw_brightness ^ 0xFFFFFFFF)
124+
header = struct.pack(">L8B", bit_count - 1, *_current_control_word(current_control))
82125
trailer = struct.pack(">L", 38400) # Delay is about 3ms
83126

84127
self._sm = StateMachine(
@@ -94,14 +137,42 @@ def __init__(self, pin, n, *, brightness=1.0, pixel_order="WRGB"):
94137
self._buf = None
95138
super().__init__(
96139
n,
97-
brightness=1.0,
140+
brightness=brightness,
98141
byteorder=pixel_order,
99142
auto_write=False,
100143
header=header,
101144
trailer=trailer,
102145
)
103146

104-
self.show()
147+
super().show()
148+
149+
def show(self) -> None:
150+
"""Does nothing, because the strip is always auto-written"""
151+
152+
@property
153+
def current_control(self) -> float | tuple[float, float, float, float]:
154+
"""Access the current control register of the TM1814
155+
156+
The TM1814 has a per-channel current control register that is shared across
157+
the entire strip.
158+
159+
The current regulation range is from 6.5mA to 38mA in 0.5mA increments.
160+
Out of range values will throw ValueError.
161+
162+
The relationship between human perception & LED current value is highly
163+
nonlinear: The lowest setting may appear only slightly less bright than the
164+
brightest setting, not 6x brighter as you might expect.
165+
166+
If this property is set to a single number, then the same value is used for
167+
each channel. Otherwise, it must be a tuple of 4 elements where each element
168+
is applied to a different channel.
169+
"""
170+
return self._current_control
171+
172+
@current_control.setter
173+
def current_control(self, value: float | tuple[float, float, float]) -> None:
174+
struct.pack_into("8B", self._buf, 4, *_current_control_word(value))
175+
self._current_control = value
105176

106177
def deinit(self) -> None:
107178
"""Deinitialize the object"""
@@ -119,11 +190,6 @@ def auto_write(self) -> bool:
119190
def auto_write(self, value: bool) -> None:
120191
pass
121192

122-
@property
123-
def brightness(self) -> float:
124-
"""Returns the strip brightness (read-only)"""
125-
return self._brightness
126-
127193
def _transmit(self, buf: bytes) -> None:
128194
self._buf = buf
129195
self._sm.background_write(loop=memoryview(buf).cast("L"), swap=True)

0 commit comments

Comments
 (0)