ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
DMD.cpp
1 /*
2  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "DMD.h"
24 #if defined(ARDUINO) && ARDUINO >= 100
25 #include <Arduino.h>
26 #else
27 #include <WProgram.h>
28 #endif
29 #include <pins_arduino.h>
30 #include <avr/io.h>
31 #include <avr/interrupt.h>
32 #include <string.h>
33 #include <stdlib.h>
34 
210 // Pins on the DMD connector board.
211 #define DMD_PIN_PHASE_LSB 6 // A
212 #define DMD_PIN_PHASE_MSB 7 // B
213 #define DMD_PIN_LATCH 8 // SCLK
214 #define DMD_PIN_OUTPUT_ENABLE 9 // nOE
215 #define DMD_PIN_SPI_SS SS // SPI Slave Select
216 #define DMD_PIN_SPI_MOSI MOSI // SPI Master Out, Slave In (R)
217 #define DMD_PIN_SPI_MISO MISO // SPI Master In, Slave Out
218 #define DMD_PIN_SPI_SCK SCK // SPI Serial Clock (CLK)
219 
220 // Dimension information for the display.
221 #define DMD_NUM_COLUMNS 32 // Number of columns in a panel.
222 #define DMD_NUM_ROWS 16 // Number of rows in a panel.
223 
224 // Refresh times.
225 #define DMD_REFRESH_MS 5
226 #define DMD_REFRESH_US 5000
227 
237 DMD::DMD(int widthPanels, int heightPanels)
238  : Bitmap(widthPanels * DMD_NUM_COLUMNS, heightPanels * DMD_NUM_ROWS)
239  , _doubleBuffer(false)
240  , phase(0)
241  , fb0(0)
242  , fb1(0)
243  , displayfb(0)
244  , lastRefresh(millis())
245 {
246  // Both rendering and display are to fb0 initially.
247  fb0 = displayfb = fb;
248 
249  // Initialize SPI to MSB-first, mode 0, clock divider = 2.
250  pinMode(DMD_PIN_SPI_SCK, OUTPUT);
251  pinMode(DMD_PIN_SPI_MOSI, OUTPUT);
252  pinMode(DMD_PIN_SPI_SS, OUTPUT);
253  digitalWrite(DMD_PIN_SPI_SCK, LOW);
254  digitalWrite(DMD_PIN_SPI_MOSI, LOW);
255  digitalWrite(DMD_PIN_SPI_SS, HIGH);
256  SPCR |= _BV(MSTR);
257  SPCR |= _BV(SPE);
258  SPCR &= ~(_BV(DORD)); // MSB-first
259  SPCR &= ~0x0C; // Mode 0
260  SPCR &= ~0x03; // Clock divider rate 2
261  SPSR |= 0x01; // MSB of clock divider rate
262 
263  // Initialize the DMD-specific pins.
264  pinMode(DMD_PIN_PHASE_LSB, OUTPUT);
265  pinMode(DMD_PIN_PHASE_MSB, OUTPUT);
266  pinMode(DMD_PIN_LATCH, OUTPUT);
267  pinMode(DMD_PIN_OUTPUT_ENABLE, OUTPUT);
268  digitalWrite(DMD_PIN_PHASE_LSB, LOW);
269  digitalWrite(DMD_PIN_PHASE_MSB, LOW);
270  digitalWrite(DMD_PIN_LATCH, LOW);
271  digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW);
272  digitalWrite(DMD_PIN_SPI_MOSI, HIGH);
273 }
274 
279 {
280  if (fb0)
281  free(fb0);
282  if (fb1)
283  free(fb1);
284  fb = 0; // Don't free the buffer again in the base class.
285 }
286 
314 void DMD::setDoubleBuffer(bool doubleBuffer)
315 {
316  if (doubleBuffer != _doubleBuffer) {
317  _doubleBuffer = doubleBuffer;
318  if (doubleBuffer) {
319  // Allocate a new back buffer.
320  unsigned int size = _stride * _height;
321  fb1 = (uint8_t *)malloc(size);
322 
323  // Clear the new back buffer and then switch to it, leaving
324  // the current contents of fb0 on the screen.
325  if (fb1) {
326  memset(fb1, 0xFF, size);
327  cli();
328  fb = fb1;
329  displayfb = fb0;
330  sei();
331  } else {
332  // Failed to allocate the memory, so revert to single-buffered.
333  _doubleBuffer = false;
334  }
335  } else if (fb1) {
336  // Disabling double-buffering, so forcibly switch to fb0.
337  cli();
338  fb = fb0;
339  displayfb = fb0;
340  sei();
341 
342  // Free the unnecessary buffer.
343  free(fb1);
344  fb1 = 0;
345  }
346  }
347 }
348 
364 {
365  if (_doubleBuffer) {
366  // Turn off interrupts while swapping buffers so that we don't
367  // accidentally try to refresh() in the middle of this code.
368  cli();
369  if (fb == fb0) {
370  fb = fb1;
371  displayfb = fb0;
372  } else {
373  fb = fb0;
374  displayfb = fb1;
375  }
376  sei();
377  }
378 }
379 
397 {
398  swapBuffers();
399  if (_doubleBuffer)
400  memcpy(fb, displayfb, _stride * _height);
401 }
402 
420 void DMD::loop()
421 {
422  unsigned long currentTime = millis();
423  if ((currentTime - lastRefresh) >= DMD_REFRESH_MS) {
424  lastRefresh = currentTime;
425  refresh();
426  }
427 }
428 
429 // Send a single byte via SPI.
430 static inline void spiSend(byte value)
431 {
432  SPDR = value;
433  while (!(SPSR & _BV(SPIF)))
434  ; // Wait for the transfer to complete.
435 }
436 
437 // Flip the bits in a byte. Table generated by genflip.c
438 static const uint8_t flipBits[256] PROGMEM = {
439  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
440  0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
441  0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4,
442  0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
443  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC,
444  0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
445  0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA,
446  0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
447  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6,
448  0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
449  0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1,
450  0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
451  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
452  0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
453  0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD,
454  0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
455  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3,
456  0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
457  0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7,
458  0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
459  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF,
460  0x3F, 0xBF, 0x7F, 0xFF
461 };
462 
479 {
480  // Bail out if there is a conflict on the SPI bus.
481  if (!digitalRead(DMD_PIN_SPI_SS))
482  return;
483 
484  // Transfer the data for the next group of interleaved rows.
485  int stride4 = _stride * 4;
486  uint8_t *data0;
487  uint8_t *data1;
488  uint8_t *data2;
489  uint8_t *data3;
490  bool flipRow = ((_height & 0x10) == 0);
491  for (int y = 0; y < _height; y += 16) {
492  if (!flipRow) {
493  // The panels in this row are the right way up.
494  data0 = displayfb + _stride * (y + phase);
495  data1 = data0 + stride4;
496  data2 = data1 + stride4;
497  data3 = data2 + stride4;
498  for (int x = _stride; x > 0; --x) {
499  spiSend(*data3++);
500  spiSend(*data2++);
501  spiSend(*data1++);
502  spiSend(*data0++);
503  }
504  flipRow = true;
505  } else {
506  // The panels in this row are upside-down and reversed.
507  data0 = displayfb + _stride * (y + 16 - phase) - 1;
508  data1 = data0 - stride4;
509  data2 = data1 - stride4;
510  data3 = data2 - stride4;
511  for (int x = _stride; x > 0; --x) {
512  spiSend(pgm_read_byte(&(flipBits[*data3--])));
513  spiSend(pgm_read_byte(&(flipBits[*data2--])));
514  spiSend(pgm_read_byte(&(flipBits[*data1--])));
515  spiSend(pgm_read_byte(&(flipBits[*data0--])));
516  }
517  flipRow = false;
518  }
519  }
520 
521  // Latch the data from the shift registers onto the actual display.
522  digitalWrite(DMD_PIN_OUTPUT_ENABLE, LOW);
523  digitalWrite(DMD_PIN_LATCH, HIGH);
524  digitalWrite(DMD_PIN_LATCH, LOW);
525  if (phase & 0x02)
526  digitalWrite(DMD_PIN_PHASE_MSB, HIGH);
527  else
528  digitalWrite(DMD_PIN_PHASE_MSB, LOW);
529  if (phase & 0x01)
530  digitalWrite(DMD_PIN_PHASE_LSB, HIGH);
531  else
532  digitalWrite(DMD_PIN_PHASE_LSB, LOW);
533  digitalWrite(DMD_PIN_OUTPUT_ENABLE, HIGH);
534  phase = (phase + 1) & 0x03;
535 }
536 
564 {
565  // Number of CPU cycles in the display's refresh period.
566  unsigned long numCycles = (F_CPU / 2000000) * DMD_REFRESH_US;
567 
568  // Determine the prescaler to be used.
569  #define TIMER1_RESOLUTION 65536UL
570  uint8_t prescaler;
571  if (numCycles < TIMER1_RESOLUTION) {
572  // No prescaling required.
573  prescaler = _BV(CS10);
574  } else if (numCycles < TIMER1_RESOLUTION * 8) {
575  // Prescaler = 8.
576  prescaler = _BV(CS11);
577  numCycles >>= 3;
578  } else if (numCycles < TIMER1_RESOLUTION * 64) {
579  // Prescaler = 64.
580  prescaler = _BV(CS11) | _BV(CS10);
581  numCycles >>= 6;
582  } else if (numCycles < TIMER1_RESOLUTION * 256) {
583  // Prescaler = 256.
584  prescaler = _BV(CS12);
585  numCycles >>= 8;
586  } else if (numCycles < TIMER1_RESOLUTION * 1024) {
587  // Prescaler = 1024.
588  prescaler = _BV(CS12) | _BV(CS10);
589  numCycles >>= 10;
590  } else {
591  // Too long, so set the maximum timeout.
592  prescaler = _BV(CS12) | _BV(CS10);
593  numCycles = TIMER1_RESOLUTION - 1;
594  }
595 
596  // Configure Timer1 for the period we want.
597  TCCR1A = 0;
598  TCCR1B = _BV(WGM13);
599  uint8_t saveSREG = SREG;
600  cli();
601  ICR1 = numCycles;
602  SREG = saveSREG; // Implicit sei() if interrupts were on previously.
603  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | prescaler;
604 
605  // Turn on the Timer1 overflow interrupt.
606  TIMSK1 |= _BV(TOIE1);
607 }
608 
615 {
616  // Turn off the Timer1 overflow interrupt.
617  TIMSK1 &= ~_BV(TOIE1);
618 }
619 
647 {
648  // Configure Timer2 for the period we want. With the prescaler set
649  // to 128, then 256 increments of Timer2 gives roughly 4 ms between
650  // overflows on a system with a 16 MHz clock. We adjust the prescaler
651  // accordingly for other clock frequencies.
652  TCCR2A = 0;
653  if (F_CPU >= 32000000)
654  TCCR2B = _BV(CS22) | _BV(CS21); // Prescaler = 256
655  else if (F_CPU >= 16000000)
656  TCCR2B = _BV(CS22) | _BV(CS20); // Prescaler = 128
657  else if (F_CPU >= 8000000)
658  TCCR2B = _BV(CS22); // Prescaler = 64
659  else
660  TCCR2B = _BV(CS21) | _BV(CS20); // Prescaler = 32
661 
662  // Reset Timer2 to kick off the process.
663  TCNT2 = 0;
664 
665  // Turn on the Timer2 overflow interrupt (also turn off OCIE2A and OCIE2B).
666  TIMSK2 = _BV(TOIE2);
667 }
668 
675 {
676  // Turn off the Timer2 overflow interrupt.
677  TIMSK2 &= ~_BV(TOIE2);
678 }
679 
690 DMD::Color DMD::fromRGB(uint8_t r, uint8_t g, uint8_t b)
691 {
692  if (r || g || b)
693  return White;
694  else
695  return Black;
696 }
void disableTimer1()
Disables Timer1 overflow interrupts.
Definition: DMD.cpp:614
void loop()
Performs regular display refresh activities from the application's main loop.
Definition: DMD.cpp:420
void disableTimer2()
Disables Timer2 overflow interrupts.
Definition: DMD.cpp:674
Represents a monochrome bitmap within main memory.
Definition: Bitmap.h:32
bool doubleBuffer() const
Returns true if the display is double-buffered; false if single-buffered. The default is false...
Definition: DMD.h:34
void swapBuffers()
Swaps the buffers that are used for rendering to the display.
Definition: DMD.cpp:363
void enableTimer1()
Enables Timer1 overflow interrupts for updating this display.
Definition: DMD.cpp:563
~DMD()
Destroys this dot matrix display handler.
Definition: DMD.cpp:278
uint8_t Color
Type that represents the color of a pixel in a bitmap.
Definition: Bitmap.h:40
void swapBuffersAndCopy()
Swaps the buffers that are used for rendering to the display and copies the former back buffer conten...
Definition: DMD.cpp:396
DMD(int widthPanels=1, int heightPanels=1)
Constructs a new dot matrix display handler for a display that is widthPanels x heightPanels in size...
Definition: DMD.cpp:237
void enableTimer2()
Enables Timer2 overflow interrupts for updating this display.
Definition: DMD.cpp:646
static Color fromRGB(uint8_t r, uint8_t g, uint8_t b)
Converts an RGB value into a pixel color value.
Definition: DMD.cpp:690
static const Color White
Color value corresponding to "white". If the bitmap is displayed on a LED array, then it may have a d...
Definition: Bitmap.h:45
void refresh()
Refresh the display.
Definition: DMD.cpp:478
void setDoubleBuffer(bool doubleBuffer)
Enables or disables double-buffering according to doubleBuffer.
Definition: DMD.cpp:314
static const Color Black
Color value corresponding to "black".
Definition: Bitmap.h:44