-
Notifications
You must be signed in to change notification settings - Fork 71
/
DMD2.h
315 lines (257 loc) · 10.1 KB
/
DMD2.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*--------------------------------------------------------------------------------------
DMD2.h - Arduino library for the Freetronics DMD, a 512 LED matrix display
panel arranged in a 32 x 16 layout.
Copyright (C) 2014 Freetronics, Inc. (info <at> freetronics <dot> com)
This is a non-backwards-compatible replacement for the original DMD library.
Updated by Angus Gratton, based on DMD by Marc Alexander.
---
This program is free software: you can redistribute it and/or modify it under the terms
of the version 3 GNU General Public License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DMD2_H
#define DMD2_H
#include "Print.h"
#include "SPI.h"
// Dimensions of a single display
const unsigned int PANEL_WIDTH = 32;
const unsigned int PANEL_HEIGHT = 16;
// Clamp a value between two limits
template<typename T> static inline void clamp(T &value, T lower, T upper) {
if(value < lower)
value = lower;
else if(value > upper)
value = upper;
}
// Swap A & B "in place" (well, with a temp variable!)
template<typename T> static inline void swap(T &a, T &b)
{
T tmp(a); a=b; b=tmp;
}
// Check a<=b, and swap them otherwise
template<typename T> static inline void ensureOrder(T &a, T &b)
{
if(b<a) swap(a,b);
}
extern const uint8_t DMD_Pixel_Lut[]; /* Lookup table for the DMD pixel locations */
enum DMDTestPattern {
PATTERN_ALT_0,
PATTERN_ALT_1,
PATTERN_STRIPE_0,
PATTERN_STRIPE_1
};
//Pixel/graphics writing modes
enum DMDGraphicsMode {
GRAPHICS_OFF, // unconditionally off (pixel turns off)
GRAPHICS_ON, //unconditionally on (pixel turns on, the usual default for drawing)
GRAPHICS_INVERSE, // on if was going to set to off
GRAPHICS_OR, // add to pixels already on
GRAPHICS_NOR, // subtract from pixels already on, don't turn any new ones on
GRAPHICS_XOR, // swap on/off state of pixels
GRAPHICS_NOOP // No-Op, ie don't actually change anything
};
// Return the inverse/"clear" version of the given mode
// ie for normal pixel-on modes, the "clear" is to turn off.
// for inverse mode, it's to turn on.
// for all other modes, this is kind of meaningless so we return a no-op
inline static DMDGraphicsMode inverseMode(DMDGraphicsMode mode) {
switch(mode) {
case GRAPHICS_ON:
return GRAPHICS_OFF;
case GRAPHICS_INVERSE:
return GRAPHICS_ON;
default:
return GRAPHICS_NOOP;
}
};
class DMD_TextBox;
/* DMDFrame is a class encapsulating a framebuffer for the DMD, and all the graphical
operations associated with it.
This allows you to implement double buffered/frame flipping, etc.
*/
class DMDFrame
{
friend class DMD_TextBox;
public:
DMDFrame(byte pixelsWide, byte pixelsHigh);
DMDFrame(const DMDFrame &source);
virtual ~DMDFrame();
// Set a single LED on or off
void setPixel(unsigned int x, unsigned int y, DMDGraphicsMode mode=GRAPHICS_ON);
// Get status of a single LED
bool getPixel(unsigned int x, unsigned int y);
// Move a region of pixels from one area to another
void movePixels(unsigned int from_x, unsigned int from_y,
unsigned int to_x, unsigned int to_y,
unsigned int width, unsigned int height);
// Extract a sub-region of the frame as a new frame
DMDFrame subFrame(unsigned int left, unsigned int top, unsigned int width, unsigned int height);
// Copy the contents of another frame back into this one at the given location
void copyFrame(DMDFrame &from, unsigned int left, unsigned int top);
// Fill the screen on or off
void fillScreen(bool on);
inline void clearScreen() { fillScreen(false); };
// the buffer passed must be large enough for all pixels plus a line ending
void debugPixelLine(unsigned int y, char *buf);
// Drawing primitives
void drawLine(int x1, int y1, int x2, int y2, DMDGraphicsMode mode=GRAPHICS_ON);
void drawCircle(unsigned int xCenter, unsigned int yCenter, int radius, DMDGraphicsMode mode=GRAPHICS_ON);
void drawBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode=GRAPHICS_ON);
void drawFilledBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode=GRAPHICS_ON);
void drawTestPattern(DMDTestPattern pattern);
// Text primitives
void selectFont(const uint8_t* font);
const inline uint8_t *getFont(void) { return font; }
int drawChar(const int x, const int y, const char letter, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL);
void drawString(int x, int y, const char *bChars, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL);
void drawString(int x, int y, const String &str, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL);
#if defined(__AVR__) || defined(ESP8266)
void drawString_P(int x, int y, const char *flashStr, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL);
inline void drawString(int x, int y, const __FlashStringHelper *flashStr, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL) {
return drawString_P(x,y,(const char*)flashStr, mode, font);
}
#endif
//Find the width of a character
int charWidth(const char letter, const uint8_t *font = NULL);
//Find the width of a string (width of all characters plus 1 pixel "kerning" between each character)
#if defined(__AVR__) || defined(ESP8266)
unsigned int stringWidth_P(const char *flashStr, const uint8_t *font = NULL);
inline unsigned int stringWidth(const __FlashStringHelper *flashStr, const uint8_t *font = NULL) {
return stringWidth_P((const char*)flashStr, font);
}
#endif
unsigned int stringWidth(const char *bChars, const uint8_t *font = NULL);
unsigned int stringWidth(const String &str, const uint8_t *font = NULL);
// Scrolling & marquee support
void scrollY(int scrollBy);
void scrollX(int scrollBy);
void marqueeScrollX(int scrollBy);
void marqueeScrollY(int scrollBy);
void swapBuffers(DMDFrame &other);
const byte width; // in pixels
const byte height; // in pixels
protected:
volatile uint8_t *bitmap;
byte row_width_bytes; // width in bitmap, bit-per-pixel rounded up to nearest byte
byte height_in_panels; // in panels
uint8_t *font;
inline size_t bitmap_bytes() {
// total bytes in the bitmap
return row_width_bytes * height;
}
inline size_t unified_width_bytes() {
// controller sees all panels as end-to-end, so bitmap arranges it that way
return row_width_bytes * height_in_panels;
}
inline int pixelToBitmapIndex(unsigned int x, unsigned int y) {
// Panels seen as stretched out in a row for purposes of finding index
uint8_t panel = (x/PANEL_WIDTH) + ((width/PANEL_WIDTH) * (y/PANEL_HEIGHT));
x = (x % PANEL_WIDTH) + (panel * PANEL_WIDTH);
y = y % PANEL_HEIGHT;
int res = x / 8 + (y * unified_width_bytes());
return res;
}
inline uint8_t pixelToBitmask(unsigned int x) {
int res = pgm_read_byte(DMD_Pixel_Lut + (x & 0x07));
return res;
}
template<typename T> inline void clamp_xy(T &x, T&y) {
clamp(x, (T)0, (T)width-1);
clamp(y, (T)0, (T)width-1);
}
};
class BaseDMD : public DMDFrame
{
protected:
BaseDMD(byte panelsWide, byte panelsHigh, byte pin_noe, byte pin_a, byte pin_b, byte pin_sck);
virtual void writeSPIData(volatile uint8_t *rows[4], const int rowsize) = 0;
public:
/* Refresh the display by manually scanning out current array of
pixels. Call often, or use begin()/end() to automatically scan
the display (see below.)
*/
virtual void scanDisplay();
/* Automatically start/stop scanning of the display output at
flicker-free speed. Setting this option will use Timer1 on
AVR-based Arduinos or Timer7 on Arduino Due.
*/
void begin();
void end();
/* Start display, but use manual scanning */
virtual void beginNoTimer();
inline void setBrightness(byte level) { this->brightness = level; };
protected:
volatile byte scan_row;
byte pin_noe;
byte pin_a;
byte pin_b;
byte pin_sck;
bool default_pins; // shortcut for default pin behaviour, can use macro writes
int8_t pin_other_cs; // CS pin to check before SPI behaviour, only makes sense for SPIDMD
uint8_t brightness;
};
class SPIDMD : public BaseDMD
{
public:
/* Create a single DMD display */
SPIDMD();
/* Create a DMD display using the default pinout, panelsWide x panelsHigh panels in size */
SPIDMD(byte panelsWide, byte panelsHigh);
/* Create a DMD display using a custom pinout for all the non-SPI pins (SPI pins set by hardware) */
SPIDMD(byte panelsWide, byte panelsHigh, byte pin_noe, byte pin_a, byte pin_b, byte pin_sck);
void beginNoTimer();
/* Set the "other CS" pin that is checked for in use before scanning the DMD */
void setOtherCS(byte pin_other_cs) { this->pin_other_cs = pin_other_cs; }
protected:
void writeSPIData(volatile uint8_t *rows[4], const int rowsize);
};
#ifdef ESP8266
// No SoftDMD for ESP8266 for now
#else
class SoftDMD : public BaseDMD
{
public:
SoftDMD(byte panelsWide, byte panelsHigh);
SoftDMD(byte panelsWide, byte panelsHigh, byte pin_noe, byte pin_a, byte pin_b, byte pin_sck,
byte pin_clk, byte pin_r_data);
void beginNoTimer();
protected:
void writeSPIData(volatile uint8_t *rows[4], const int rowsize);
private:
byte pin_clk;
byte pin_r_data;
};
#endif
class DMD_TextBox : public Print {
public:
DMD_TextBox(DMDFrame &dmd, int left = 0, int top = 0, int width = 0, int height = 0);
virtual size_t write(uint8_t);
void clear();
void reset();
void invertDisplay() { inverted = !inverted; }
void scrollY(int scrollBy);
void scrollX(int scrollBy);
private:
DMDFrame &dmd;
bool inverted;
int left;
int top;
int width;
int height;
int16_t cur_x;
int16_t cur_y;
bool pending_newline;
};
// Six byte header at beginning of FontCreator font structure, stored in PROGMEM
struct FontHeader {
uint16_t size;
uint8_t fixedWidth;
uint8_t height;
uint8_t firstChar;
uint8_t charCount;
};
#endif