3434# Datasheet high times for a "1" bit are 650 (min) 720 (typ) 1000 (max) ns
3535#
3636# 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 )
6248
6349
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
6856
6957
7058class TM1814PixelBackground ( # pylint: disable=too-few-public-methods
7159 adafruit_pixelbuf .PixelBuf
7260):
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+
74120 byte_count = 4 * n
75121 bit_count = byte_count * 8 + 64 # count the 64 brightness bits
76122
77- self ._brightness = brightness
78- raw_brightness = _convert_brightness (brightness )
123+ self ._current_control = current_control
79124
80125 # 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 ) )
82127 trailer = struct .pack (">L" , 38400 ) # Delay is about 3ms
83128
84129 self ._sm = StateMachine (
@@ -94,14 +139,42 @@ def __init__(self, pin, n, *, brightness=1.0, pixel_order="WRGB"):
94139 self ._buf = None
95140 super ().__init__ (
96141 n ,
97- brightness = 1.0 ,
142+ brightness = brightness ,
98143 byteorder = pixel_order ,
99144 auto_write = False ,
100145 header = header ,
101146 trailer = trailer ,
102147 )
103148
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
105178
106179 def deinit (self ) -> None :
107180 """Deinitialize the object"""
@@ -119,11 +192,6 @@ def auto_write(self) -> bool:
119192 def auto_write (self , value : bool ) -> None :
120193 pass
121194
122- @property
123- def brightness (self ) -> float :
124- """Returns the strip brightness (read-only)"""
125- return self ._brightness
126-
127195 def _transmit (self , buf : bytes ) -> None :
128196 self ._buf = buf
129197 self ._sm .background_write (loop = memoryview (buf ).cast ("L" ), swap = True )
0 commit comments