| Parola for Arduino 1.0 Text effects for Parola modular hardware

- Main Page
- Related Pages
- Classes
- Files

- File List

MD_Parola.cpp
1 /*
2 MD_Parola - Library for modular scrolling text and Effects
3
4 See header file for comments
5 This file contains methods to implement core functions
6
7 Copyright (C) 2013 Marco Colli. All rights reserved.
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <MD_Parola.h>
25 #include <MD_Parola_lib.h>
26 #include <MD_MAX72xx.h>
27
28 MD_Parola::MD_Parola(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices):
29 _D(dataPin, clkPin, csPin, numDevices),
30 _fsmState(END), _suspend(false),
31 _pText(NULL)
32 {
33 // Set up the MAX72XX library
34 // The MAX72XX is in power-saving mode on startup,
35 // we have to do a wakeup call, set the brightness, enable updates and clear the display
36 _D.control(MD_MAX72XX::SHUTDOWN, MD_MAX72XX::OFF);
37 _D.control(MD_MAX72XX::INTENSITY, MAX_INTENSITY/2);
38 _D.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
39
40 // initialise options in this library using methods supplied
41 setSpeed(10);
42 setPause(10*getSpeed());
43 setCharSpacing(1);
44 setTextAlignment(LEFT);
45 setTextEffect(PRINT, NO_EFFECT);
46 displayClear();
47
48 // Now set the default viewing parameters for this library
49 _D.setFont(MD_MAX72XX::SYS_VAR);
50 }
51
52 MD_Parola::~MD_Parola(void)
53 {
54 }
55
56 void MD_Parola::displayScroll(char *pText, textPosition_t align, textEffect_t effect, uint16_t speed)
57 {
58 setTextAlignment(align);
59 setSpeed(speed);
60 setPause(0);
61 setTextEffect(effect, effect);
62 setTextBuffer(pText);
63
64 displayReset();
65 }
66
67 void MD_Parola::displayText(char *pText, textPosition_t align, uint16_t speed, uint16_t pause, textEffect_t effectIn, textEffect_t effectOut)
68 {
69 setTextAlignment(align);
70 setSpeed(speed);
71 setPause(pause);
72 setTextEffect(effectIn, effectOut);
73 setTextBuffer(pText);
74
75 displayReset();
76 }
77
78 bool MD_Parola::displayAnimate(void)
79 {
80 // work through things that stop us running this at all
81 if (((_fsmState == PAUSE) && (millis() - _lastRunTime < _pauseTime)) ||
82 (millis() - _lastRunTime < _tickTime) ||
83 (_suspend))
84 return(false);
85
86 // save the time now, before we run the animation, so that the animation is part of the
87 // delay between animations giving more accurate frame timing.
88 _lastRunTime = millis();
89
90 // suspend the display while we animate a frame
91 _D.update(MD_MAX72XX::OFF);
92
93 // any text to display?
94 if (_pText != NULL)
95 {
96 switch (_fsmState)
97 {
98 case END: // do nothing in this state
99 PRINT_STATE("ANIMATE");
100 break;
101
102 case INITIALISE:
103 PRINT_STATE("ANIMATE");
104
105 setInitialConditions();
106 _moveIn = true;
107 // fall through to process the effect, first call will be with INITIALISE
108
109 default: // All state except END are handled by the special effect functions
110 switch (_moveIn ? _effectIn : _effectOut)
111 {
112 case PRINT: effectPrint(_moveIn); break;
113 case SLICE: effectSlice(_moveIn); break;
114 case WIPE: effectWipe(false, _moveIn); break;
115 case WIPE_CURSOR: effectWipe(true, _moveIn); break;
116 case OPENING: effectOpen(false, _moveIn); break;
117 case OPENING_CURSOR: effectOpen(true, _moveIn); break;
118 case CLOSING: effectClose(false, _moveIn); break;
119 case CLOSING_CURSOR: effectClose(true, _moveIn); break;
120 case BLINDS: effectBlinds(_moveIn); break;
121 case DISSOLVE: effectDissolve(_moveIn); break;
122 case SCROLL_UP:
123 case SCROLL_DOWN: effectVScroll((_moveIn ? _effectIn : _effectOut), _moveIn); break;
124 case SCROLL_LEFT:
125 case SCROLL_RIGHT: effectHScroll((_moveIn ? _effectIn : _effectOut), _moveIn); break;
126 default:
127 _fsmState = END;
128 }
129
130 // one way toggle for input to output, reset on Initialise
131 _moveIn = _moveIn && !(_fsmState == PAUSE);
132 break;
133 }
134 }
135
136 TIME_PROFILE("\nAnimation time ");
137
138 // re-enable and update the display
139 _D.update(MD_MAX72XX::ON);
140
141 TIME_PROFILE(": Cycle time ");
142
143 return(_fsmState == END);
144 }
145
146 void MD_Parola::displayReset(void)
147 {
148 _suspend = false;
149 _fsmState = INITIALISE;
150 }
151
152 void MD_Parola::setInitialConditions(void)
153 // set the global variables initial conditions for all display effects
154 {
155 PRINTS("\nsetInitialConditions");
156
157 if (_pText == NULL)
158 return;
159
160 _pCurChar = _pText;
161 _limitOverflow = !calcTextLimits(_pText);
162 }
163
164 uint16_t MD_Parola::getTextWidth(char *p)
165 // Get the width in columns for the text string passed to the function
166 // This is the sum of all the characters and the space betwen them.
167 {
168 uint16_t sum = 0;
169
170 PRINTS("\ngetTextWidth");
171
172 while (*p != '\0')
173 {
174 sum += _D.getChar(*p++, ARRAY_SIZE(_cBuf), _cBuf);
175 if (*p)
176 sum += _charSpacing;
177 }
178
179 PRINT(": W=", sum);
180
181 return(sum);
182 }
183
184 bool MD_Parola::calcTextLimits(char *p)
185 // Work out left and right sides for the text to be displayed,
186 // depending on the text alignment. If the message will not fit
187 // in the current display the return false, otherwise true.
188 {
189 bool b = true;
190 uint16_t displayWidth = _D.getColumnCount();
191
192 _textLen = getTextWidth(p);
193
194 PRINTS("\ncalcTextLimits");
195
196 switch (_textAlignment)
197 {
198 case LEFT:
199 _limitLeft = displayWidth-1;
200 if (_textLen > displayWidth)
201 {
202 _limitRight = 0;
203 b = false;
204 }
205 else
206 {
207 _limitRight = _limitLeft - _textLen;
208 }
209 break;
210
211 case RIGHT:
212 _limitRight = 0;
213 if (_textLen > displayWidth)
214 {
215 _limitLeft = displayWidth-1;
216 b = false;
217 }
218 else
219 {
220 _limitLeft = _limitRight + _textLen;
221 }
222 break;
223
224 case CENTER:
225 if (_textLen > displayWidth)
226 {
227 _limitLeft = displayWidth-1;
228 _limitRight = 0;
229 b= false;
230 }
231 else
232 {
233 _limitRight = (displayWidth - _textLen)/2;
234 _limitLeft = _limitRight + _textLen;
235 }
236 break;
237 }
238
239 PRINT(" L:", _limitLeft);
240 PRINT(" R:", _limitRight);
241 PRINT(" O:", !b);
242
243 return (b);
244 }
245
246 uint8_t MD_Parola::makeChar(char c)
247 // Load a character bitmap and add in trailing char spacing blanks
248 {
249 uint8_t len;
250
251 PRINTX("\nmakeChar 0x", c);
252
253 len = _D.getChar(c, ARRAY_SIZE(_cBuf), _cBuf);
254 for (uint8_t i = 0; i<_charSpacing; i++)
255 {
256 if (len < ARRAY_SIZE(_cBuf))
257 _cBuf[len++] = 0;
258 }
259
260 PRINT(", len=", len);
261
262 return(len);
263 }
264
265 uint8_t MD_Parola::reverseBuf(uint8_t *p, uint8_t size)
266 // reverse the elements of the specified buffer
267 // useful when we are scrolling right and want to insert the columns in reverse order
268 {
269 for (uint8_t i=0; i<size/2; i++)
270 {
271 uint8_t t;
272
273 t = p[i];
274 p[i] = p[size-1-i];
275 p[size-1-i] = t;
276 }
277 }
278
279 #define SFX(s) ((_moveIn && _effectIn == (s)) || (!_moveIn && _effectOut == (s)))
280
281 void MD_Parola::moveTextPointer(void)
282 // This method works when increment is done AFTER processing the character
283 // the _endOfText flag is set as a look ahead (ie, when the last character
284 // is still valid)
285 // We need to move a pointer forward or back, depending on the way we are
286 // travelling through the text buffer.
287 {
288 PRINTS("\nMovePtr");
289
290 if (SFX(SCROLL_RIGHT))
291 {
292 PRINTS(" --");
293 _endOfText = (_pCurChar == _pText);
294 _pCurChar--;
295 }
296 else
297 {
298 PRINTS(" ++");
299 _pCurChar++;
300 _endOfText = (*_pCurChar == '\0');
301 }
302
303 PRINT(": endOfText ", _endOfText);
304 }
305
306 uint8_t MD_Parola::getFirstChar(void)
307 // load the first char into the char buffer
308 // return 0 if there are no characters
309 {
310 uint8_t len = 0;
311
312 PRINTS("\ngetFirst");
313
314 // initialise pointers and make sure we have a good string to process
315 _pCurChar = _pText;
316 if ((_pCurChar == NULL) || (*_pCurChar == '\0'))
317 {
318 _endOfText = true;
319 return(0);
320 }
321 _endOfText = false;
322 if (SFX(SCROLL_RIGHT))
323 _pCurChar += strlen(_pText) - 1;
324
325 // good string, get the first char into the current buffer
326 len = makeChar(*_pCurChar);
327
328 if (SFX(SCROLL_RIGHT))
329 reverseBuf(_cBuf, len);
330
331 moveTextPointer();
332
333 return(len);
334 }
335
336 uint8_t MD_Parola::getNextChar(void)
337 // load the next char into the char buffer
338 // return 0 if there are no characters
339 {
340 uint8_t len = 0;
341
342 PRINTS("\ngetNext ");
343
344 if (_endOfText)
345 return(0);
346
347 len = makeChar(*_pCurChar);
348
349 if (SFX(SCROLL_RIGHT))
350 reverseBuf(_cBuf, len);
351
352 moveTextPointer();
353
354 return(len);
355 }