ArduinoLibs
|
00001 /* 00002 * Copyright (C) 2012 Southern Storm Software, Pty Ltd. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00020 * DEALINGS IN THE SOFTWARE. 00021 */ 00022 00023 #include "DMD.h" 00024 #if defined(ARDUINO) && ARDUINO >= 100 00025 #include <Arduino.h> 00026 #else 00027 #include <WProgram.h> 00028 #endif 00029 #include <pins_arduino.h> 00030 #include <avr/io.h> 00031 #include <avr/interrupt.h> 00032 #include <string.h> 00033 #include <stdlib.h> 00034 00210 // Pins on the DMD connector board. 00211 #define DMD_PIN_PHASE_LSB 6 // A 00212 #define DMD_PIN_PHASE_MSB 7 // B 00213 #define DMD_PIN_LATCH 8 // SCLK 00214 #define DMD_PIN_OUTPUT_ENABLE 9 // nOE 00215 #define DMD_PIN_SPI_SS SS // SPI Slave Select 00216 #define DMD_PIN_SPI_MOSI MOSI // SPI Master Out, Slave In (R) 00217 #define DMD_PIN_SPI_MISO MISO // SPI Master In, Slave Out 00218 #define DMD_PIN_SPI_SCK SCK // SPI Serial Clock (CLK) 00219 00220 // Dimension information for the display. 00221 #define DMD_NUM_COLUMNS 32 // Number of columns in a panel. 00222 #define DMD_NUM_ROWS 16 // Number of rows in a panel. 00223 00224 // Refresh times. 00225 #define DMD_REFRESH_MS 5 00226 #define DMD_REFRESH_US 5000 00227 00237 DMD::DMD(int widthPanels, int heightPanels) 00238 : Bitmap(widthPanels * DMD_NUM_COLUMNS, heightPanels * DMD_NUM_ROWS) 00239 , _doubleBuffer(false) 00240 , phase(0) 00241 , fb0(0) 00242 , fb1(0) 00243 , displayfb(0) 00244 , lastRefresh(millis()) 00245 { 00246 // Both rendering and display are to fb0 initially. 00247 fb0 = displayfb = fb; 00248 00249 // Initialize SPI to MSB-first, mode 0, clock divider = 2. 00250 pinMode(DMD_PIN_SPI_SCK, OUTPUT); 00251 pinMode(DMD_PIN_SPI_MOSI, OUTPUT); 00252 pinMode(DMD_PIN_SPI_SS, OUTPUT); 00253 digitalWrite(DMD_PIN_SPI_SCK, LOW); 00254 digitalWrite(DMD_PIN_SPI_MOSI, LOW); 00255 digitalWrite(DMD_PIN_SPI_SS, HIGH); 00256 SPCR |= _BV(MSTR); 00257 SPCR |= _BV(SPE); 00258 SPCR &= ~(_BV(DORD)); // MSB-first 00259 SPCR &= ~0x0C; // Mode 0 00260 SPCR &= ~0x03; // Clock divider rate 2 00261 SPSR |= 0x01; // MSB of clock divider rate 00262 00263 // Initialize the DMD-specific pins. 00264 pinMode(DMD_PIN_PHASE_LSB, OUTPUT); 00265 pinMode(DMD_PIN_PHASE_MSB, OUTPUT); 00266 pinMode(DMD_PIN_LATCH, OUTPUT); 00267 pinMode(DMD_PIN_OUTPUT_ENABLE, OUTPUT); 00268 digitalWrite(DMD_PIN_PHASE_LSB, LOW); 00269 digitalWrite(DMD_PIN_PHASE_MSB, LOW); 00270 digitalWrite(DMD_PIN_LATCH, LOW); 00271 digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW); 00272 digitalWrite(DMD_PIN_SPI_MOSI, HIGH); 00273 } 00274 00278 DMD::~DMD() 00279 { 00280 if (fb0) 00281 free(fb0); 00282 if (fb1) 00283 free(fb1); 00284 fb = 0; // Don't free the buffer again in the base class. 00285 } 00286 00314 void DMD::setDoubleBuffer(bool doubleBuffer) 00315 { 00316 if (doubleBuffer != _doubleBuffer) { 00317 _doubleBuffer = doubleBuffer; 00318 if (doubleBuffer) { 00319 // Allocate a new back buffer. 00320 unsigned int size = _stride * _height; 00321 fb1 = (uint8_t *)malloc(size); 00322 00323 // Clear the new back buffer and then switch to it, leaving 00324 // the current contents of fb0 on the screen. 00325 if (fb1) { 00326 memset(fb1, 0xFF, size); 00327 cli(); 00328 fb = fb1; 00329 displayfb = fb0; 00330 sei(); 00331 } else { 00332 // Failed to allocate the memory, so revert to single-buffered. 00333 _doubleBuffer = false; 00334 } 00335 } else if (fb1) { 00336 // Disabling double-buffering, so forcibly switch to fb0. 00337 cli(); 00338 fb = fb0; 00339 displayfb = fb0; 00340 sei(); 00341 00342 // Free the unnecessary buffer. 00343 free(fb1); 00344 fb1 = 0; 00345 } 00346 } 00347 } 00348 00363 void DMD::swapBuffers() 00364 { 00365 if (_doubleBuffer) { 00366 // Turn off interrupts while swapping buffers so that we don't 00367 // accidentally try to refresh() in the middle of this code. 00368 cli(); 00369 if (fb == fb0) { 00370 fb = fb1; 00371 displayfb = fb0; 00372 } else { 00373 fb = fb0; 00374 displayfb = fb1; 00375 } 00376 sei(); 00377 } 00378 } 00379 00396 void DMD::swapBuffersAndCopy() 00397 { 00398 swapBuffers(); 00399 if (_doubleBuffer) 00400 memcpy(fb, displayfb, _stride * _height); 00401 } 00402 00420 void DMD::loop() 00421 { 00422 unsigned long currentTime = millis(); 00423 if ((currentTime - lastRefresh) >= DMD_REFRESH_MS) { 00424 lastRefresh = currentTime; 00425 refresh(); 00426 } 00427 } 00428 00429 // Send a single byte via SPI. 00430 static inline void spiSend(byte value) 00431 { 00432 SPDR = value; 00433 while (!(SPSR & _BV(SPIF))) 00434 ; // Wait for the transfer to complete. 00435 } 00436 00437 // Flip the bits in a byte. Table generated by genflip.c 00438 static const uint8_t flipBits[256] PROGMEM = { 00439 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 00440 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 00441 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 00442 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 00443 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 00444 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 00445 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 00446 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 00447 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 00448 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 00449 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 00450 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 00451 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 00452 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 00453 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 00454 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 00455 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 00456 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 00457 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 00458 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 00459 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 00460 0x3F, 0xBF, 0x7F, 0xFF 00461 }; 00462 00478 void DMD::refresh() 00479 { 00480 // Bail out if there is a conflict on the SPI bus. 00481 if (!digitalRead(DMD_PIN_SPI_SS)) 00482 return; 00483 00484 // Transfer the data for the next group of interleaved rows. 00485 int stride4 = _stride * 4; 00486 uint8_t *data0; 00487 uint8_t *data1; 00488 uint8_t *data2; 00489 uint8_t *data3; 00490 bool flipRow = ((_height & 0x10) == 0); 00491 for (int y = 0; y < _height; y += 16) { 00492 if (!flipRow) { 00493 // The panels in this row are the right way up. 00494 data0 = displayfb + _stride * (y + phase); 00495 data1 = data0 + stride4; 00496 data2 = data1 + stride4; 00497 data3 = data2 + stride4; 00498 for (int x = _stride; x > 0; --x) { 00499 spiSend(*data3++); 00500 spiSend(*data2++); 00501 spiSend(*data1++); 00502 spiSend(*data0++); 00503 } 00504 flipRow = true; 00505 } else { 00506 // The panels in this row are upside-down and reversed. 00507 data0 = displayfb + _stride * (y + 16 - phase) - 1; 00508 data1 = data0 - stride4; 00509 data2 = data1 - stride4; 00510 data3 = data2 - stride4; 00511 for (int x = _stride; x > 0; --x) { 00512 spiSend(pgm_read_byte(&(flipBits[*data3--]))); 00513 spiSend(pgm_read_byte(&(flipBits[*data2--]))); 00514 spiSend(pgm_read_byte(&(flipBits[*data1--]))); 00515 spiSend(pgm_read_byte(&(flipBits[*data0--]))); 00516 } 00517 flipRow = false; 00518 } 00519 } 00520 00521 // Latch the data from the shift registers onto the actual display. 00522 digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW); 00523 digitalWrite(DMD_PIN_LATCH, HIGH); 00524 digitalWrite(DMD_PIN_LATCH, LOW); 00525 if (phase & 0x02) 00526 digitalWrite(DMD_PIN_PHASE_MSB, HIGH); 00527 else 00528 digitalWrite(DMD_PIN_PHASE_MSB, LOW); 00529 if (phase & 0x01) 00530 digitalWrite(DMD_PIN_PHASE_LSB, HIGH); 00531 else 00532 digitalWrite(DMD_PIN_PHASE_LSB, LOW); 00533 digitalWrite(DMD_PIN_OUTPUT_ENABLE, HIGH); 00534 phase = (phase + 1) & 0x03; 00535 } 00536 00563 void DMD::enableTimer1() 00564 { 00565 // Number of CPU cycles in the display's refresh period. 00566 unsigned long numCycles = (F_CPU / 2000000) * DMD_REFRESH_US; 00567 00568 // Determine the prescaler to be used. 00569 #define TIMER1_RESOLUTION 65536UL 00570 uint8_t prescaler; 00571 if (numCycles < TIMER1_RESOLUTION) { 00572 // No prescaling required. 00573 prescaler = _BV(CS10); 00574 } else if (numCycles < TIMER1_RESOLUTION * 8) { 00575 // Prescaler = 8. 00576 prescaler = _BV(CS11); 00577 numCycles >>= 3; 00578 } else if (numCycles < TIMER1_RESOLUTION * 64) { 00579 // Prescaler = 64. 00580 prescaler = _BV(CS11) | _BV(CS10); 00581 numCycles >>= 6; 00582 } else if (numCycles < TIMER1_RESOLUTION * 256) { 00583 // Prescaler = 256. 00584 prescaler = _BV(CS12); 00585 numCycles >>= 8; 00586 } else if (numCycles < TIMER1_RESOLUTION * 1024) { 00587 // Prescaler = 1024. 00588 prescaler = _BV(CS12) | _BV(CS10); 00589 numCycles >>= 10; 00590 } else { 00591 // Too long, so set the maximum timeout. 00592 prescaler = _BV(CS12) | _BV(CS10); 00593 numCycles = TIMER1_RESOLUTION - 1; 00594 } 00595 00596 // Configure Timer1 for the period we want. 00597 TCCR1A = 0; 00598 TCCR1B = _BV(WGM13); 00599 uint8_t saveSREG = SREG; 00600 cli(); 00601 ICR1 = numCycles; 00602 SREG = saveSREG; // Implicit sei() if interrupts were on previously. 00603 TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | prescaler; 00604 00605 // Turn on the Timer1 overflow interrupt. 00606 TIMSK1 |= _BV(TOIE1); 00607 } 00608 00614 void DMD::disableTimer1() 00615 { 00616 // Turn off the Timer1 overflow interrupt. 00617 TIMSK1 &= ~_BV(TOIE1); 00618 } 00619 00646 void DMD::enableTimer2() 00647 { 00648 // Configure Timer2 for the period we want. With the prescaler set 00649 // to 128, then 256 increments of Timer2 gives roughly 4 ms between 00650 // overflows on a system with a 16 MHz clock. We adjust the prescaler 00651 // accordingly for other clock frequencies. 00652 TCCR2A = 0; 00653 if (F_CPU >= 32000000) 00654 TCCR2B = _BV(CS22) | _BV(CS21); // Prescaler = 256 00655 else if (F_CPU >= 16000000) 00656 TCCR2B = _BV(CS22) | _BV(CS20); // Prescaler = 128 00657 else if (F_CPU >= 8000000) 00658 TCCR2B = _BV(CS22); // Prescaler = 64 00659 else 00660 TCCR2B = _BV(CS21) | _BV(CS20); // Prescaler = 32 00661 00662 // Reset Timer2 to kick off the process. 00663 TCNT2 = 0; 00664 00665 // Turn on the Timer2 overflow interrupt (also turn off OCIE2A and OCIE2B). 00666 TIMSK2 = _BV(TOIE2); 00667 } 00668 00674 void DMD::disableTimer2() 00675 { 00676 // Turn off the Timer2 overflow interrupt. 00677 TIMSK2 &= ~_BV(TOIE2); 00678 } 00679 00690 DMD::Color DMD::fromRGB(uint8_t r, uint8_t g, uint8_t b) 00691 { 00692 if (r || g || b) 00693 return White; 00694 else 00695 return Black; 00696 }