added double buffering to prevent tearning
[Arduino] / Hub08_LedMatrix / LEDMatrix.cpp
1 /**
2  * LED Matrix library for http://www.seeedstudio.com/depot/ultrathin-16x32-red-led-matrix-panel-p-1582.html
3  * The LED Matrix panel has 32x16 pixels. Several panel can be combined together as a large screen.
4  *
5  * Coordinate & Connection (Arduino -> panel 0 -> panel 1 -> ...)
6  *   (0, 0)                                     (0, 0)
7  *     +--------+--------+--------+               +--------+--------+
8  *     |   5    |    4   |    3   |               |    1   |    0   |
9  *     |        |        |        |               |        |        |<----- Arduino
10  *     +--------+--------+--------+               +--------+--------+
11  *     |   2    |    1   |    0   |                              (64, 16)
12  *     |        |        |        |<----- Arduino
13  *     +--------+--------+--------+
14  *                             (96, 32)
15  *  Copyright (c) 2013 Seeed Technology Inc.
16  *  @auther     Yihui Xiong
17  *  @date       Nov 8, 2013
18  *  @license    MIT
19  */
20
21 #include "LEDMatrix.h"
22 #include "Arduino.h"
23
24 #if USE_SPI
25 #include <SPI.h>
26 #endif
27
28 #if 0
29 #define ASSERT(e)   if (!(e)) { Serial.println(#e); while (1); }
30 #else
31 #define ASSERT(e)
32 #endif
33
34 LEDMatrix::LEDMatrix(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t oe, uint8_t r1, uint8_t stb, uint8_t clk)
35 {
36     this->clk = clk;
37     this->r1 = r1;
38     this->stb = stb;
39     this->oe = oe;
40     this->a = a;
41     this->b = b;
42     this->c = c;
43     this->d = d;
44
45     mask = 0xff;
46     state = 0;
47 #if USE_SPI
48         SPI.begin();
49 #endif
50         buffer = 0;
51 }
52
53 void LEDMatrix::begin(uint8_t *displaybuf, uint16_t width, uint16_t height)
54 {
55     ASSERT(0 == (width % 32));
56     ASSERT(0 == (height % 16));
57
58     this->displaybuf = displaybuf;
59     this->width = width;
60     this->height = height;
61
62     pinMode(a, OUTPUT);
63     pinMode(b, OUTPUT);
64     pinMode(c, OUTPUT);
65     pinMode(d, OUTPUT);
66     pinMode(oe, OUTPUT);
67     pinMode(r1, OUTPUT);
68     pinMode(clk, OUTPUT);
69     pinMode(stb, OUTPUT);
70
71     state = 1;
72 }
73
74 void LEDMatrix::drawPoint(uint16_t x, uint16_t y, uint8_t pixel)
75 {
76     ASSERT(width > x);
77     ASSERT(height > y);
78
79     uint8_t *byte = displaybuf + x / 8 + y * width / 8;
80     uint8_t  bit = x % 8;
81
82     if (pixel) {
83         *byte |= 0x80 >> bit;
84     } else {
85         *byte &= ~(0x80 >> bit);
86     }
87 }
88
89 void LEDMatrix::drawRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t pixel)
90 {
91     for (uint16_t x = x1; x < x2; x++) {
92         for (uint16_t y = y1; y < y2; y++) {
93             drawPoint(x, y, pixel);
94         }
95     }
96 }
97
98 void LEDMatrix::drawImage(uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height, const uint8_t *image)
99 {
100     for (uint16_t y = 0; y < height; y++) {
101         for (uint16_t x = 0; x < width; x++) {
102             const uint8_t *byte = image + (x + y * width) / 8;
103             uint8_t  bit = 7 - x % 8;
104             uint8_t  pixel = (*byte >> bit) & 1;
105
106             drawPoint(x + xoffset, y + yoffset, pixel);
107         }
108     }
109 }
110
111 void LEDMatrix::clear()
112 {
113     uint8_t *ptr = displaybuf;
114     for (uint16_t i = 0; i < (width * height / 8); i++) {
115         *ptr = 0x00;
116         ptr++;
117     }
118 }
119
120 void LEDMatrix::reverse()
121 {
122     mask = ~mask;
123 }
124
125 uint8_t LEDMatrix::isReversed()
126 {
127     return mask;
128 }
129
130 void LEDMatrix::scan()
131 {
132     static uint8_t row = 0;  // from 0 to 15
133
134     if (!state) {
135         return;
136     }
137
138     uint8_t *head = displaybuf + row * (width / 8);
139
140     if ( buffer ) head += (width/8) * height;
141
142     for (uint8_t line = 0; line < (height / 16); line++) {
143         uint8_t *ptr = head;
144         head += width * 2;              // width * 16 / 8
145
146         for (uint8_t byte = 0; byte < (width / 8); byte++) {
147             uint8_t pixels = *ptr;
148             ptr++;
149             pixels = pixels ^ mask;     // reverse: mask = 0xff, normal: mask =0x00
150 #if USE_SPI
151                 SPI.transfer(pixels);
152 #else
153             for (uint8_t bit = 0; bit < 8; bit++) {
154                 digitalWrite(clk, LOW);
155                 digitalWrite(r1, pixels & (0x80 >> bit));
156                 digitalWrite(clk, HIGH);
157             }
158 #endif
159         }
160     }
161
162     // select row
163     digitalWrite(a, (row & 0x01));
164     digitalWrite(b, (row & 0x02));
165     digitalWrite(c, (row & 0x04));
166     digitalWrite(d, (row & 0x08));
167
168     digitalWrite(oe, HIGH);              // disable display
169
170     // latch data
171     digitalWrite(stb, LOW);
172     digitalWrite(stb, HIGH);
173     digitalWrite(stb, LOW);
174
175     digitalWrite(oe, LOW);              // enable display
176
177     row = (row + 1) & 0x0F;
178 }
179
180 uint8_t *LEDMatrix::offscreen_buffer()
181 {
182         uint8_t *buff = displaybuf;
183         if ( ! buffer ) buff += (width/8) * height;
184         return buff;
185 }
186
187 void LEDMatrix::swap()
188 {
189     buffer = ! buffer;
190 }
191
192 void LEDMatrix::on()
193 {
194     state = 1;
195 }
196
197 void LEDMatrix::off()
198 {
199     state = 0;
200     digitalWrite(oe, HIGH);
201 }