1

Topic: [8051] printf to anything

hello!

i'm working on a project in our school. we're in the near end implementing the software for the hardware we developed.
it's a modular hardware where you can plug in a lcd, use a soft-uart for communication, print something via the normal uart and so on.
i've already implemented the basic routines for printing to every device, but i'd like to make it printf-compatible.

i already read about setting a bit which determines the way the string is sent out, but this method is not comfortable enough.
i'd like to implement for example lcd_printf which calls printf outputting to the LCD. same for uart and i2c.

i saw on other compiles printf-implementations where the output-rounine used is passed on as a parameter

extern int _doprnt( void (*iofunk)(uchar) reentrant, far char* pfmt, va_list (fap));

int lcd_printf(far char* pfmt, ...){
        va_list(ap);
        va_start(ap,pfmt);
        return _doprnt(lcd_putc, pfmt, ap);
}

is something like this also possible in RIDE?

or other way round: is it possible to get the address of the printf-buffer used by the compiler internally? i could conserve memory if i use the same buffer. using that address i could sprintf into the
buffer and output it comfortably with my existing functions.

thanks in advance!

greets, christian hartl

2

Re: [8051] printf to anything

Hi Christian,

In stead of tackling <i>printf</i> itself, I found it much easier to alter <i>putchar</i>, which is ultimately called by <i>printf</i> to output the characters.

The way I went about is was to declare a global variable, <b>printf_to</b>, and use a switch statement in the <i>putchar</i> routine to determine where the character should go to. The <b>printf_to</b> variable is set before each call to <i>printf</i>.

So your <i>putchar</i> would look something like this:
<pre>int putchar(const int c) {
  switch (printf_to) {
  case UART0:
    <i>code to output to UART0</i>
    break;
  case UART1:
    <i>code to output to UART1</i>
    break;
  case LCD:
    <i>code to output to LCD</i>
    break;
  <i>Add more cases as required</i>
  }
}</pre>
And you could use it like this:
<pre><b>char</b> printf_to;
....
printf_to = UART1;  // The output of all following calls to printf() will go to UART1, until printf_to is changed
printf("Some text to UART1");
...
printf("More text to UART1);
...
printf_to = LCD;
printf("This text goes to the LCD");</pre>

To a real software engineer this may not be the most sophisticated solution but for this hardware engineer it gets the job done :-)


Hope this helps a bit.

Best regards,
Rob Klein.

3

Re: [8051] printf to anything

thanks for your quick and detailed reply!

unfortunately, this is the way i already read about and i don't want to implement it.
i explain you why:
if you need the LCD, you plug it in. to display something, you have to use lcd_printf - the function is in a special library; printing to the LCD requires the big i2c-library. then theres the not too small uart-library, the suart-library and so on.
so, the putchar-function would include all of this fuctions and therefore get
pretty big - altough for example some of these fuctions aren't used anyway they will be included...
all this would be no problem if my flash-code-memory wasn't limited to 8k...

i'd prefer the method getting the printf-internal buffer-address.
how does RIDE's printf acutally work? does it print everything to a buffer before sending it out via uart0? or does it need no buffer and prints each byte directly to the uart?

thanks in advance!
greets, christian hartl

4

Re: [8051] printf to anything

Hi again Christian,

If you have the LCD-, I2C-, and other libraries in <i>actual</i> pre-compiled library form, why not use the method I outlined and bracket all possible cases in <i>#ifdef</i>'s? Then add a header file to your project, containing the hardware configuration (call it "hw_config.h" or something).

int putchar(const int c) {

  switch (printf_to) {
<b>#ifdef UART0_USED</b>
  case UART0:
    <i>code to output to UART0</i>
    break;
<b>#endif

#ifdef UART1_USED</b>
  case UART1:
    <i>code to output to UART1</i>
    break;
<b>#endif

#ifdef LCD_USED</b>
  case LCD:
    <i>code to output to LCD</i>
    break;
<b>#endif</b>

  <i>Add more cases as required</i>
  }
  return(c);
}

And the header file something like this:

<i>// This header file contains all possible hardware extentions. Uncomment each line as required</i>
// #define UART0_USED  1
// #define UART1_USED  1
// #define LCD_USED  1
<i>etc.</i>

This way, only the code that is actually required to support the hardware present is linked and thus code size is minimised.
The same principle can also be applied if you do <u>not</u> use pre-compiled libraries, but then you will have to bracket the supplied source code in the same way I showed for <i>putchar</i>.

As for the way <i>printf</i> works, I'm not to sure about the way characters are buffered, but what I do know (and said in my previous post) is that it calls <i>putchar</i> to output each character to the UART. That's why I said it is easiest to alter <i>putchar</i>!


Best regards,
Rob.

5

Re: [8051] printf to anything

hello!

thanks for your great help and good ideas. i was playing around with the printf-routine itself and found out that the vprintf (which accepts va_lists) doesn't print any floats. you're right; it's easiest to alter putchar, so i found a completely different (dirty?) method. it uses a pointer to a real putchar-function.

#include<stdio.h>
#include<uart_lib.h>

