Update preset.
[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         /* If no legal character is recognized return a "space" */
178         uint8_t hexchar=38;
179
180         /* Numbers */
181         if ((source > 47) && (source < 58))
182                 hexchar = source-48;
183         else
184                 /* Capital Letters */
185                 if ((source > 64) && (source < 91))
186                         hexchar = source-53;
187                 else
188                         /* Letters */
189                         if ((source > 96) && (source < 123))
190                                 hexchar = source-85;
191                         else
192                                 /* Minus */
193                                 if (source == 45)
194                                         hexchar = 11;
195                                 else
196                                         /* Space */
197                                         if (source == 32)
198                                                 hexchar = 38;
199                                         else
200                                                 /* Dot */
201                                                 if (source == 46)
202                                                         hexchar = 10;
203         return hexchar;
204 }
205
206 /*
207  * FUNCTION: sseg_digitbuild
208  *
209  * This function return the hex value of the graphic digit
210  * from a list of segments (ex. ACDP).
211  *
212  * param source The string of segments
213  */
214 INLINE uint8_t sseg_digitbuild(const char *gstring, size_t start, size_t stop)
215 {
216         /* the default "space" char returned if no "legal" segments recognized */
217         uint8_t graphdigit = SEGMENT_EMPTY;
218         size_t x;
219
220         /* Main cicle */
221         for (x=start;x<=stop;x++)
222         {
223                 switch (gstring[x])
224                 {
225                         case 'A':
226                         case 'a':
227                                                 SET_SEGMENT(graphdigit, SEGMENT_A);
228                                                 break;
229                         case 'B':
230                         case 'b':
231                                                 SET_SEGMENT(graphdigit, SEGMENT_B);
232                                                 break;
233                         case 'C':
234                         case 'c':
235                                                 SET_SEGMENT(graphdigit, SEGMENT_C);
236                                                 break;
237                         case 'D':
238                         case 'd':
239                                                 SET_SEGMENT(graphdigit, SEGMENT_D);
240                                                 break;
241                         case 'E':
242                         case 'e':
243                                                 SET_SEGMENT(graphdigit, SEGMENT_E);
244                                                 break;
245                         case 'F':
246                         case 'f':
247                                                 SET_SEGMENT(graphdigit, SEGMENT_F);
248                                                 break;
249                         case 'G':
250                         case 'g':
251                                                 SET_SEGMENT(graphdigit, SEGMENT_G);
252                                                 break;
253                         case 'P':
254                         case 'p':
255                                                 SET_SEGMENT(graphdigit, SEGMENT_P);
256                                                 break;
257                 }
258         }
259         return graphdigit;
260 }
261
262 /**
263  * \brief Print a string on the display
264  *
265  * This is the procedure that fills the seven_seg structure with the translated
266  * string to display.
267  *
268  * To print a string simply call the procedure with a text string as the second parameter.
269  * Unrecognized characters are printed as a space
270  *
271  * To print "graphic" digits you have to enter them as a sequence of segments between "<>"
272  * for example:
273  * \code
274  * sevenseg_print(display,"<bf>Test Graphic<bf>");
275  * \endcode
276  * This example will print "Test Graphic"
277  *
278  * This is the segments table:
279  * \code
280  *   ___
281  * F| A |B
282  *  |___|
283  *  | G |
284  * E|___|C oP
285  *    D
286  * \endcode
287  *
288  * \param SS Pointer to the SevenSeg structure
289  * \param sstring String to be displayed
290  *
291  *  \return 0 if all went well, -1 if the display is locked, -2 if the string too long -3 if the string is malformed.
292  */
293 int sevenseg_print(SevenSeg *SS, const char *sstring)
294 {
295         size_t x,y,string_lenght,dotnumber,graph_digit_num,bracket_num,startgraph,stopgraph;
296         bool dotjump = false;
297         uint8_t hexchar;
298
299         /* Check if the display is unlocked */
300         if (SS->busyedit == false)
301                 return -1;
302
303         /* Check if the string is too big */
304         if (sizeof(&sstring) > (CONFIG_LED_7SEG_STRLEN-(2*CONFIG_LED_7SEG_DIGIT)))
305                 return -2;
306
307         /* get the string length and set the number of dots and graphic in the string to 0 */
308         string_lenght = strlen(sstring);
309         dotnumber = 0;
310         graph_digit_num = 0;
311         bracket_num = 0;
312
313         /* check if there are some dots an graphics in the string and report the number in dotnumber and graphnumber */
314         for (x=0;x<string_lenght;x++)
315         {
316                 /* If the first char is a "lonely" dot it has to be counted as a character */
317                 if ((sstring[x] == '.') & (x > 0))
318                 {
319                         /* If the previuos charachter is not a dot or a space we have a dot that
320                          * has to be considered as a part of a previous character so we have to
321                          * to count it */
322                         if ((sstring[x-1] != '.') & (sstring[x-1] != ' '))
323                                 dotnumber++;
324                 }
325                 /* If we have a "<" or a ">" we have a graphic char */
326                 if (sstring[x] == '<')
327                 {
328                         bracket_num++;
329                         x++;
330                         while ((sstring[x] != '>') & (x<string_lenght))
331                         {
332                                 x++;
333                                 graph_digit_num++;
334                         }
335                                 bracket_num++;
336                 }
337         }
338         graph_digit_num+=bracket_num;
339         /* if graphoc is > 0 and is not pair we have a malformed string so exit with error */
340                 if (bracket_num > 0)
341                 {
342                         if (bracket_num & 1)
343                                 return -3;
344                         else
345                                 /* Set the number of characters that compose the graphic
346                                  * to remove from the string, we subtract the "real" number
347                                  * of graphic character to display */
348                                 graph_digit_num-=(bracket_num/2);
349                 }
350
351         /* If the *REAL* lenght of the string is less or equal than the number of digits */
352         if ((string_lenght-dotnumber-graph_digit_num) <= CONFIG_LED_7SEG_DIGIT)
353         {
354                 /* If the *REAL* lenght of the string is less than number of digits */
355                 if ((string_lenght-dotnumber-graph_digit_num) < CONFIG_LED_7SEG_DIGIT)
356                 {
357                         /* Fill the left side of the string with blanks */
358                         for (x=0; x<(CONFIG_LED_7SEG_DIGIT-(string_lenght-dotnumber-graph_digit_num)); x++)
359                                 SS->string[x] = segstable[38];
360                         y = x;
361                 }
362                 else
363                 {
364                         /* Else we have the exact string length of the display */
365                         y = 0;
366                 }
367         }
368         else
369         {
370                 /* Else we have the string length bigger than the display and we need to fill
371                  * the entire left side of the string with blanks to begin the scroll from the
372                  * rigthest side of the display */
373                 for (x=0; x<CONFIG_LED_7SEG_DIGIT; x++)
374                         SS->string[x] = segstable[38];
375                 y = CONFIG_LED_7SEG_DIGIT;
376         }
377         /* Here we start to fill the string with the Hex 7seg characters values */
378         hexchar = 0;
379         for (x=0; x<string_lenght; x++)
380         {
381                 /* check if the charcter is a graphic character delimiter */
382                 if (sstring[x] == '<')
383                 {
384                         x++;
385                         /* set the first character of the graphic "string" to be passed to sseg_digitbuild */
386                         startgraph = x;
387                         /* if we have an empty graphic char we return an error */
388                         if (sstring[x] == '>')
389                                 return -3;
390                         while (sstring[x] != '>')
391                                 x++;
392                         /* set the last character of the graphic "string" to be passed to sseg_digitbuild */
393                         stopgraph = x-1;
394                         /* if we have a graphic char bigger than 8 (all the segments of the digit) we return an error */
395                         if (stopgraph-startgraph > 7)
396                                 return -3;
397                         /* fill the destination string with the graphic digit returned by sseg_digitbuild */
398                         SS->string[y] = sseg_digitbuild(sstring,startgraph,stopgraph);
399                         /* set next digit */
400                         y++;
401                 }
402                 else
403                 {
404                         hexchar = sseg_tabcheck(sstring[x]);
405                         /* do we have a dot? */
406                         if (hexchar == 10)
407                         {
408                                 /* If we are at the first character of the string it has to be forced
409                                 * as "lonly" dot ;) */
410                                 if ((x > 0) & ((sstring[x-1] != '.') & (sstring[x-1] != ' ')))
411                                 {
412 #if CONFIG_LED_7SEG_CCAT
413                                         SS->string[y-1] = SS->string[y-1] | segstable[hexchar];
414 #else
415                                         SS->string[y-1] = SS->string[y-1] & segstable[hexchar];
416 #endif
417                                         dotjump = true;
418                                 }
419                         }
420                         /* If the last character was a dot and we aren't at the first character of the string
421                         * we have just inserted it */
422                         if (dotjump)
423                                 dotjump = false;
424                         /* Let's put the character in the structure's string */
425                         else
426                         {
427                                 SS->string[y] = segstable[hexchar];
428                                 y++;
429                         }
430                 }
431         }
432         /* If we have the string length bigger than the display we need to fill
433          * the entire right side of the string with blanks to end the scroll
434          * to the rigthest side of the display */
435         if ((string_lenght-dotnumber-graph_digit_num) > CONFIG_LED_7SEG_DIGIT)
436         {
437                 for (x=0; x<CONFIG_LED_7SEG_DIGIT; x++)
438                 {
439                         SS->string[y] = segstable[38];
440                         y++;
441                 }
442         }
443         /* Let's put the total string length to the structure */
444         SS->string_len = y;
445
446         return 0;
447 }
448
449 /**
450  * \brief initialize the structure and the timer for the display
451  *
452  * This is the procedure that inits all the structures that rules the 7 segments
453  * display and set the timer for the proper print/refresh of the text.
454  *
455  *  \param SS Pointer to the SevenSeg structure
456  */
457 void sevenseg_init(SevenSeg *SS)
458 {
459         /*
460          * Init the 7segments string structure
461          */
462         SS->busyedit = true;
463         sevenseg_clear(SS);
464         SS->busyedit = false;
465
466         /*
467          * Init the I/O ports and set the display OFF
468          */
469         sseg_init();
470
471         /*
472          * Define the timer for the refresh of the display
473          * The timer calls the sevenseg_refresh function
474          * every "CONFIG_LED_7SEG_RTIME" milliseconds for
475          * an acceptable persistance of a single 7 segments
476          * display.
477          */
478         // set the callback
479         timer_setSoftint(&sseg_trefresh, sseg_refresh_wrapper, (void *)SS);
480         // expire time: 1s
481         timer_setDelay(&sseg_trefresh, ms_to_ticks(CONFIG_LED_7SEG_RTIME));
482         // start the timer
483         timer_add(&sseg_trefresh);
484 }
485
486 /**
487  * \brief check if is possible to do a new print
488  *
489  * This is the procedure that check if the print of the current text is run
490  * almost one time and we're ready to print a new text.
491  *
492  *  \param SS Pointer to the SevenSeg structure
493  *
494  *  \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.
495  */
496 bool sevenseg_isReady(SevenSeg *SS)
497 {
498                 return !SS->firstrun;
499 }
500
501 /**
502  * \brief unlock the SevenSeg structure and stops the print
503  *
504  * This is the procedure that check if the print of the current text is run
505  * almost one time and then set the status of the display to "unlocked".
506  *
507  *  \param SS Pointer to the SevenSeg structure
508  *
509  *  \return true if the display is unlocked, false if the dispaly is still locked.
510  */
511 bool sevenseg_unlock(SevenSeg *SS)
512 {
513         if (SS->firstrun == false)
514         {
515                 SS->busyedit = true;
516                 SS->firstrun = true;
517                 SS->curdigit = 0;
518                 SS->curpos = 0;
519         }
520         else
521                 return false;
522         return true;
523 }
524
525 /**
526  * \brief lock the SeveSeg structure and starts a new print
527  *
528  * This is the procedure that lock the display and permit
529  * the print of the text.
530  *
531  *  \param SS Pointer to the SevenSeg structure
532  *
533  *  \return true if the display is now locked, false if the display was already locked.
534  */
535 bool sevenseg_lock(SevenSeg *SS)
536 {
537         if (SS->busyedit == true)
538         {
539         /* If the string is longer than the number of the digit of the display we
540          * reset the single digit blink to zero to be sure that the display of
541          * the text is clean from previous single digit blinking settings */
542                 if (SS->string_len > CONFIG_LED_7SEG_DIGIT)
543                         SS->bdigit = 0;
544                 SS->busyedit = false;
545         }
546         else
547                 return false;
548         return true;
549 }
550
551 /**
552  * \brief set the blinking of the digits of the display
553  *
554  * This is the procedure that set the blinking of the display.
555  * You can choose to blink all the display or only a single
556  * digit.
557  *
558  *  \param SS Pointer to the SevenSeg structure
559  *  \param blink if true the display will blink
560  *  \param digit if 0 all the digits have to blink, else the digit that has to blink
561  *
562  *  \return true if the set was succesfull, false if the set was not succesfull.
563  */
564 bool sevenseg_setBlink(SevenSeg *SS, bool blink, uint8_t digit)
565 {
566         if (SS->busyedit == true)
567         {
568                 if (blink == true)
569                 {
570                         if (digit == 0)
571                                 SS->bdigit = digit;
572                         else
573                                 if ((digit-1) <= CONFIG_LED_7SEG_DIGIT)
574                                         SS->bdigit = digit;
575                                 else
576                                         return false;
577                 }
578                 SS->blink = blink;
579         }
580         else
581                 return false;
582         return true;
583 }
584
585 /**
586  * \brief set if the text has to be displayed just one time
587  *
588  * This is the procedure that set if the text has to be displayed
589  * just one time
590  *
591  *  \param SS Pointer to the SevenSeg structure
592  *  \param runonce true if the text has to be displayed only one time, false if the text has to be displayed till next print
593  *
594  *  \return true if the set was succesfull, false if the set was not succesfull.
595  */
596 bool sevenseg_setRunonce(SevenSeg *SS, bool runonce)
597 {
598         if (SS->busyedit == true)
599                 SS->runonce = runonce;
600         else
601                 return false;
602         return true;
603 }
604
605 /**
606  * \brief set the scrolling speed of the text
607  *
608  * This is the procedure that set the scrolling speed of the text
609  * if the text is longer than the display digits or the
610  * duration of the display if the text is smaller or equal the
611  * length of display digits.
612  *
613  *  \param SS Pointer to the SevenSeg structure
614  *  \param r_speed the Scrolling speed or display time
615  *
616  *  \return true if the set was succesfull, false if the set was not succesfull.
617  */
618 bool sevenseg_setRunspeed(SevenSeg *SS, unsigned int r_speed)
619 {
620         if (SS->busyedit == true)
621         {
622                 SS->speed = r_speed;
623                 SS->curspeed = r_speed;
624         }
625         else
626                 return false;
627         return true;
628 }
629
630 /**
631  * \brief clear the display
632  *
633  * This is the procedure that blanks the text to be displayed
634  * and so on clear the display.
635  *
636  *  \param SS Pointer to the SevenSeg structure
637  *
638  *  \return true if the clear was succesfull, false if the clear was not succesfull.
639  */
640 bool sevenseg_clear(SevenSeg *SS)
641 {
642         if (SS->busyedit == true)
643         {
644                 memset(((void *)&SS->string),segstable[38],sizeof(SS->string));
645                 SS->string_len = CONFIG_LED_7SEG_DIGIT;
646                 SS->blink = false;
647                 SS->bdigit = 0;
648                 SS->runonce = false;
649                 SS->curdigit = 0;
650                 SS->curpos = 0;
651                 SS->speed = CONFIG_LED_7SEG_SSPEED;
652                 SS->curspeed = CONFIG_LED_7SEG_SSPEED;
653                 SS->firstrun = false;
654         }
655         else
656                 return false;
657         return true;
658 }
659  /** \} */ //defgroup drivers