Parola for Arduino  1.0
Text effects for Parola modular hardware
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);
46  displayClear();
47 
48  // Now set the default viewing parameters for this library
49  _D.setFont(MD_MAX72XX::SYS_VAR);
50 }
51 
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 
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 
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 }