34
34
# Datasheet high times for a "1" bit are 650 (min) 720 (typ) 1000 (max) ns
35
35
#
36
36
# Operating PIO at 14x the bit clock lets us achieve nominal 357ns and 714ns
37
- _program = Program (
38
- f"""
39
- .side_set 1
40
- .wrap_target
41
- pull block side 1
42
- out y, 32 side 1 ; get count of pixel bits
43
-
44
- bitloop:
45
- pull ifempty side 1 ; drive low
46
- out x 1 side 1 [4]
47
- jmp !x do_zero side 0 [3] ; drive low and branch depending on bit val
48
- jmp y--, bitloop side 0 [3] ; drive low for a one (long pulse)
49
- jmp end_sequence side 1 ; sequence is over
50
-
51
- do_zero:
52
- jmp y--, bitloop side 1 [3] ; drive high for a zero (short pulse)
53
-
54
- end_sequence:
55
- pull block side 1 ; get fresh delay value
56
- out y, 32 side 1 ; get delay count
57
- wait_reset:
58
- jmp y--, wait_reset side 1 ; wait until delay elapses
59
- .wrap
60
- """
61
- )
37
+ _pio_source = ()
38
+
39
+ TM1814_MIN_CURRENT = 6.5
40
+ TM1814_MAX_CURRENT = 38
41
+ TM1814_CURRENT_SCALE = 2
42
+
43
+
44
+ def _convert_one_current (value ):
45
+ if value < TM1814_MIN_CURRENT or value > TM1814_MAX_CURRENT :
46
+ raise ValueError ("Current control out of range" )
47
+ return round ((value - TM1814_MIN_CURRENT ) * TM1814_CURRENT_SCALE )
62
48
63
49
64
- def _convert_brightness (x ):
65
- x = int (x * 63 ) + 13
66
- x |= x << 8
67
- return x | (x << 16 )
50
+ def _current_control_word (arg ):
51
+ if isinstance (arg , (int , float )):
52
+ arg = arg , arg , arg , arg
53
+ result = [_convert_one_current (value ) for value in arg ]
54
+ result += [value ^ 0xFF for value in result ]
55
+ return result
68
56
69
57
70
58
class TM1814PixelBackground ( # pylint: disable=too-few-public-methods
71
59
adafruit_pixelbuf .PixelBuf
72
60
):
73
- def __init__ (self , pin , n , * , brightness = 1.0 , pixel_order = "WRGB" ):
61
+ """
62
+ A sequence of TM1814 addressable pixels
63
+
64
+ Except as noted, provides all the functionality of
65
+ `adafruit_pixelbuf.PixelBuf`, particularly
66
+ `adafruit_pixelbuf.PixelBuf.fill` and
67
+ `adafruit_pixelbuf.PixelBuf.__setitem__`.
68
+
69
+ As the strip always auto-written, there is no need to call the `show` method.
70
+
71
+ :param ~microcontroller.Pin pin: The pin to output neopixel data on.
72
+ :param int n: The number of neopixels in the chain
73
+ :param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full
74
+ brightness. This brightness value is software-multiplied with raw pixel values.
75
+ :param float|tuple[float,float,float] current_control: TM1814 current
76
+ control register. See documentation of the ``current_control`` property
77
+ below.
78
+ :param str pixel_order: Set the pixel color channel order. WRGB is set by
79
+ default. Only 4-bytes-per-pixel formats are supported.
80
+ :param bool inverted: True to invert the polarity of the output signal.
81
+ """
82
+
83
+ def __init__ ( # noqa: PLR0913
84
+ self ,
85
+ pin ,
86
+ n : int ,
87
+ * ,
88
+ brightness : float = 1.0 ,
89
+ pixel_order : str = "WRGB" ,
90
+ current_control : float | tuple [float , float , float , float ] = 38.0 ,
91
+ inverted : bool = False ,
92
+ ):
93
+ if len (pixel_order ) != 4 :
94
+ raise ValueError ("Invalid pixel_order" )
95
+
96
+ _program = Program (f"""
97
+ .side_set 1
98
+ .wrap_target
99
+ pull block side { not inverted :1d}
100
+ out y, 32 side { not inverted :1d} ; get count of pixel bits
101
+
102
+ bitloop:
103
+ pull ifempty side { not inverted :1d} ; drive low
104
+ out x 1 side { not inverted :1d} [4]
105
+ jmp !x do_zero side { inverted :1d} [3] ; drive low and branch depending on bit val
106
+ jmp y--, bitloop side { inverted :1d} [3] ; drive low for a one (long pulse)
107
+ jmp end_sequence side { not inverted :1d} ; sequence is over
108
+
109
+ do_zero:
110
+ jmp y--, bitloop side { not inverted :1d} [3] ; drive high for a zero (short pulse)
111
+
112
+ end_sequence:
113
+ pull block side { not inverted :1d} ; get fresh delay value
114
+ out y, 32 side { not inverted :1d} ; get delay count
115
+ wait_reset:
116
+ jmp y--, wait_reset side { not inverted :1d} ; wait until delay elapses
117
+ .wrap
118
+ """ )
119
+
74
120
byte_count = 4 * n
75
121
bit_count = byte_count * 8 + 64 # count the 64 brightness bits
76
122
77
- self ._brightness = brightness
78
- raw_brightness = _convert_brightness (brightness )
123
+ self ._current_control = current_control
79
124
80
125
# backwards, so that dma byteswap corrects it!
81
- header = struct .pack (">LLL " , bit_count - 1 , raw_brightness , raw_brightness ^ 0xFFFFFFFF )
126
+ header = struct .pack (">L8B " , bit_count - 1 , * _current_control_word ( current_control ) )
82
127
trailer = struct .pack (">L" , 38400 ) # Delay is about 3ms
83
128
84
129
self ._sm = StateMachine (
@@ -94,14 +139,42 @@ def __init__(self, pin, n, *, brightness=1.0, pixel_order="WRGB"):
94
139
self ._buf = None
95
140
super ().__init__ (
96
141
n ,
97
- brightness = 1.0 ,
142
+ brightness = brightness ,
98
143
byteorder = pixel_order ,
99
144
auto_write = False ,
100
145
header = header ,
101
146
trailer = trailer ,
102
147
)
103
148
104
- self .show ()
149
+ super ().show ()
150
+
151
+ def show (self ) -> None :
152
+ """Does nothing, because the strip is always auto-written"""
153
+
154
+ @property
155
+ def current_control (self ) -> float | tuple [float , float , float , float ]:
156
+ """Access the current control register of the TM1814
157
+
158
+ The TM1814 has a per-channel current control register that is shared across
159
+ the entire strip.
160
+
161
+ The current regulation range is from 6.5mA to 38mA in 0.5mA increments.
162
+ Out of range values will throw ValueError.
163
+
164
+ The relationship between human perception & LED current value is highly
165
+ nonlinear: The lowest setting may appear only slightly less bright than the
166
+ brightest setting, not 6x brighter as you might expect.
167
+
168
+ If this property is set to a single number, then the same value is used for
169
+ each channel. Otherwise, it must be a tuple of 4 elements where each element
170
+ is applied to a different channel.
171
+ """
172
+ return self ._current_control
173
+
174
+ @current_control .setter
175
+ def current_control (self , value : float | tuple [float , float , float ]) -> None :
176
+ struct .pack_into ("8B" , self ._buf , 4 , * _current_control_word (value ))
177
+ self ._current_control = value
105
178
106
179
def deinit (self ) -> None :
107
180
"""Deinitialize the object"""
@@ -119,11 +192,6 @@ def auto_write(self) -> bool:
119
192
def auto_write (self , value : bool ) -> None :
120
193
pass
121
194
122
- @property
123
- def brightness (self ) -> float :
124
- """Returns the strip brightness (read-only)"""
125
- return self ._brightness
126
-
127
195
def _transmit (self , buf : bytes ) -> None :
128
196
self ._buf = buf
129
197
self ._sm .background_write (loop = memoryview (buf ).cast ("L" ), swap = True )
0 commit comments