Skip to content

Commit 9c7f335

Browse files
facchinmpennam
authored andcommitted
Support Modulino LED Matrix
ATTENTION: the firmware included will ONLY work on led matrix modulinos
1 parent 85f22aa commit 9c7f335

File tree

4 files changed

+3408
-0
lines changed

4 files changed

+3408
-0
lines changed

src/Modulino_LED_Matrix.h

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#pragma once
2+
3+
#include "Arduino.h"
4+
#include "Wire.h"
5+
#include "gallery.h"
6+
7+
#define NUM_LEDS 96
8+
9+
#if __has_include("ArduinoGraphics.h")
10+
#include <ArduinoGraphics.h>
11+
#define MATRIX_WITH_ARDUINOGRAPHICS
12+
#endif
13+
14+
static uint32_t reverse(uint32_t x)
15+
{
16+
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
17+
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
18+
x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
19+
x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8);
20+
x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16);
21+
return x;
22+
}
23+
24+
// TODO: this is dangerous, use with care
25+
#define loadSequence(frames) loadWrapper(frames, sizeof(frames))
26+
#define renderBitmap(bitmap, rows, columns) loadPixels(&bitmap[0][0], rows*columns)
27+
#define endTextAnimation(scrollDirection, anim) endTextToAnimationBuffer(scrollDirection, anim ## _buf, sizeof(anim ## _buf), anim ## _buf_used)
28+
#define loadTextAnimationSequence(anim) loadWrapper(anim ## _buf, anim ## _buf_used)
29+
30+
#if defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_NANO_R4) || defined(ARDUINO_UNO_Q)
31+
#define DEFAULT_WIRE Wire1
32+
#else
33+
#define DEFAULT_WIRE Wire
34+
#endif
35+
36+
class ModulinoLEDMatrix
37+
#ifdef MATRIX_WITH_ARDUINOGRAPHICS
38+
: public ArduinoGraphics
39+
#endif
40+
{
41+
42+
public:
43+
ModulinoLEDMatrix(HardwareI2C& wire = DEFAULT_WIRE)
44+
#ifdef MATRIX_WITH_ARDUINOGRAPHICS
45+
: ArduinoGraphics(canvasWidth, canvasHeight)
46+
#endif
47+
{
48+
_wire = &wire;
49+
}
50+
// TODO: find a better name
51+
// autoscroll will be slower than calling next() at precise times
52+
void autoscroll(uint32_t interval_ms) {
53+
_interval = interval_ms;
54+
}
55+
int begin() {
56+
bool rv = true;
57+
_wire->begin();
58+
return rv;
59+
}
60+
void next() {
61+
uint32_t frame[3];
62+
frame[0] = reverse(*(_frames+(_currentFrame*4)+0));
63+
frame[1] = reverse(*(_frames+(_currentFrame*4)+1));
64+
frame[2] = reverse(*(_frames+(_currentFrame*4)+2));
65+
_interval = *(_frames+(_currentFrame*4)+3);
66+
_currentFrame = (_currentFrame + 1) % _framesCount;
67+
if(_currentFrame == 0){
68+
if(!_loop){
69+
_interval = 0;
70+
}
71+
if(_callBack != nullptr){
72+
_callBack();
73+
}
74+
_sequenceDone = true;
75+
}
76+
_wire->beginTransmission(0x39);
77+
_wire->write((uint8_t*)frame, sizeof(frame));
78+
_wire->endTransmission();
79+
}
80+
void loadFrame(const uint32_t buffer[3]){
81+
uint32_t tempBuffer[][4] = {{
82+
buffer[0], buffer[1], buffer[2], 0
83+
}};
84+
loadSequence(tempBuffer);
85+
next();
86+
_interval = 0;
87+
}
88+
void renderFrame(uint8_t frameNumber){
89+
_currentFrame = frameNumber % _framesCount;
90+
next();
91+
_interval = 0;
92+
}
93+
void play(bool looping = false){
94+
_loop = looping;
95+
_sequenceDone = false;
96+
do {
97+
next();
98+
delay(_interval);
99+
} while (_sequenceDone == false);
100+
}
101+
bool sequenceDone(){
102+
if(_sequenceDone){
103+
_sequenceDone = false;
104+
return true;
105+
}
106+
return false;
107+
}
108+
109+
static void loadPixelsToBuffer(uint8_t* arr, size_t size, uint32_t* dst) {
110+
uint32_t partialBuffer = 0;
111+
uint8_t pixelIndex = 0;
112+
uint8_t *frameP = arr;
113+
uint32_t *frameHolderP = dst;
114+
while (pixelIndex < size) {
115+
partialBuffer |= *frameP++;
116+
if ((pixelIndex + 1) % 32 == 0) {
117+
*(frameHolderP++) = partialBuffer;
118+
}
119+
partialBuffer = partialBuffer << 1;
120+
pixelIndex++;
121+
}
122+
}
123+
124+
void loadPixels(uint8_t *arr, size_t size){
125+
loadPixelsToBuffer(arr, size, _frameHolder);
126+
loadFrame(_frameHolder);
127+
};
128+
129+
void loadWrapper(const uint32_t frames[][4], uint32_t howMany) {
130+
_currentFrame = 0;
131+
_frames = (uint32_t*)frames;
132+
_framesCount = (howMany / 4) / sizeof(uint32_t);
133+
}
134+
// WARNING: callbacks are fired from ISR. The execution time will be limited.
135+
void setCallback(voidFuncPtr callBack){
136+
_callBack = callBack;
137+
}
138+
139+
void clear() {
140+
const uint32_t fullOff[] = {
141+
0x00000000,
142+
0x00000000,
143+
0x00000000
144+
};
145+
loadFrame(fullOff);
146+
#ifdef MATRIX_WITH_ARDUINOGRAPHICS
147+
memset(_canvasBuffer, 0, sizeof(_canvasBuffer));
148+
#endif
149+
}
150+
151+
152+
#ifdef MATRIX_WITH_ARDUINOGRAPHICS
153+
virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
154+
if (y >= canvasHeight || x >= canvasWidth || y < 0 || x < 0) {
155+
return;
156+
}
157+
// the r parameter is (mis)used to set the character to draw with
158+
_canvasBuffer[y][x] = (r | g | b) > 0 ? 1 : 0;
159+
}
160+
161+
void endText(int scrollDirection = NO_SCROLL) {
162+
ArduinoGraphics::endText(scrollDirection);
163+
renderBitmap(_canvasBuffer, canvasHeight, canvasWidth);
164+
}
165+
166+
// display the drawing or capture it to buffer when rendering dynamic anymation
167+
void endDraw() {
168+
ArduinoGraphics::endDraw();
169+
170+
if (!captureAnimation) {
171+
renderBitmap(_canvasBuffer, canvasHeight, canvasWidth);
172+
} else {
173+
if (captureAnimationHowManyRemains >= 4) {
174+
loadPixelsToBuffer(&_canvasBuffer[0][0], sizeof(_canvasBuffer), captureAnimationFrame);
175+
captureAnimationFrame[3] = _textScrollSpeed;
176+
captureAnimationFrame += 4;
177+
captureAnimationHowManyRemains -= 16;
178+
}
179+
}
180+
}
181+
182+
void endTextToAnimationBuffer(int scrollDirection, uint32_t frames[][4], uint32_t howManyMax, uint32_t& howManyUsed) {
183+
captureAnimationFrame = &frames[0][0];
184+
captureAnimationHowManyRemains = howManyMax;
185+
186+
captureAnimation = true;
187+
ArduinoGraphics::textScrollSpeed(0);
188+
ArduinoGraphics::endText(scrollDirection);
189+
ArduinoGraphics::textScrollSpeed(_textScrollSpeed);
190+
captureAnimation = false;
191+
192+
howManyUsed = howManyMax - captureAnimationHowManyRemains;
193+
}
194+
195+
void textScrollSpeed(unsigned long speed) {
196+
ArduinoGraphics::textScrollSpeed(speed);
197+
_textScrollSpeed = speed;
198+
}
199+
200+
private:
201+
uint32_t* captureAnimationFrame = nullptr;
202+
uint32_t captureAnimationHowManyRemains = 0;
203+
bool captureAnimation = false;
204+
static const byte canvasWidth = 12;
205+
static const byte canvasHeight = 8;
206+
uint8_t _canvasBuffer[canvasHeight][canvasWidth] = {{0}};
207+
unsigned long _textScrollSpeed = 100;
208+
#endif
209+
210+
private:
211+
int _currentFrame = 0;
212+
uint32_t _frameHolder[3];
213+
uint32_t* _frames;
214+
uint32_t _framesCount;
215+
uint32_t _interval = 0;
216+
uint32_t _lastInterval = 0;
217+
bool _loop = false;
218+
bool _sequenceDone = false;
219+
voidFuncPtr _callBack = nullptr;
220+
HardwareI2C* _wire;
221+
};

src/TextAnimation.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
3+
#include "Arduino.h"
4+
5+
#if not __has_include("ArduinoGraphics.h")
6+
#error "TextAnimation work only when ArduinoGraphics is installed and used. Include ArduinoGraphics first."
7+
#endif
8+
9+
#define TEXT_ANIMATION_DECLARE(NAME, MAX_CHARS) \
10+
extern uint32_t NAME ## _buf[MAX_CHARS][4]; \
11+
extern uint32_t NAME ## _buf_used;
12+
13+
#define TEXT_ANIMATION_DEFINE(NAME, MAX_CHARS) \
14+
uint32_t NAME ## _buf[MAX_CHARS][4]; \
15+
uint32_t NAME ## _buf_used = 0;

0 commit comments

Comments
 (0)