2
2
# SPDX-FileCopyrightText: 2017 Ladyada for Adafruit Industries
3
3
# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
4
4
# SPDX-FileCopyrightText: 2018 Kevin J. Walters
5
+ # SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
5
6
#
6
7
# SPDX-License-Identifier: MIT
7
8
8
9
"""
9
10
`adafruit_ws2801` - WS2801 LED pixel string driver
10
11
====================================================
11
12
12
- * Author(s): Damien P. George, Limor Fried & Scott Shawcroft, Kevin J Walters
13
+ * Author(s): Damien P. George, Limor Fried & Scott Shawcroft, Kevin J Walters, Tim Cocks
13
14
"""
14
- import math
15
15
16
+ import adafruit_pixelbuf
16
17
import busio
17
18
import digitalio
18
19
19
20
try :
20
- from typing import Any , Union , Tuple , List
21
+ from typing import Type , Optional
22
+ from circuitpython_typing import ReadableBuffer
23
+ from types import TracebackType
21
24
from microcontroller import Pin
22
25
except ImportError :
23
26
pass
27
30
28
31
# based on https://github.com/adafruit/Adafruit_CircuitPython_DotStar
29
32
30
-
31
- class WS2801 :
33
+ # Pixel color order constants
34
+ RBG = "PRBG"
35
+ """Red Blue Green"""
36
+ RGB = "PRGB"
37
+ """Red Green Blue"""
38
+ GRB = "PGRB"
39
+ """Green Red Blue"""
40
+ GBR = "PGBR"
41
+ """Green Blue Red"""
42
+ BRG = "PBRG"
43
+ """Blue Red Green"""
44
+ BGR = "PBGR"
45
+ """Blue Green Red"""
46
+
47
+
48
+ class WS2801 (adafruit_pixelbuf .PixelBuf ):
32
49
"""
33
50
A sequence of WS2801 controlled LEDs.
34
51
@@ -38,7 +55,9 @@ class WS2801:
38
55
:param float brightness: The brightness between 0.0 and (default) 1.0.
39
56
:param bool auto_write: True if the dotstars should immediately change when
40
57
set. If False, `show` must be called explicitly.
41
-
58
+ :param str pixel_order: Set the pixel order on the strip - different
59
+ strips implement this differently. If you send red, and it looks blue
60
+ or green on the strip, modify this! It should be one of the values above.
42
61
43
62
Example for Gemma M0:
44
63
@@ -53,16 +72,34 @@ class WS2801:
53
72
with adafruit_ws2801.WS2801(board.D2, board.D0, 25, brightness=1.0) as pixels:
54
73
pixels[0] = darkred
55
74
time.sleep(2)
75
+
76
+ .. py:method:: show()
77
+
78
+ Shows the new colors on the ws2801 LEDs themselves if they haven't already
79
+ been autowritten.
80
+
81
+ The colors may or may not be showing after this function returns because
82
+ it may be done asynchronously.
83
+
84
+ .. py:method:: fill(color)
85
+
86
+ Colors all ws2801 LEDs the given ***color***.
87
+
88
+ .. py:attribute:: brightness
89
+
90
+ Overall brightness of all ws2801 LEDs (0 to 1.0)
91
+
56
92
"""
57
93
58
- def __init__ (
94
+ def __init__ ( # pylint: disable=too-many-arguments
59
95
self ,
60
96
clock : Pin ,
61
97
data : Pin ,
62
98
n : int ,
63
99
* ,
64
100
brightness : float = 1.0 ,
65
- auto_write : bool = True
101
+ auto_write : bool = True ,
102
+ pixel_order : str = "RGB" ,
66
103
) -> None :
67
104
self ._spi = None
68
105
try :
@@ -76,21 +113,31 @@ def __init__(
76
113
self .dpin .direction = digitalio .Direction .OUTPUT
77
114
self .cpin .direction = digitalio .Direction .OUTPUT
78
115
self .cpin .value = False
79
- self ._n = n
80
- self ._buf = bytearray (n * 3 )
81
- self ._brightness = 1.0 # keeps pylint happy
82
- # Set auto_write to False temporarily so brightness setter does _not_
83
- # call show() while in __init__.
84
- self .auto_write = False
85
- self .brightness = brightness
86
- self .auto_write = auto_write
87
- # TODO - review/consider adding GRB support like that in c++ version
116
+
117
+ # Supply one extra clock cycle for each two pixels in the strip.
118
+ trailer_size = n // 16
119
+ if n % 16 != 0 :
120
+ trailer_size += 1
121
+
122
+ # Empty header.
123
+ header = bytearray (0 )
124
+ # Zero bits, not ones, for the trailer, to avoid lighting up
125
+ # downstream pixels, if there are more physical pixels than
126
+ # the length of this object.
127
+ trailer = bytearray (trailer_size )
128
+
129
+ super ().__init__ (
130
+ n ,
131
+ byteorder = pixel_order ,
132
+ brightness = brightness ,
133
+ auto_write = auto_write ,
134
+ header = header ,
135
+ trailer = trailer ,
136
+ )
88
137
89
138
def deinit (self ) -> None :
90
- """Blank out the DotStars and release the resources."""
91
- self .auto_write = False
92
- black = (0 , 0 , 0 )
93
- self .fill (black )
139
+ """Blank out the ws2801 LEDs and release the resources."""
140
+ self .fill (0 )
94
141
self .show ()
95
142
if self ._spi :
96
143
self ._spi .deinit ()
@@ -102,81 +149,16 @@ def __enter__(self) -> "WS2801":
102
149
return self
103
150
104
151
def __exit__ (
105
- self , exception_type : Any , exception_value : Any , traceback : Any
152
+ self ,
153
+ exception_type : Optional [Type [type ]],
154
+ exception_value : Optional [BaseException ],
155
+ traceback : Optional [TracebackType ],
106
156
) -> None :
107
157
self .deinit ()
108
158
109
159
def __repr__ (self ):
110
160
return "[" + ", " .join ([str (x ) for x in self ]) + "]"
111
161
112
- def _set_item (self , index : int , value : Union [Tuple [int , ...], int ]):
113
- offset = index * 3
114
- if isinstance (value , int ):
115
- r = value >> 16
116
- g = (value >> 8 ) & 0xFF
117
- b = value & 0xFF
118
- else :
119
- r , g , b = value
120
- # red/green/blue order for WS2801
121
- self ._buf [offset ] = r
122
- self ._buf [offset + 1 ] = g
123
- self ._buf [offset + 2 ] = b
124
-
125
- def __setitem__ (self , index : int , val : Union [Tuple [int , ...], int ]):
126
- if isinstance (index , slice ):
127
- start , stop , step = index .indices (self ._n )
128
- length = stop - start
129
- if step != 0 :
130
- length = math .ceil (length / step )
131
- if len (val ) != length :
132
- raise ValueError ("Slice and input sequence size do not match." )
133
- for val_i , in_i in enumerate (range (start , stop , step )):
134
- self ._set_item (in_i , val [val_i ])
135
- else :
136
- self ._set_item (index , val )
137
-
138
- if self .auto_write :
139
- self .show ()
140
-
141
- def __getitem__ (
142
- self , index : Union [slice , int ]
143
- ) -> Union [Tuple [int , ...], List [Tuple [int , ...]]]:
144
- if isinstance (index , slice ):
145
- out = []
146
- for in_i in range (* index .indices (self ._n )):
147
- out .append (tuple (self ._buf [in_i * 3 + i ] for i in range (3 )))
148
- return out
149
- if index < 0 :
150
- index += len (self )
151
- if index >= self ._n or index < 0 :
152
- raise IndexError
153
- offset = index * 3
154
- return tuple (self ._buf [offset + i ] for i in range (3 ))
155
-
156
- def __len__ (self ) -> int :
157
- return self ._n
158
-
159
- @property
160
- def brightness (self ) -> float :
161
- """Overall brightness of the pixel"""
162
- return self ._brightness
163
-
164
- @brightness .setter
165
- def brightness (self , brightness : float ) -> None :
166
- self ._brightness = min (max (brightness , 0.0 ), 1.0 )
167
- if self .auto_write :
168
- self .show ()
169
-
170
- def fill (self , color : Union [Tuple [int , ...], int ]) -> None :
171
- """Colors all pixels the given ***color***."""
172
- auto_write = self .auto_write
173
- self .auto_write = False
174
- for i , _ in enumerate (self ):
175
- self [i ] = color
176
- if auto_write :
177
- self .show ()
178
- self .auto_write = auto_write
179
-
180
162
def _ds_writebytes (self , buf : bytearray ) -> None :
181
163
for b in buf :
182
164
for _ in range (8 ):
@@ -185,21 +167,8 @@ def _ds_writebytes(self, buf: bytearray) -> None:
185
167
self .cpin .value = False
186
168
b = b << 1
187
169
188
- def show (self ) -> None :
189
- """Shows the new colors on the pixels themselves if they haven't already
190
- been autowritten.
191
-
192
- The colors may or may not be showing after this function returns because
193
- it may be done asynchronously."""
194
- # Create a second output buffer if we need to compute brightness
195
- buf = self ._buf
196
- if self .brightness < 1.0 :
197
- buf = bytearray (len (self ._buf ))
198
- for i , val in enumerate (self ._buf ):
199
- buf [i ] = int (val * self ._brightness )
200
-
170
+ def _transmit (self , buffer : ReadableBuffer ) -> None :
201
171
if self ._spi :
202
- self ._spi .write (buf )
172
+ self ._spi .write (buffer )
203
173
else :
204
- self ._ds_writebytes (buf )
205
- self .cpin .value = False
174
+ self ._ds_writebytes (buffer )
0 commit comments