Jump to content
43oh

getting printf() working


Recommended Posts

I'm faced with the challenge of getting printf() working on my MSP430F5438 experimenter's board.

 

My mentor tells me that I should modify fputc() for my needs.

My intuition tells me to roll my own routines.

 

If you had the need to redirect STDIO to a hardware serial port on the msp430, would you:

 

A. Follow Harman's advice as posted here?

 

B. Follow Michael Barr's advice as posted here?

 

What do you think?

Link to post
Share on other sites

On my Arm, when I got my printf to work, I simply created a c file and put this in it


struct __FILE
{
 int handle;                 // Add whatever you need here 
};
FILE __stdout;
FILE __stdin;


/*===============================================
* fputc
*=================================================*/
int fputc(int ch, FILE *f) 
{
 return (SendChar(ch));
}

 

SendChar is the function I used to send a byte to the serial port.

Link to post
Share on other sites

If you are not getting all the formating abilities of printf/scanf, I can't see a reason to implement them, you can just write your own routines preferably with a circular buffer implementation(I just skimmed the text but as I understand, Michael Barr says this.) and name them whatever you like.

But if the reason for wanting printf is formating purposes than you should go as Harman says.

 

If you ask my opinion: printf/scanf formatting is not so suitable for uart, I did write my own routines in the past with support for different configurations(by implementing handlers) and advanced formating with start and stop frames and such.

Link to post
Share on other sites

I've tried similar thing (i.e implement put_char()) and enable cut down version printf in CCS, but when I tried to use it, I had problem with not enough memory on the 22F74 (1K RAM). Couldn't figure out how to solve it, but then I was able to find this "tiny printf" which worked well

 

http://www.sparetimelabs.com/tinyprintf/index.html

 

-Thanh

Link to post
Share on other sites

In the last couple of days, I have collected some primo information on this topic and I thought I should post links so others could benefit.

 

1. how to point printf to UART on MSP430F1232

2. Help! CCS4.0 with MSP430F2252 has Trouble with 32 bit (Long) Data.

3. IAR AppNote 430-03

4. Tips for using printf

5. Printf support in compiler

6. printf on msp430

 

Today, I was able to successfully test the solution presented at link 2 but it took a lot of flash space.

MSP430: Program loaded. Code Size - Text: 3024 bytes Data: 774 bytes

 

Good thing the F5438 has a lot of flash. :D

 

 

There is one more method that I would like to try. It's a combination of link 6 and some ideas my mentor had. Stay tuned.

Link to post
Share on other sites
...but then I was able to find this "tiny printf" which worked well
...memory footprint...of about 1.4 kb.

Ouch! The built-in stdio library only took 1.3kb when I tried to use sprintf(). I'm pretty sure using printf() from stdio would require less. :?

 

Have you actually tried it?

 

I didn't have problem with the code size. I have problem with the memory foot print (RAM) when I was trying to use it. As soon as I have a call to "printf" in my code, the linker couldn't find memory for it.

 

I have written some basic printing functions in both assembly (for Microchip PIC), and in C (for MSP430), but there they were far from ease of use and speed of coding when using some thing like printf.

 

-Thanh

Link to post
Share on other sites

Last night, I tried the C I/O method as described here: Tips for using printf but the code bloated to 6K flash and almost 1500 bytes of ram. The worst problem was that it couldn't handle (long ints) at all.

 

Ultimately, I had the best success with the method described here: Help! CCS4.0 with MSP430F2252 has Trouble with 32 bit (Long) Data. It does everything that I need right now.

 

The bonus is a smaller memory footprint than the TI method.

Link to post
Share on other sites

I just wanted to follow up and say that if you need the printf() function then this routine will be your best bet, in my opinion.

 

Help! CCS4.0 with MSP430F2252 has Trouble with 32 bit (Long) Data.

 

For long term storage, I post the message that contains the code here:

Posted by Bin replied on 30 Dec 2009 1:55 PM
Verified by Bin Verified Answer

Prodigy80 Points
 Good inspiration. I casted all numbers with (long int) when calling printf() and it works! I think it is acceptable. A warning (is also an explanation) is written into the printf() comment section. See following source code. The printf() is ok to use now. Everyone is free to use and modify the source code below and I assume no responsibility.


/*-------------------------------------------------------------------
DESCRIPTION: Send one char in TX buffer, if it is not busy. Wait until not busy.
INPUTS:      One char.
OUTPUTS:     Send all the char in TX buffer.
RETURNS:     None.
---------------------------------------------------------------------*/

// Modify this routine so that it points to YOUR UART (zeke)
void putChar(unsigned char byte)
{
 while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
 UCA0TXBUF = byte;                    // Load Tx register that clear UCA0TXIFG
}


/*-------------------------------------------------------------------
DESCRIPTION: Move numbers of lines up in the HyperTerminal.
INPUTS:      None.
OUTPUTS:     Line up to TX buffer.
RETURNS:     None.
---------------------------------------------------------------------*/

