apa102.py
Go to the documentation of this file.
1 """
2 from https://github.com/tinue/APA102_Pi
3 This is the main driver module for APA102 LEDs
4 """
5 import spidev
6 from math import ceil
7 
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] }
10 
11 class APA102:
12  """
13  Driver for APA102 LEDS (aka "DotStar").
14 
15  (c) Martin Erzberger 2016-2017
16 
17  My very first Python code, so I am sure there is a lot to be optimized ;)
18 
19  Public methods are:
20  - set_pixel
21  - set_pixel_rgb
22  - show
23  - clear_strip
24  - cleanup
25 
26  Helper methods for color manipulation are:
27  - combine_color
28  - wheel
29 
30  The rest of the methods are used internally and should not be used by the
31  user of the library.
32 
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.
35 
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.
39 
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.
44 
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.
47 
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.
53 
54  So that's really the entire protocol:
55  - Start by sending 32 bits of zeroes. This prepares LED 1 to update
56  its color.
57  - Send color information one by one, starting with the color for LED 1,
58  then LED 2 etc.
59  - Finish off by cycling the clock line a few times to get all data
60  to the very last LED on the strip
61 
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.
72  """
73  # Constants
74  MAX_BRIGHTNESS = 31 # Safeguard: Set to a value appropriate for your setup
75  LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
76 
77  def __init__(self, num_led, global_brightness=MAX_BRIGHTNESS,
78  order='rgb', bus=0, device=1, max_speed_hz=8000000):
79  self.num_led = num_led # The number of LEDs in the Strip
80  order = order.lower()
81  self.rgb = RGB_MAP.get(order, RGB_MAP['rgb'])
82  # Limit the brightness to the maximum if it's set higher
83  if global_brightness > self.MAX_BRIGHTNESS:
85  else:
86  self.global_brightness = global_brightness
87 
88  self.leds = [self.LED_START,0,0,0] * self.num_led # Pixel buffer
89  self.spi = spidev.SpiDev() # Init the SPI device
90  self.spi.open(bus, device) # Open SPI port 0, slave device (CS) 1
91  # Up the speed a bit, so that the LEDs are painted faster
92  if max_speed_hz:
93  self.spi.max_speed_hz = max_speed_hz
94 
95  def clock_start_frame(self):
96  """Sends a start frame to the LED strip.
97 
98  This method clocks out a start frame, telling the receiving LED
99  that it must update its own color now.
100  """
101  self.spi.xfer2([0] * 4) # Start frame, 32 zero bits
102 
103 
104  def clock_end_frame(self):
105  """Sends an end frame to the LED strip.
106 
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.
112 
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.
118 
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.
123 
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".
130  """
131  # Round up num_led/2 bits (or num_led/16 bytes)
132  for _ in range((self.num_led + 15) // 16):
133  self.spi.xfer2([0x00])
134 
135 
136  def clear_strip(self):
137  """ Turns off the strip and shows the result right away."""
138 
139  for led in range(self.num_led):
140  self.set_pixel(led, 0, 0, 0)
141  self.show()
142 
143 
144  def set_pixel(self, led_num, red, green, blue, bright_percent=100):
145  """Sets the color of one pixel in the LED stripe.
146 
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.
150  """
151  if led_num < 0:
152  return # Pixel is invisible, so ignore
153  if led_num >= self.num_led:
154  return # again, invisible
155 
156  # Calculate pixel brightness as a percentage of the
157  # defined global_brightness. Round up to nearest integer
158  # as we expect some brightness unless set to 0
159  brightness = ceil(bright_percent*self.global_brightness/100.0)
160  brightness = int(brightness)
161 
162  # LED startframe is three "1" bits, followed by 5 brightness bits
163  ledstart = (brightness & 0b00011111) | self.LED_START
164 
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
170 
171 
172  def set_pixel_rgb(self, led_num, rgb_color, bright_percent=100):
173  """Sets the color of one pixel in the LED stripe.
174 
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.
179  """
180  self.set_pixel(led_num, (rgb_color & 0xFF0000) >> 16,
181  (rgb_color & 0x00FF00) >> 8, rgb_color & 0x0000FF,
182  bright_percent)
183 
184 
185  def rotate(self, positions=1):
186  """ Rotate the LEDs by the specified number of positions.
187 
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.
191  """
192  cutoff = 4 * (positions % self.num_led)
193  self.leds = self.leds[cutoff:] + self.leds[:cutoff]
194 
195 
196  def show(self):
197  """Sends the content of the pixel buffer to the strip.
198 
199  Todo: More than 1024 LEDs requires more than one xfer operation.
200  """
201  self.clock_start_frame()
202  # xfer2 kills the list, unfortunately. So it must be copied first
203  # SPI takes up to 4096 Integers. So we are fine for up to 1024 LEDs.
204  self.spi.xfer2(list(self.leds))
205  self.clock_end_frame()
206 
207 
208  def cleanup(self):
209  """Release the SPI device; Call this method at the end"""
210 
211  self.spi.close() # Close SPI port
212 
213  @staticmethod
214  def combine_color(red, green, blue):
215  """Make one 3*8 byte color value."""
216 
217  return (red << 16) + (green << 8) + blue
218 
219 
220  def wheel(self, wheel_pos):
221  """Get a color from a color wheel; Green -> Red -> Blue -> Green"""
222 
223  if wheel_pos > 255:
224  wheel_pos = 255 # Safeguard
225  if wheel_pos < 85: # Green -> Red
226  return self.combine_color(wheel_pos * 3, 255 - wheel_pos * 3, 0)
227  if wheel_pos < 170: # Red -> Blue
228  wheel_pos -= 85
229  return self.combine_color(255 - wheel_pos * 3, 0, wheel_pos * 3)
230  # Blue -> Green
231  wheel_pos -= 170
232  return self.combine_color(0, wheel_pos * 3, 255 - wheel_pos * 3)
233 
234 
235  def dump_array(self):
236  """For debug purposes: Dump the LED array onto the console."""
237 
238  print(self.leds)
ppnrespeakerdemo.apa102.APA102.global_brightness
global_brightness
Definition: apa102.py:83
ppnrespeakerdemo.apa102.APA102.wheel
def wheel(self, wheel_pos)
Definition: apa102.py:220
ppnrespeakerdemo.apa102.APA102
Definition: apa102.py:11
ppnrespeakerdemo.apa102.APA102.spi
spi
Definition: apa102.py:88
ppnrespeakerdemo.apa102.APA102.combine_color
def combine_color(red, green, blue)
Definition: apa102.py:214
ppnrespeakerdemo.apa102.APA102.clock_end_frame
def clock_end_frame(self)
Definition: apa102.py:104
ppnrespeakerdemo.apa102.APA102.show
def show(self)
Definition: apa102.py:196
ppnrespeakerdemo.apa102.APA102.rgb
rgb
Definition: apa102.py:80
ppnrespeakerdemo.apa102.APA102.MAX_BRIGHTNESS
int MAX_BRIGHTNESS
Definition: apa102.py:74
ppnrespeakerdemo.apa102.APA102.LED_START
int LED_START
Definition: apa102.py:75
ppnrespeakerdemo.apa102.APA102.clear_strip
def clear_strip(self)
Definition: apa102.py:136
ppnrespeakerdemo.apa102.APA102.__init__
def __init__(self, num_led, global_brightness=MAX_BRIGHTNESS, order='rgb', bus=0, device=1, max_speed_hz=8000000)
Definition: apa102.py:77
ppnrespeakerdemo.apa102.APA102.set_pixel_rgb
def set_pixel_rgb(self, led_num, rgb_color, bright_percent=100)
Definition: apa102.py:172
ppnrespeakerdemo.apa102.APA102.leds
leds
Definition: apa102.py:87
ppnrespeakerdemo.apa102.APA102.set_pixel
def set_pixel(self, led_num, red, green, blue, bright_percent=100)
Definition: apa102.py:144
ppnrespeakerdemo.apa102.APA102.clock_start_frame
def clock_start_frame(self)
Definition: apa102.py:95
ppnrespeakerdemo.apa102.APA102.cleanup
def cleanup(self)
Definition: apa102.py:208
ppnrespeakerdemo.apa102.APA102.rotate
def rotate(self, positions=1)
Definition: apa102.py:185
ppnrespeakerdemo.apa102.APA102.num_led
num_led
Definition: apa102.py:78
ppnrespeakerdemo.apa102.APA102.dump_array
def dump_array(self)
Definition: apa102.py:235


picovoice_driver
Author(s):
autogenerated on Fri Apr 1 2022 02:13:47