Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1768 - 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
}