void linesUp(unsigned char lines)
{
 unsigned char i;
 for (i = 0; i < lines; ++i)                
 {
   putChar(0x1b);
   putChar(0x5b);
   putChar(0x41);
 }
}


/*-------------------------------------------------------------------
DESCRIPTION: Send out charater strings with defined width, justification
                      and padding. Width = 0 or width < string length means
                      unlimited width. Normal padding is space and left justification,
                      but it can pad '0' or pad to the right side, depending on pad value.
                                                             pad                        justification        padding char
                                                      bxxxxxx00            left                                     ' '
                                                      bxxxxxx1x            right                                    ' ' or '0'
                                                      bxxxxxxx1            left or right        '0' 
INPUTS:      Valid string and special charater in form of "\n" for example
                                               refered by pointer *string. Output field width. Justification
                                               and padding flag pad.
OUTPUTS:     Sent formated string to com port output.
RETURNS:     Total of chars sent.
---------------------------------------------------------------------*/
#define PAD_RIGHT    0x01
#define PAD_ZERO           0x02

int prints(char *string, unsigned char width, unsigned char pad)       
{
      int pc = 0;
      unsigned char padchar = ' ';                           // The pading char is space normally

      if (width > 0)                                                                           // If output width is defined
      {                                   
             unsigned char len = 0;
             char *ptr;
             for (ptr = string; *ptr; ++ptr) ++len;      // Calculate string length and put it in len
             if (len >= width) width = 0;    // If string is longer than width, then width is not applicable define as zero
             else width -= len;              // Else redefine width as padding spaces
             if (pad & PAD_ZERO) padchar = '0';      // If padding char is zero, then get padchar as zero ready instead of original space
      }
      if (!(pad & PAD_RIGHT))                                // If not right padding - left justification
      {                                                  
             for (; width > 0; --width)               // If ther is padding width. Output padding char as '0' or ' '.
             {  
                    putChar (padchar);
                    ++pc;
             }
      }
      for (; *string ; ++string)                      // Output the full string
      {          
             putChar (*string);
             ++pc;
      }
      for (; width > 0; --width) {      // Write padding char to the right if normal left justification
             putChar (padchar);
             ++pc;
      }
      return pc;                                                                 // Return the output char number
}

/*-------------------------------------------------------------------
* DESCRIPTION: Print 32 bit signed interger in dec or hex. In specific
*                   width, padding and justification using prints(). Use 12 byte buffer
*                   which is enough for 32 bit int.
* INPUTS: Up to 32 byte signed interger i. Counting base: 10 or 16.
*                   Sign flag sg. Output string width. padding and justification flag.
*                   Leter base for number conversion.
* OUTPUTS:     Sent formated interger as string to com port output.
* RETURNS:     Total of chars sent.
---------------------------------------------------------------------*/

#define PRINT_BUF_LEN 12

int printi(long int i, unsigned char b, unsigned char sg, unsigned char width, unsigned char pad, unsigned char letbase)
{
      char print_buf[PRINT_BUF_LEN];                         // Interger as string array
      char *s;
      char neg = 0;
      unsigned long int t;
      unsigned long int u = i;
      int pc = 0;

      if (i == 0)                                                                       // If output char is 0, then just output it with padding and width.
      {                      
             print_buf[0] = '0';
             print_buf[1] = '\0';                     // Always remenber to put string end
             return prints(print_buf, width, pad);           //Print out zero and done.
      }

      if (sg && (b == 10) && (i < 0))                 // If it is a negative int, then record the '-' and number as positive
      {             
             neg = 1;
             u = -i;
      }

      s = print_buf + PRINT_BUF_LEN-1;  // Point s to the end of the output buffer and put a null there.
      *s = '\0';

      while (u)                                // Convert the positive int to string with whatever counting base, dec, or hex.
      {                         
             t = u % b;
             if( t >= 10 )
                    t += letbase - '0' - 10;
             *--s = t + '0';
             u /= b;
      }

      if (neg)
      {                          // If it is a negative number
             if( width && (pad & PAD_ZERO) )
             {     // If there is width, right justified and pad with zero, output negative sign.
                    putChar ('-');
                    ++pc;
                    --width;
             }
             else *--s = '-';                  // Otherwise put the '-' to string buffer.

      }
      return pc + prints (s, width, pad);          // Output the string buffer and return the output counter.
}

