SHOW:
|
|
- or go back to the newest paste.
1 | /*------------------------------------------------------------------------ | |
2 | Python module to control Adafruit Dot Star addressable RGB LEDs. | |
3 | ||
4 | This has some Known Issues(tm): | |
5 | ||
6 | It's modeled after the Adafruit_DotStar Arduino library (C++), for | |
7 | better or for worse. The idea is that the majority of existing Arduino | |
8 | code for DotStar & NeoPixel LEDs can then port over with less fuss. | |
9 | As such, it's less "Python-like" than it could (and perhaps should) | |
10 | be...for example, RGB colors might be more elegantly expressed as | |
11 | tuples or something other than the packed 32-bit integers used here. | |
12 | Also, it does not have 100% feature parity with that library...e.g. | |
13 | getPixels() is missing here, and this code allows changing the SPI | |
14 | bitrate (Arduino lib does not). | |
15 | ||
16 | There's no doc strings yet. | |
17 | ||
18 | The library can use either hardware SPI or "bitbang" output....but one | |
19 | must be careful in the latter case not to overlap the SPI GPIO pins... | |
20 | once they're set as bitbang outputs by this code, they're no longer | |
21 | usable for SPI even after the code exits (and not just by this library; | |
22 | subsequent runs, other code, etc. all are locked out of SPI, only fix | |
23 | seems to be a reboot). The library checks for an exact overlap between | |
24 | the requested bitbang data & clock pins and the hardware SPI pins, and | |
25 | will switch over to hardware SPI in that case...but partial overlaps | |
26 | (just the data -or- clock pin, or if their positions are swapped) are | |
27 | not protected. | |
28 | ||
29 | As of 9/15 this is using the empirical APA102 data format (rather than | |
30 | the datasheet specification). If it suddenly starts misbehaving with | |
31 | new LEDs in the future, may be a hardware production change in the LEDs. | |
32 | ||
33 | Written by Phil Burgess for Adafruit Industries, with contributions from | |
34 | the open source community. | |
35 | ||
36 | Adafruit invests time and resources providing this open source code, | |
37 | please support Adafruit and open-source hardware by purchasing products | |
38 | from Adafruit! | |
39 | ||
40 | ------------------------------------------------------------------------ | |
41 | This file is part of the Adafruit Dot Star library. | |
42 | ||
43 | Adafruit Dot Star is free software: you can redistribute it and/or | |
44 | modify it under the terms of the GNU Lesser General Public License | |
45 | as published by the Free Software Foundation, either version 3 of | |
46 | the License, or (at your option) any later version. | |
47 | ||
48 | Adafruit Dot Star is distributed in the hope that it will be useful, | |
49 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
50 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
51 | GNU Lesser General Public License for more details. | |
52 | ||
53 | You should have received a copy of the GNU Lesser General Public | |
54 | License along with NeoPixel. If not, see <http://www.gnu.org/licenses/>. | |
55 | ------------------------------------------------------------------------*/ | |
56 | ||
57 | // #include <python2.7/Python.h> | |
58 | #include <stdint.h> | |
59 | #include <stdbool.h> | |
60 | #include <stdlib.h> | |
61 | #include <stdio.h> | |
62 | #include <string.h> | |
63 | #include <unistd.h> | |
64 | #include <stddef.h> | |
65 | #include <assert.h> | |
66 | #include <limits.h> | |
67 | #include <fcntl.h> | |
68 | #include <time.h> | |
69 | #include <sys/mman.h> | |
70 | #include <sys/ioctl.h> | |
71 | #include <linux/spi/spidev.h> | |
72 | ||
73 | // From GPIO example code by Dom and Gert van Loo on elinux.org: | |
74 | #define PI1_BCM2708_PERI_BASE 0x20000000 | |
75 | #define PI1_GPIO_BASE (PI1_BCM2708_PERI_BASE + 0x200000) | |
76 | #define PI2_BCM2708_PERI_BASE 0x3F000000 | |
77 | #define PI2_GPIO_BASE (PI2_BCM2708_PERI_BASE + 0x200000) | |
78 | #define BLOCK_SIZE (32768*10) | |
79 | #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) | |
80 | #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) | |
81 | ||
82 | #define SPI_MOSI_PIN 10 | |
83 | #define SPI_CLK_PIN 11 | |
84 | ||
85 | static volatile unsigned | |
86 | *gpio = NULL, // Memory-mapped GPIO peripheral | |
87 | *gpioSet, // Write bitmask of GPIO pins to set here | |
88 | *gpioClr; // Write bitmask of GPIO pins to clear here | |
89 | ||
90 | static uint8_t isPi2 = 0; // For clock pulse timing & stuff | |
91 | ||
92 | // SPI transfer operation setup. These are only used w/hardware SPI | |
93 | // and LEDs at full brightness (or raw write); other conditions require | |
94 | // per-byte processing. Explained further in the show() method. | |
95 | static struct spi_ioc_transfer xfer[3] = { | |
96 | { .tx_buf = 0, // Header (zeros) | |
97 | .rx_buf = 0, | |
98 | .len = 4, | |
99 | .delay_usecs = 0, | |
100 | .bits_per_word = 8, | |
101 | .cs_change = 0 }, | |
102 | { .rx_buf = 0, // Color payload | |
103 | .delay_usecs = 0, | |
104 | .bits_per_word = 8, | |
105 | .cs_change = 0 }, | |
106 | { .tx_buf = 0, // Footer (zeros) | |
107 | .rx_buf = 0, | |
108 | .delay_usecs = 0, | |
109 | .bits_per_word = 8, | |
110 | .cs_change = 0 } | |
111 | }; | |
112 | ||
113 | typedef struct { | |
114 | uint32_t numLEDs, // Number of pixels in strip | |
115 | dataMask, // Data pin bitmask if using bitbang SPI | |
116 | clockMask, // Clock pin bitmask if bitbang SPI | |
117 | bitrate; // SPI clock speed if using hardware SPI | |
118 | int fd; // File descriptor if using hardware SPI | |
119 | uint8_t *pixels, // -> pixel data | |
120 | *pBuf, // -> temp buf for brightness-scaling w/SPI | |
121 | dataPin, // Data pin # if bitbang SPI | |
122 | clockPin, // Clock pin # if bitbang SPI | |
123 | brightness, // Global brightness setting | |
124 | rOffset, // Index of red in 4-byte pixel | |
125 | gOffset, // Index of green byte | |
126 | bOffset; // Index of blue byte | |
127 | } DotStarObject; | |
128 | ||
129 | // Allocate new DotStar object. There's a few ways this can be called: | |
130 | // x = Adafruit_DotStar(nleds, datapin, clockpin) Bitbang output | |
131 | // x = Adafruit_DotStar(nleds, bitrate) Use hardware SPI @ bitrate | |
132 | // x = Adafruit_DotStar(nleds) Hardware SPI @ default rate | |
133 | // x = Adafruit_DotStar() 0 LEDs, HW SPI, default rate | |
134 | // 0 LEDs is valid, but one must then pass a properly-sized and -rendered | |
135 | // bytearray to the show() method. | |
136 | static DotStarObject *DotStar_new(uint32_t pCount, uint8_t mosiPin, uint8_t clkPin, uint32_t bRate) { | |
137 | // DotStarObject *self = malloc(sizeof(DotStarObject)); | |
138 | DotStarObject *self = (DotStarObject*)malloc(sizeof(DotStarObject)); | |
139 | uint8_t *pixels = NULL, dPin = 0xFF, cPin = 0xFF; | |
140 | uint32_t n_pixels = pCount; | |
141 | printf("Pixel count assigned %i\n", n_pixels); | |
142 | uint32_t bitrate = bRate; | |
143 | uint32_t i; | |
144 | // PyObject *string; | |
145 | char *order = NULL, *c; | |
146 | // uint8_t rOffset = 2, gOffset = 3, bOffset = 1; // BRG default | |
147 | uint8_t rOffset = 3, gOffset = 1, bOffset = 2; | |
148 | ||
149 | // If pins happen to correspond to hardware SPI data and | |
150 | // clock, hardware SPI is used instead. Because reasons. | |
151 | // if((dPin == SPI_MOSI_PIN) && (cPin == SPI_CLK_PIN)){ | |
152 | // dPin = cPin = 0xFF; | |
153 | // } | |
154 | ||
155 | // Can optionally append keyword to specify R/G/B pixel order | |
156 | // "order='rgb'" or similar (switch r/g/b around to match strip). | |
157 | // Order string isn't much validated; nonsense may occur. | |
158 | // if(kw && (string = PyDict_GetItemString(kw, "order")) && | |
159 | // (order = PyString_AsString(string))) { | |
160 | // order = 'bgr'; | |
161 | // for(i=0; order[i]; i++) order[i] = tolower(order[i]); | |
162 | // if((c = strchr(order, 'r'))) rOffset = c - order + 1; | |
163 | // if((c = strchr(order, 'g'))) gOffset = c - order + 1; | |
164 | // if((c = strchr(order, 'b'))) bOffset = c - order + 1; | |
165 | // } | |
166 | ||
167 | // Allocate space for LED data: | |
168 | if((!n_pixels) || ((pixels = (uint8_t *)malloc(n_pixels * 8)))) { | |
169 | - | if(true) { |
169 | + | |
170 | self->dataMask = 0; | |
171 | self->clockMask = 0; | |
172 | self->bitrate = bitrate; | |
173 | self->fd = -1; | |
174 | self->pixels = pixels; // NULL if 0 pixels | |
175 | self->pBuf = NULL; // alloc'd on 1st use | |
176 | self->dataPin = dPin; | |
177 | self->clockPin = cPin; | |
178 | self->brightness = 0; | |
179 | self->rOffset = rOffset; | |
180 | self->gOffset = gOffset; | |
181 | self->bOffset = bOffset; | |
182 | } | |
183 | - | } else if(pixels) { |
183 | + | |
184 | - | free(pixels); |
184 | + | |
185 | } | |
186 | ||
187 | // Initialize DotStar object | |
188 | static int DotStar_init(DotStarObject *self) { | |
189 | uint32_t i; | |
190 | // Set first byte of each 4-byte pixel to 0xFF, rest to 0x00 (off) | |
191 | memset(self->pixels, 0, self->numLEDs * 4); | |
192 | for(i=0; i<self->numLEDs; i++) self->pixels[i * 4] = 0xFF; | |
193 | return 0; | |
194 | } | |
195 | ||
196 | // Detect Pi board type. Doesn't return super-granular details, | |
197 | // just the most basic distinction needed for GPIO compatibility: | |
198 | // 0: Pi 1 Model B revision 1 | |
199 | // 1: Pi 1 Model B revision 2, Model A, Model B+, Model A+ | |
200 | // 2: Pi 2 Model B | |
201 | ||
202 | static int boardType(void) { | |
203 | FILE *fp; | |
204 | char buf[1024], *ptr; | |
205 | int n, board = 1; // Assume Pi1 Rev2 by default | |
206 | ||
207 | // Relies on info in /proc/cmdline. If this becomes unreliable | |
208 | // in the future, alt code below uses /proc/cpuinfo if any better. | |
209 | #if 1 | |
210 | if((fp = fopen("/proc/cmdline", "r"))) { | |
211 | while(fgets(buf, sizeof(buf), fp)) { | |
212 | if((ptr = strstr(buf, "mem_size=")) && | |
213 | (sscanf(&ptr[9], "%x", &n) == 1) && | |
214 | (n == 0x3F000000)) { | |
215 | board = 2; // Appears to be a Pi 2 | |
216 | printf("Board is PI2\n"); | |
217 | break; | |
218 | } else if((ptr = strstr(buf, "boardrev=")) && | |
219 | (sscanf(&ptr[9], "%x", &n) == 1) && | |
220 | ((n == 0x02) || (n == 0x03))) { | |
221 | board = 0; // Appears to be an early Pi | |
222 | printf("Board is PI1\n"); | |
223 | break; | |
224 | } | |
225 | } | |
226 | fclose(fp); | |
227 | } | |
228 | #else | |
229 | char s[8]; | |
230 | if((fp = fopen("/proc/cpuinfo", "r"))) { | |
231 | while(fgets(buf, sizeof(buf), fp)) { | |
232 | if((ptr = strstr(buf, "Hardware")) && | |
233 | (sscanf(&ptr[8], " : %7s", s) == 1) && | |
234 | (!strcmp(s, "BCM2709"))) { | |
235 | board = 2; // Appears to be a Pi 2 | |
236 | break; | |
237 | } else if((ptr = strstr(buf, "Revision")) && | |
238 | (sscanf(&ptr[8], " : %x", &n) == 1) && | |
239 | ((n == 0x02) || (n == 0x03))) { | |
240 | board = 0; // Appears to be an early Pi | |
241 | break; | |
242 | } | |
243 | } | |
244 | fclose(fp); | |
245 | } | |
246 | #endif | |
247 | ||
248 | return board; | |
249 | } | |
250 | ||
251 | // Initialize pins/SPI for output | |
252 | static void begin(DotStarObject *self) { | |
253 | if(self->dataPin == 0xFF) { // Use hardware SPI | |
254 | printf("Using hardware SPI\n"); | |
255 | if((self->fd = open("/dev/spidev0.0", O_RDWR)) < 0) { | |
256 | printf("Can't open /dev/spidev0.0 (try 'sudo')\n"); | |
257 | return; | |
258 | } | |
259 | uint8_t mode = SPI_MODE_0 | SPI_NO_CS; | |
260 | ioctl(self->fd, SPI_IOC_WR_MODE, &mode); | |
261 | // The actual data rate may be less than requested. | |
262 | // Hardware SPI speed is a function of the system core | |
263 | // frequency and the smallest power-of-two prescaler | |
264 | // that will not exceed the requested rate. | |
265 | // e.g. 8 MHz request: 250 MHz / 32 = 7.8125 MHz. | |
266 | ioctl(self->fd, SPI_IOC_WR_MAX_SPEED_HZ, self->bitrate); | |
267 | } else { // Use bitbang "soft" SPI (any 2 pins) | |
268 | if(gpio == NULL) { // First time accessing GPIO? | |
269 | int fd; | |
270 | ||
271 | if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { | |
272 | printf("Can't open /dev/mem (try 'sudo')\n"); | |
273 | return; | |
274 | } | |
275 | isPi2 = (boardType() == 2); | |
276 | gpio = (volatile unsigned *)mmap( // Memory-map I/O | |
277 | NULL, // Any adddress will do | |
278 | BLOCK_SIZE, // Mapped block length | |
279 | PROT_READ|PROT_WRITE, // Enable read+write | |
280 | MAP_SHARED, // Shared w/other processes | |
281 | fd, // File to map | |
282 | isPi2 ? | |
283 | PI2_GPIO_BASE : // -> GPIO registers | |
284 | PI1_GPIO_BASE); | |
285 | close(fd); // Not needed after mmap() | |
286 | if(gpio == MAP_FAILED) { | |
287 | err("Can't mmap()"); | |
288 | return; | |
289 | } | |
290 | gpioSet = &gpio[7]; | |
291 | gpioClr = &gpio[10]; | |
292 | } | |
293 | ||
294 | self->dataMask = 1 << self->dataPin; | |
295 | self->clockMask = 1 << self->clockPin; | |
296 | ||
297 | // Set 2 pins as outputs. Must use INP before OUT. | |
298 | INP_GPIO(self->dataPin); OUT_GPIO(self->dataPin); | |
299 | INP_GPIO(self->clockPin); OUT_GPIO(self->clockPin); | |
300 | ||
301 | *gpioClr = self->dataMask | self->clockMask; // data+clock LOW | |
302 | } | |
303 | return; | |
304 | } | |
305 | ||
306 | // Set strip data to 'off' (just clears buffer, does not write to strip) | |
307 | static void clear(DotStarObject *self) { | |
308 | uint8_t *ptr; | |
309 | uint32_t i; | |
310 | for(ptr = self->pixels, i=0; i<self->numLEDs; i++, ptr += 4) { | |
311 | ptr[1] = 0x00; ptr[2] = 0x00; ptr[3] = 0x00; | |
312 | } | |
313 | } | |
314 | ||
315 | // Set global strip brightness. This does not have an immediate effect; | |
316 | // must be followed by a call to show(). Not a fan of this...for various | |
317 | // reasons I think it's better handled in one's application, but it's here | |
318 | // for parity with the Arduino NeoPixel library. | |
319 | static void setBrightness(DotStarObject *self, uint8_t b) { | |
320 | ||
321 | // Stored brightness value is different than what's passed. This | |
322 | // optimizes the actual scaling math later, allowing a fast multiply | |
323 | // and taking the MSB. 'brightness' is a uint8_t, adding 1 here may | |
324 | // (intentionally) roll over...so 0 = max brightness (color values | |
325 | // are interpreted literally; no scaling), 1 = min brightness (off), | |
326 | // 255 = just below max brightness. | |
327 | self->brightness = b + 1; | |
328 | } | |
329 | ||
330 | // Valid syntaxes: | |
331 | // x.setPixelColor(index, red, green, blue) | |
332 | // x.setPixelColor(index, 0x00RRGGBB) | |
333 | static void setPixelColor(DotStarObject *self, uint32_t i, uint8_t r, uint8_t g, uint8_t b) { | |
334 | // uint32_t i, v; | |
335 | // uint8_t r, g, b; | |
336 | ||
337 | // switch(PyTuple_Size(arg)) { | |
338 | // case 4: // Index, r, g, b | |
339 | // if(!PyArg_ParseTuple(arg, "Ibbb", &i, &r, &g, &b)) | |
340 | // return; | |
341 | // break; | |
342 | // case 2: // Index, value | |
343 | // if(!PyArg_ParseTuple(arg, "II", &i, &v)) | |
344 | // return; | |
345 | // r = v >> 16; | |
346 | // g = v >> 8; | |
347 | // b = v; | |
348 | // break; | |
349 | // default: | |
350 | // return; | |
351 | // } | |
352 | ||
353 | if(i < self->numLEDs) { | |
354 | uint8_t *ptr = &self->pixels[i * 4]; | |
355 | ptr[self->rOffset] = r; | |
356 | ptr[self->gOffset] = g; | |
357 | ptr[self->bOffset] = b; | |
358 | } | |
359 | return; | |
360 | } | |
361 | ||
362 | // Bitbang requires throttle on clock set/clear to avoid outpacing strip | |
363 | static void clockPulse(uint32_t mask) { | |
364 | volatile uint8_t hi, lo; | |
365 | *gpioSet = mask; | |
366 | if(isPi2) { | |
367 | hi = 60; // These were found empirically | |
368 | lo = 50; // using 'Pi2' overclock setting and... | |
369 | } else { | |
370 | hi = 14; // ...'Medium' setting, respectively, | |
371 | lo = 2; // driving a 2 meter x 144 LED strip. | |
372 | } | |
373 | while(hi--); | |
374 | *gpioClr = mask; | |
375 | while(lo--); | |
376 | return; | |
377 | } | |
378 | ||
379 | // Private method. Writes pixel data without brightness scaling. | |
380 | static void raw_write(DotStarObject *self, uint8_t *ptr, uint32_t len) { | |
381 | if(self->fd >= 0) { // Hardware SPI | |
382 | // printf("SPI write\n"); | |
383 | xfer[0].speed_hz = self->bitrate; | |
384 | xfer[1].speed_hz = self->bitrate; | |
385 | xfer[2].speed_hz = self->bitrate; | |
386 | xfer[1].tx_buf = (unsigned long)ptr; | |
387 | xfer[1].len = len; | |
388 | if(self->numLEDs) xfer[2].len = (self->numLEDs + 15) / 16; | |
389 | else xfer[2].len = ((len / 4) + 15) / 16; | |
390 | // All that spi_ioc_transfer struct stuff earlier in | |
391 | // the code is so we can use this single ioctl to concat | |
392 | // the data & footer into one operation: | |
393 | (void)ioctl(self->fd, SPI_IOC_MESSAGE(3), xfer); | |
394 | } else if(self->dataMask) { // Bitbang | |
395 | printf("Bitbang write\n"); | |
396 | unsigned char byte, bit, | |
397 | headerLen = 32; | |
398 | uint32_t footerLen; | |
399 | if(self->numLEDs) footerLen = (self->numLEDs + 1) / 2; | |
400 | else footerLen = ((len / 4) + 1) / 2; | |
401 | *gpioClr = self->dataMask; | |
402 | while(headerLen--) clockPulse(self->clockMask); | |
403 | while(len--) { // Pixel data | |
404 | byte = *ptr++; | |
405 | for(bit = 0x80; bit; bit >>= 1) { | |
406 | if(byte & bit) *gpioSet = self->dataMask; | |
407 | else *gpioClr = self->dataMask; | |
408 | clockPulse(self->clockMask); | |
409 | } | |
410 | } | |
411 | *gpioClr = self->dataMask; | |
412 | while(footerLen--) clockPulse(self->clockMask); | |
413 | } | |
414 | } | |
415 | ||
416 | // Issue data to strip. Optional arg = raw bytearray to issue to strip | |
417 | // (else object's pixel buffer is used). If passing raw data, it must | |
418 | // be in strip-ready format (4 bytes/pixel, 0xFF/B/G/R) and no brightness | |
419 | // scaling is performed...it's all about speed (for POV, etc.) | |
420 | static void show(DotStarObject *self) { | |
421 | // if(PyTuple_Size(arg) == 1) { // Raw bytearray passed | |
422 | // Py_buffer buf; | |
423 | // if(!PyArg_ParseTuple(arg, "s*", &buf)) return NULL; | |
424 | // raw_write(self, buf.buf, buf.len); | |
425 | // PyBuffer_Release(&buf); | |
426 | // } else { // Write object's pixel buffer | |
427 | if(self->brightness == 0) { // Send raw (no scaling) | |
428 | printf("Sending raw\n"); | |
429 | raw_write(self, self->pixels, self->numLEDs * 8); | |
430 | } else { // Adjust brightness during write | |
431 | uint32_t i; | |
432 | uint8_t *ptr = self->pixels; | |
433 | uint16_t scale = self->brightness; | |
434 | if(self->fd >= 0) { // Hardware SPI | |
435 | // Allocate pBuf if using hardware | |
436 | // SPI and not previously alloc'd | |
437 | if((self->pBuf == NULL) && ((self->pBuf = | |
438 | (uint8_t *)malloc(self->numLEDs * 8)))) { | |
439 | memset(self->pBuf, 0xFF, | |
440 | self->numLEDs * 8); // Init MSBs | |
441 | } | |
442 | ||
443 | if(self->pBuf) { | |
444 | // Scale from 'pixels' buffer into | |
445 | // 'pBuf' (if available) and then | |
446 | // use a single efficient write | |
447 | // operation (thx Eric Bayer). | |
448 | uint8_t *pb = self->pBuf; | |
449 | for(i=0; i<self->numLEDs; | |
450 | i++, ptr += 4, pb += 4) { | |
451 | pb[1] = (ptr[1] * scale) >> 8; | |
452 | pb[2] = (ptr[2] * scale) >> 8; | |
453 | pb[3] = (ptr[3] * scale) >> 8; | |
454 | } | |
455 | raw_write(self, self->pBuf, | |
456 | self->numLEDs * 4); | |
457 | } else { | |
458 | // Fallback if pBuf not available | |
459 | // (just in case malloc fails), | |
460 | // also write() bugfix via Eric Bayer | |
461 | uint8_t x[4]; | |
462 | // Header: | |
463 | x[0] = 0; | |
464 | i = 4; | |
465 | while(i--) write(self->fd, x, 1); | |
466 | // Payload: | |
467 | x[0] = 0xFF; | |
468 | for(i=0; i<self->numLEDs; | |
469 | i++, ptr += 4) { | |
470 | x[1] = (ptr[1] * scale) >> 8; | |
471 | x[2] = (ptr[2] * scale) >> 8; | |
472 | x[3] = (ptr[3] * scale) >> 8; | |
473 | write(self->fd, x, sizeof(x)); | |
474 | } | |
475 | // Footer: | |
476 | x[0] = 0; | |
477 | i = (self->numLEDs + 15) / 16; | |
478 | while(i--) write(self->fd, x, 1); | |
479 | } | |
480 | } else if(self->dataMask) { | |
481 | uint32_t word, bit; | |
482 | // Header (32 bits) | |
483 | *gpioClr = self->dataMask; | |
484 | bit = 32; | |
485 | while(bit--) clockPulse(self->clockMask); | |
486 | for(i=0; i<self->numLEDs; i++, ptr += 4) { | |
487 | word = 0xFF000000 | | |
488 | (((ptr[1] * scale) & 0xFF00) << 8) | | |
489 | ( (ptr[2] * scale) & 0xFF00 ) | | |
490 | ( (ptr[3] * scale) >> 8); | |
491 | for(bit = 0x80000000; bit; bit >>= 1) { | |
492 | if(word & bit) | |
493 | *gpioSet = self->dataMask; | |
494 | else | |
495 | *gpioClr = self->dataMask; | |
496 | clockPulse(self->clockMask); | |
497 | } | |
498 | } | |
499 | // Footer (1/2 bit per LED) | |
500 | *gpioClr = self->dataMask; | |
501 | bit = (self->numLEDs + 1) / 2; | |
502 | while(bit--) clockPulse(self->clockMask); | |
503 | } | |
504 | } | |
505 | // } | |
506 | } | |
507 | ||
508 | // Given separate R, G, B, return a packed 32-bit color. | |
509 | // Meh, mostly here for parity w/Arduino library. | |
510 | static unsigned int Color(uint8_t r, uint8_t g, uint8_t b) { | |
511 | return (r << 16) | (g << 8) | b; | |
512 | } | |
513 | ||
514 | // Return color of previously-set pixel (as packed 32-bit value) | |
515 | static unsigned int getPixelColor(DotStarObject *self, uint32_t i) { | |
516 | uint8_t r=0, g=0, b=0; | |
517 | if(i < self->numLEDs) { | |
518 | uint8_t *ptr = &self->pixels[i * 4]; | |
519 | r = ptr[self->rOffset]; | |
520 | g = ptr[self->gOffset]; | |
521 | b = ptr[self->bOffset]; | |
522 | } | |
523 | ||
524 | return (r << 16) | (g << 8) | b; | |
525 | } | |
526 | ||
527 | // Return strip length | |
528 | static int numPixels(DotStarObject *self) { | |
529 | return self->numLEDs; | |
530 | } | |
531 | ||
532 | // Return strip brightness | |
533 | static int getBrightness(DotStarObject *self) { | |
534 | return (uint8_t)(self->brightness - 1); | |
535 | } | |
536 | ||
537 | // DON'T USE THIS. One of those "parity with Arduino library" methods, | |
538 | // but current'y doesn't work (and might never). Supposed to return strip's | |
539 | // pixel buffer, but doesn't seem to be an easy way to do this in Python 2.X. | |
540 | // That's okay -- instead of 'raw' access to a strip's previously-allocated | |
541 | // buffer, a Python program can instead allocate its own buffer and pass this | |
542 | // to the show() method, basically achieving the same thing and then some. | |
543 | // static PyObject *getPixels(DotStarObject *self) { | |
544 | // PyObject *result = Py_BuildValue("s#", | |
545 | // self->pixels, self->numLEDs * 4); | |
546 | // Py_INCREF(result); | |
547 | // return result; | |
548 | // } | |
549 | ||
550 | static void _close(DotStarObject *self) { | |
551 | if(self->fd) { | |
552 | close(self->fd); | |
553 | self->fd = -1; | |
554 | } else { | |
555 | INP_GPIO(self->dataPin); | |
556 | INP_GPIO(self->clockPin); | |
557 | self->dataMask = 0; | |
558 | self->clockMask = 0; | |
559 | } | |
560 | return; | |
561 | } | |
562 | ||
563 | static void DotStar_dealloc(DotStarObject *self) { | |
564 | _close(self); | |
565 | if(self->pBuf) free(self->pBuf); | |
566 | if(self->pixels) free(self->pixels); | |
567 | return; | |
568 | } | |
569 | ||
570 | // Method names are silly and inconsistent, but following NeoPixel | |
571 | // and prior libraries, which formed through centuries of accretion. | |
572 | // static PyMethodDef methods[] = { | |
573 | // { "begin" , (PyCFunction)begin , METH_NOARGS , NULL }, | |
574 | // { "clear" , (PyCFunction)clear , METH_NOARGS , NULL }, | |
575 | // { "setBrightness", (PyCFunction)setBrightness, METH_VARARGS, NULL }, | |
576 | // { "setPixelColor", (PyCFunction)setPixelColor, METH_VARARGS, NULL }, | |
577 | // { "show" , (PyCFunction)show , METH_VARARGS, NULL }, | |
578 | // { "Color" , (PyCFunction)Color , METH_VARARGS, NULL }, | |
579 | // { "getPixelColor", (PyCFunction)getPixelColor, METH_VARARGS, NULL }, | |
580 | // { "numPixels" , (PyCFunction)numPixels , METH_NOARGS , NULL }, | |
581 | // { "getBrightness", (PyCFunction)getBrightness, METH_NOARGS , NULL }, | |
582 | // { "getPixels" , (PyCFunction)getPixels , METH_NOARGS , NULL }, | |
583 | // { "close" , (PyCFunction)_close , METH_NOARGS , NULL }, | |
584 | // { NULL, NULL, 0, NULL } | |
585 | // }; | |
586 | ||
587 | // static PyTypeObject DotStarObjectType = { | |
588 | // PyObject_HEAD_INIT(NULL) | |
589 | // 0, // ob_size (not used, always set to 0) | |
590 | // "dotstar.Adafruit_DotStar", // tp_name (module name, object name) | |
591 | // sizeof(DotStarObject), // tp_basicsize | |
592 | // 0, // tp_itemsize | |
593 | // (destructor)DotStar_dealloc, // tp_dealloc | |
594 | // 0, // tp_print | |
595 | // 0, // tp_getattr | |
596 | // 0, // tp_setattr | |
597 | // 0, // tp_compare | |
598 | // 0, // tp_repr | |
599 | // 0, // tp_as_number | |
600 | // 0, // tp_as_sequence | |
601 | // 0, // tp_as_mapping | |
602 | // 0, // tp_hash | |
603 | // 0, // tp_call | |
604 | // 0, // tp_str | |
605 | // 0, // tp_getattro | |
606 | // 0, // tp_setattro | |
607 | // 0, // tp_as_buffer | |
608 | // Py_TPFLAGS_DEFAULT, // tp_flags | |
609 | // 0, // tp_doc | |
610 | // 0, // tp_traverse | |
611 | // 0, // tp_clear | |
612 | // 0, // tp_richcompare | |
613 | // 0, // tp_weaklistoffset | |
614 | // 0, // tp_iter | |
615 | // 0, // tp_iternext | |
616 | // methods, // tp_methods | |
617 | // 0, // tp_members | |
618 | // 0, // tp_getset | |
619 | // 0, // tp_base | |
620 | // 0, // tp_dict | |
621 | // 0, // tp_descr_get | |
622 | // 0, // tp_descr_set | |
623 | // 0, // tp_dictoffset | |
624 | // (initproc)DotStar_init, // tp_init | |
625 | // 0, // tp_alloc | |
626 | // DotStar_new, // tp_new | |
627 | // 0, // tp_free | |
628 | // }; | |
629 | ||
630 | // PyMODINIT_FUNC initdotstar(void) { // Module initialization function | |
631 | // PyObject* m; | |
632 | ||
633 | // if((m = Py_InitModule("dotstar", methods)) && | |
634 | // (PyType_Ready(&DotStarObjectType) >= 0)) { | |
635 | // Py_INCREF(&DotStarObjectType); | |
636 | // PyModule_AddObject(m, "Adafruit_DotStar", | |
637 | // (PyObject *)&DotStarObjectType); | |
638 | // } | |
639 | // } | |
640 | ||
641 | int main(){ | |
642 | printf("Hello APA102\n"); | |
643 | DotStarObject *dotty; | |
644 | dotty = DotStar_new(300, 10, 11, 16000000); | |
645 | uint8_t r = 0, g = 255, b = 255; | |
646 | bool direction = true; | |
647 | begin(dotty); | |
648 | DotStar_init(dotty); | |
649 | setBrightness(dotty,3); | |
650 | // for (int i = 1; i < dotty->numLEDs-1; ++i){ | |
651 | // setPixelColor(dotty,i,r,g,b); | |
652 | // } | |
653 | // show(dotty); | |
654 | while(1){ | |
655 | for (uint8_t i = 0; i < dotty->numLEDs; ++i){ | |
656 | // setPixelColor(dotty,i,r,g,b); | |
657 | setPixelColor(dotty,i,r,0,0); | |
658 | } | |
659 | if(direction && r != 255){ | |
660 | r++; | |
661 | // r = 255; | |
662 | }else if (direction && r == 255){ | |
663 | direction = !direction; | |
664 | } | |
665 | ||
666 | if (!direction && r != 0){ | |
667 | r--; | |
668 | // r = 0; | |
669 | }else if (!direction && r == 0){ | |
670 | direction = !direction; | |
671 | } | |
672 | // printf("%i\n", r); | |
673 | show(dotty); | |
674 | // usleep(16 * 1000); | |
675 | } | |
676 | } |