60
60
"""
61
61
)
62
62
63
+ TM1814_MIN_CURRENT = 6.5
64
+ TM1814_MAX_CURRENT = 38
65
+ TM1814_CURRENT_SCALE = 2
63
66
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
68
80
69
81
70
82
class TM1814PixelBackground ( # pylint: disable=too-few-public-methods
71
83
adafruit_pixelbuf .PixelBuf
72
84
):
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
+
74
118
byte_count = 4 * n
75
119
bit_count = byte_count * 8 + 64 # count the 64 brightness bits
76
120
77
- self ._brightness = brightness
78
- raw_brightness = _convert_brightness (brightness )
121
+ self ._current_control = current_control
79
122
80
123
# 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 ) )
82
125
trailer = struct .pack (">L" , 38400 ) # Delay is about 3ms
83
126
84
127
self ._sm = StateMachine (
@@ -94,14 +137,42 @@ def __init__(self, pin, n, *, brightness=1.0, pixel_order="WRGB"):
94
137
self ._buf = None
95
138
super ().__init__ (
96
139
n ,
97
- brightness = 1.0 ,
140
+ brightness = brightness ,
98
141
byteorder = pixel_order ,
99
142
auto_write = False ,
100
143
header = header ,
101
144
trailer = trailer ,
102
145
)
103
146
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
105
176
106
177
def deinit (self ) -> None :
107
178
"""Deinitialize the object"""
@@ -119,11 +190,6 @@ def auto_write(self) -> bool:
119
190
def auto_write (self , value : bool ) -> None :
120
191
pass
121
192
122
- @property
123
- def brightness (self ) -> float :
124
- """Returns the strip brightness (read-only)"""
125
- return self ._brightness
126
-
127
193
def _transmit (self , buf : bytes ) -> None :
128
194
self ._buf = buf
129
195
self ._sm .background_write (loop = memoryview (buf ).cast ("L" ), swap = True )
0 commit comments