Skip to content

Commit 4fee1cc

Browse files
dotstar: add DotStar library v1.0.1 from Adafruit
- Adafruit_Dotstar Github repository: https://github.com/adafruit/Adafruit_DotStar Tag: v1.0.1 Signed-off-by: Dan O'Donovan <dan@emutex.com>
1 parent 6e2de13 commit 4fee1cc

File tree

6 files changed

+1291
-0
lines changed

6 files changed

+1291
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/*------------------------------------------------------------------------
2+
Arduino library to control Adafruit Dot Star addressable RGB LEDs.
3+
4+
Written by Limor Fried and Phil Burgess for Adafruit Industries.
5+
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing products
8+
from Adafruit!
9+
10+
------------------------------------------------------------------------
11+
This file is part of the Adafruit Dot Star library.
12+
13+
Adafruit Dot Star is free software: you can redistribute it and/or
14+
modify it under the terms of the GNU Lesser General Public License
15+
as published by the Free Software Foundation, either version 3 of
16+
the License, or (at your option) any later version.
17+
18+
Adafruit Dot Star is distributed in the hope that it will be useful,
19+
but WITHOUT ANY WARRANTY; without even the implied warranty of
20+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+
GNU Lesser General Public License for more details.
22+
23+
You should have received a copy of the GNU Lesser General Public
24+
License along with DotStar. If not, see <http://www.gnu.org/licenses/>.
25+
------------------------------------------------------------------------*/
26+
27+
#include "Adafruit_DotStar.h"
28+
#if !defined(__AVR_ATtiny85__)
29+
#include <SPI.h>
30+
#endif
31+
32+
#define USE_HW_SPI 255 // Assign this to dataPin to indicate 'hard' SPI
33+
34+
// Constructor for hardware SPI -- must connect to MOSI, SCK pins
35+
Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t o) :
36+
numLEDs(n), dataPin(USE_HW_SPI), brightness(0), pixels(NULL),
37+
rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3)
38+
{
39+
updateLength(n);
40+
}
41+
42+
// Constructor for 'soft' (bitbang) SPI -- any two pins can be used
43+
Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t data, uint8_t clock,
44+
uint8_t o) :
45+
dataPin(data), clockPin(clock), brightness(0), pixels(NULL),
46+
rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3)
47+
{
48+
updateLength(n);
49+
}
50+
51+
Adafruit_DotStar::~Adafruit_DotStar(void) { // Destructor
52+
if(pixels) free(pixels);
53+
if(dataPin == USE_HW_SPI) hw_spi_end();
54+
else sw_spi_end();
55+
}
56+
57+
void Adafruit_DotStar::begin(void) { // Initialize SPI
58+
if(dataPin == USE_HW_SPI) hw_spi_init();
59+
else sw_spi_init();
60+
}
61+
62+
// Pins may be reassigned post-begin(), so a sketch can store hardware
63+
// config in flash, SD card, etc. rather than hardcoded. Also permits
64+
// "recycling" LED ram across multiple strips: set pins to first strip,
65+
// render & write all data, reassign pins to next strip, render & write,
66+
// etc. They won't update simultaneously, but usually unnoticeable.
67+
68+
// Change to hardware SPI -- must connect to MOSI, SCK pins
69+
void Adafruit_DotStar::updatePins(void) {
70+
sw_spi_end();
71+
dataPin = USE_HW_SPI;
72+
hw_spi_init();
73+
}
74+
75+
// Change to 'soft' (bitbang) SPI -- any two pins can be used
76+
void Adafruit_DotStar::updatePins(uint8_t data, uint8_t clock) {
77+
hw_spi_end();
78+
dataPin = data;
79+
clockPin = clock;
80+
sw_spi_init();
81+
}
82+
83+
// Length can be changed post-constructor for similar reasons (sketch
84+
// config not hardcoded). But DON'T use this for "recycling" strip RAM...
85+
// all that reallocation is likely to fragment and eventually fail.
86+
// Instead, set length once to longest strip.
87+
void Adafruit_DotStar::updateLength(uint16_t n) {
88+
if(pixels) free(pixels);
89+
uint16_t bytes = (rOffset == gOffset) ?
90+
n + ((n + 3) / 4) : // MONO: 10 bits/pixel, round up to next byte
91+
n * 3; // COLOR: 3 bytes/pixel
92+
if((pixels = (uint8_t *)malloc(bytes))) {
93+
numLEDs = n;
94+
clear();
95+
} else {
96+
numLEDs = 0;
97+
}
98+
}
99+
100+
// SPI STUFF ---------------------------------------------------------------
101+
102+
void Adafruit_DotStar::hw_spi_init(void) { // Initialize hardware SPI
103+
#ifdef __AVR_ATtiny85__
104+
PORTB &= ~(_BV(PORTB1) | _BV(PORTB2)); // Outputs
105+
DDRB |= _BV(PORTB1) | _BV(PORTB2); // DO (NOT MOSI) + SCK
106+
#else
107+
SPI.begin();
108+
#if defined(__AVR__) || defined(CORE_TEENSY)
109+
SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (6 MHz on Pro Trinket 3V)
110+
#else
111+
SPI.setClockDivider((F_CPU + 4000000L) / 8000000L); // 8-ish MHz on Due
112+
#endif
113+
SPI.setBitOrder(MSBFIRST);
114+
SPI.setDataMode(SPI_MODE0);
115+
#endif
116+
}
117+
118+
void Adafruit_DotStar::hw_spi_end(void) { // Stop hardware SPI
119+
#ifdef __AVR_ATtiny85__
120+
DDRB &= ~(_BV(PORTB1) | _BV(PORTB2)); // Inputs
121+
#else
122+
SPI.end();
123+
#endif
124+
}
125+
126+
void Adafruit_DotStar::sw_spi_init(void) { // Init 'soft' (bitbang) SPI
127+
pinMode(dataPin , OUTPUT);
128+
pinMode(clockPin, OUTPUT);
129+
#ifdef __AVR__
130+
dataPort = portOutputRegister(digitalPinToPort(dataPin));
131+
clockPort = portOutputRegister(digitalPinToPort(clockPin));
132+
dataPinMask = digitalPinToBitMask(dataPin);
133+
clockPinMask = digitalPinToBitMask(clockPin);
134+
*dataPort &= ~dataPinMask;
135+
*clockPort &= ~clockPinMask;
136+
#else
137+
digitalWrite(dataPin , LOW);
138+
digitalWrite(clockPin, LOW);
139+
#endif
140+
}
141+
142+
void Adafruit_DotStar::sw_spi_end() { // Stop 'soft' SPI
143+
pinMode(dataPin , INPUT);
144+
pinMode(clockPin, INPUT);
145+
}
146+
147+
#ifdef __AVR_ATtiny85__
148+
149+
// Teensy/Gemma-specific stuff for hardware-half-assisted SPI
150+
151+
#define SPIBIT \
152+
USICR = ((1<<USIWM0)|(1<<USITC)); \
153+
USICR = ((1<<USIWM0)|(1<<USITC)|(1<<USICLK)); // Clock bit tick, tock
154+
155+
static void spi_out(uint8_t n) { // Clock out one byte
156+
USIDR = n;
157+
SPIBIT SPIBIT SPIBIT SPIBIT SPIBIT SPIBIT SPIBIT SPIBIT
158+
}
159+
160+
#else
161+
162+
// All other boards have full-featured hardware support for SPI
163+
164+
#define spi_out(n) (void)SPI.transfer(n)
165+
// Pipelining reads next byte while current byte is clocked out
166+
#if (defined(__AVR__) && !defined(__AVR_ATtiny85__)) || defined(CORE_TEENSY)
167+
#define SPI_PIPELINE
168+
#endif
169+
170+
#endif
171+
172+
void Adafruit_DotStar::sw_spi_out(uint8_t n) { // Bitbang SPI write
173+
for(uint8_t i=8; i--; n <<= 1) {
174+
#ifdef __AVR__
175+
if(n & 0x80) *dataPort |= dataPinMask;
176+
else *dataPort &= ~dataPinMask;
177+
*clockPort |= clockPinMask;
178+
*clockPort &= ~clockPinMask;
179+
#else
180+
if(n & 0x80) digitalWrite(dataPin, HIGH);
181+
else digitalWrite(dataPin, LOW);
182+
digitalWrite(clockPin, HIGH);
183+
digitalWrite(clockPin, LOW);
184+
#endif
185+
}
186+
}
187+
188+
/* ISSUE DATA TO LED STRIP -------------------------------------------------
189+
190+
Although the LED driver has an additional per-pixel 5-bit brightness
191+
setting, it is NOT used or supported here because it's a brain-dead
192+
misfeature that's counter to the whole point of Dot Stars, which is to
193+
have a much faster PWM rate than NeoPixels. It gates the high-speed
194+
PWM output through a second, much slower PWM (about 400 Hz), rendering
195+
it useless for POV. This brings NOTHING to the table that can't be
196+
already handled better in one's sketch code. If you really can't live
197+
without this abomination, you can fork the library and add it for your
198+
own use, but any pull requests for this will NOT be merged, nuh uh!
199+
*/
200+
201+
void Adafruit_DotStar::show(void) {
202+
203+
if(!pixels) return;
204+
205+
uint8_t *ptr = pixels, i; // -> LED data
206+
uint16_t n = numLEDs; // Counter
207+
uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math
208+
209+
if(dataPin == USE_HW_SPI) {
210+
211+
#ifdef SPI_PIPELINE
212+
uint8_t next;
213+
for(i=0; i<3; i++) spi_out(0x00); // First 3 start-frame bytes
214+
SPDR = 0x00; // 4th is pipelined
215+
do { // For each pixel...
216+
while(!(SPSR & _BV(SPIF))); // Wait for prior byte out
217+
SPDR = 0xFF; // Pixel start
218+
for(i=0; i<3; i++) { // For R,G,B...
219+
next = brightness ? (*ptr++ * b16) >> 8 : *ptr++; // Read, scale
220+
while(!(SPSR & _BV(SPIF))); // Wait for prior byte out
221+
SPDR = next; // Write scaled color
222+
}
223+
} while(--n);
224+
while(!(SPSR & _BV(SPIF))); // Wait for last byte out
225+
#else
226+
for(i=0; i<4; i++) spi_out(0x00); // 4 byte start-frame marker
227+
if(brightness) { // Scale pixel brightness on output
228+
do { // For each pixel...
229+
spi_out(0xFF); // Pixel start
230+
for(i=0; i<3; i++) spi_out((*ptr++ * b16) >> 8); // Scale, write RGB
231+
} while(--n);
232+
} else { // Full brightness (no scaling)
233+
do { // For each pixel...
234+
spi_out(0xFF); // Pixel start
235+
for(i=0; i<3; i++) spi_out(*ptr++); // Write R,G,B
236+
} while(--n);
237+
}
238+
#endif
239+
// Four end-frame bytes are seemingly indistinguishable from a white
240+
// pixel, and empirical testing suggests it can be left out...but it's
241+
// always a good idea to follow the datasheet, in case future hardware
242+
// revisions are more strict (e.g. might mandate use of end-frame
243+
// before start-frame marker). i.e. let's not remove this.
244+
for(i=0; i<4; i++) spi_out(0xFF);
245+
246+
} else { // Soft (bitbang) SPI
247+
248+
for(i=0; i<4; i++) sw_spi_out(0); // Start-frame marker
249+
if(brightness) { // Scale pixel brightness on output
250+
do { // For each pixel...
251+
sw_spi_out(0xFF); // Pixel start
252+
for(i=0; i<3; i++) sw_spi_out((*ptr++ * b16) >> 8); // Scale, write
253+
} while(--n);
254+
} else { // Full brightness (no scaling)
255+
do { // For each pixel...
256+
sw_spi_out(0xFF); // Pixel start
257+
for(i=0; i<3; i++) sw_spi_out(*ptr++); // R,G,B
258+
} while(--n);
259+
}
260+
for(i=0; i<4; i++) sw_spi_out(0xFF); // End-frame marker (see note above)
261+
}
262+
}
263+
264+
void Adafruit_DotStar::clear() { // Write 0s (off) to full pixel buffer
265+
memset(pixels, 0, (rOffset == gOffset) ?
266+
numLEDs + ((numLEDs + 3) / 4) : // MONO: 10 bits/pixel
267+
numLEDs * 3); // COLOR: 3 bytes/pixel
268+
}
269+
270+
// Set pixel color, separate R,G,B values (0-255 ea.)
271+
void Adafruit_DotStar::setPixelColor(
272+
uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
273+
if(n < numLEDs) {
274+
uint8_t *p = &pixels[n * 3];
275+
p[rOffset] = r;
276+
p[gOffset] = g;
277+
p[bOffset] = b;
278+
}
279+
}
280+
281+
// Set pixel color, 'packed' RGB value (0x000000 - 0xFFFFFF)
282+
void Adafruit_DotStar::setPixelColor(uint16_t n, uint32_t c) {
283+
if(n < numLEDs) {
284+
uint8_t *p = &pixels[n * 3];
285+
p[rOffset] = (uint8_t)(c >> 16);
286+
p[gOffset] = (uint8_t)(c >> 8);
287+
p[bOffset] = (uint8_t)c;
288+
}
289+
}
290+
291+
// Convert separate R,G,B to packed value
292+
uint32_t Adafruit_DotStar::Color(uint8_t r, uint8_t g, uint8_t b) {
293+
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
294+
}
295+
296+
// Read color from previously-set pixel, returns packed RGB value.
297+
uint32_t Adafruit_DotStar::getPixelColor(uint16_t n) const {
298+
if(n >= numLEDs) return 0;
299+
uint8_t *p = &pixels[n * 3];
300+
return ((uint32_t)p[rOffset] << 16) |
301+
((uint32_t)p[gOffset] << 8) |
302+
(uint32_t)p[bOffset];
303+
}
304+
305+
uint16_t Adafruit_DotStar::numPixels(void) { // Ret. strip length
306+
return numLEDs;
307+
}
308+
309+
// Set global strip brightness. This does not have an immediate effect;
310+
// must be followed by a call to show(). Not a fan of this...for various
311+
// reasons I think it's better handled in one's sketch, but it's here for
312+
// parity with the NeoPixel library. Good news is that brightness setting
313+
// in this library is 'non destructive' -- it's applied as color data is
314+
// being issued to the strip, not during setPixel(), and also means that
315+
// getPixelColor() returns the exact value originally stored.
316+
void Adafruit_DotStar::setBrightness(uint8_t b) {
317+
// Stored brightness value is different than what's passed. This
318+
// optimizes the actual scaling math later, allowing a fast 8x8-bit
319+
// multiply and taking the MSB. 'brightness' is a uint8_t, adding 1
320+
// here may (intentionally) roll over...so 0 = max brightness (color
321+
// values are interpreted literally; no scaling), 1 = min brightness
322+
// (off), 255 = just below max brightness.
323+
brightness = b + 1;
324+
}
325+
326+
uint8_t Adafruit_DotStar::getBrightness(void) const {
327+
return brightness - 1; // Reverse above operation
328+
}
329+
330+
// Return pointer to the library's pixel data buffer. Use carefully,
331+
// much opportunity for mayhem. It's mostly for code that needs fast
332+
// transfers, e.g. SD card to LEDs. Color data is in BGR order.
333+
uint8_t *Adafruit_DotStar::getPixels(void) const {
334+
return pixels;
335+
}

0 commit comments

Comments
 (0)