3
3
# Copyright (c) 2016 Damien P. George (original Neopixel object)
4
4
# Copyright (c) 2017 Ladyada
5
5
# Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
6
+ # Copyright (c) 2019 Roy Hooper
6
7
#
7
8
# Permission is hereby granted, free of charge, to any person obtaining a copy
8
9
# of this software and associated documentation files (the "Software"), to deal
23
24
# THE SOFTWARE.
24
25
25
26
"""
26
- `adafruit_dotstar` - DotStar strip driver
27
- ====================================================
27
+ `adafruit_dotstar` - DotStar strip driver (for CircuitPython 5.0+ with _pixelbuf)
28
+ =================================================================================
28
29
29
- * Author(s): Damien P. George, Limor Fried & Scott Shawcroft
30
+ * Author(s): Damien P. George, Limor Fried, Scott Shawcroft & Roy Hooper
30
31
"""
31
32
import busio
32
33
import digitalio
34
+ try :
35
+ import _pixelbuf
36
+ except ImportError :
37
+ import adafruit_pypixelbuf as _pixelbuf
33
38
34
39
__version__ = "0.0.0-auto.0"
35
40
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git"
36
41
37
42
START_HEADER_SIZE = 4
38
- LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
39
43
40
- # Pixel color order constants
41
- RGB = ( 0 , 1 , 2 )
42
- RBG = ( 0 , 2 , 1 )
43
- GRB = ( 1 , 0 , 2 )
44
- GBR = ( 1 , 2 , 0 )
45
- BRG = ( 2 , 0 , 1 )
46
- BGR = ( 2 , 1 , 0 )
44
+ RBG = 'PRBG'
45
+ RGB = 'PRGB'
46
+ GRB = 'PGRB'
47
+ GBR = 'PGBR'
48
+ BRG = 'PBRG'
49
+ BGR = 'PBGR'
50
+ BGR = 'PBGR'
47
51
48
52
49
- class DotStar :
53
+ class DotStar ( _pixelbuf . PixelBuf ) :
50
54
"""
51
55
A sequence of dotstars.
52
56
@@ -56,16 +60,14 @@ class DotStar:
56
60
:param float brightness: Brightness of the pixels between 0.0 and 1.0
57
61
:param bool auto_write: True if the dotstars should immediately change when
58
62
set. If False, `show` must be called explicitly.
59
- :param tuple pixel_order: Set the pixel order on the strip - different
60
- strips implement this differently. If you send red, and it looks blue
61
- or green on the strip, modify this! It should be one of the values
62
- above.
63
+ :param str pixel_order: Set the pixel order on the strip - different
64
+ strips implement this differently. If you send red, and it looks blue
65
+ or green on the strip, modify this! It should be one of the values above.
63
66
:param int baudrate: Desired clock rate if using hardware SPI (ignored if
64
67
using 'soft' SPI). This is only a recommendation; the actual clock
65
68
rate may be slightly different depending on what the system hardware
66
69
can provide.
67
70
68
-
69
71
Example for Gemma M0:
70
72
71
73
.. code-block:: python
@@ -105,36 +107,60 @@ def __init__(
105
107
self .dpin .direction = digitalio .Direction .OUTPUT
106
108
self .cpin .direction = digitalio .Direction .OUTPUT
107
109
self .cpin .value = False
108
- self ._n = n
110
+ self .n = n
111
+
109
112
# Supply one extra clock cycle for each two pixels in the strip.
110
- self . end_header_size = n // 16
113
+ end_header_size = n // 16
111
114
if n % 16 != 0 :
112
- self . end_header_size += 1
113
- self . _buf = bytearray ( n * 4 + START_HEADER_SIZE + self . end_header_size )
114
- self . end_header_index = len ( self . _buf ) - self . end_header_size
115
+ end_header_size += 1
116
+ bufsize = 4 * n + START_HEADER_SIZE + end_header_size
117
+ end_header_index = bufsize - end_header_size
115
118
self .pixel_order = pixel_order
119
+
120
+ self ._buf = bytearray (bufsize )
121
+ self ._rawbuf = bytearray (bufsize )
122
+
116
123
# Four empty bytes to start.
117
124
for i in range (START_HEADER_SIZE ):
118
- self ._buf [i ] = 0x00
119
- # Mark the beginnings of each pixel.
120
- for i in range (START_HEADER_SIZE , self .end_header_index , 4 ):
121
- self ._buf [i ] = 0xFF
125
+ self ._rawbuf [i ] = 0x00
122
126
# 0xff bytes at the end.
123
- for i in range (self .end_header_index , len (self ._buf )):
124
- self ._buf [i ] = 0xFF
125
- self ._brightness = 1.0
126
- # Set auto_write to False temporarily so brightness setter does _not_
127
- # call show() while in __init__.
128
- self .auto_write = False
129
- self .brightness = brightness
130
- self .auto_write = auto_write
127
+ for i in range (end_header_index , bufsize ):
128
+ self ._rawbuf [i ] = 0xff
129
+ # Mark the beginnings of each pixel.
130
+ for i in range (START_HEADER_SIZE , end_header_index , 4 ):
131
+ self ._rawbuf [i ] = 0xff
132
+ self ._buf [:] = self ._rawbuf [:]
133
+
134
+ super (DotStar , self ).__init__ (n , self ._buf , byteorder = pixel_order ,
135
+ rawbuf = self ._rawbuf , offset = START_HEADER_SIZE ,
136
+ brightness = brightness , auto_write = auto_write )
137
+
138
+ def show (self ):
139
+ """Shows the new colors on the pixels themselves if they haven't already
140
+ been autowritten.
141
+
142
+ The colors may or may not be showing after this method returns because
143
+ it may be done asynchronously.
144
+
145
+ This method is called automatically if auto_write is set to True.
146
+ """
147
+ if self ._spi :
148
+ self ._spi .write (self ._buf )
149
+ else :
150
+ self .ds_writebytes ()
151
+
152
+ def _ds_writebytes (self ):
153
+ for b in self .buf :
154
+ for _ in range (8 ):
155
+ self .dpin .value = (b & 0x80 )
156
+ self .cpin .value = True
157
+ self .cpin .value = False
158
+ b = b << 1
159
+ self .cpin .value = False
131
160
132
161
def deinit (self ):
133
162
"""Blank out the DotStars and release the resources."""
134
- self .auto_write = False
135
- for i in range (START_HEADER_SIZE , self .end_header_index ):
136
- if i % 4 != 0 :
137
- self ._buf [i ] = 0
163
+ self .fill (0 )
138
164
self .show ()
139
165
if self ._spi :
140
166
self ._spi .deinit ()
@@ -151,136 +177,6 @@ def __exit__(self, exception_type, exception_value, traceback):
151
177
def __repr__ (self ):
152
178
return "[" + ", " .join ([str (x ) for x in self ]) + "]"
153
179
154
- def _set_item (self , index , value ):
155
- """
156
- value can be one of three things:
157
- a (r,g,b) list/tuple
158
- a (r,g,b, brightness) list/tuple
159
- a single, longer int that contains RGB values, like 0xFFFFFF
160
- brightness, if specified should be a float 0-1
161
-
162
- Set a pixel value. You can set per-pixel brightness here, if it's not passed it
163
- will use the max value for pixel brightness value, which is a good default.
164
-
165
- Important notes about the per-pixel brightness - it's accomplished by
166
- PWMing the entire output of the LED, and that PWM is at a much
167
- slower clock than the rest of the LEDs. This can cause problems in
168
- Persistence of Vision Applications
169
- """
170
-
171
- offset = index * 4 + START_HEADER_SIZE
172
- rgb = value
173
- if isinstance (value , int ):
174
- rgb = (value >> 16 , (value >> 8 ) & 0xFF , value & 0xFF )
175
-
176
- if len (rgb ) == 4 :
177
- brightness = value [3 ]
178
- # Ignore value[3] below.
179
- else :
180
- brightness = 1
181
-
182
- # LED startframe is three "1" bits, followed by 5 brightness bits
183
- # then 8 bits for each of R, G, and B. The order of those 3 are configurable and
184
- # vary based on hardware
185
- # same as math.ceil(brightness * 31) & 0b00011111
186
- # Idea from https://www.codeproject.com/Tips/700780/Fast-floor-ceiling-functions
187
- brightness_byte = 32 - int (32 - brightness * 31 ) & 0b00011111
188
- self ._buf [offset ] = brightness_byte | LED_START
189
- self ._buf [offset + 1 ] = rgb [self .pixel_order [0 ]]
190
- self ._buf [offset + 2 ] = rgb [self .pixel_order [1 ]]
191
- self ._buf [offset + 3 ] = rgb [self .pixel_order [2 ]]
192
-
193
- def __setitem__ (self , index , val ):
194
- if isinstance (index , slice ):
195
- start , stop , step = index .indices (self ._n )
196
- length = stop - start
197
- if step != 0 :
198
- # same as math.ceil(length / step)
199
- # Idea from https://fizzbuzzer.com/implement-a-ceil-function/
200
- length = (length + step - 1 ) // step
201
- if len (val ) != length :
202
- raise ValueError ("Slice and input sequence size do not match." )
203
- for val_i , in_i in enumerate (range (start , stop , step )):
204
- self ._set_item (in_i , val [val_i ])
205
- else :
206
- self ._set_item (index , val )
207
-
208
- if self .auto_write :
209
- self .show ()
210
-
211
- def __getitem__ (self , index ):
212
- if isinstance (index , slice ):
213
- out = []
214
- for in_i in range (* index .indices (self ._n )):
215
- out .append (
216
- tuple (
217
- self ._buf [in_i * 4 + (3 - i ) + START_HEADER_SIZE ]
218
- for i in range (3 )
219
- )
220
- )
221
- return out
222
- if index < 0 :
223
- index += len (self )
224
- if index >= self ._n or index < 0 :
225
- raise IndexError
226
- offset = index * 4
227
- return tuple (self ._buf [offset + (3 - i ) + START_HEADER_SIZE ] for i in range (3 ))
228
-
229
- def __len__ (self ):
230
- return self ._n
231
-
232
- @property
233
- def brightness (self ):
234
- """Overall brightness of the pixel"""
235
- return self ._brightness
236
-
237
- @brightness .setter
238
- def brightness (self , brightness ):
239
- self ._brightness = min (max (brightness , 0.0 ), 1.0 )
240
- if self .auto_write :
241
- self .show ()
242
-
243
180
def fill (self , color ):
244
181
"""Colors all pixels the given ***color***."""
245
- auto_write = self .auto_write
246
- self .auto_write = False
247
- for i in range (self ._n ):
248
- self [i ] = color
249
- if auto_write :
250
- self .show ()
251
- self .auto_write = auto_write
252
-
253
- def _ds_writebytes (self , buf ):
254
- for b in buf :
255
- for _ in range (8 ):
256
- self .dpin .value = b & 0x80
257
- self .cpin .value = True
258
- self .cpin .value = False
259
- b = b << 1
260
-
261
- def show (self ):
262
- """Shows the new colors on the pixels themselves if they haven't already
263
- been autowritten.
264
-
265
- The colors may or may not be showing after this function returns because
266
- it may be done asynchronously."""
267
- # Create a second output buffer if we need to compute brightness
268
- buf = self ._buf
269
- if self .brightness < 1.0 :
270
- buf = bytearray (self ._buf )
271
- # Four empty bytes to start.
272
- for i in range (START_HEADER_SIZE ):
273
- buf [i ] = 0x00
274
- for i in range (START_HEADER_SIZE , self .end_header_index ):
275
- buf [i ] = (
276
- self ._buf [i ] if i % 4 == 0 else int (self ._buf [i ] * self ._brightness )
277
- )
278
- # Four 0xff bytes at the end.
279
- for i in range (self .end_header_index , len (buf )):
280
- buf [i ] = 0xFF
281
-
282
- if self ._spi :
283
- self ._spi .write (buf )
284
- else :
285
- self ._ds_writebytes (buf )
286
- self .cpin .value = False
182
+ _pixelbuf .fill (self , color )
0 commit comments