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 00192 // Pins on the DMD connector board. 00193 #define DMD_PIN_PHASE_LSB 6 // A 00194 #define DMD_PIN_PHASE_MSB 7 // B 00195 #define DMD_PIN_LATCH 8 // SCLK 00196 #define DMD_PIN_OUTPUT_ENABLE 9 // nOE 00197 #define DMD_PIN_SPI_SS SS // SPI Slave Select 00198 #define DMD_PIN_SPI_MOSI MOSI // SPI Master Out, Slave In (R) 00199 #define DMD_PIN_SPI_MISO MISO // SPI Master In, Slave Out 00200 #define DMD_PIN_SPI_SCK SCK // SPI Serial Clock (CLK) 00201 00202 // Dimension information for the display. 00203 #define DMD_NUM_COLUMNS 32 // Number of columns in a panel. 00204 #define DMD_NUM_ROWS 16 // Number of rows in a panel. 00205 00206 // Refresh times. 00207 #define DMD_REFRESH_MS 5 00208 #define DMD_REFRESH_US 5000 00209 00219 DMD::DMD(int widthPanels, int heightPanels) 00220 : Bitmap(widthPanels * DMD_NUM_COLUMNS, heightPanels * DMD_NUM_ROWS) 00221 , _doubleBuffer(false) 00222 , phase(0) 00223 , fb0(0) 00224 , fb1(0) 00225 , displayfb(0) 00226 , lastRefresh(millis()) 00227 { 00228 // Both rendering and display are to fb0 initially. 00229 fb0 = displayfb = fb; 00230 00231 // Initialize SPI to MSB-first, mode 0, clock divider = 2. 00232 pinMode(DMD_PIN_SPI_SCK, OUTPUT); 00233 pinMode(DMD_PIN_SPI_MOSI, OUTPUT); 00234 pinMode(DMD_PIN_SPI_SS, OUTPUT); 00235 digitalWrite(DMD_PIN_SPI_SCK, LOW); 00236 digitalWrite(DMD_PIN_SPI_MOSI, LOW); 00237 digitalWrite(DMD_PIN_SPI_SS, HIGH); 00238 SPCR |= _BV(MSTR); 00239 SPCR |= _BV(SPE); 00240 SPCR &= ~(_BV(DORD)); // MSB-first 00241 SPCR &= ~0x0C; // Mode 0 00242 SPCR &= ~0x03; // Clock divider rate 2 00243 SPSR |= 0x01; // MSB of clock divider rate 00244 00245 // Initialize the DMD-specific pins. 00246 pinMode(DMD_PIN_PHASE_LSB, OUTPUT); 00247 pinMode(DMD_PIN_PHASE_MSB, OUTPUT); 00248 pinMode(DMD_PIN_LATCH, OUTPUT); 00249 pinMode(DMD_PIN_OUTPUT_ENABLE, OUTPUT); 00250 digitalWrite(DMD_PIN_PHASE_LSB, LOW); 00251 digitalWrite(DMD_PIN_PHASE_MSB, LOW); 00252 digitalWrite(DMD_PIN_LATCH, LOW); 00253 digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW); 00254 digitalWrite(DMD_PIN_SPI_MOSI, HIGH); 00255 } 00256 00260 DMD::~DMD() 00261 { 00262 if (fb0) 00263 free(fb0); 00264 if (fb1) 00265 free(fb1); 00266 fb = 0; // Don't free the buffer again in the base class. 00267 } 00268 00296 void DMD::setDoubleBuffer(bool doubleBuffer) 00297 { 00298 if (doubleBuffer != _doubleBuffer) { 00299 _doubleBuffer = doubleBuffer; 00300 if (doubleBuffer) { 00301 // Allocate a new back buffer. 00302 unsigned int size = _stride * _height; 00303 fb1 = (uint8_t *)malloc(size); 00304 00305 // Clear the new back buffer and then switch to it, leaving 00306 // the current contents of fb0 on the screen. 00307 if (fb1) { 00308 memset(fb1, 0xFF, size); 00309 cli(); 00310 fb = fb1; 00311 displayfb = fb0; 00312 sei(); 00313 } else { 00314 // Failed to allocate the memory, so revert to single-buffered. 00315 _doubleBuffer = false; 00316 } 00317 } else if (fb1) { 00318 // Disabling double-buffering, so forcibly switch to fb0. 00319 cli(); 00320 fb = fb0; 00321 displayfb = fb0; 00322 sei(); 00323 00324 // Free the unnecessary buffer. 00325 free(fb1); 00326 fb1 = 0; 00327 } 00328 } 00329 } 00330 00345 void DMD::swapBuffers() 00346 { 00347 if (_doubleBuffer) { 00348 // Turn off interrupts while swapping buffers so that we don't 00349 // accidentally try to refresh() in the middle of this code. 00350 cli(); 00351 if (fb == fb0) { 00352 fb = fb1; 00353 displayfb = fb0; 00354 } else { 00355 fb = fb0; 00356 displayfb = fb1; 00357 } 00358 sei(); 00359 } 00360 } 00361 00378 void DMD::swapBuffersAndCopy() 00379 { 00380 swapBuffers(); 00381 if (_doubleBuffer) 00382 memcpy(fb, displayfb, _stride * _height); 00383 } 00384 00402 void DMD::loop() 00403 { 00404 unsigned long currentTime = millis(); 00405 if ((currentTime - lastRefresh) >= DMD_REFRESH_MS) { 00406 lastRefresh = currentTime; 00407 refresh(); 00408 } 00409 } 00410 00411 // Send a single byte via SPI. 00412 static inline void spiSend(byte value) 00413 { 00414 SPDR = value; 00415 while (!(SPSR & _BV(SPIF))) 00416 ; // Wait for the transfer to complete. 00417 } 00418 00419 // Flip the bits in a byte. Table generated by genflip.c 00420 static const uint8_t flipBits[256] PROGMEM = { 00421 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 00422 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 00423 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 00424 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 00425 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 00426 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 00427 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 00428 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 00429 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 00430 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 00431 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 00432 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 00433 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 00434 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 00435 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 00436 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 00437 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 00438 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 00439 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 00440 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 00441 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 00442 0x3F, 0xBF, 0x7F, 0xFF 00443 }; 00444 00460 void DMD::refresh() 00461 { 00462 // Bail out if there is a conflict on the SPI bus. 00463 if (!digitalRead(DMD_PIN_SPI_SS)) 00464 return; 00465 00466 // Transfer the data for the next group of interleaved rows. 00467 int stride4 = _stride * 4; 00468 uint8_t *data0; 00469 uint8_t *data1; 00470 uint8_t *data2; 00471 uint8_t *data3; 00472 bool flipRow = ((_height & 0x10) == 0); 00473 for (int y = 0; y < _height; y += 16) { 00474 if (!flipRow) { 00475 // The panels in this row are the right way up. 00476 data0 = displayfb + _stride * (y + phase); 00477 data1 = data0 + stride4; 00478 data2 = data1 + stride4; 00479 data3 = data2 + stride4; 00480 for (int x = _stride; x > 0; --x) { 00481 spiSend(*data3++); 00482 spiSend(*data2++); 00483 spiSend(*data1++); 00484 spiSend(*data0++); 00485 } 00486 flipRow = true; 00487 } else { 00488 // The panels in this row are upside-down and reversed. 00489 data0 = displayfb + _stride * (y + 16 - phase) - 1; 00490 data1 = data0 - stride4; 00491 data2 = data1 - stride4; 00492 data3 = data2 - stride4; 00493 for (int x = _stride; x > 0; --x) { 00494 spiSend(pgm_read_byte(&(flipBits[*data3--]))); 00495 spiSend(pgm_read_byte(&(flipBits[*data2--]))); 00496 spiSend(pgm_read_byte(&(flipBits[*data1--]))); 00497 spiSend(pgm_read_byte(&(flipBits[*data0--]))); 00498 } 00499 flipRow = false; 00500 } 00501 } 00502 00503 // Latch the data from the shift registers onto the actual display. 00504 digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW); 00505 digitalWrite(DMD_PIN_LATCH, HIGH); 00506 digitalWrite(DMD_PIN_LATCH, LOW); 00507 if (phase & 0x02) 00508 digitalWrite(DMD_PIN_PHASE_MSB, HIGH); 00509 else 00510 digitalWrite(DMD_PIN_PHASE_MSB, LOW); 00511 if (phase & 0x01) 00512 digitalWrite(DMD_PIN_PHASE_LSB, HIGH); 00513 else 00514 digitalWrite(DMD_PIN_PHASE_LSB, LOW); 00515 digitalWrite(DMD_PIN_OUTPUT_ENABLE, HIGH); 00516 phase = (phase + 1) & 0x03; 00517 } 00518 00545 void DMD::enableTimer1() 00546 { 00547 // Number of CPU cycles in the display's refresh period. 00548 unsigned long numCycles = (F_CPU / 2000000) * DMD_REFRESH_US; 00549 00550 // Determine the prescaler to be used. 00551 #define TIMER1_RESOLUTION 65536UL 00552 uint8_t prescaler; 00553 if (numCycles < TIMER1_RESOLUTION) { 00554 // No prescaling required. 00555 prescaler = _BV(CS10); 00556 } else if (numCycles < TIMER1_RESOLUTION * 8) { 00557 // Prescaler = 8. 00558 prescaler = _BV(CS11); 00559 numCycles >>= 3; 00560 } else if (numCycles < TIMER1_RESOLUTION * 64) { 00561 // Prescaler = 64. 00562 prescaler = _BV(CS11) | _BV(CS10); 00563 numCycles >>= 6; 00564 } else if (numCycles < TIMER1_RESOLUTION * 256) { 00565 // Prescaler = 256. 00566 prescaler = _BV(CS12); 00567 numCycles >>= 8; 00568 } else if (numCycles < TIMER1_RESOLUTION * 1024) { 00569 // Prescaler = 1024. 00570 prescaler = _BV(CS12) | _BV(CS10); 00571 numCycles >>= 10; 00572 } else { 00573 // Too long, so set the maximum timeout. 00574 prescaler = _BV(CS12) | _BV(CS10); 00575 numCycles = TIMER1_RESOLUTION - 1; 00576 } 00577 00578 // Configure Timer1 for the period we want. 00579 TCCR1A = 0; 00580 TCCR1B = _BV(WGM13); 00581 uint8_t saveSREG = SREG; 00582 cli(); 00583 ICR1 = numCycles; 00584 SREG = saveSREG; // Implicit sei() if interrupts were on previously. 00585 TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | prescaler; 00586 00587 // Turn on the Timer1 overflow interrupt. 00588 TIMSK1 |= _BV(TOIE1); 00589 } 00590 00596 void DMD::disableTimer1() 00597 { 00598 // Turn off the Timer1 overflow interrupt. 00599 TIMSK1 &= ~_BV(TOIE1); 00600 } 00601 00612 DMD::Color DMD::fromRGB(uint8_t r, uint8_t g, uint8_t b) 00613 { 00614 if (r || g || b) 00615 return White; 00616 else 00617 return Black; 00618 }