EmbDev.net

Forum: ARM programming with GCC/GNU tools help with rprintf


von Sevc D. (sevc)


Rate this post
useful
not useful
Hi all.
On example for LPC are used function rprintf to serial port .
But I can't print float value (double).
can some help???
regards

von Clifford S. (clifford)


Rate this post
useful
not useful
I just downloaded the examples and looked at the code and it quite
clearly says in the boiler-plate comment:

------------
// Todo:
// %f, %F for floating point numbers
------------

It is a 'reduced' printf after all and fp handling would increase the
size. Use the  standard printf if you need full functionality.

Clifford

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
Sevc Dominik wrote:
> But I can't print float value (double).

The rprintf in the Michael Wolf's Project in the thread "[ANN]
AT91SAM7S-128 USB MSD Example" includes a rprintf with FP support.
(While most of the projects seems to be under GPL license the lincense
for the rprintf-code seems to be BSD or BSD-compatible and so reather
unrestricted)

von Sławomir W. (wil)


Rate this post
useful
not useful
Sevc Dominik wrote:
> Hi all.
> On example for LPC are used function rprintf to serial port .
> But I can't print float value (double).
> can some help???
> regards

You are welcome to use following files:

rprintf.h:
==============
#ifndef __PRINTF_H
#define __PRINTF_H

extern void rputs(char const *txt);
extern void rprintf(char const *fmt0, ...);

#endif


rprintf.c:
===========
//###################################################################### 
########
// (r)printf.c
//
// code based on sprintf() from gcctest9.c by Volker Oth
//
// Changes made by Holger Klabunde:
// Now takes format strings from FLASH (was into RAM ! before)
// Fixed bug for %i, %I. u_val was used before it had the right value
// Added %d, %D (is same as %i, %I)
// Support for long variables %li, %ld, %Lu, %LX ....
// %x, %X now gives upper case hex characters A,B,C,D,E,F
// Output can be redirected in a single function: myputchar()
// Make printf() smaller by commenting out a few #defines
// Added some SPACE and ZERO padding %02x or % 3u up to 9 characters
//
// Changes made by Martin Thomas:
// LPC2000 port using the R O Software UART-Interface (see myputchar)
// ARM7S   port using minimal uart interface
// reverted Klabundes "from flash" since only useful for AVR "Harvard"
//
// Changes made by Sławomir Wilczewski:
//   - added %f, %F for floating point numbers conversion
//  -  accuracy max 1 digit, width max 2 digits in format string (ex: %
10.4f)
//  -  max number of characters (incl. sign): to fit into buffer SCRATCH
size
//  -  floating point conversion code size is 6.5kB in .text area
//
//###################################################################### 
########

#include <stdarg.h>
#include <string.h>
#include "rprintf.h"

#define SCRATCH 12  //32Bits go up to 4GB + 1 Byte for \0

//Spare some program space by making a comment of all not used format
flag lines
#define USE_LONG        // %lx, %Lu and so on, else only 16 bit integer
is allowed
//#define USE_OCTAL      // %o, %O Octal output. Who needs this ?
#define USE_STRING      // %s, %S Strings as parameters
#define USE_CHAR        // %c, %C Chars as parameters
#define USE_INTEGER      // %i, %I Remove this format flag. %d, %D does
the same
#define USE_HEX          // %x, %X Hexadezimal output
#define USE_UPPERHEX    // %x, %X outputs A,B,C... else a,b,c...
#ifndef USE_HEX
 #undef USE_UPPERHEX    // ;)
#endif
#define USE_UPPER        // uncommenting this removes
%C,%D,%I,%O,%S,%U,%X and %L..
                        // only lowercase format flags are used
#define PADDING         //SPACE and ZERO padding

#define USE_FLOAT        // %f %F floating point numbers as parameters.


//float params
#ifdef USE_FLOAT
  #define LEFT_ALIGN      0x01
  #define SHOW_SIGN        0x02
  #define MINUS_ONLY      0x04
  #define SHOW_ALTERNATE  0x08  //always display dot
  #define NEGATIVE        0x10

  //defaults:
  #define DFLT_F_ACCURACY  2
  #define DFLT_F_WIDTH    6
  #define DFLT_F_FILL      ' '

