ArduinoLibs
|
Handle large dot matrix displays composed of LED's. More...
#include <DMD.h>
Public Member Functions | |
DMD (int widthPanels=1, int heightPanels=1) | |
Constructs a new dot matrix display handler for a display that is widthPanels x heightPanels in size. | |
~DMD () | |
Destroys this dot matrix display handler. | |
bool | doubleBuffer () const |
Returns true if the display is double-buffered; false if single-buffered. The default is false. | |
void | setDoubleBuffer (bool doubleBuffer) |
Enables or disables double-buffering according to doubleBuffer. | |
void | swapBuffers () |
Swaps the buffers that are used for rendering to the display. | |
void | swapBuffersAndCopy () |
Swaps the buffers that are used for rendering to the display and copies the former back buffer contents to the new back buffer. | |
void | loop () |
Performs regular display refresh activities from the application's main loop. | |
void | refresh () |
Refresh the display. | |
void | enableTimer1 () |
Enables Timer1 overflow interrupts for updating this display. | |
void | disableTimer1 () |
Disables Timer1 overflow interrupts. | |
Static Public Member Functions | |
static Color | fromRGB (uint8_t r, uint8_t g, uint8_t b) |
Converts an RGB value into a pixel color value. |
Handle large dot matrix displays composed of LED's.
This class is designed for use with Freetronics Large Dot Matrix Displays. These displays have 512 LED's arranged in a 32x16 matrix and controlled by an SPI interface. The displays are available in red, blue, green, yellow, and white variations (for which this class always uses the constant White regardless of the physical color).
DMD inherits from Bitmap so that any of the drawing functions in that class can be used to draw directly to dot matrix displays. The following example initializes a single display panel and draws a rectangle and a circle into it at setup time:
#include <DMD.h> DMD display; void setup() { display.drawRect(5, 2, 27, 13); display.drawCircle(16, 8, 4); }
The display must be updated frequently from the application's main loop:
The loop() method simplifies updating the display from the application's main loop but it can sometimes be inconvenient to arrange for it to be called regularly, especially if the application wishes to use delay()
or delayMicroseconds()
.
DMD provides an asynchronous display update mechanism using Timer1 interrupts. The application turns on interrupts using enableTimer1() and then calls refresh() from the interrupt service routine:
#include <DMD.h> DMD display; ISR(TIMER1_OVF_vect) { display.refresh(); } void setup() { display.enableTimer1(); }
DMD can also be used with third-party timer libraries such as TimerOne:
#include <DMD.h> #include <TimerOne.h> DMD display; void refreshDisplay() { display.refresh(); } void setup() { Timer1.initialize(5000); Timer1.attachInterrupt(refreshDisplay); }
When using interrupts, the system can sometimes exhibit "tearing" artifacts where half-finished images are displayed because an interrupt fired in the middle of a screen update.
This problem can be alleviated using double buffering: all rendering is done to an off-screen buffer that is swapped onto the screen once it is ready for display. Rendering then switches to the other buffer that is now off-screen. The following example demonstrates this:
#include <DMD.h> DMD display; ISR(TIMER1_OVF_vect) { display.refresh(); } void setup() { display.setDoubleBuffer(true); display.enableTimer1(); } void loop() { updateDisplay(); display.swapBuffers(); delay(50); // Delay between frames. } void updateDisplay() { // Draw the new display contents into the off-screen buffer. display.clear(); ... }
The downside of double buffering is that it uses twice as much main memory to manage the contents of the screen.
Multiple panels can be daisy-chained together using ribbon cables. If there is a single row of panels, then they must be connected to the Arduino board as follows:
If there are multiple rows of panels, then alternating rows are flipped upside-down so that the short ribbon cables provided by Freetronics reach (this technique is thanks to Chris Debenham; see http://www.adebenham.com/category/arduino/dmd/ for more details):
This technique can be repeated for as many rows as required, with the bottom row always right-way-up:
DMD automatically takes care of flipping the data for panels in the alternating rows. No special action is required by the user except to physically connect the panels as shown and to initialize the DMD class appropriately:
#include <DMD.h> DMD display(4, 2); // 4 panels wide, 2 panels high
DMD::DMD | ( | int | widthPanels = 1 , |
int | heightPanels = 1 |
||
) | [explicit] |
void DMD::disableTimer1 | ( | ) |
Disables Timer1 overflow interrupts.
bool DMD::doubleBuffer | ( | ) | const [inline] |
Returns true if the display is double-buffered; false if single-buffered. The default is false.
void DMD::enableTimer1 | ( | ) |
Enables Timer1 overflow interrupts for updating this display.
The application must also provide an interrupt service routine for Timer1 that calls refresh():
#include <DMD.h> DMD display; ISR(TIMER1_OVF_vect) { display.refresh(); } void setup() { display.enableTimer1(); }
If timer interrupts are being used to update the display, then it is unnecessary to call loop().
DMD::Color DMD::fromRGB | ( | uint8_t | r, |
uint8_t | g, | ||
uint8_t | b | ||
) | [static] |
void DMD::loop | ( | ) |
void DMD::refresh | ( | ) |
Refresh the display.
This function must be called at least once every 5 milliseconds for smooth non-flickering update of the display. It is usually called by loop(), but can also be called in response to a timer interrupt.
If this function is called from an interrupt service routine, then it is recommended that double-buffering be enabled with setDoubleBuffer() to prevent "tearing" artifacts that result from simultaneous update of a single shared buffer.
void DMD::setDoubleBuffer | ( | bool | doubleBuffer | ) |
Enables or disables double-buffering according to doubleBuffer.
When double-buffering is enabled, rendering operations are sent to a memory buffer that isn't currently displayed on-screen. Once the application has completed the screen update, it calls swapBuffers() to display the current buffer and switch rendering to the other now invisible buffer.
Double-buffering is recommended if refresh() is being called from an interrupt service routine, to prevent "tearing" artifacts that result from simultaneous update of a single shared buffer.
This function will allocate memory for the extra buffer when doubleBuffer is true. If there is insufficient memory for the second screen buffer, then this class will revert to single-buffered mode.
void DMD::swapBuffers | ( | ) |
Swaps the buffers that are used for rendering to the display.
When doubleBuffer() is false, this function does nothing. Otherwise the front and back rendering buffers are swapped. See the description of setDoubleBuffer() for more information.
The new rendering back buffer will have undefined contents and will probably need to be re-inialized with clear() or fill() before drawing to it. The swapBuffersAndCopy() function can be used instead to preserve the screen contents from one frame to the next.
void DMD::swapBuffersAndCopy | ( | ) |
Swaps the buffers that are used for rendering to the display and copies the former back buffer contents to the new back buffer.
Normally when swapBuffers() is called, the new rendering back buffer will have undefined contents from two frames prior and must be cleared with clear() or fill() before writing new contents to it. This function instead copies the previous frame into the new rendering buffer so that it can be updated in-place.
This function is useful if the screen does not change much from one frame to the next. If the screen changes a lot between frames, then it is usually better to explicitly clear() or fill() the new back buffer.