2 from https://github.com/tinue/APA102_Pi
3 This is the main driver module for APA102 LEDs
8 RGB_MAP = {
'rgb': [3, 2, 1],
'rbg': [3, 1, 2],
'grb': [2, 3, 1],
9 'gbr': [2, 1, 3],
'brg': [1, 3, 2],
'bgr': [1, 2, 3] }
13 Driver for APA102 LEDS (aka "DotStar").
15 (c) Martin Erzberger 2016-2017
17 My very first Python code, so I am sure there is a lot to be optimized ;)
26 Helper methods for color manipulation are:
30 The rest of the methods are used internally and should not be used by the
33 Very brief overview of APA102: An APA102 LED is addressed with SPI. The bits
34 are shifted in one by one, starting with the least significant bit.
36 An LED usually just forwards everything that is sent to its data-in to
37 data-out. While doing this, it remembers its own color and keeps glowing
38 with that color as long as there is power.
40 An LED can be switched to not forward the data, but instead use the data
41 to change it's own color. This is done by sending (at least) 32 bits of
42 zeroes to data-in. The LED then accepts the next correct 32 bit LED
43 frame (with color information) as its new color setting.
45 After having received the 32 bit color frame, the LED changes color,
46 and then resumes to just copying data-in to data-out.
48 The really clever bit is this: While receiving the 32 bit LED frame,
49 the LED sends zeroes on its data-out line. Because a color frame is
50 32 bits, the LED sends 32 bits of zeroes to the next LED.
51 As we have seen above, this means that the next LED is now ready
52 to accept a color frame and update its color.
54 So that's really the entire protocol:
55 - Start by sending 32 bits of zeroes. This prepares LED 1 to update
57 - Send color information one by one, starting with the color for LED 1,
59 - Finish off by cycling the clock line a few times to get all data
60 to the very last LED on the strip
62 The last step is necessary, because each LED delays forwarding the data
63 a bit. Imagine ten people in a row. When you yell the last color
64 information, i.e. the one for person ten, to the first person in
65 the line, then you are not finished yet. Person one has to turn around
66 and yell it to person 2, and so on. So it takes ten additional "dummy"
67 cycles until person ten knows the color. When you look closer,
68 you will see that not even person 9 knows its own color yet. This
69 information is still with person 2. Essentially the driver sends additional
70 zeroes to LED 1 as long as it takes for the last color frame to make it
71 down the line to the last LED.
75 LED_START = 0b11100000
77 def __init__(self, num_led, global_brightness=MAX_BRIGHTNESS,
78 order='rgb', bus=0, device=1, max_speed_hz=8000000):
81 self.
rgb = RGB_MAP.get(order, RGB_MAP[
'rgb'])
89 self.
spi = spidev.SpiDev()
90 self.
spi.open(bus, device)
93 self.
spi.max_speed_hz = max_speed_hz
96 """Sends a start frame to the LED strip.
98 This method clocks out a start frame, telling the receiving LED
99 that it must update its own color now.
101 self.
spi.xfer2([0] * 4)
105 """Sends an end frame to the LED strip.
107 As explained above, dummy data must be sent after the last real colour
108 information so that all of the data can reach its destination down the line.
109 The delay is not as bad as with the human example above.
110 It is only 1/2 bit per LED. This is because the SPI clock line
111 needs to be inverted.
113 Say a bit is ready on the SPI data line. The sender communicates
114 this by toggling the clock line. The bit is read by the LED
115 and immediately forwarded to the output data line. When the clock goes
116 down again on the input side, the LED will toggle the clock up
117 on the output to tell the next LED that the bit is ready.
119 After one LED the clock is inverted, and after two LEDs it is in sync
120 again, but one cycle behind. Therefore, for every two LEDs, one bit
121 of delay gets accumulated. For 300 LEDs, 150 additional bits must be fed to
122 the input of LED one so that the data can reach the last LED.
124 Ultimately, we need to send additional numLEDs/2 arbitrary data bits,
125 in order to trigger numLEDs/2 additional clock changes. This driver
126 sends zeroes, which has the benefit of getting LED one partially or
127 fully ready for the next update to the strip. An optimized version
128 of the driver could omit the "clockStartFrame" method if enough zeroes have
129 been sent as part of "clockEndFrame".
132 for _
in range((self.
num_led + 15) // 16):
133 self.
spi.xfer2([0x00])
137 """ Turns off the strip and shows the result right away."""
139 for led
in range(self.
num_led):
144 def set_pixel(self, led_num, red, green, blue, bright_percent=100):
145 """Sets the color of one pixel in the LED stripe.
147 The changed pixel is not shown yet on the Stripe, it is only
148 written to the pixel buffer. Colors are passed individually.
149 If brightness is not set the global brightness setting is used.
160 brightness = int(brightness)
163 ledstart = (brightness & 0b00011111) | self.
LED_START
165 start_index = 4 * led_num
166 self.
leds[start_index] = ledstart
167 self.
leds[start_index + self.
rgb[0]] = red
168 self.
leds[start_index + self.
rgb[1]] = green
169 self.
leds[start_index + self.
rgb[2]] = blue
173 """Sets the color of one pixel in the LED stripe.
175 The changed pixel is not shown yet on the Stripe, it is only
176 written to the pixel buffer.
177 Colors are passed combined (3 bytes concatenated)
178 If brightness is not set the global brightness setting is used.
180 self.
set_pixel(led_num, (rgb_color & 0xFF0000) >> 16,
181 (rgb_color & 0x00FF00) >> 8, rgb_color & 0x0000FF,
186 """ Rotate the LEDs by the specified number of positions.
188 Treating the internal LED array as a circular buffer, rotate it by
189 the specified number of positions. The number could be negative,
190 which means rotating in the opposite direction.
192 cutoff = 4 * (positions % self.
num_led)
197 """Sends the content of the pixel buffer to the strip.
199 Todo: More than 1024 LEDs requires more than one xfer operation.
204 self.
spi.xfer2(list(self.
leds))
209 """Release the SPI device; Call this method at the end"""
215 """Make one 3*8 byte color value."""
217 return (red << 16) + (green << 8) + blue
221 """Get a color from a color wheel; Green -> Red -> Blue -> Green"""
226 return self.
combine_color(wheel_pos * 3, 255 - wheel_pos * 3, 0)
229 return self.
combine_color(255 - wheel_pos * 3, 0, wheel_pos * 3)
232 return self.
combine_color(0, wheel_pos * 3, 255 - wheel_pos * 3)
236 """For debug purposes: Dump the LED array onto the console."""