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