Rev Author Line No. Line
1269 kakl 1 /*! \file rprintf.c \brief printf routine and associated routines. */
2 //*****************************************************************************
3 //
4 // File Name : 'rprintf.c'
5 // Title : printf routine and associated routines
6 // Author : Pascal Stang - Copyright (C) 2000-2002
7 // Created : 2000.12.26
8 // Revised : 2003.5.1
9 // Version : 1.0
10 // Target MCU : Atmel AVR series and other targets
11 // Editor Tabs : 4
12 //
13 // NOTE: This code is currently below version 1.0, and therefore is considered
14 // to be lacking in some functionality or documentation, or may not be fully
15 // tested. Nonetheless, you can expect most functions to work.
16 //
17 // This code is distributed under the GNU Public License
18 // which can be found at http://www.gnu.org/licenses/gpl.txt
19 //
20 //*****************************************************************************
21  
22 #include <avr/pgmspace.h>
23 //#include <string-avr.h>
24 //#include <stdlib.h>
25 #include <stdarg.h>
26 #include "global.h"
27 #include "rprintf.h"
28  
29 #ifndef TRUE
30 #define TRUE -1
31 #define FALSE 0
32 #endif
33  
34 #define INF 32766 // maximum field size to print
35 #define READMEMBYTE(a,char_ptr) ((a)?(pgm_read_byte(char_ptr)):(*char_ptr))
36  
37 #ifdef RPRINTF_COMPLEX
38 static unsigned char buf[128];
39 #endif
40  
41 // use this to store hex conversion in RAM
42 //static char HexChars[] = "0123456789ABCDEF";
43 // use this to store hex conversion in program memory
44 //static prog_char HexChars[] = "0123456789ABCDEF";
45 static char __attribute__ ((progmem)) HexChars[] = "0123456789ABCDEF";
46  
47 #define hexchar(x) pgm_read_byte( HexChars+((x)&0x0f) )
48 //#define hexchar(x) ((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
49  
50 // function pointer to single character output routine
51 static void (*rputchar)(unsigned char c);
52  
53 // *** rprintf initialization ***
54 // you must call this function once and supply the character output
55 // routine before using other functions in this library
56 void rprintfInit(void (*putchar_func)(unsigned char c))
57 {
58 rputchar = putchar_func;
59 }
60  
61 // *** rprintfChar ***
62 // send a character/byte to the current output device
63 void rprintfChar(unsigned char c)
64 {
65 // do LF -> CR/LF translation
66 if(c == '\n')
67 rputchar('\r');
68 // send character
69 rputchar(c);
70 }
71  
72 // *** rprintfStr ***
73 // prints a null-terminated string stored in RAM
74 void rprintfStr(char str[])
75 {
76 // send a string stored in RAM
77 // check to make sure we have a good pointer
78 if (!str) return;
79  
80 // print the string until a null-terminator
81 while (*str)
82 rprintfChar(*str++);
83 }
84  
85 // *** rprintfStrLen ***
86 // prints a section of a string stored in RAM
87 // begins printing at position indicated by <start>
88 // prints number of characters indicated by <len>
89 void rprintfStrLen(char str[], unsigned int start, unsigned int len)
90 {
91 register int i=0;
92  
93 // check to make sure we have a good pointer
94 if (!str) return;
95 // spin through characters up to requested start
96 // keep going as long as there's no null
97 while((i++<start) && (*str++));
98 // for(i=0; i<start; i++)
99 // {
100 // // keep steping through string as long as there's no null
101 // if(*str) str++;
102 // }
103  
104 // then print exactly len characters
105 for(i=0; i<len; i++)
106 {
107 // print data out of the string as long as we haven't reached a null yet
108 // at the null, start printing spaces
109 if(*str)
110 rprintfChar(*str++);
111 else
112 rprintfChar(' ');
113 }
114  
115 }
116  
117 // *** rprintfProgStr ***
118 // prints a null-terminated string stored in program ROM
119 void rprintfProgStr(const prog_char str[])
120 {
121 // print a string stored in program memory
122 register char c;
123  
124 // check to make sure we have a good pointer
125 if (!str) return;
126  
127 // print the string until the null-terminator
128 while((c = pgm_read_byte(str++)))
129 rprintfChar(c);
130 }
131  
132 // *** rprintfCRLF ***
133 // prints carriage return and line feed
134 void rprintfCRLF(void)
135 {
136 // print CR/LF
137 //rprintfChar('\r');
138 // LF -> CR/LF translation built-in to rprintfChar()
139 rprintfChar('\n');
140 }
141  
142 // *** rprintfu04 ***
143 // prints an unsigned 4-bit number in hex (1 digit)
144 void rprintfu04(unsigned char data)
145 {
146 // print 4-bit hex value
147 // char Character = data&0x0f;
148 // if (Character>9)
149 // Character+='A'-10;
150 // else
151 // Character+='0';
152 rprintfChar(hexchar(data));
153 }
154  
155 // *** rprintfu08 ***
156 // prints an unsigned 8-bit number in hex (2 digits)
157 void rprintfu08(unsigned char data)
158 {
159 // print 8-bit hex value
160 rprintfu04(data>>4);
161 rprintfu04(data);
162 }
163  
164 // *** rprintfu16 ***
165 // prints an unsigned 16-bit number in hex (4 digits)
166 void rprintfu16(unsigned short data)
167 {
168 // print 16-bit hex value
169 rprintfu08(data>>8);
170 rprintfu08(data);
171 }
172  
173 // *** rprintfu32 ***
174 // prints an unsigned 32-bit number in hex (8 digits)
175 void rprintfu32(unsigned long data)
176 {
177 // print 32-bit hex value
178 rprintfu16(data>>16);
179 rprintfu16(data);
180 }
181  
182 // *** rprintfNum ***
183 // special printf for numbers only
184 // see formatting information below
185 // Print the number "n" in the given "base"
186 // using exactly "numDigits"
187 // print +/- if signed flag "isSigned" is TRUE
188 // use the character specified in "padchar" to pad extra characters
189 //
190 // Examples:
191 // uartPrintfNum(10, 6, TRUE, ' ', 1234); --> " +1234"
192 // uartPrintfNum(10, 6, FALSE, '0', 1234); --> "001234"
193 // uartPrintfNum(16, 6, FALSE, '.', 0x5AA5); --> "..5AA5"
194 void rprintfNum(char base, char numDigits, char isSigned, char padchar, long n)
195 {
196 // define a global HexChars or use line below
197 //static char HexChars[16] = "0123456789ABCDEF";
198 char *p, buf[32];
199 unsigned long x;
200 unsigned char count;
201  
202 // prepare negative number
203 if( isSigned && (n < 0) )
204 {
205 x = -n;
206 }
207 else
208 {
209 x = n;
210 }
211  
212 // setup little string buffer
213 count = (numDigits-1)-(isSigned?1:0);
214 p = buf + sizeof (buf);
215 *--p = '\0';
216  
217 // force calculation of first digit
218 // (to prevent zero from not printing at all!!!)
219 *--p = hexchar(x%base); x /= base;
220 // calculate remaining digits
221 while(count--)
222 {
223 if(x != 0)
224 {
225 // calculate next digit
226 *--p = hexchar(x%base); x /= base;
227 }
228 else
229 {
230 // no more digits left, pad out to desired length
231 *--p = padchar;
232 }
233 }
234  
235 // apply signed notation if requested
236 if( isSigned )
237 {
238 if(n < 0)
239 {
240 *--p = '-';
241 }
242 else if(n > 0)
243 {
244 *--p = '+';
245 }
246 else
247 {
248 *--p = ' ';
249 }
250 }
251  
252 // print the string right-justified
253 count = numDigits;
254 while(count--)
255 {
256 rprintfChar(*p++);
257 }
258 }
259  
260 #ifdef RPRINTF_FLOAT
261 // *** rprintfFloat ***
262 // floating-point print
263 void rprintfFloat(char numDigits, double x)
264 {
265 unsigned char firstplace = FALSE;
266 unsigned char negative;
267 unsigned char i, digit;
268 double place = 1.0;
269  
270 // save sign
271 negative = (x<0);
272 // convert to absolute value
273 x = (x>0)?(x):(-x);
274  
275 // find starting digit place
276 for(i=0; i<15; i++)
277 {
278 if((x/place) < 10.0)
279 break;
280 else
281 place *= 10.0;
282 }
283 // print polarity character
284 if(negative)
285 rprintfChar('-');
286 else
287 rprintfChar('+');
288  
289 // print digits
290 for(i=0; i<numDigits; i++)
291 {
292 digit = (x/place);
293  
294 if(digit | firstplace | (place == 1.0))
295 {
296 firstplace = TRUE;
297 rprintfChar(digit+0x30);
298 }
299 else
300 rprintfChar(' ');
301  
302 if(place == 1.0)
303 {
304 rprintfChar('.');
305 }
306  
307 x -= (digit*place);
308 place /= 10.0;
309 }
310 }
311 #endif
312  
313 void rprintfFloatMy(char numDigits, double x)
314 {
315 unsigned char firstplace = FALSE;
316 unsigned char negative;
317 unsigned char i, digit;
318 double place = 1.0;
319  
320 // save sign
321 negative = (x<0);
322 // convert to absolute value
323 x = (x>0)?(x):(-x);
324  
325 // find starting digit place
326 for(i=0; i<15; i++)
327 {
328 if((x/place) < 10.0)
329 break;
330 else
331 place *= 10.0;
332 }
333 // print polarity character
334 /*
335 if(negative)
336 rprintfChar('-');
337 else
338 rprintfChar('+');
339 */
340 // print digits
341 for(i=0; i<numDigits; i++)
342 {
343 if(place == 0.1)
344 {
345 rprintfChar('.');
346 }
347  
348 digit = (x/place);
349  
350 if(digit | firstplace | (place == 1.0))
351 {
352 firstplace = TRUE;
353 rprintfChar(digit+0x30);
354 }
355 else
356 rprintfChar(' ');
357  
358 x -= (digit*place);
359 place /= 10.0;
360 }
361 }
362  
363  
364 #ifdef RPRINTF_SIMPLE
365 // *** rprintf1RamRom ***
366 // called by rprintf() - does a simple printf (supports %d, %x, %c)
367 // Supports:
368 // %d - decimal
369 // %x - hex
370 // %c - character
371 int rprintf1RamRom(unsigned char stringInRom, const char *format, ...)
372 {
373 // simple printf routine
374 // define a global HexChars or use line below
375 //static char HexChars[16] = "0123456789ABCDEF";
376 char format_flag;
377 unsigned int u_val, div_val, base;
378 va_list ap;
379  
380 va_start(ap, format);
381 for (;;)
382 {
383 while ((format_flag = READMEMBYTE(stringInRom,format++) ) != '%')
384 { // Until '%' or '\0'
385 if (!format_flag)
386 {
387 va_end(ap);
388 return(0);
389 }
390 rprintfChar(format_flag);
391 }
392  
393 switch (format_flag = READMEMBYTE(stringInRom,format++) )
394 {
395 case 'c': format_flag = va_arg(ap,int);
396 default: rprintfChar(format_flag); continue;
397 case 'd': base = 10; div_val = 10000; goto CONVERSION_LOOP;
398 // case 'x': base = 16; div_val = 0x10;
399 case 'x': base = 16; div_val = 0x1000;
400  
401 CONVERSION_LOOP:
402 u_val = va_arg(ap,int);
403 if (format_flag == 'd')
404 {
405 if (((int)u_val) < 0)
406 {
407 u_val = - u_val;
408 rprintfChar('-');
409 }
410 while (div_val > 1 && div_val > u_val) div_val /= 10;
411 }
412 do
413 {
414 //rprintfChar(pgm_read_byte(HexChars+(u_val/div_val)));
415 rprintfu04(u_val/div_val);
416 u_val %= div_val;
417 div_val /= base;
418 } while (div_val);
419 }
420 }
421 va_end(ap);
422 }
423 #endif
424  
425  
426 #ifdef RPRINTF_COMPLEX
427 // *** rprintf2RamRom ***
428 // called by rprintf() - does a more powerful printf (supports %d, %u, %o, %x, %c, %s)
429 // Supports:
430 // %d - decimal
431 // %u - unsigned decimal
432 // %o - octal
433 // %x - hex
434 // %c - character
435 // %s - strings
436 // and the width,precision,padding modifiers
437 // **this printf does not support floating point numbers
438 int rprintf2RamRom(unsigned char stringInRom, const char *sfmt, ...)
439 {
440 register unsigned char *f, *bp;
441 register long l;
442 register unsigned long u;
443 register int i;
444 register int fmt;
445 register unsigned char pad = ' ';
446 int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
447 int sign = 0;
448  
449 va_list ap;
450 va_start(ap, sfmt);
451  
452 f = (unsigned char *) sfmt;
453  
454 for (; READMEMBYTE(stringInRom,f); f++)
455 {
456 if (READMEMBYTE(stringInRom,f) != '%')
457 { // not a format character
458 // then just output the char
459 rprintfChar(READMEMBYTE(stringInRom,f));
460 }
461 else
462 {
463 f++; // if we have a "%" then skip it
464 if (READMEMBYTE(stringInRom,f) == '-')
465 {
466 flush_left = 1; // minus: flush left
467 f++;
468 }
469 if (READMEMBYTE(stringInRom,f) == '0'
470 || READMEMBYTE(stringInRom,f) == '.')
471 {
472 // padding with 0 rather than blank
473 pad = '0';
474 f++;
475 }
476 if (READMEMBYTE(stringInRom,f) == '*')
477 { // field width
478 f_width = va_arg(ap, int);
479 f++;
480 }
481 else if (Isdigit(READMEMBYTE(stringInRom,f)))
482 {
483 f_width = atoiRamRom(stringInRom, (char *) f);
484 while (Isdigit(READMEMBYTE(stringInRom,f)))
485 f++; // skip the digits
486 }
487 if (READMEMBYTE(stringInRom,f) == '.')
488 { // precision
489 f++;
490 if (READMEMBYTE(stringInRom,f) == '*')
491 {
492 prec = va_arg(ap, int);
493 f++;
494 }
495 else if (Isdigit(READMEMBYTE(stringInRom,f)))
496 {
497 prec = atoiRamRom(stringInRom, (char *) f);
498 while (Isdigit(READMEMBYTE(stringInRom,f)))
499 f++; // skip the digits
500 }
501 }
502 if (READMEMBYTE(stringInRom,f) == '#')
503 { // alternate form
504 hash = 1;
505 f++;
506 }
507 if (READMEMBYTE(stringInRom,f) == 'l')
508 { // long format
509 do_long = 1;
510 f++;
511 }
512  
513 fmt = READMEMBYTE(stringInRom,f);
514 bp = buf;
515 switch (fmt) { // do the formatting
516 case 'd': // 'd' signed decimal
517 if (do_long)
518 l = va_arg(ap, long);
519 else
520 l = (long) (va_arg(ap, int));
521 if (l < 0)
522 {
523 sign = 1;
524 l = -l;
525 }
526 do {
527 *bp++ = l % 10 + '0';
528 } while ((l /= 10) > 0);
529 if (sign)
530 *bp++ = '-';
531 f_width = f_width - (bp - buf);
532 if (!flush_left)
533 while (f_width-- > 0)
534 rprintfChar(pad);
535 for (bp--; bp >= buf; bp--)
536 rprintfChar(*bp);
537 if (flush_left)
538 while (f_width-- > 0)
539 rprintfChar(' ');
540 break;
541 case 'o': // 'o' octal number
542 case 'x': // 'x' hex number
543 case 'u': // 'u' unsigned decimal
544 if (do_long)
545 u = va_arg(ap, unsigned long);
546 else
547 u = (unsigned long) (va_arg(ap, unsigned));
548 if (fmt == 'u')
549 { // unsigned decimal
550 do {
551 *bp++ = u % 10 + '0';
552 } while ((u /= 10) > 0);
553 }
554 else if (fmt == 'o')
555 { // octal
556 do {
557 *bp++ = u % 8 + '0';
558 } while ((u /= 8) > 0);
559 if (hash)
560 *bp++ = '0';
561 }
562 else if (fmt == 'x')
563 { // hex
564 do {
565 i = u % 16;
566 if (i < 10)
567 *bp++ = i + '0';
568 else
569 *bp++ = i - 10 + 'a';
570 } while ((u /= 16) > 0);
571 if (hash)
572 {
573 *bp++ = 'x';
574 *bp++ = '0';
575 }
576 }
577 i = f_width - (bp - buf);
578 if (!flush_left)
579 while (i-- > 0)
580 rprintfChar(pad);
581 for (bp--; bp >= buf; bp--)
582 rprintfChar((int) (*bp));
583 if (flush_left)
584 while (i-- > 0)
585 rprintfChar(' ');
586 break;
587 case 'c': // 'c' character
588 i = va_arg(ap, int);
589 rprintfChar((int) (i));
590 break;
591 case 's': // 's' string
592 bp = va_arg(ap, unsigned char *);
593 if (!bp)
594 bp = (unsigned char *) "(nil)";
595 f_width = f_width - strlen((char *) bp);
596 if (!flush_left)
597 while (f_width-- > 0)
598 rprintfChar(pad);
599 for (i = 0; *bp && i < prec; i++)
600 {
601 rprintfChar(*bp);
602 bp++;
603 }
604 if (flush_left)
605 while (f_width-- > 0)
606 rprintfChar(' ');
607 break;
608 case '%': // '%' character
609 rprintfChar('%');
610 break;
611 }
612 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
613 sign = 0;
614 pad = ' ';
615 }
616 }
617  
618 va_end(ap);
619 return 0;
620 }
621  
622 unsigned char Isdigit(char c)
623 {
624 if((c >= 0x30) && (c <= 0x39))
625 return TRUE;
626 else
627 return FALSE;
628 }
629  
630 int atoiRamRom(unsigned char stringInRom, char *str)
631 {
632 int num = 0;;
633  
634 while(Isdigit(READMEMBYTE(stringInRom,str)))
635 {
636 num *= 10;
637 num += ((READMEMBYTE(stringInRom,str++)) - 0x30);
638 }
639 return num;
640 }
641  
642 #endif
643  
644 //******************************************************************************
645 // code below this line is commented out and can be ignored
646 //******************************************************************************
647 /*
648 char* sprintf(const char *sfmt, ...)
649 {
650 register unsigned char *f, *bp, *str;
651 register long l;
652 register unsigned long u;
653 register int i;
654 register int fmt;
655 register unsigned char pad = ' ';
656 int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
657 int sign = 0;
658  
659 va_list ap;
660 va_start(ap, sfmt);
661  
662 str = bufstring;
663 f = (unsigned char *) sfmt;
664  
665 for (; *f; f++)
666 {
667 if (*f != '%')
668 { // not a format character
669 *str++ = (*f); // then just output the char
670 }
671 else
672 {
673 f++; // if we have a "%" then skip it
674 if (*f == '-')
675 {
676 flush_left = 1; // minus: flush left
677 f++;
678 }
679 if (*f == '0' || *f == '.')
680 {
681 // padding with 0 rather than blank
682 pad = '0';
683 f++;
684 }
685 if (*f == '*')
686 { // field width
687 f_width = va_arg(ap, int);
688 f++;
689 }
690 else if (Isdigit(*f))
691 {
692 f_width = atoi((char *) f);
693 while (Isdigit(*f))
694 f++; // skip the digits
695 }
696 if (*f == '.')
697 { // precision
698 f++;
699 if (*f == '*')
700 {
701 prec = va_arg(ap, int);
702 f++;
703 }
704 else if (Isdigit(*f))
705 {
706 prec = atoi((char *) f);
707 while (Isdigit(*f))
708 f++; // skip the digits
709 }
710 }
711 if (*f == '#')
712 { // alternate form
713 hash = 1;
714 f++;
715 }
716 if (*f == 'l')
717 { // long format
718 do_long = 1;
719 f++;
720 }
721  
722 fmt = *f;
723 bp = buf;
724 switch (fmt) { // do the formatting
725 case 'd': // 'd' signed decimal
726 if (do_long)
727 l = va_arg(ap, long);
728 else
729 l = (long) (va_arg(ap, int));
730 if (l < 0)
731 {
732 sign = 1;
733 l = -l;
734 }
735 do {
736 *bp++ = l % 10 + '0';
737 } while ((l /= 10) > 0);
738 if (sign)
739 *bp++ = '-';
740 f_width = f_width - (bp - buf);
741 if (!flush_left)
742 while (f_width-- > 0)
743 *str++ = (pad);
744 for (bp--; bp >= buf; bp--)
745 *str++ = (*bp);
746 if (flush_left)
747 while (f_width-- > 0)
748 *str++ = (' ');
749 break;
750 case 'o': // 'o' octal number
751 case 'x': // 'x' hex number
752 case 'u': // 'u' unsigned decimal
753 if (do_long)
754 u = va_arg(ap, unsigned long);
755 else
756 u = (unsigned long) (va_arg(ap, unsigned));
757 if (fmt == 'u')
758 { // unsigned decimal
759 do {
760 *bp++ = u % 10 + '0';
761 } while ((u /= 10) > 0);
762 }
763 else if (fmt == 'o')
764 { // octal
765 do {
766 *bp++ = u % 8 + '0';
767 } while ((u /= 8) > 0);
768 if (hash)
769 *bp++ = '0';
770 }
771 else if (fmt == 'x')
772 { // hex
773 do {
774 i = u % 16;
775 if (i < 10)
776 *bp++ = i + '0';
777 else
778 *bp++ = i - 10 + 'a';
779 } while ((u /= 16) > 0);
780 if (hash)
781 {
782 *bp++ = 'x';
783 *bp++ = '0';
784 }
785 }
786 i = f_width - (bp - buf);
787 if (!flush_left)
788 while (i-- > 0)
789 *str++ = (pad);
790 for (bp--; bp >= buf; bp--)
791 *str++ = ((int) (*bp));
792 if (flush_left)
793 while (i-- > 0)
794 *str++ = (' ');
795 break;
796 case 'c': // 'c' character
797 i = va_arg(ap, int);
798 *str++ = ((int) (i));
799 break;
800 case 's': // 's' string
801 bp = va_arg(ap, unsigned char *);
802 if (!bp)
803 bp = (unsigned char *) "(nil)";
804 f_width = f_width - strlen((char *) bp);
805 if (!flush_left)
806 while (f_width-- > 0)
807 *str++ = (pad);
808 for (i = 0; *bp && i < prec; i++)
809 {
810 *str++ = (*bp);
811 bp++;
812 }
813 if (flush_left)
814 while (f_width-- > 0)
815 *str++ = (' ');
816 break;
817 case '%': // '%' character
818 *str++ = ('%');
819 break;
820 }
821 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
822 sign = 0;
823 pad = ' ';
824 }
825 }
826  
827 va_end(ap);
828 // terminate string with null
829 *str++ = '\0';
830 return bufstring;
831 }
832  
833 */