static char * strrev( char * pa );
unsigned char* ftoa( float f, char const *format, unsigned char
*ucaFloat );

#endif



#include "lcd.h"

static void myputchar( unsigned char c ){
    LCDPreter( c );
}



void rprintf( char const *format, ... ){
  unsigned char scratch[SCRATCH];
  unsigned char format_flag;
  unsigned short base;
  unsigned char *ptr;
  unsigned char issigned=0;
  va_list ap;

#ifdef USE_LONG
  unsigned char islong=0;
  unsigned long u_val=0;
  long s_val=0;
#else
  unsigned int u_val=0;
  int s_val=0;
#endif

#ifdef USE_FLOAT
  double f_val = 0;
  char const * pStr;
#endif

  unsigned char fill;
  unsigned char width;

  va_start (ap, format);
  for (;;){
    while ((format_flag = *(format++)) != '%'){      // Until '%' or
'\0'
      if (!format_flag){va_end (ap); return;}
      myputchar(format_flag);
    }

#ifdef USE_FLOAT
    pStr = format;
    fill = 0;

     while( *pStr ){
      if( ( *pStr == 'l' ) || ( *pStr == 'L' ) ) break;
      if( ( *pStr == 'o' ) || ( *pStr == 'O' ) ) break;
      if( ( *pStr == 's' ) || ( *pStr == 'S' ) ) break;
      if( ( *pStr == 'c' ) || ( *pStr == 'C' ) ) break;
      if( ( *pStr == 'i' ) || ( *pStr == 'I' ) ) break;
      if( ( *pStr == 'd' ) || ( *pStr == 'D' ) ) break;
      if( ( *pStr == 'u' ) || ( *pStr == 'U' ) ) break;
      if( ( *pStr == 'x' ) || ( *pStr == 'X' ) ) break;

      if( ( *pStr == 'f' ) || ( *pStr == 'F' ) ){
        fill = 1;
        f_val = va_arg( ap, double );
        ptr = ftoa( (float)f_val, format, scratch );
        format = ++pStr;
        while( *ptr ) { myputchar( *ptr ); ptr++; }
        break;
      }
      pStr++;
    }
    if( fill ) continue;
#endif

    issigned=0; //default unsigned
    base = 10;

    format_flag = *format++; //get char after '%'

#ifdef PADDING
    width=0; //no formatting
    fill=0;  //no formatting
    if(format_flag=='0' || format_flag==' ') //SPACE or ZERO padding  ?
    {
      fill=format_flag;
      format_flag = *format++; //get char after padding char
      if(format_flag>='0' && format_flag<='9')
       {
        width=format_flag-'0';
        format_flag = *format++; //get char after width char
       }
     }
#endif

#ifdef USE_LONG
    islong=0; //default int value
#ifdef USE_UPPER
    if(format_flag=='l' || format_flag=='L') //Long value
#else
    if(format_flag=='l') //Long value
#endif
     {
      islong=1;
      format_flag = *format++; //get char after 'l' or 'L'
     }
#endif

    switch (format_flag)
    {
#ifdef USE_CHAR
    case 'c':
#ifdef USE_UPPER
    case 'C':
#endif
      format_flag = va_arg(ap,int);
      // no break -> run into default
#endif

    default:
      myputchar(format_flag);
      continue;

#ifdef USE_STRING
#ifdef USE_UPPER
    case 'S':
#endif
    case 's':
      ptr = (unsigned char*)va_arg(ap,char *);
      while(*ptr) { myputchar(*ptr); ptr++; }
      continue;
#endif

#ifdef USE_OCTAL
    case 'o':
#ifdef USE_UPPER
    case 'O':
#endif
      base = 8;
      myputchar('0');
      goto CONVERSION_LOOP;
#endif

#ifdef USE_INTEGER //don't use %i, is same as %d
    case 'i':
#ifdef USE_UPPER
    case 'I':
#endif
#endif
    case 'd':
#ifdef USE_UPPER
    case 'D':
#endif
      issigned=1;
      // no break -> run into next case
    case 'u':
#ifdef USE_UPPER
    case 'U':
#endif

//don't insert some case below this if USE_HEX is undefined !
//or put       goto CONVERSION_LOOP;  before next case.
#ifdef USE_HEX
      goto CONVERSION_LOOP;
    case 'x':
#ifdef USE_UPPER
    case 'X':
#endif
      base = 16;
#endif

    CONVERSION_LOOP:

      if(issigned) //Signed types
       {
#ifdef USE_LONG
        if(islong) { s_val = va_arg(ap,long); }
        else { s_val = va_arg(ap,int); }
#else
        s_val = va_arg(ap,int);
#endif

        if(s_val < 0) //Value negativ ?
         {
          s_val = - s_val; //Make it positiv
          myputchar('-');    //Output sign
         }

        u_val = (unsigned long)s_val;
       }
      else //Unsigned types
       {
#ifdef USE_LONG
        if(islong) { u_val = va_arg(ap,unsigned long); }
        else { u_val = va_arg(ap,unsigned int); }
#else
        u_val = va_arg(ap,unsigned int);
#endif
       }

      ptr = scratch + SCRATCH;
      *--ptr = 0;
      do
       {
        char ch = u_val % base + '0';
#ifdef USE_HEX
        if (ch > '9')
         {
          ch += 'a' - '9' - 1;
#ifdef USE_UPPERHEX
          ch-=0x20;
#endif
         }
#endif
        *--ptr = ch;
        u_val /= base;

#ifdef PADDING
        if(width) width--; //calculate number of padding chars
#endif
      } while (u_val);

#ifdef PADDING
     while(width--) *--ptr = fill; //insert padding chars
#endif

      while(*ptr) { myputchar(*ptr); ptr++; }
    }
  }
}