//important Preprocessor-Instruction; replaces uart_printf by a normal printf
//with an additional line before (set ptr_putchar)
#define uart_printf 
  ptr_putchar=uart_transmit; 
  printf

void (*ptr_putchar)(char c); //pointer to the putchar-routine which is used

int putchar(const int c) //actual putchar-routine; is called from printf
{ ptr_putchar(c); //redirect to the device-dependant putchar-routine (pointer ptr_putchar
  return c;       //points to it)
}

void main()
{ int inumber=23;
  char* str="abcd";
  
  uart_init(3); //initialize the UART
  for(inumber=0;inumber<100;inumber++)
  { //print something to the uart
    uart_printf("this is a test %02d %f %sn",inumber,1.332,str);
    /*--> becomes:
    //set Pointer to device-depandant putchar-routine
    ptr_putchar=uart_transmit;
    //call printf with the givern Parameters
    //(putchar is used for output)
    printf("this is a test %d %1.2f %s",23,3.2,str);
    
    the brackets are important as the preprocessor generates two
    lines out of the single instruction (uart_printf)
    */
  }
  while(1);
}

so, putchar doesn't need to know about all devices; other preprocessor-directives aren't needed.
i've already tested it and it works great.

greets, christian hartl

Re: [8051] printf to anything

Hi Christian,

Hope its not too late to share ideas!
I have been working on a similar problem. I have found:

a) vsprintf() and vprintf() do not print floats.
b) stdarg() macro seem to corrupt stack e.g.
main()
{
    myprintf( "Hello world" );

void myprintf( const char *sFmt, ... )

will crash but

main()
     dosomework();

void dosomework(void)
{
   mytprintf( "Hello world" );
}

seems OK.

How did you get on, did you see this issue? Did you find a solution to enable float prints?

Re: [8051] printf to anything

Hello,

i have the same issue with vsprintf and float support. Is their a way to add float support to vsprintf() ?

Best Regards
Bernhard

Re: [8051] printf to anything

Hi,
Write it!
There might be a patch now but I doubt it.

I removed all floats. I cannot remember if sprintf() works with floats. I think not but if it does try
something with macros e.g.:
vprintf( "%s plus %s equals %s", F_TO_STR(a), F_TO_STR_2(b), F_TO_STR_3(a+b))
The macros would sprintf() into a file scope / global and return that string.
You need storage for each float, obviously.

I was trying to dump readings to a serial port.
I ended up using %x and %u. I was just dumping ADC readings.
I learnt to add Hex in my head along the way!
If your just logging on a serial port then the cool way is to write yourself a C# PC program which acts as a front end and decodes your Hex.
You will get better throughput and have an excuse to use C#.

Last edited by Peter (2009-03-20 12:06:52)

Re: [8051] printf to anything

Hi Peter,

I tested sprintf() and the function works with float values.
The solution with macros sounds interesting. It would be very nice if you can give me more information on how I can solve my problem with macros.
Using hex instead of float values is a good idea, but I have to store float values.

Best Regards
Bernhard

Re: [8051] printf to anything

Hi,
First up, are you absolutely sure that sprintf() works?
I took all floats out. Cannot remember if that was because of library size or corruption but I think corruption.

The macro isn't a magic solution and won't do any more than
char strF[32];
sprintf(strF, "f", (a_float)
You then simply reference strF and you have a pointer to the character string.
As all the fun is writing the code, I won't write the code for you! Remember that you need as many stores as items which you print and go on stack.
Out of interest, why do you have to store float values? Floats are big and slow.

Re: [8051] printf to anything

Dear All,

Raisonance RTL printf/sprintf fully support floats (both BCD and IEEE). In fact they share the same code.

The provided vprintf/vsprintf currently don't due to a library build bug (it seems that 80C51 customers don't use it very often).

As vprintf/vsprintf are implemented in C, we can freely send you the sources.
Just email support@raisonance.com

Regards,

Stéphane

Re: [8051] printf to anything

Dear Stéphane,

Thanks for the tip!
I emailed the support and got the sourcecode, but how can I replace the old function vsprintf() with the new one?
I am not sure how I can do that. I thought, maybe I have to comment the line "extern int        vsprintf ( char *, const char *, va_list ) reentrant;" in the stdio.h file and add the new vsprintf() C - sourcecode to my main.c file.
I can compile the project, but the function vsprintf() doesn't work correctly.
I don't have much experience with C libraries.

@Peter: I know, that the float support needs a lot of memory, but i don't know how I can get rid of it. I have to store "the real" float values on a USB flash memory.

Re: [8051] printf to anything

Hi,
You need to compile the file with the correct compiler flags/memory model etc.
I would build everything. Once you're built then I would try:
Ride IDE->Tools->Library Manager
You will know which ones to delete if you look at your linker's .m51 file and look for which libraries containing routines for which you have the source e.g. vprintf() .
Add the supplied code to your project and build.
Once you have a working build then you can use Library Manager to add your rebuilt library code to the library.
I would do it this way so that you are certain that you have removed everything, library's do not complain about duplication, they just use the first definition.
I am impressed that you are trying to log to a USB memory device. Hope you have a good library and support.
If all else fails change the requirements, give then an Excel macro which loads the data and converts Hex to floats etc.
wink