/*-------------------------------------------------------------------
* DESCRIPTION: short form of printf. Print argument strings with mixed
*                                              varables (string or interger)inside formated.
* INPUTS:      Argument string pointer. 
* OUTPUTS:     print out the argument with style using prints() and printi().
* RETURNS:     Total of chars sent.
* Warning!!!        varables and constant numbers even 0, must casted with
*                         (long int)in printf(), if it is going to print out using
*                         format "u", "d", "X" and "x"! Or the complier will assigned
*                         16-bit for data smaller than 16 bit and the argument pointer
*                         will fetch a wrong 32-bit data and the argument point
*                         increament will be in wrong size.
* Limitations:      1) It treats all interger as 32 bit data only.
*                         2) No floating point data presentation.
*                         3) Has left/right alignment with 0 padding.
*                         4) Has format code "s", "d", "X", "x", "u" and "c" only.
---------------------------------------------------------------------*/

int printf(char *format, ...)
{
      int width, pad;
      int pc = 0;
      char scr[2];
 va_list args;
 va_start(args, format);

      for (; *format != 0; ++format) {
             if (*format == '%') {
                    ++format;
                    width = pad = 0;
                    if (*format == '\0') break;
                    if (*format == '%') goto out;
                    if (*format == '-') {
                          ++format;
                          pad = PAD_RIGHT;
                    }
                    while (*format == '0') {
                          ++format;
                          pad |= PAD_ZERO;
                    }
                    for ( ; *format >= '0' && *format <= '9'; ++format) {
                           width *= 10;
                          width += *format - '0';
                    }
                    if( *format == 's' ) {
                          char *s = (char *)va_arg( args, int );
                          pc += prints (s?s:"(null)", width, pad);
                          continue;
                    }
                    if( *format == 'd' ) {
                          pc += printi (va_arg( args, long int ), 10, 1, width, pad, 'a');
                          continue;
                    }
                    if( *format == 'x' ) {
                          pc += printi (va_arg( args, long int ), 16, 0, width, pad, 'a');
                          continue;
                    }
                    if( *format == 'X' ) {
                          pc += printi (va_arg( args, long int ), 16, 0, width, pad, 'A');
                          continue;
                    }
                    if( *format == 'u' ) {
                                 pc += printi (va_arg( args, long int ), 10, 0, width, pad, 'a');
                          continue;
                    }
                    if( *format == 'c' ) {                                 // char are converted to int then pushed on the stack
                          scr[0] = (char)va_arg( args, int );
                          scr[1] = '\0';
                          pc += prints (scr, width, pad);
                          continue;
                    }
             }
             else {
             out:
                    putChar(*format);
                    ++pc;
             }
      }
      va_end( args );
      return pc;
}




/*  Test printf() below   */

main
{
      char *ptr = "Hello world!";
      char *np = 0;
      long int i = 5;
      int bs = sizeof(long int)*8;                           // Bit to shift
      long int mi = ((long int)1 << (bs-1)) + 1;             // Maximum negative number

      printf("%s\n", ptr);
      printf("printf test\n");
      printf("%s is null pointer\n", np);
      printf("%d = 5\n", (long int)i);
      printf("%d = - max int\n", mi);
      printf("Long int 123456789 print out is %u", (long int)123456789);
      printf("\nmi in hex is %x\n", (long int)mi);
      printf("bs in dec is %u\n", (long int)bs);     
      printf("char %c = 'a'\n", 'a');
      printf("hex %x = ff\n", (long int)0xff);
      printf("hex %02x = 00\n", (long int)0);
      printf("signed %d = unsigned %u = hex %x\n", (long int)-32767, (long int)-32767, (long int)-32767);
      printf("signed %d = unsigned %u = hex %x\n", (long int)-3, (long int)-3, (long int)-3);
      printf("%d %s(s)%", (long int)0, "message");
      printf("\n");
      printf("%d %s(s) with %%\n", (long int)0, "message");
      printf("justif: \"%-10s\"\n", "left");
      printf("justif: \"%10s\"\n", "right");
      printf(" 3: %04d zero padded\n", (long int)3);
      printf(" 3: %-4d left justif.\n", (long int)3);
      printf(" 3: %4d right justif.\n", (long int)3);
      printf("-3: %04d zero padded\n", (long int)-3);
      printf("-3: %-4d left justif.\n", (long int)-3);
      printf("-3: %4d right justif.\n\n\n", (long int)-3);

}


Result:
Hello world!
printf test
(null) is null pointer
5 = 5
-2147483647 = - max int
Long int 123456789 print out is 123456789
mi in hex is 80000001
bs in dec is 32
char a = 'a'
hex ff = ff
hex 00 = 00
signed -32767 = unsigned 4294934529 = hex ffff8001
signed -3 = unsigned 4294967293 = hex fffffffd
0 message(s)
0 message(s) with %
justif: "left      "
justif: "     right"
3: 0003 zero padded
3: 3    left justif.
3:    3 right justif.
-3: -003 zero padded
-3: -3   left justif.
-3:   -3 right justif.

 

To use this code, modify the Putchar() function so that it points to your UART.

To verify it works, run the sample main() routine and compare the output to what is posted here.

 

@Bin, wherever you are, Thank you!

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...