Engduino v2.1
|
00001 /** 00002 * \addtogroup EngduinoLEDs 00003 * 00004 * This is the driver code for LEDs on the Engduino 00005 * These LEDS are not directly connected to pins on the 00006 * AtMega32u4 processor. Instead they are connected through 00007 * LED drivers so should only be accessed through this code. 00008 * 00009 * The Engduino has 16 RGB LEDs on it, each of which 00010 * can be controlled independently. To make the granularity 00011 * of control greater, we implement a software PWM for each 00012 * LED. This allows for 16 levels of brightness in each of 00013 * the RGB channels on each LED with minimal flicker. 00014 * 00015 * This implementation uses TIMER4 Comparator A to 00016 * implement the PWM. The timer is set to run at ~320Hz. The 00017 * clock is reset on interrupt, so you should be very wary 00018 * about using the other comparators. 00019 * 00020 * @{ 00021 */ 00022 00023 /** 00024 * \file 00025 * Engduino LED driver 00026 * \author 00027 * Engduino team: support@engduino.org 00028 */ 00029 00030 #include "pins_arduino.h" 00031 #include "EngduinoLEDs.h" 00032 00033 /*---------------------------------------------------------------------------*/ 00034 /** 00035 * \brief Constructor 00036 * 00037 * C++ constructor for this class. Empty. 00038 */ 00039 EngduinoLEDsClass::EngduinoLEDsClass() 00040 { 00041 } 00042 00043 /*---------------------------------------------------------------------------*/ 00044 /** 00045 * \brief begin function - must be called before using other functions 00046 * 00047 * The connection to the LEDs is not direct. Instead, it happens through 00048 * three LED drivers - one each for the R, G, and B channels. These are 00049 * connected through a daisy-chained SPI connection. There is a single latch 00050 * line connected to all driver chips, which latches the value set by SPI when 00051 * the line is pulsed high. The output from each driver chip can be switched on 00052 * or off with a separate output enable line to each driver - when these are LOW 00053 * the driver output lines go to the LEDs; when HIGH, they do not. 00054 * 00055 */ 00056 void EngduinoLEDsClass::begin() 00057 { 00058 for (int i = 0; i < 15; i++) { 00059 RSet[i] = GSet[i] = BSet[i] = 0; 00060 RAccum[i] = GAccum[i] = BAccum[i] = 0; 00061 RDisp[i] = GDisp[i] = BDisp[i] = 0; 00062 } 00063 00064 // RGB LED drivers - output enable lines 00065 pinMode(LED_R_OE, OUTPUT); 00066 pinMode(LED_G_OE, OUTPUT); 00067 pinMode(LED_B_OE, OUTPUT); 00068 00069 // Disable the output from the LED drivers while we set up 00070 digitalWrite(LED_R_OE, HIGH); 00071 digitalWrite(LED_G_OE, HIGH); 00072 digitalWrite(LED_B_OE, HIGH); 00073 00074 // Set up SPI data connection to the RGB LEDs 00075 pinMode(LED_MISO, INPUT); 00076 pinMode(LED_MOSI, OUTPUT); 00077 pinMode(LED_SCLK, OUTPUT); 00078 pinMode(LED_LATCH, OUTPUT); 00079 00080 // Clock line is initially low. 00081 // We must pulse high to tick 00082 digitalWrite(LED_SCLK, LOW); 00083 00084 // Latch line is initially low 00085 // We must pulse high to latch 00086 digitalWrite(LED_LATCH, LOW); 00087 00088 // The buffers might have something latched from the 00089 // last time they were programmed, so remove it - nothing 00090 // will be shown, because the LED driver OE lines are pulled 00091 // high at this point in time. 00092 setAll(OFF); 00093 00094 // Set up timer 4. We use a software PWM to drive the LEDs to control both 00095 // brightness and colour. This requires a tick, which we set at 320Hz - this 00096 // is determined by the fact that we have 16 brightness levels per channel on 00097 // our PWM. Brightness 1 corresponds to 1 on period and 15 off periods. To 00098 // avoid flicker, we need this to happen at >15-18Hz, so we choose to run 00099 // through the 16 on/off periods 20 times per second, meaning that we need 00100 // a timer tick of 320Hz. 00101 // 00102 // Timer 4 is a free running 8 or 10 bit timer; NOTE: there is no CTC 00103 // (Clear Timer on Compare match) mode as for timers 1 and 3, so we must 00104 // manually reset the clock when an interrupt occurs. 00105 // 00106 // Note, as with other timers, if we set the count to, n, then we will 00107 // interrupt after n+1 clock cycles 00108 // 00109 // For a 10 bit write we must write the high byte before the low byte; 00110 // in this case there is a single shared register for the high bits, TC4H 00111 // and this is used for writing to ANY 10 bit register, so we need to be 00112 // aware of what is in there when we write what we might think is an 00113 // 8 bit value. 00114 // 00115 // At 8MHz with a /128 prescaler, one count is equal to 16 us 00116 // At 194 counts, we will interrupt every 195 periods: 00117 // at 3.12ms intervals, or 320.51Hz 00118 // 00119 00120 cli(); // Disable interrupts 00121 00122 TIMSK4 = 0x00; // Disable interrupts for all timer 4 comparisons, plus overflow 00123 00124 TC4H = 0x00; // Reset the timer to zero, high byte first 00125 TCNT4 = 0x00; // Reset the timer to zero 00126 // This must be done first because the TC4H is used to provide 00127 // high bits for the 10 bit registers accessed below 00128 00129 TCCR4A = 0x00; // Turn off OC4Ax/OC4Bx connections, FfOC and PWM 00130 TCCR4B = 0x08; // No PWM inversion, don't reset the prescaler, don't set dead time, clock/128 00131 TCCR4C = 0x00; // Comparator B and D to normal mode 00132 TCCR4D = 0x00; // Fault protection off 00133 TCCR4E = 0x00; // Don't lock and switch off all output compare pins 00134 OCR4A = 0x4F; // Set the counter to 194 (0xC2), giving ~320Hz 00135 00136 TIMSK4 |= 0x40; // And enable interrupts for a compare A match only 00137 00138 // Finally, enable the output from the LED drivers 00139 digitalWrite(LED_R_OE, LOW); 00140 digitalWrite(LED_G_OE, LOW); 00141 digitalWrite(LED_B_OE, LOW); 00142 00143 sei(); // Enable interrupts 00144 } 00145 00146 00147 /*---------------------------------------------------------------------------*/ 00148 /** 00149 * \brief end function - switch off the LEDs 00150 * 00151 * We drive the output enables for all three drivers high. This disconnects 00152 * the driver from the LEDs and so all subsequent changes will have no effect. 00153 * We also stop the timer interrupt. 00154 * 00155 */ 00156 void EngduinoLEDsClass::end() 00157 { 00158 // Disable output from LED drivers 00159 digitalWrite(LED_R_OE, HIGH); 00160 digitalWrite(LED_G_OE, HIGH); 00161 digitalWrite(LED_B_OE, HIGH); 00162 00163 TIMSK4 = 0x00; // Disable interrupts for all timer 4 comparisons, plus overflow 00164 } 00165 00166 00167 /*---------------------------------------------------------------------------*/ 00168 /** 00169 * \brief Internal function to set an LED to a given colour/brightness. 00170 * \param LEDidx LED index, ranging from 0 (for LED0) to 15 (LED15) 00171 * \param c Colour, as chosen from the colour enum 00172 * \param brightness A 0-MAX_BRIGHTNESS(=15) value 00173 * 00174 * Internal function to set the colour/brightness of an LED. The colour value 00175 * here is chosen from an enum and corresponds to the primary and secondary 00176 * colours of light, plus white and off. Brightness ranges from 0 to 00177 * MAX_BRIGHTNESS (currently 15), and is shifted here to an internal 8 bit 00178 * representation (0-255) to make some of the maths slightly quicker later. 00179 * Choosing a brightness of zero will turn an LED off. 00180 * 00181 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00182 * when the colour white is requested, simply because this causes too big a 00183 * drain on current whilst the device is attached to the USB. This 00184 * causes a system reset, which is both irritating and makes reprogramming 00185 * rather tedious. 00186 * 00187 */ 00188 void EngduinoLEDsClass::_setLED(uint8_t LEDidx, colour c, uint8_t brightness) 00189 { 00190 LEDidx &= 0x0F; // We only have 16 LEDS 00191 brightness = brightness << 4; // Translate brightness to a 0-255 scale 00192 00193 switch (c) { 00194 case RED: 00195 RSet[LEDidx]=brightness; GSet[LEDidx]=0x00; BSet[LEDidx]=0x00; 00196 break; 00197 case GREEN: 00198 RSet[LEDidx]=0x00; GSet[LEDidx]=brightness; BSet[LEDidx]=0x00; 00199 break; 00200 case BLUE: 00201 RSet[LEDidx]=0x00; GSet[LEDidx]=0x00; BSet[LEDidx]=brightness; 00202 break; 00203 case YELLOW: 00204 RSet[LEDidx]=brightness; GSet[LEDidx]=brightness; BSet[LEDidx]=0x00; 00205 break; 00206 case MAGENTA: 00207 RSet[LEDidx]=brightness; GSet[LEDidx]=0x00; BSet[LEDidx]=brightness; 00208 break; 00209 case CYAN: 00210 RSet[LEDidx]=0x00; GSet[LEDidx]=brightness; BSet[LEDidx]=brightness; 00211 break; 00212 case WHITE: 00213 brightness = (brightness > 0xB0) ? 0xB0 : brightness; // Can't use full scale for white 00214 RSet[LEDidx]=brightness; GSet[LEDidx]=brightness; BSet[LEDidx]=brightness; 00215 break; 00216 case OFF: 00217 RSet[LEDidx]=0x00; GSet[LEDidx]=0x00; BSet[LEDidx]=0x00; 00218 break; 00219 } 00220 } 00221 00222 /*---------------------------------------------------------------------------*/ 00223 /** 00224 * \brief Internal function to set an LED to a given point in the rgb space. 00225 * \param LEDidx LED index, ranging from 0 (for LED0) to 15 (LED15) 00226 * \param r Brightness of the red channel from 0-MAX_BRIGHTNESS (15) 00227 * \param g Brightness of the green channel from 0-MAX_BRIGHTNESS (15) 00228 * \param b Brightness of the blue channel from 0-MAX_BRIGHTNESS (15) 00229 * 00230 * Internal function to set the colour/brightness of an LED to a point in rgb 00231 * space. The brightness in each channel may range from 0 to MAX_BRIGHTNESS 00232 * (currently 15), and is shifted here to an internal 8 bit representation 00233 * (0-255) to make some of the maths slightly quicker later. 00234 * 00235 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00236 * when the colour white, or colours close to white are requested, simply 00237 * because this causes too big a drain on current whilst the device is attached 00238 * to the USB. This causes a system reset, which is both irritating and makes 00239 * reprogramming rather tedious. So we limit the sum of the rgb values to 00240 * a number we chose empirically, and we reduce the chosen rgb values until they 00241 * sum to less than that chosen. 00242 * 00243 */ 00244 void EngduinoLEDsClass::_setLED(uint8_t LEDidx, uint8_t r, uint8_t g, uint8_t b) 00245 { uint16_t sum; 00246 00247 LEDidx &= 0x0F; // We only have 16 LEDS 00248 r = r << 4; // We only use 16 brightness levels to avoid flickering 00249 g = g << 4; // Turn the 0-15 scale into a 0-255 scale; makes maths easier 00250 b = b << 4; 00251 00252 // The brightest colours cause a reset on the engduino v1.0 00253 // when running on USB. So we limit what we allow someone to choose. 00254 // This subtraction avoids division, which is slow. 00255 // 00256 sum = r + g + b; 00257 while (sum > 0x240) { // Empirically determined: (0x24 << 4) 00258 r -= 1; 00259 g -= 1; 00260 b -= 1; 00261 sum = r + g + b; 00262 } 00263 00264 RSet[LEDidx] = r; 00265 GSet[LEDidx] = g; 00266 BSet[LEDidx] = b; 00267 } 00268 00269 /*---------------------------------------------------------------------------*/ 00270 /** 00271 * \brief Set the colour of a single LED at maximum brightness 00272 * \param LEDNumber LED number, ranging from 0 (for LED0) to 15 (LED15) 00273 * \param c Colour, as chosen from the colour enum 00274 * 00275 * Set the colour of an LED. The colour value here is chosen from an enum and 00276 * corresponds to the primary and secondary colours of light, plus white and 00277 * off. 00278 * 00279 */ 00280 void EngduinoLEDsClass::setLED(uint8_t LEDNumber, colour c) 00281 { 00282 _setLED(LEDNumber, c, MAX_BRIGHTNESS); 00283 } 00284 00285 00286 /*---------------------------------------------------------------------------*/ 00287 /** 00288 * \brief Set an LED to a given colour/brightness. 00289 * \param LEDNumber LED number, ranging from 0 (for LED0) to 15 (LED15) 00290 * \param c Colour, as chosen from the colour enum 00291 * \param brightness A 0-MAX_BRIGHTNESS(=15) value 00292 * 00293 * Set the colour of an LED. The colour value here is chosen from an enum and 00294 * corresponds to the primary and secondary colours of light, plus white and 00295 * off. Brightness ranges from 0 to MAX_BRIGHTNESS (currently 15). Choosing a 00296 * brightness of zero will turn an LED off. 00297 * 00298 */ 00299 void EngduinoLEDsClass::setLED(uint8_t LEDNumber, colour c, uint8_t brightness) 00300 { 00301 _setLED(LEDNumber, c, brightness); 00302 } 00303 00304 /*---------------------------------------------------------------------------*/ 00305 /** 00306 * \brief Set an LED to a given point in the rgb space. 00307 * \param LEDNumber LED number, ranging from 0 (for LED0) to 15 (LED15) 00308 * \param r Brightness of the red channel from 0-MAX_BRIGHTNESS (15) 00309 * \param g Brightness of the green channel from 0-MAX_BRIGHTNESS (15) 00310 * \param b Brightness of the blue channel from 0-MAX_BRIGHTNESS (15) 00311 * 00312 * Set the colour/brightness of an LED to a point in rgb space. The brightness 00313 * in each channel may range from 0 to MAX_BRIGHTNESS (currently 15). 00314 * 00315 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00316 * when the colour white, or colours close to white are requested, simply 00317 * because this causes too big a drain on current whilst the device is attached 00318 * to the USB. This causes a system reset, which is both irritating and makes 00319 * reprogramming rather tedious. So we limit the sum of the rgb values to 00320 * a number we chose empirically, and we reduce the chosen rgb values until they 00321 * sum to less than that chosen. 00322 * 00323 */ 00324 void EngduinoLEDsClass::setLED(uint8_t LEDNumber, uint8_t r, uint8_t g, uint8_t b) 00325 { 00326 _setLED(LEDNumber, r, g, b); 00327 } 00328 00329 /*---------------------------------------------------------------------------*/ 00330 /** 00331 * \brief Set the colour of all LEDs at maximum brightness 00332 * \param c Colour, as chosen from the colour enum 00333 * 00334 * Set the colour of all LEDs. The colour value here is chosen from an enum and 00335 * corresponds to the primary and secondary colours of light, plus white and 00336 * off. 00337 * 00338 */ 00339 void EngduinoLEDsClass::setAll(colour c) 00340 { 00341 for (int i = 0; i < 16; i++) 00342 _setLED(i, c, MAX_BRIGHTNESS); 00343 } 00344 00345 00346 /*---------------------------------------------------------------------------*/ 00347 /** 00348 * \brief Set all LEDs to a given colour/brightness. 00349 * \param c Colour, as chosen from the colour enum 00350 * \param brightness A 0-MAX_BRIGHTNESS(=15) value 00351 * 00352 * Set the colour of all LEDs. The colour value here is chosen from an enum and 00353 * corresponds to the primary and secondary colours of light, plus white and 00354 * off. Brightness ranges from 0 to MAX_BRIGHTNESS (currently 15). Choosing a 00355 * brightness of zero will turn all LEDs off. 00356 * 00357 */ 00358 void EngduinoLEDsClass::setAll(colour c, uint8_t brightness) 00359 { 00360 for (int i = 0; i < 16; i++) { 00361 _setLED(i, c, brightness); 00362 } 00363 } 00364 00365 /*---------------------------------------------------------------------------*/ 00366 /** 00367 * \brief Set all LEDs to a given point in the rgb space. 00368 * \param r Brightness of the red channel from 0-MAX_BRIGHTNESS (15) 00369 * \param g Brightness of the green channel from 0-MAX_BRIGHTNESS (15) 00370 * \param b Brightness of the blue channel from 0-MAX_BRIGHTNESS (15) 00371 * 00372 * Set the colour/brightness of all LEDs to a point in rgb space. The brightness 00373 * in each channel may range from 0 to MAX_BRIGHTNESS (currently 15). 00374 * 00375 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00376 * when the colour white, or colours close to white are requested, simply 00377 * because this causes too big a drain on current whilst the device is attached 00378 * to the USB. This causes a system reset, which is both irritating and makes 00379 * reprogramming rather tedious. So we limit the sum of the rgb values to 00380 * a number we chose empirically, and we reduce the chosen rgb values until they 00381 * sum to less than that chosen. 00382 * 00383 */ 00384 void EngduinoLEDsClass::setAll(uint8_t r, uint8_t g, uint8_t b) 00385 { 00386 for (int i = 0; i < 16; i++) 00387 _setLED(i, r, g, b); 00388 } 00389 00390 /*---------------------------------------------------------------------------*/ 00391 /** 00392 * \brief Set the colour of all LEDs at maximum brightness from an array of 00393 * individual values 00394 * \param c Array of colour values, as chosen from the colour enum 00395 * 00396 * Set the colour of all LEDs from an array of colour values - i.e. each LED 00397 * can be set to a different colour, though all are at maximum brightness. The 00398 * colour value here is chosen from an enum and corresponds to the primary and 00399 * secondary colours of light, plus white and off. 00400 * 00401 */ 00402 void EngduinoLEDsClass::setLEDs(colour c[16]) 00403 { 00404 for (int i = 0; i < 16; i++) 00405 _setLED(i, c[i], MAX_BRIGHTNESS); 00406 } 00407 00408 00409 /*---------------------------------------------------------------------------*/ 00410 /** 00411 * \brief Set all LEDs to a given colour/brightness from arrays of 00412 * individual values 00413 * \param c Array of colour values, as chosen from the colour enum 00414 * \param brightness Array of brightness values, from 0-MAX_BRIGHTNESS(=15) 00415 * 00416 * Set the colour/brightness of all LEDs from two arrays of values - i.e. 00417 * each LED can be set to a different colour/brightness value. The colour 00418 * value here is chosen from an enum and corresponds to the primary and 00419 * secondary colours of light, plus white and off. Brightness ranges from 0 00420 * to MAX_BRIGHTNESS (currently 15). Choosing a brightness of zero will turn 00421 * an LED off. 00422 * 00423 */ 00424 void EngduinoLEDsClass::setLEDs(colour c[16], uint8_t brightness[16]) 00425 { 00426 for (int i = 0; i < 16; i++) { 00427 _setLED(i, c[i], brightness[i]); 00428 } 00429 } 00430 00431 /*---------------------------------------------------------------------------*/ 00432 /** 00433 * \brief Set all LEDs to a given point in the rgb space from arrays of 00434 * individual values. 00435 * \param r Array of red channel brightness, from 0-MAX_BRIGHTNESS (15) 00436 * \param g Array of green channel brightness, from 0-MAX_BRIGHTNESS (15) 00437 * \param b Array of blue channel brightness, from 0-MAX_BRIGHTNESS (15) 00438 * 00439 * Set the colour/brightness of all LEDs to a point in rgb space from three 00440 * arrays of values - i.e. each LED can be set to a different rgb point. The 00441 * brightness in each channel may range from 0 to MAX_BRIGHTNESS (currently 15). 00442 * 00443 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00444 * when the colour white, or colours close to white are requested, simply 00445 * because this causes too big a drain on current whilst the device is attached 00446 * to the USB. This causes a system reset, which is both irritating and makes 00447 * reprogramming rather tedious. So we limit the sum of the rgb values to 00448 * a number we chose empirically, and we reduce the chosen rgb values until they 00449 * sum to less than that chosen. 00450 * 00451 */ 00452 void EngduinoLEDsClass::setLEDs(uint8_t r[16], uint8_t g[16], uint8_t b[16]) 00453 { 00454 for (int i = 0; i < 16; i++) 00455 _setLED(i, r[i], g[i], b[i]); 00456 } 00457 00458 /*---------------------------------------------------------------------------*/ 00459 /** 00460 * \brief Set all LEDs to a given point in the rgb space a 2D array of 00461 * individual values. The array is of form rgb[3][16] 00462 * \param rgb 2D array of rgb brightnesses, from 0-MAX_BRIGHTNESS (15) 00463 * 00464 * Set the colour/brightness of all LEDs to a point in rgb space from a 2D 00465 * array values - i.e. each LED can be set to a different rgb point. The array 00466 * is of form rgb[3][16] - i.e. the first index is the red/green/blue channel 00467 * and the second is the LED (ranging from 0-15). The brightness in each 00468 * channel may range from 0 to MAX_BRIGHTNESS (currently 15). 00469 * 00470 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00471 * when the colour white, or colours close to white are requested, simply 00472 * because this causes too big a drain on current whilst the device is attached 00473 * to the USB. This causes a system reset, which is both irritating and makes 00474 * reprogramming rather tedious. So we limit the sum of the rgb values to 00475 * a number we chose empirically, and we reduce the chosen rgb values until they 00476 * sum to less than that chosen. 00477 * 00478 */ 00479 void EngduinoLEDsClass::setLEDs(uint8_t rgb[3][16]) 00480 { 00481 for (int i = 0; i < 16; i++) 00482 _setLED(i, rgb[0][i], rgb[1][i], rgb[2][i]); 00483 } 00484 00485 00486 /*---------------------------------------------------------------------------*/ 00487 /** 00488 * \brief Set all LEDs to a given point in the rgb space a 2D array of 00489 * individual values. The array is of form rgb[16][3] 00490 * \param rgb 2D array of rgb brightnesses, from 0-MAX_BRIGHTNESS (15) 00491 * 00492 * Set the colour/brightness of all LEDs to a point in rgb space from a 2D 00493 * array values - i.e. each LED can be set to a different rgb point. The array 00494 * is of form rgb[16][3] - i.e. the first index is the LED (ranging from 0-15). 00495 * and the second is the red/green/blue channel. The brightness in each 00496 * channel may range from 0 to MAX_BRIGHTNESS (currently 15). 00497 * 00498 * Note: in the Engduino 1.0, we cannot use the full brightness for the LEDs 00499 * when the colour white, or colours close to white are requested, simply 00500 * because this causes too big a drain on current whilst the device is attached 00501 * to the USB. This causes a system reset, which is both irritating and makes 00502 * reprogramming rather tedious. So we limit the sum of the rgb values to 00503 * a number we chose empirically, and we reduce the chosen rgb values until they 00504 * sum to less than that chosen. 00505 * 00506 */ 00507 void EngduinoLEDsClass::setLEDs(uint8_t rgb[16][3]) 00508 { 00509 for (int i = 0; i < 16; i++) 00510 _setLED(i, rgb[i][0], rgb[i][1], rgb[i][2]); 00511 } 00512 00513 00514 /*---------------------------------------------------------------------------*/ 00515 /** 00516 * \brief This is an internal routine to set the LED latches appropriately 00517 * \param value The on/off bits for each LED on a given channel 00518 * 00519 * This function is written using low level C programming primitives for 00520 * changing the voltages on ATMega32U4 pins rather than the Arduino 00521 * digitalWrite equivalents. This has to be done in this way for speed - it is 00522 * around 18 times faster than the alternative and, because we update the 00523 * values rather frequently to avoid flickering, speed is important. This code 00524 * manually emulates SPI functionality (mode 0), both for simplicity and speed 00525 * 00526 * Notes: 00527 * PB1 is pin 9 - SPI SCLK 00528 * PB2 is pin 10 - SPI MOSI 00529 * PB3 is pin 11 - SPI MISO 00530 * 00531 */ 00532 inline void writeLEDs(volatile uint8_t *value) 00533 { 00534 // This is code to emulate SPI mode 0 = we set the 00535 // data value first and then tick the clock with a rising 00536 // edge to latch the data into the slave. 00537 // 00538 for (int i = 15; i >= 0; i--) { 00539 if (value[i] != 0) // Set the data line... 00540 PORTB |= _BV(PORTB2); // Drive MOSI high 00541 else 00542 PORTB &= ~_BV(PORTB2); // Drive MOSI low 00543 PORTB |= _BV(PORTB1); // ...and tick the SPI clock 00544 PORTB &= ~_BV(PORTB1); 00545 } 00546 } 00547 00548 /*---------------------------------------------------------------------------*/ 00549 /** 00550 * \brief ISR routine for comparator A of TIMER 4 00551 * 00552 * TIMER4 interrupt code ticks at ~320Hz, which means we can do 16 brightness 00553 * levels at 20Hz, so flickering is invisible to the naked eye. This ISR 00554 * implements the software PWM and calls the LED display function. 00555 * 00556 * Because we must update the LEDs very frequently, and because it still takes 00557 * a significant period to write all 3*16 values to the LED drivers, we need 00558 * this code to run as fast as possible. We also need to ensure that we do not 00559 * prevent other interrupts from occurring whilst we process this. To that end 00560 * we stop interrupts for this ISR to prevent any nesting, reset the counter 00561 * and re-enable interrupts generally before calling setting the LED values. 00562 * 00563 * The PWM implementation relies on counting in units of the brightness on a 00564 * channel and setting the apropriate colour channel on for an LED when the 00565 * accumulated value overflows. This is slightly faster if the rgb values are 00566 * in the range 0-255 than 0-15, because we can do it with a simple comparison. 00567 * 00568 * This function then calls writeLEDs for each of the RGB values, in reverse 00569 * order. The drivers are daisy chained and we need to shift the blue values 00570 * to the far end of that chain before pulsing the latch. This written using 00571 * low level C programming primitives of changing the voltages on ATMega32U4 00572 * pins rather than the Arduino digitalWrite equivalents. This has to be done 00573 * in this way for speed - it is around 18 times faster than the alternative 00574 * and, because we update the values rather frequently to avoid flickering, 00575 * speed is important. 00576 * 00577 * Notes: 00578 * The PWM implementation will occasionally put one more dark period 00579 * in than it should, but we can live with this for simplicity. 00580 * PB7 is pin 12 on the ATMega32U4 and is attached to the LED LATCH 00581 * 00582 */ 00583 ISR(TIMER4_COMPA_vect) 00584 { 00585 // Temporarily disable interrupts on this ISR to avoid nesting. 00586 // 00587 TIMSK4 = 0x00; 00588 00589 // Because there is no CTC mode, we need to reset the timer 00590 // count manually. 00591 // 00592 TC4H = 0x00; // Reset the timer to zero, high byte first 00593 TCNT4 = 0x00; // Reset the timer to zero 00594 00595 // And re-enable interrupts for all other ISRs whilst we set the LEDs 00596 sei(); 00597 00598 00599 // Accumulate RGB values for each LED into their respective arrays 00600 // and only set the LED on when we overflow the count; it is otherwise 00601 // set off. 00602 // 00603 for (int i = 0; i < 16; i++) { 00604 uint8_t ra = EngduinoLEDs.RAccum[i]; 00605 uint8_t ga = EngduinoLEDs.GAccum[i]; 00606 uint8_t ba = EngduinoLEDs.BAccum[i]; 00607 00608 EngduinoLEDs.RAccum[i] += EngduinoLEDs.RSet[i]; 00609 EngduinoLEDs.GAccum[i] += EngduinoLEDs.GSet[i]; 00610 EngduinoLEDs.BAccum[i] += EngduinoLEDs.BSet[i]; 00611 00612 EngduinoLEDs.RDisp[i] = (EngduinoLEDs.RAccum[i] < ra); 00613 EngduinoLEDs.GDisp[i] = (EngduinoLEDs.GAccum[i] < ga); 00614 EngduinoLEDs.BDisp[i] = (EngduinoLEDs.BAccum[i] < ba); 00615 } 00616 00617 // Now display the values we calculated. 00618 // 00619 PORTB &= ~_BV(PORTB4); // Drive LATCH low - disable 00620 00621 writeLEDs(EngduinoLEDs.BDisp); // Write the RGB values 00622 writeLEDs(EngduinoLEDs.GDisp); 00623 writeLEDs(EngduinoLEDs.RDisp); 00624 00625 PORTB |= _BV(PORTB4); // Pulse LATCH to switch 00626 PORTB &= ~_BV(PORTB4); 00627 00628 // And we're done with this call of the ISR, so re-enable interrupts 00629 // 00630 TIMSK4 |= 0x40; // And enable interrupts for a compare A match only 00631 } 00632 00633 00634 /*---------------------------------------------------------------------------*/ 00635 /* 00636 * Preinstantiate Objects 00637 */ 00638 EngduinoLEDsClass EngduinoLEDs = EngduinoLEDsClass(); 00639 00640 /** @} */