Minor fixes.
[bertos.git] / bertos / drv / led_7seg.c
1 /**
2  * \file led_7seg.c
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief 7 segments LED displays (impl)
33  *
34  * \author Fabio Bizzi <fbizzi@bizzi.org>
35  *
36  * \addtogroup SevenSegDisplay 7 Segments LED Displays Driver
37  * \{
38  *
39  */
40
41 #include "drv/led_7seg.h"
42 #include "hw/hw_led_7seg.h"
43 #include "cfg/cfg_arch.h"
44 #include <drv/timer.h>
45 #include <string.h>
46
47 /*
48  * Define the timer for the refreshing
49  */
50 static Timer sseg_trefresh;
51
52 /*
53  * FUNCTION: sseg_refresh
54  *
55  * This is the procedure that prints the seven_seg structure'string to the display.
56  * It prints a single digit at time and does all the checks to a proper display.
57  * It is called by the wrapper function fired by the timer set in the init procedure.
58  * 
59  * param SS The void pointer that holds the pointer to the data structure
60  *
61  */
62 static void sseg_refresh(SevenSeg *SS)
63 {
64         /* First Check if the structure is being edited we do a silent exit*/
65         if (SS->busyedit == true)
66                 return;
67
68         /* If we have displayed all the digits */
69         if (SS->curdigit == CONFIG_LED_7SEG_DIGIT)
70         {
71                 sseg_off();
72                 /* And if we have to display again all the digit */
73                 if (SS->curspeed > 0)
74                 {
75                         SS->curspeed--;
76                         SS->curdigit = 0;
77                         SS->curpos -= CONFIG_LED_7SEG_DIGIT;
78                 }
79                 /* Else is time to scroll the text */
80                 else
81                 {
82                         /* If we aren't at the end of the string */
83                         if (SS->curpos < SS->string_len)
84                         {
85                                 SS->curpos -= CONFIG_LED_7SEG_DIGIT;
86                                 SS->curpos++;
87                                 SS->curdigit = 0;
88                                 SS->curspeed = SS->speed;
89                         }
90                         /* Else we are at the end of the string */
91                         else
92                         {
93                                 /* Set that the string was displayed at least one time and we can accept a new text*/
94                                 SS->firstrun = false;
95                                 /* If the string has to be displayed only one time we set an empty string
96                                  * to display till next print request*/
97                                 if (SS->runonce == true)
98                                         sevenseg_clear(SS);
99                                 /* Else we reset the text to it's first character ad do again the display */
100                                 else
101                                 {
102                                         SS->curdigit = 0;
103                                         SS->curpos = 0;
104                                         SS->curspeed = SS->speed;
105                                 }
106                         }
107                 }
108         }
109         /* Else We have to do a print*/
110         else
111         {
112                 /* If the text doesn't have to blink we write the character to the proper disply's digit */
113                 if (SS->blink == false)
114                         sseg_on(SS->string[SS->curpos], SS->curdigit);
115                 /* Else we do the blinking */
116                 else
117                 {
118                         /* If bdigit == 0 we have to blink all the digits */
119                         if (SS->bdigit == 0)
120                         {
121                                 if (SS->curspeed >= (SS->speed/2))
122                                         sseg_on(SS->string[SS->curpos], SS->curdigit);
123                                 else
124                                         sseg_off();
125                         }
126                         /* else we have to blink only one digit (bdigit -1) */
127                         else
128                                 /* is this the digit to blink? */
129                                 if (SS->curdigit == ((unsigned int)SS->bdigit-1))
130                                 {
131                                         if (SS->curspeed >= (SS->speed/2))
132                                                 sseg_on(SS->string[SS->curpos], SS->curdigit);
133                                         else
134                                                 sseg_off();
135                                 }
136                                 /* no, so let do a normal print */
137                                 else
138                                         sseg_on(SS->string[SS->curpos], SS->curdigit);
139                 }
140                 /* Ok, next time next char.... ;) */
141                 SS->curdigit++;
142                 SS->curpos++;
143         }
144 }
145
146 /*
147  * FUNCTION: sseg_refresh_wrapper
148  *
149  * This is a "wrapper" procedure that is called by the timer_setSoftint()
150  * with the unique purpose to call the real sseg_refresh procedure without
151  * the cast of the structure from void to SevenSeg.
152  *
153  * param VSS The void pointer that holds the pointer to the data structure
154  *
155  */
156 static void sseg_refresh_wrapper(void *VSS)
157 {
158         /* Here we cast the Structure from void to proper type */
159         SevenSeg *SS;
160         SS = (SevenSeg *)VSS;
161         /* now we call the right refresh routine */
162         sseg_refresh(SS);
163         /* ReStart the timer */
164         timer_add(&sseg_trefresh);
165 }
166
167 /*
168  * FUNCTION: sseg_tabcheck
169  *
170  * This function return the position of the ascii character in the hex
171  * segstable.
172  *
173  * param source The ascii char to be positioned
174  */
175 INLINE uint8_t sseg_tabcheck(char source)
176 {
177         uint8_t hexchar=38;
178
179         /* Numbers */
180         if ((((int)source) > 47) && (((int)source) < 58))
181                 hexchar = source-48;
182         else
183                 /* Capital Letters */
184                 if ((((int)source) > 64) && (((int)source) < 91))
185                         hexchar = source-53;
186                 else
187                         /* Letters */
188                         if ((((int)source) > 96) && (((int)source) < 123))
189                                 hexchar = source-85;
190                         else
191                                 /* Minus */
192                                 if (((int)source) == 45)
193                                         hexchar = 11;
194                                 else
195                                         /* Space */
196                                         if (((int)source) == 32)
197                                                 hexchar = 38;
198                                         else
199                                                 /* Dot */
200                                                 if (((int)source) == 46)
201                                                         hexchar = 10;
202         return hexchar;
203 }
204
205 /**
206  * \brief Print a string on the display
207  *
208  * This is the procedure that fills the seven_seg structure with the translated
209  * string to display. It swaps also the structures to display the new text when
210  * all the data is ready to display.
211  *
212  * \param SS Pointer to the SevenSeg structure
213  * \param sstring String to be displayed
214  *
215  *  \return 0 if all went well, -1 if the display is locked, -2 if the string too long.
216  */
217 int sevenseg_print(SevenSeg *SS, const char *sstring)
218 {
219         size_t string_lenght;
220         unsigned int x,y,dotnumber;
221         bool dotjump = false;
222         uint8_t hexchar;
223
224         /* Check if the display is unlocked */
225         if (SS->busyedit == false)
226                 return -1;
227
228         /* Check if the string is too big */
229         if (sizeof(&sstring) > (CONFIG_LED_7SEG_STRLEN-(2*CONFIG_LED_7SEG_DIGIT)))
230                 return -2;
231
232         /* get the string length and set the number of dots in the string to 0 */
233         string_lenght = strlen(sstring);
234         dotnumber = 0;
235
236         /* check if there are some dots in the string and report the number in dotnumber */
237         for (x=0;x<(unsigned int)string_lenght;x++)
238         {
239                 if (((int)sstring[x]) == 46)
240                         dotnumber++;
241         }
242
243         /* If the *REAL* lenght of the string is less or equal than the number of digits */
244         if (((int)string_lenght-dotnumber) <= CONFIG_LED_7SEG_DIGIT)
245         {
246                 /* If the *REAL* lenght of the string is less than number of digits */
247                 if (((int)string_lenght-dotnumber) < CONFIG_LED_7SEG_DIGIT)
248                 {
249                         /* Fill the left side of the string with blanks */
250                         for (x=0; x<(CONFIG_LED_7SEG_DIGIT-((int)string_lenght-dotnumber)); x++)
251                                 SS->string[x] = segstable[38];
252                         y = x;
253                 }
254                 else
255                 {
256                         /* Else we have the exact string length of the display */
257                         y = 0;
258                 }
259         }
260         else
261         {
262                 /* Else we have the string length bigger than the display and we need to fill
263                  * the entire left side of the string with blanks to begin the scroll from the
264                  * rigthest side of the display */
265                 for (x=0; x<CONFIG_LED_7SEG_DIGIT; x++)
266                         SS->string[x] = segstable[38];
267                 y = CONFIG_LED_7SEG_DIGIT;
268         }
269         /* Here we start to fill the string with the Hex 7seg characters values */
270         hexchar = 0;
271         for (x=0; x<(unsigned int)string_lenght; x++)
272         {
273                 hexchar = sseg_tabcheck(sstring[x]);
274                 /* do we have a dot? */
275                 if (hexchar == 10)
276                 {
277                         /* If we are at the first character of the string it has to be forced
278                          * as "lonly" dot ;) */
279                         if (x > 0)
280                         {
281 #if CONFIG_LED_7SEG_CCAT
282                                 SS->string[y-1] = SS->string[y-1] | segstable[(int)hexchar];
283 #else
284                                 SS->string[y-1] = SS->string[y-1] & segstable[(int)hexchar];
285 #endif
286                                 dotjump = true;
287                         }
288                 }
289                 /* If the last character was a dot and we aren't at the first character of the string
290                  * we have just inserted it */
291                 if (dotjump)
292                         dotjump = false;
293                 /* Let's put the character in the structure's string */
294                 else
295                 {
296                         SS->string[y] = segstable[(int)hexchar];
297                         y++;
298                 }
299         }
300         /* If we have the string length bigger than the display we need to fill
301          * the entire right side of the string with blanks to end the scroll
302          * to the rigthest side of the display */
303         if (((int)string_lenght-dotnumber) > CONFIG_LED_7SEG_DIGIT)
304         {
305                 for (x=0; x<CONFIG_LED_7SEG_DIGIT; x++)
306                 {
307                         SS->string[y] = segstable[38];
308                         y++;
309                 }
310         }
311         /* Let's put the total string length to the structure */
312         SS->string_len = y;
313
314         return 0;
315 }
316
317 /**
318  * \brief initialize the structure and the timer for the display
319  *
320  * This is the procedure that inits all the structures that rules the 7 segments
321  * display and set the timer for the proper print/refresh of the text.
322  *
323  *  \param SS Pointer to the SevenSeg structure
324  */
325 void sevenseg_init(SevenSeg *SS)
326 {
327         /*
328          * Init the 7segments string structure
329          */
330         SS->busyedit = true;
331         sevenseg_clear(SS);
332         SS->busyedit = false;
333
334         /*
335          * Init the I/O ports and set the display OFF
336          */
337         sseg_init();
338
339         /*
340          * Define the timer for the refresh of the display
341          * The timer calls the sevenseg_refresh function
342          * every "CONFIG_LED_7SEG_RTIME" milliseconds for
343          * an acceptable persistance of a single 7 segments
344          * display.
345          */
346         // set the callback
347         timer_setSoftint(&sseg_trefresh, sseg_refresh_wrapper, (void *)SS);
348         // expire time: 1s
349         timer_setDelay(&sseg_trefresh, ms_to_ticks(CONFIG_LED_7SEG_RTIME));
350         // start the timer
351         timer_add(&sseg_trefresh);
352 }
353
354 /**
355  * \brief check if is possible to do a new print
356  *
357  * This is the procedure that check if the print of the current text is run
358  * almost one time and we're ready to print a new text.
359  *
360  *  \param SS Pointer to the SevenSeg structure
361  *
362  *  \return true if we can print a new text, false if we're still printing the previous text for the first time and we have to wait.
363  */
364 bool sevenseg_isReady(SevenSeg *SS)
365 {
366                 return !SS->firstrun;
367 }
368
369 /**
370  * \brief unlock the SevenSeg structure and stops the print
371  *
372  * This is the procedure that check if the print of the current text is run
373  * almost one time and then set the status of the display to "unlocked".
374  *
375  *  \param SS Pointer to the SevenSeg structure
376  *
377  *  \return true if the display is unlocked, false if the dispaly is still locked.
378  */
379 bool sevenseg_unlock(SevenSeg *SS)
380 {
381         if (SS->firstrun == false)
382         {
383                 SS->busyedit = true;
384                 SS->firstrun = true;
385                 SS->curdigit = 0;
386                 SS->curpos = 0;
387         }
388         else
389                 return false;
390         return true;
391 }
392
393 /**
394  * \brief lock the SeveSeg structure and starts a new print
395  *
396  * This is the procedure that lock the display and permit
397  * the print of the text.
398  *
399  *  \param SS Pointer to the SevenSeg structure
400  *
401  *  \return true if the display is now locked, false if the display was already locked.
402  */
403 bool sevenseg_lock(SevenSeg *SS)
404 {
405         if (SS->busyedit == true)
406         {
407         /* If the string is longer than the number of the digit of the display we
408          * reset the single digit blink to zero to be sure that the display of
409          * the text is clean from previous single digit blinking settings */
410                 if (SS->string_len > CONFIG_LED_7SEG_DIGIT)
411                         SS->bdigit = 0;
412                 SS->busyedit = false;
413         }
414         else
415                 return false;
416         return true;
417 }
418
419 /**
420  * \brief set the blinking of the digits of the display
421  *
422  * This is the procedure that set the blinking of the display.
423  * You can choose to blink all the display or only a single
424  * digit.
425  *
426  *  \param SS Pointer to the SevenSeg structure
427  *  \param blink if true the display will blink
428  *  \param digit if 0 all the digits have to blink, else the digit that has to blink
429  *
430  *  \return true if the set was succesfull, false if the set was not succesfull.
431  */
432 bool sevenseg_setBlink(SevenSeg *SS, bool blink, uint8_t digit)
433 {
434         if (SS->busyedit == true)
435         {
436                 if (blink == true)
437                 {
438                         if (digit == 0)
439                                 SS->bdigit = digit;
440                         else
441                                 if ((digit-1) <= CONFIG_LED_7SEG_DIGIT)
442                                         SS->bdigit = digit;
443                                 else
444                                         return false;
445                 }
446                 SS->blink = blink;
447         }
448         else
449                 return false;
450         return true;
451 }
452
453 /**
454  * \brief set if the text has to be displayed just one time
455  *
456  * This is the procedure that set if the text has to be displayed
457  * just one time
458  *
459  *  \param SS Pointer to the SevenSeg structure
460  *  \param runonce true if the text has to be displayed only one time, false if the text has to be displayed till next print
461  *
462  *  \return true if the set was succesfull, false if the set was not succesfull.
463  */
464 bool sevenseg_setRunonce(SevenSeg *SS, bool runonce)
465 {
466         if (SS->busyedit == true)
467                 SS->runonce = runonce;
468         else
469                 return false;
470         return true;
471 }
472
473 /**
474  * \brief set the scrolling speed of the text
475  *
476  * This is the procedure that set the scrolling speed of the text
477  * if the text is longer than the display digits or the
478  * duration of the display if the text is smaller or equal the
479  * length of display digits.
480  *
481  *  \param SS Pointer to the SevenSeg structure
482  *  \param r_speed the Scrolling speed or display time
483  *
484  *  \return true if the set was succesfull, false if the set was not succesfull.
485  */
486 bool sevenseg_setRunspeed(SevenSeg *SS, unsigned int r_speed)
487 {
488         if (SS->busyedit == true)
489         {
490                 SS->speed = r_speed;
491                 SS->curspeed = r_speed;
492         }
493         else
494                 return false;
495         return true;
496 }
497
498 /**
499  * \brief clear the display
500  *
501  * This is the procedure that blanks the text to be displayed
502  * and so on clear the display.
503  *
504  *  \param SS Pointer to the SevenSeg structure
505  *
506  *  \return true if the clear was succesfull, false if the clear was not succesfull.
507  */
508 bool sevenseg_clear(SevenSeg *SS)
509 {
510         if (SS->busyedit == true)
511         {
512                 memset(((void *)&SS->string),segstable[38],sizeof(SS->string));
513                 SS->string_len = CONFIG_LED_7SEG_DIGIT;
514                 SS->blink = false;
515                 SS->bdigit = 0;
516                 SS->runonce = false;
517                 SS->curdigit = 0;
518                 SS->curpos = 0;
519                 SS->speed = CONFIG_LED_7SEG_SSPEED;
520                 SS->curspeed = CONFIG_LED_7SEG_SSPEED;
521                 SS->firstrun = false;
522         }
523         else
524                 return false;
525         return true;
526 }
527  /** \} */ //defgroup drivers