#ifdef USE_FLOAT
static char* strrev( char *pa ){
  char * pStr;
  char c;
  int i, len;


  pStr = pa;
  len = 0;

  while( *pStr++ )  ++len;
  if( len < 2 ) return pa;

  for( i = 0; i < len / 2; ++i ){
    c = *( pa + i );
    *( pa + i ) = *( pa + len - 1 - i );
    *( pa + len - 1 - i ) = c;
  }
  return pa;
}
#endif


#ifdef USE_FLOAT

unsigned char* ftoa( float f, char const *format, unsigned char
*ucaFloat ){
  char  cFlag,
        cWidth = DFLT_F_WIDTH,
        cAccuracy = DFLT_F_ACCURACY,
        * pStr,
//        caTemp[ 82 ],
        caTemp[ 2 * SCRATCH ],
        caFillChar[ 2 ] = " ";  //space or zero

  char const    * pcStr;

  unsigned long  i, j;
  unsigned long long  lNumber;



  //find accuracy or use default
  pcStr = --format;
  while( *pcStr++ ){
    if( ( *pcStr == 'f' ) || ( *pcStr == 'F' ) ) break;
    if( *pcStr == '.' ){  //assume 1 digit for accuracy
      if( ( *( pcStr + 1 ) == 'f' ) || ( *( pcStr + 1 ) == 'F' ) )
cAccuracy = 0;
      else cAccuracy = *( pcStr + 1 ) - '0';
      break;
    }
  }

  //find width or use default. Max 2 digits.
  cFlag = 0;
  pcStr = format;
  while( *pcStr++ ){
    if( ( *pcStr == 'f' ) || ( *pcStr == 'F' ) || ( *pcStr == '.' ) )
break;
    if( ( *pcStr >= '0' ) && ( *pcStr <= '9' ) ){
      if( ( *pcStr == '0' ) && ( cFlag == 0 )){  //leading zero (fill
char)
        *caFillChar = '0';
        *( caFillChar + 1 ) = 0;
        continue;
      }
      *( caTemp + cFlag ) = *pcStr;
      ++cFlag;
    }
  }
  *( caTemp + cFlag ) = 0;
  if( strlen( caTemp ) > 0 ){
    strrev( caTemp );

    cWidth = 0; i = 1;
    pStr = &caTemp[ 0 ];
    while( *pStr ){
      cWidth += ( *pStr - '0' ) * i;
      i *= 10;
      pStr++;
    }
  }

  //special format characters
  cFlag = 0;
  pcStr = format;

  while( *pcStr++ ){
    if( ( *pcStr == 'f' ) || ( *pcStr == 'F' ) || ( *pcStr == '.' ) )
break;
    if( ( *pcStr >= '0' ) && ( *pcStr <= '9' ) ) break;

    if( *pcStr == '-' ) cFlag |= LEFT_ALIGN;
    if( *pcStr == '+' ) cFlag |= SHOW_SIGN;
    if( *pcStr == ' ' ) cFlag |= MINUS_ONLY;
    if( *pcStr == '#' ) cFlag |= SHOW_ALTERNATE;
  }
  //to do: eliminate invalid / useless format combinations

  if( f < 0 ){
    cFlag |= NEGATIVE;
    f = -f;
  }

  if( cAccuracy > 0 )
    for( i = 0; i < cAccuracy; ++i ) f *= 10;

  lNumber = (unsigned long long)( f );
  if( ( ( (unsigned long long)( f * 10 ) ) % 10 ) >= 5 ) lNumber++;
