-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAdafruit_WS2801.cpp
208 lines (181 loc) · 6.76 KB
/
Adafruit_WS2801.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include "Adafruit_WS2801.h"
// Example to control WS2801-based RGB LED Modules in a strand or strip
// Written by Adafruit - MIT license
/*****************************************************************************/
// Constructor for use with hardware SPI (specific clock/data pins):
Adafruit_WS2801::Adafruit_WS2801(uint16_t n, uint8_t order) {
rgb_order = order;
alloc(n);
updatePins();
}
// Constructor for use with arbitrary clock/data pins:
Adafruit_WS2801::Adafruit_WS2801(uint16_t n, uint8_t dpin, uint8_t cpin, uint8_t order) {
rgb_order = order;
alloc(n);
updatePins(dpin, cpin);
}
// Constructor for use with a matrix configuration, specify w, h for size of matrix
// assumes configuration where string starts at coordinate 0,0 and continues to w-1,0, w-1,1
// and on to 0,1, 0,2 and on to w-1,2 and so on. Snaking back and forth till the end.
// other function calls with provide access to pixels via an x,y coordinate system
Adafruit_WS2801::Adafruit_WS2801(uint16_t w, uint16_t h, uint8_t dpin, uint8_t cpin, uint8_t order) {
rgb_order = order;
alloc(w * h);
width = w;
height = h;
updatePins(dpin, cpin);
}
// Allocate 3 bytes per pixel, init to RGB 'off' state:
void Adafruit_WS2801::alloc(uint16_t n) {
begun = false;
numLEDs = ((pixels = (uint8_t *)calloc(n, 3)) != NULL) ? n : 0;
}
// via Michael Vogt/neophob: empty constructor is used when strand length
// isn't known at compile-time; situations where program config might be
// read from internal flash memory or an SD card, or arrive via serial
// command. If using this constructor, MUST follow up with updateLength()
// and updatePins() to establish the strand length and output pins!
// Also, updateOrder() to change RGB vs GRB order (RGB is default).
Adafruit_WS2801::Adafruit_WS2801(void) {
begun = false;
numLEDs = 0;
pixels = NULL;
rgb_order = WS2801_RGB;
updatePins(); // Must assume hardware SPI until pins are set
}
// Release memory (as needed):
Adafruit_WS2801::~Adafruit_WS2801(void) {
if (pixels != NULL) {
free(pixels);
}
}
// Activate hard/soft SPI as appropriate:
void Adafruit_WS2801::begin(void) {
pinMode(datapin, OUTPUT);
pinMode(clkpin , OUTPUT);
begun = true;
}
// Change pin assignments post-constructor, switching to hardware SPI:
void Adafruit_WS2801::updatePins(void) {
datapin = clkpin = 0;
}
// Change pin assignments post-constructor, using arbitrary pins:
void Adafruit_WS2801::updatePins(uint8_t dpin, uint8_t cpin) {
if(begun == true) { // If begin() was previously invoked...
// Regardless, now enable output on 'soft' SPI pins:
pinMode(dpin, OUTPUT);
pinMode(cpin, OUTPUT);
} // Otherwise, pins are not set to outputs until begin() is called.
// Note: any prior clock/data pin directions are left as-is and are
// NOT restored as inputs!
datapin = dpin;
clkpin = cpin;
clkport = portOutputRegister(digitalPinToPort(cpin));
clkpinmask = digitalPinToBitMask(cpin);
dataport = portOutputRegister(digitalPinToPort(dpin));
datapinmask = digitalPinToBitMask(dpin);
}
// Enable SPI hardware and set up protocol details:
void Adafruit_WS2801::startSPI(void) {
}
uint16_t Adafruit_WS2801::numPixels(void) {
return numLEDs;
}
// Change strand length (see notes with empty constructor, above):
void Adafruit_WS2801::updateLength(uint16_t n) {
if(pixels != NULL) free(pixels); // Free existing data (if any)
// Allocate new data -- note: ALL PIXELS ARE CLEARED
numLEDs = ((pixels = (uint8_t *)calloc(n, 3)) != NULL) ? n : 0;
// 'begun' state does not change -- pins retain prior modes
}
// Change RGB data order (see notes with empty constructor, above):
void Adafruit_WS2801::updateOrder(uint8_t order) {
rgb_order = order;
// Existing LED data, if any, is NOT reformatted to new data order.
// Calling function should clear or fill pixel data anew.
}
void Adafruit_WS2801::show(void) {
uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED
uint8_t bit;
// Write 24 bits per pixel:
for(i=0; i<nl3; i++ ) {
for(bit=0x80; bit; bit >>= 1) {
if(pixels[i] & bit) *dataport |= datapinmask;
else *dataport &= ~datapinmask;
*clkport |= clkpinmask;
*clkport &= ~clkpinmask;
}
}
delay(1); // Data is latched by holding clock pin low for 1 millisecond
}
// Set pixel color from separate 8-bit R, G, B components:
void Adafruit_WS2801::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
uint8_t *p = &pixels[n * 3];
// See notes later regarding color order
if(rgb_order == WS2801_RGB) {
*p++ = r;
*p++ = g;
} else {
*p++ = g;
*p++ = r;
}
*p++ = b;
}
}
// Set pixel color from separate 8-bit R, G, B components using x,y coordinate system:
void Adafruit_WS2801::setPixelColor(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) {
boolean evenRow = ((y % 2) == 0);
// calculate x offset first
uint16_t offset = x % width;
if (!evenRow) {
offset = (width-1) - offset;
}
// add y offset
offset += y * width;
setPixelColor(offset, r, g, b);
}
// Set pixel color from 'packed' 32-bit RGB value:
void Adafruit_WS2801::setPixelColor(uint16_t n, uint32_t c) {
if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
uint8_t *p = &pixels[n * 3];
// To keep the show() loop as simple & fast as possible, the
// internal color representation is native to different pixel
// types. For compatibility with existing code, 'packed' RGB
// values passed in or out are always 0xRRGGBB order.
if(rgb_order == WS2801_RGB) {
*p++ = c >> 16; // Red
*p++ = c >> 8; // Green
} else {
*p++ = c >> 8; // Green
*p++ = c >> 16; // Red
}
*p++ = c; // Blue
}
}
// Set pixel color from 'packed' 32-bit RGB value using x,y coordinate system:
void Adafruit_WS2801::setPixelColor(uint16_t x, uint16_t y, uint32_t c) {
boolean evenRow = ((y % 2) == 0);
// calculate x offset first
uint16_t offset = x % width;
if (!evenRow) {
offset = (width-1) - offset;
}
// add y offset
offset += y * width;
setPixelColor(offset, c);
}
// Query color from previously-set pixel (returns packed 32-bit RGB value)
uint32_t Adafruit_WS2801::getPixelColor(uint16_t n) {
if(n < numLEDs) {
uint16_t ofs = n * 3;
// To keep the show() loop as simple & fast as possible, the
// internal color representation is native to different pixel
// types. For compatibility with existing code, 'packed' RGB
// values passed in or out are always 0xRRGGBB order.
return (rgb_order == WS2801_RGB) ?
((uint32_t)pixels[ofs] << 16) | ((uint16_t) pixels[ofs + 1] << 8) | pixels[ofs + 2] :
(pixels[ofs] << 8) | ((uint32_t)pixels[ofs + 1] << 16) | pixels[ofs + 2];
}
return 0; // Pixel # is out of bounds
}