View difference between Paste ID: xddN9JMs and AdYj4z8L
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
}