Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1760 | - | 1 | #include "SPI.h" |
2 | #include "LPD8806_kopterlight.h" |
||
3 | |||
4 | // Arduino library to control LPD8806-based RGB LED Strips |
||
5 | // (c) Adafruit industries |
||
6 | // MIT license |
||
7 | |||
8 | /*****************************************************************************/ |
||
9 | // This library has been modified to fix the red and green issue with the stripes |
||
10 | // used by "MikroKopter-Forum" Users, because the design of these stripes is |
||
11 | // different and red and green are inverted !! |
||
12 | // Additionally this library has been modified and extended to provide light |
||
13 | // sequence requirements on multikopter. |
||
14 | // Magomora |
||
15 | /*****************************************************************************/ |
||
16 | |||
17 | // Constructor for use with hardware SPI (specific clock/data pins): |
||
18 | LPD8806::LPD8806(uint16_t n, uint8_t rig) { |
||
19 | pixels = NULL; |
||
20 | begun = false; |
||
21 | updateLength(n); |
||
22 | updatePins(); |
||
23 | rigger = rig; |
||
24 | } |
||
25 | |||
26 | // Constructor for use with arbitrary clock/data pins: |
||
27 | LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin, uint8_t rig) { |
||
28 | pixels = NULL; |
||
29 | begun = false; |
||
30 | updateLength(n); |
||
31 | updatePins(dpin, cpin); |
||
32 | rigger = rig; |
||
33 | } |
||
34 | |||
35 | // via Michael Vogt/neophob: empty constructor is used when strip length |
||
36 | // isn't known at compile-time; situations where program config might be |
||
37 | // read from internal flash memory or an SD card, or arrive via serial |
||
38 | // command. If using this constructor, MUST follow up with updateLength() |
||
39 | // and updatePins() to establish the strip length and output pins! |
||
40 | LPD8806::LPD8806(void) { |
||
41 | numLEDs = 0; |
||
42 | pixels = NULL; |
||
43 | begun = false; |
||
44 | updatePins(); // Must assume hardware SPI until pins are set |
||
45 | } |
||
46 | |||
47 | // Activate hard/soft SPI as appropriate: |
||
48 | void LPD8806::begin(void) { |
||
49 | if(hardwareSPI == true) startSPI(); |
||
50 | else startBitbang(); |
||
51 | begun = true; |
||
52 | } |
||
53 | |||
54 | // Change pin assignments post-constructor, switching to hardware SPI: |
||
55 | void LPD8806::updatePins(void) { |
||
56 | hardwareSPI = true; |
||
57 | datapin = clkpin = 0; |
||
58 | // If begin() was previously invoked, init the SPI hardware now: |
||
59 | if(begun == true) startSPI(); |
||
60 | // Otherwise, SPI is NOT initted until begin() is explicitly called. |
||
61 | |||
62 | // Note: any prior clock/data pin directions are left as-is and are |
||
63 | // NOT restored as inputs! |
||
64 | } |
||
65 | |||
66 | // Change pin assignments post-constructor, using arbitrary pins: |
||
67 | void LPD8806::updatePins(uint8_t dpin, uint8_t cpin) { |
||
68 | |||
69 | datapin = dpin; |
||
70 | clkpin = cpin; |
||
71 | clkport = portOutputRegister(digitalPinToPort(cpin)); |
||
72 | clkpinmask = digitalPinToBitMask(cpin); |
||
73 | dataport = portOutputRegister(digitalPinToPort(dpin)); |
||
74 | datapinmask = digitalPinToBitMask(dpin); |
||
75 | |||
76 | if(begun == true) { // If begin() was previously invoked... |
||
77 | // If previously using hardware SPI, turn that off: |
||
78 | if(hardwareSPI == true) SPI.end(); |
||
79 | startBitbang(); // Regardless, now enable 'soft' SPI outputs |
||
80 | } // Otherwise, pins are not set to outputs until begin() is called. |
||
81 | |||
82 | // Note: any prior clock/data pin directions are left as-is and are |
||
83 | // NOT restored as inputs! |
||
84 | |||
85 | hardwareSPI = false; |
||
86 | } |
||
87 | |||
88 | // Enable SPI hardware and set up protocol details: |
||
89 | void LPD8806::startSPI(void) { |
||
90 | SPI.begin(); |
||
91 | SPI.setBitOrder(MSBFIRST); |
||
92 | SPI.setDataMode(SPI_MODE0); |
||
93 | // SPI.setClockDivider(SPI_CLOCK_DIV8); // 2 MHz |
||
94 | SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz |
||
95 | // SPI bus is run at 2MHz. Although the LPD8806 should, in theory, |
||
96 | // work up to 20MHz, the unshielded wiring from the Arduino is more |
||
97 | // susceptible to interference. Experiment and see what you get. |
||
98 | |||
99 | SPDR = 0; // 'Prime' the SPI bus with initial latch (no wait) |
||
100 | } |
||
101 | |||
102 | // Enable software SPI pins and issue initial latch: |
||
103 | void LPD8806::startBitbang() { |
||
104 | pinMode(datapin, OUTPUT); |
||
105 | pinMode(clkpin , OUTPUT); |
||
106 | *dataport &= ~datapinmask; // Data is held low throughout (latch = 0) |
||
107 | for(uint8_t i = 8; i>0; i--) { |
||
108 | *clkport |= clkpinmask; |
||
109 | *clkport &= ~clkpinmask; |
||
110 | } |
||
111 | } |
||
112 | |||
113 | // Change strip length (see notes with empty constructor, above): |
||
114 | void LPD8806::updateLength(uint16_t n) { |
||
115 | if(pixels != NULL) free(pixels); // Free existing data (if any) |
||
116 | numLEDs = n; |
||
117 | n *= 3; // 3 bytes per pixel |
||
118 | if(NULL != (pixels = (uint8_t *)malloc(n + 1))) { // Alloc new data |
||
119 | memset(pixels, 0x80, n); // Init to RGB 'off' state |
||
120 | pixels[n] = 0; // Last byte is always zero for latch |
||
121 | } else numLEDs = 0; // else malloc failed |
||
122 | // 'begun' state does not change -- pins retain prior modes |
||
123 | } |
||
124 | |||
125 | uint16_t LPD8806::numPixels(void) { |
||
126 | return numLEDs; |
||
127 | } |
||
128 | |||
129 | // This is how data is pushed to the strip. Unfortunately, the company |
||
130 | // that makes the chip didnt release the protocol document or you need |
||
131 | // to sign an NDA or something stupid like that, but we reverse engineered |
||
132 | // this from a strip controller and it seems to work very nicely! |
||
133 | void LPD8806::show(void) { |
||
134 | uint16_t i, n3 = numLEDs * 3 + 1; // 3 bytes per LED + 2 for latch |
||
135 | |||
136 | // write 24 bits per pixel |
||
137 | if (hardwareSPI) { |
||
138 | for (i=0; i<n3; i++ ) { |
||
139 | while(!(SPSR & (1<<SPIF))); // Wait for prior byte out |
||
140 | SPDR = pixels[i]; // Issue new byte |
||
141 | } |
||
142 | while(!(SPSR & (1<<SPIF))); // Wait for prior byte out |
||
143 | SPDR = 0; // Issue new byte |
||
144 | } else { |
||
145 | for (i=0; i<n3; i++ ) { |
||
146 | for (uint8_t bit=0x80; bit; bit >>= 1) { |
||
147 | if(pixels[i] & bit) *dataport |= datapinmask; |
||
148 | else *dataport &= ~datapinmask; |
||
149 | *clkport |= clkpinmask; |
||
150 | *clkport &= ~clkpinmask; |
||
151 | } |
||
152 | } |
||
153 | for (uint8_t bit=0x80; bit; bit >>= 1) { |
||
154 | if(pixels[i] & bit) *dataport |= datapinmask; |
||
155 | else *dataport &= ~datapinmask; |
||
156 | *clkport |= clkpinmask; |
||
157 | *clkport &= ~clkpinmask; |
||
158 | } |
||
159 | |||
160 | } |
||
161 | } |
||
162 | |||
163 | // Convert separate R,G,B into combined 32-bit GRB color: |
||
164 | uint32_t LPD8806::Color(byte g, byte r, byte b) { |
||
165 | return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; |
||
166 | } |
||
167 | |||
168 | // Set pixel color from separate 7-bit R, G, B components: |
||
169 | // All rigger in parallel |
||
170 | void LPD8806::setPixelColor(uint16_t n, uint8_t g, uint8_t r, uint8_t b) { |
||
171 | uint8_t i; |
||
172 | for (i=1; i<rigger; i++){ |
||
173 | if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<=' |
||
174 | uint8_t *p = &pixels[n * 3]; |
||
175 | *p++ = g | 0x80; // LPD8806 color order is GRB, |
||
176 | *p++ = r | 0x80; // not the more common RGB, |
||
177 | *p++ = b | 0x80; // so the order here is intentional; don't "fix" |
||
178 | } |
||
179 | n = n+(numLEDs/rigger); |
||
180 | } |
||
181 | } |
||
182 | |||
183 | // Set pixel color from separate 7-bit R, G, B components: |
||
184 | // Only on selected rigger |
||
185 | void LPD8806::setPixelColor(uint16_t n, uint8_t g, uint8_t r, uint8_t b, uint8_t r1, uint8_t r2, uint8_t r3, uint8_t r4, uint8_t r5, uint8_t r6, uint8_t r7, uint8_t r8) { |
||
186 | int i; |
||
187 | int pix; |
||
188 | uint16_t z; |
||
189 | |||
190 | // Select rigger to set LEDs |
||
191 | for (i=1; i<=rigger; i++){ |
||
192 | if (i == 1 && r1 == 1){ |
||
193 | z = ((numLEDs/rigger)*1)+n-(numLEDs/rigger); |
||
194 | pix = 1; |
||
195 | } |
||
196 | if (i == 2 && r2 == 1){ |
||
197 | z = ((numLEDs/rigger)*2)+n-(numLEDs/rigger); |
||
198 | pix = 1; |
||
199 | } |
||
200 | if (i == 3 && r3 == 1){ |
||
201 | z = ((numLEDs/rigger)*3)+n-(numLEDs/rigger); |
||
202 | pix = 1; |
||
203 | } |
||
204 | if (i == 4 && r4 == 1){ |
||
205 | z = ((numLEDs/rigger)*4)+(n-(numLEDs/rigger)); |
||
206 | pix = 1; |
||
207 | } |
||
208 | if (i == 5 && r5 == 1){ |
||
209 | z = ((numLEDs/rigger)*5)+n-(numLEDs/rigger); |
||
210 | pix = 1; |
||
211 | } |
||
212 | if (i == 6 && r6 == 1){ |
||
213 | z = ((numLEDs/rigger)*6)+n-(numLEDs/rigger); |
||
214 | pix = 1; |
||
215 | } |
||
216 | if (i == 7 && r7 == 1){ |
||
217 | z = ((numLEDs/rigger)*7)+n-(numLEDs/rigger); |
||
218 | pix = 1; |
||
219 | } |
||
220 | if (i == 8 && r8 == 1){ |
||
221 | z = ((numLEDs/rigger)*8)+n-(numLEDs/rigger); |
||
222 | pix = 1; |
||
223 | } |
||
224 | if((pix == 1) && (z < numLEDs)) { // Arrays are 0-indexed, thus NOT '<=' |
||
225 | uint8_t *p = &pixels[z * 3]; |
||
226 | *p++ = g | 0x80; // LPD8806 color order is GRB, |
||
227 | *p++ = r | 0x80; // not the more common RGB, |
||
228 | *p++ = b | 0x80; // so the order here is intentional; don't "fix" |
||
229 | } |
||
230 | pix = 0; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | // Set pixel color from 'packed' 32-bit RGB value: |
||
235 | // All rigger in parallel |
||
236 | void LPD8806::setPixelColor(uint16_t n, uint32_t c) { |
||
237 | uint8_t i; |
||
238 | for (i=0; i<rigger; i++){ |
||
239 | if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<=' |
||
240 | uint8_t *p = &pixels[n * 3]; |
||
241 | *p++ = (c >> 16) | 0x80; |
||
242 | *p++ = (c >> 8) | 0x80; |
||
243 | *p++ = c | 0x80; |
||
244 | } |
||
245 | n = n+(numLEDs/rigger); |
||
246 | } |
||
247 | } |
||
248 | |||
249 | // Set pixel color from 'packed' 32-bit RGB value: |
||
250 | // Only on selected rigger's |
||
251 | void LPD8806::setPixelColor(uint16_t n, uint32_t c, uint8_t r1, uint8_t r2, uint8_t r3, uint8_t r4, uint8_t r5, uint8_t r6, uint8_t r7, uint8_t r8) { |
||
252 | int i; |
||
253 | int pix; |
||
254 | uint16_t z; |
||
255 | |||
256 | // Select rigger to set LEDs |
||
257 | for (i=1; i<=rigger; i++){ |
||
258 | if (i == 1 && r1 == 1){ |
||
259 | z = ((numLEDs/rigger)*1)+n-(numLEDs/rigger); |
||
260 | pix = 1; |
||
261 | } |
||
262 | if (i == 2 && r2 == 1){ |
||
263 | z = ((numLEDs/rigger)*2)+n-(numLEDs/rigger); |
||
264 | pix = 1; |
||
265 | } |
||
266 | if (i == 3 && r3 == 1){ |
||
267 | z = ((numLEDs/rigger)*3)+n-(numLEDs/rigger); |
||
268 | pix = 1; |
||
269 | } |
||
270 | if (i == 4 && r4 == 1){ |
||
271 | z = ((numLEDs/rigger)*4)+n-(numLEDs/rigger); |
||
272 | pix = 1; |
||
273 | } |
||
274 | if (i == 5 && r5 == 1){ |
||
275 | z = ((numLEDs/rigger)*5)+n-(numLEDs/rigger); |
||
276 | pix = 1; |
||
277 | } |
||
278 | if (i == 6 && r6 == 1){ |
||
279 | z = ((numLEDs/rigger)*6)+n-(numLEDs/rigger); |
||
280 | pix = 1; |
||
281 | } |
||
282 | if (i == 7 && r7 == 1){ |
||
283 | z = ((numLEDs/rigger)*7)+n-(numLEDs/rigger); |
||
284 | pix = 1; |
||
285 | } |
||
286 | if (i == 8 && r8 == 1){ |
||
287 | z = ((numLEDs/rigger)*8)+n-(numLEDs/rigger); |
||
288 | pix = 1; |
||
289 | } |
||
290 | if((pix == 1) && (z < numLEDs)) { // Arrays are 0-indexed, thus NOT '<=' |
||
291 | uint8_t *p = &pixels[z * 3]; |
||
292 | *p++ = (c >> 16) | 0x80; |
||
293 | *p++ = (c >> 8) | 0x80; |
||
294 | *p++ = c | 0x80; |
||
295 | } |
||
296 | pix = 0; |
||
297 | } |
||
298 | |||
299 | } |
||
300 | |||
301 | // Query color from previously-set pixel (returns packed 32-bit GRB value) |
||
302 | uint32_t LPD8806::getPixelColor(uint16_t n) { |
||
303 | if(n < numLEDs) { |
||
304 | uint16_t ofs = n * 3; |
||
305 | return ((uint32_t)((uint32_t)pixels[ofs ] << 16) | |
||
306 | (uint32_t)((uint32_t)pixels[ofs + 1] << 8) | |
||
307 | (uint32_t)pixels[ofs + 2]) & 0x7f7f7f; |
||
308 | } |
||
309 | |||
310 | return 0; // Pixel # is out of bounds |
||
311 | } |