//rounding


  //conversion to ascii
  pStr = &caTemp[ 0 ];
  *pStr = 0;
  do{
    *pStr++ = ( lNumber % 10 ) + '0';
    lNumber /= 10;
  } while( lNumber );
  *pStr = 0;

  //handling small numbers
  if( ( j = strlen( caTemp ) ) <= cAccuracy ){
    for( i = 0; i < cAccuracy - j; ++i ){
      *( caTemp + j + i ) = '0';
    }
    *( caTemp + cAccuracy ) = '0';  //ensure 0 before dot
    *( caTemp + cAccuracy + 1 ) = 0;
  }

  //insert dot
  i = strlen( caTemp );
  *( caTemp + i + 1 ) = 0;

  for( j = 0; j < i - cAccuracy; ++j )
    *( caTemp + i - j ) = *( caTemp + i - j - 1 );

  *( caTemp + cAccuracy ) = '.';


  //check size
  if( ( strlen( caTemp ) > SCRATCH - 2 )  // one char for sign
      || ( cWidth > SCRATCH - 2 ) )
  {
    *ucaFloat = 0;
     *ucaFloat = '?';
    *( ucaFloat + 1 ) = 0;
    return ucaFloat;
  }

  //sign processing
  *ucaFloat = 0;
  //show sign with 0 ??
  if( cFlag & NEGATIVE ){
    *ucaFloat = '-';
    *( ucaFloat + 1 ) = 0;
    if( cWidth > 1 ) --cWidth;  //include sign into string width
  }
  else{
    if( cFlag & SHOW_SIGN ){
      *ucaFloat = '+';
      *( ucaFloat + 1 ) = 0;
      if( cWidth > 1 ) --cWidth;
    }
    if( cFlag & MINUS_ONLY ){  //insert space in this case
      *ucaFloat = ' ';
      *( ucaFloat + 1 ) = 0;
      if( cWidth > 1 ) --cWidth;
    }
  }

  //formating
  if( cFlag & SHOW_ALTERNATE ){};  //dot always inserted

  //fill character.
  if( ( cFlag & LEFT_ALIGN ) == 0 ){  //leading fill chars
    while( strlen( caTemp ) < cWidth )
      strcat( caTemp, caFillChar );
  }
  strrev( caTemp );

  if( cFlag & LEFT_ALIGN ){
    while( strlen( caTemp ) < cWidth )
      strcat( caTemp, caFillChar );
  }
  strcat( (char*)ucaFloat, caTemp );

  return ucaFloat;
}
#endif

von ciccio (Guest)


Rate this post
useful
not useful
I LOVE YOU!

Ciccio.

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.