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
"""
32
+
33
+ # pylint: disable=ungrouped-imports
34
+ import sys
31
35
import busio
32
36
import digitalio
33
37
38
+ if sys .implementation .version [0 ] < 5 :
39
+ import adafruit_pypixelbuf as _pixelbuf
40
+ else :
41
+ try :
42
+ import _pixelbuf
43
+ except ImportError :
44
+ import adafruit_pypixelbuf as _pixelbuf
45
+
34
46
__version__ = "0.0.0-auto.0"
35
47
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git"
36
48
37
49
START_HEADER_SIZE = 4
38
- LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
39
50
40
51
# 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 )
47
-
48
-
49
- class DotStar :
52
+ RBG = "PRBG"
53
+ """Red Blue Green"""
54
+ RGB = "PRGB"
55
+ """Red Green Blue"""
56
+ GRB = "PGRB"
57
+ """Green Red Blue"""
58
+ GBR = "PGBR"
59
+ """Green Blue Red"""
60
+ BRG = "PBRG"
61
+ """Blue Red Green"""
62
+ BGR = "PBGR"
63
+ """Blue Green Red"""
64
+
65
+
66
+ class DotStar (_pixelbuf .PixelBuf ):
50
67
"""
51
68
A sequence of dotstars.
52
69
@@ -56,16 +73,14 @@ class DotStar:
56
73
:param float brightness: Brightness of the pixels between 0.0 and 1.0
57
74
:param bool auto_write: True if the dotstars should immediately change when
58
75
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.
76
+ :param str pixel_order: Set the pixel order on the strip - different
77
+ strips implement this differently. If you send red, and it looks blue
78
+ or green on the strip, modify this! It should be one of the values above.
63
79
:param int baudrate: Desired clock rate if using hardware SPI (ignored if
64
80
using 'soft' SPI). This is only a recommendation; the actual clock
65
81
rate may be slightly different depending on what the system hardware
66
82
can provide.
67
83
68
-
69
84
Example for Gemma M0:
70
85
71
86
.. code-block:: python
@@ -79,6 +94,22 @@ class DotStar:
79
94
with adafruit_dotstar.DotStar(APA102_SCK, APA102_MOSI, 1) as pixels:
80
95
pixels[0] = RED
81
96
time.sleep(2)
97
+
98
+ .. py:method:: DotStar.show()
99
+
100
+ Shows the new colors on the dotstars themselves if they haven't already
101
+ been autowritten.
102
+
103
+ The colors may or may not be showing after this function returns because
104
+ it may be done asynchronously.
105
+
106
+ .. py:method:: DotStar.fill(color)
107
+
108
+ Colors all dotstars the given ***color***.
109
+
110
+ .. py:attribute:: brightness
111
+
112
+ Overall brightness of all dotstars (0 to 1.0)
82
113
"""
83
114
84
115
def __init__ (
@@ -105,36 +136,29 @@ def __init__(
105
136
self .dpin .direction = digitalio .Direction .OUTPUT
106
137
self .cpin .direction = digitalio .Direction .OUTPUT
107
138
self .cpin .value = False
108
- self . _n = n
139
+
109
140
# Supply one extra clock cycle for each two pixels in the strip.
110
- self . end_header_size = n // 16
141
+ trailer_size = n // 16
111
142
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
- self .pixel_order = pixel_order
116
- # Four empty bytes to start.
117
- 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
122
- # 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
143
+ trailer_size += 1
144
+
145
+ # Four empty bytes for the header.
146
+ header = bytearray (START_HEADER_SIZE )
147
+ # 0xff bytes for the trailer.
148
+ trailer = bytearray (b"\xff " ) * trailer_size
149
+
150
+ super ().__init__ (
151
+ n ,
152
+ byteorder = pixel_order ,
153
+ brightness = brightness ,
154
+ auto_write = auto_write ,
155
+ header = header ,
156
+ trailer = trailer ,
157
+ )
131
158
132
159
def deinit (self ):
133
160
"""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
161
+ self .fill (0 )
138
162
self .show ()
139
163
if self ._spi :
140
164
self ._spi .deinit ()
@@ -151,136 +175,24 @@ def __exit__(self, exception_type, exception_value, traceback):
151
175
def __repr__ (self ):
152
176
return "[" + ", " .join ([str (x ) for x in self ]) + "]"
153
177
154
- def _set_item (self , index , value ):
178
+ @property
179
+ def n (self ):
155
180
"""
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
181
+ The number of dotstars in the chain (read-only)
169
182
"""
183
+ return len (self )
170
184
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 ])
185
+ def _transmit (self , buffer ):
186
+ if self ._spi :
187
+ self ._spi .write (buffer )
205
188
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
189
+ self ._ds_writebytes (buffer )
231
190
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
- def fill (self , color ):
244
- """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 :
191
+ def _ds_writebytes (self , buffer ):
192
+ for b in buffer :
255
193
for _ in range (8 ):
256
194
self .dpin .value = b & 0x80
257
195
self .cpin .value = True
258
196
self .cpin .value = False
259
197
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
198
+ self .cpin .value = False
0 commit comments