ArduinoLibs
DMD.cpp
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 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator