Author Topic: C function like printf but not sending to stdout? (XC8)  (Read 39191 times)

0 Members and 1 Guest are viewing this topic.

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: C function like printf but not sending to stdout? (XC8)
« Reply #50 on: March 12, 2013, 07:45:56 am »
Note that if you're not using printf for anything you can override the putch() function to redirect the output somewhere else.

Offline David_AVDTopic starter

  • Super Contributor
  • ***
  • Posts: 2797
  • Country: au
Re: C function like printf but not sending to stdout? (XC8)
« Reply #51 on: March 12, 2013, 08:49:39 am »
I will be using serial comms in due course, but that's good to know anyway.
 

Offline dfnr2

  • Regular Contributor
  • *
  • Posts: 240
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #52 on: March 12, 2013, 06:53:21 pm »
To answer your original question, there is no stdout.  the printf() function you get with xc8 calls putch(), which you can define.  So if you rename your lcd_char() function to putch(), then calling printf will print to the lcd.

Having said that, printf() is usually not used with these tiny chips.  You can put it in if you have lots of space, but as people have indicated, writing your own number->string functions is much more efficient.  For what you're doing, copying strings, etc. is unwarranted, and way too busy.  Better to have little routines tailored to your output.  For example, for an 8-bit unsigned, you could use a dedicated routine such as print_dec_8(uint8), like so:

uint8 val;
val = some_stuff();
lcd_print("The number is: ");
lcd_print_dec_8(val);
...

Code: [Select]
/* PROCEDURE:lcd_print_dec_u8
 * INPUTS: val (uint8): 8-bit unsigned value to be printed
 * OUTPUTS: none
 *
 * DESCRIPTION: Prints argument, in decimal, to the serial port, using
 * lcd_char().
 */
void lcd_print_dec_u8(uint8 val)
{
    uint8 hundreds = 0;
   
    /* If > 100, only two choices for most sig digit. */
    if (val >= 200) {
        hundreds = 2;
        val = val - 200;
        lcd_char('2');
    } else if (val >= 100) {
        hundreds = 1;
        val = val - 100;
        lcd_char('1');
    }

    /* calculate the tens digit, if present, and print.*/
    if ((0 != hundreds) || (val >= 10)) {
        uint8 tens = 0;

        while (val >= 10) {
            val -= 10;
            tens++;
        }
        lcd_char(tens + '0');
    }

    lcd_char(val + '0');
}

« Last Edit: March 12, 2013, 06:58:05 pm by dfnr2 »
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: C function like printf but not sending to stdout? (XC8)
« Reply #53 on: March 12, 2013, 08:37:57 pm »
The XC8 compiler will actually generate an optimized printf based on the format strings used, so the results should be more compact than a typical library implementation. Doing the work manually may still be more efficient, but it depends on usage.

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #54 on: March 12, 2013, 10:58:33 pm »
... writing your own number->string functions is much more efficient.  For what you're doing, copying strings, etc. is unwarranted, and way too busy.  Better to have little routines tailored to your output...
Pardon me, but: Yuck.

Re-inventing the wheel and all that.
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2905
  • Country: gb
Re: C function like printf but not sending to stdout? (XC8)
« Reply #55 on: March 12, 2013, 11:02:37 pm »
Quote
Re-inventing the wheel and all that.

Agree, and once you've called (v)(s)(n)printf a few times the overhead will start to look cheap given the convenience.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #56 on: March 12, 2013, 11:31:33 pm »
Quote
Re-inventing the wheel and all that.

Agree, and once you've called (v)(s)(n)printf a few times the overhead will start to look cheap given the convenience.
Touché.
 

Offline melonstorm

  • Newbie
  • Posts: 7
  • Country: ../cake
  • Becomes 200% fluffier when statically charged!
Re: C function like printf but not sending to stdout? (XC8)
« Reply #57 on: March 13, 2013, 09:06:27 pm »
There are also size-reduced implementations of (s)printf available, like this LGPL-licensed one. If you need sprintf in your project and don't want to use the standard library's comparatively large implementation, you could choose to use something like this.
 

Offline David_AVDTopic starter

  • Super Contributor
  • ***
  • Posts: 2797
  • Country: au
Re: C function like printf but not sending to stdout? (XC8)
« Reply #58 on: April 15, 2013, 05:21:49 am »
My next issue is not getting the leading zeros like I though this would:

Code: [Select]
sprintf(lcd_buffer,"%3d",sw_result);
I thought this should give me a string "000" ... "255", but it seems to still be "0" ... "255" (no leading zeros).
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 11790
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #59 on: April 15, 2013, 05:32:31 am »
My next issue is not getting the leading zeros like I though this would:

Code: [Select]
sprintf(lcd_buffer,"%3d",sw_result);
I thought this should give me a string "000" ... "255", but it seems to still be "0" ... "255" (no leading zeros).

Have you tried "%3.3d" as the format string?
 

Offline ecat

  • Frequent Contributor
  • **
  • Posts: 296
  • Country: gb
Re: C function like printf but not sending to stdout? (XC8)
« Reply #60 on: April 15, 2013, 05:38:22 am »
Some printf implementations provide only limited options, still the format specifier for 001, 002 etc is "%03d"

printf("%03d", 5);

Should result in 005



 

Offline David_AVDTopic starter

  • Super Contributor
  • ***
  • Posts: 2797
  • Country: au
Re: C function like printf but not sending to stdout? (XC8)
« Reply #61 on: April 15, 2013, 05:39:55 am »
My next issue is not getting the leading zeros like I though this would:

Code: [Select]
sprintf(lcd_buffer,"%3d",sw_result);
I thought this should give me a string "000" ... "255", but it seems to still be "0" ... "255" (no leading zeros).

Have you tried "%3.3d" as the format string?

Ah, that's got it.  I read the XC8 (and other) docs several times and didn't understand that was how it worked.  Thanks.
 

Offline David_AVDTopic starter

  • Super Contributor
  • ***
  • Posts: 2797
  • Country: au
Re: C function like printf but not sending to stdout? (XC8)
« Reply #62 on: April 15, 2013, 05:40:35 am »
Some printf implementations provide only limited options, still the format specifier for 001, 002 etc is "%03d"

printf("%03d", 5);

Should result in 005

Should, but doesn't for some reason.  The "%3.3d" does work however.
 

Offline Rick Law

  • Super Contributor
  • ***
  • Posts: 3423
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #63 on: May 11, 2013, 12:09:18 am »
People keep telling me how C code is so much quicker to write than assembler, but I'm old (actually middle age!) and have been somewhat set in my ways.

I'm sure I'll get better at writing C code, but it's painfully slow at present.

I've been pondering whether the standard or pro version of XC8 would be the way to go.  Code space it probably not so much of an issue, but some projects I do use DMX and some high speed SPI data streaming so speed of execution can be important.

David, yeah it is worth the trouble.  C is a great language to develop program with.  Do watch out, with C, it doesn't protect you against yourself.

To quote someone: "If it is absolutely dummy proof, it would be in a form only absolute dummy will use."

With C, it is not dummy proof, you have to watch you are doing, but it let you do what you want to do and assumes you really know what you are doing.

Rick
 

Offline Rick Law

  • Super Contributor
  • ***
  • Posts: 3423
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #64 on: May 11, 2013, 12:34:23 am »
My lcd output routine was adapted from something I found on the 'net:

Code: [Select]
void lcd_outstr(const char *buffer)
{
while(*buffer)              // Write data to LCD up to null
{
    lcd_char(*buffer); // Write character to LCD
    buffer++;               // Increment buffer
}
}

Does that make sense with the method of calling it?

David_AVD,

Using this as an opportunity to show you why C is powerful but you have to watch out.

First, incase you don’t know the two cases of using ++.
int   counter=0;
int   another;

another = ++counter;  // here counter is incremented before assigning to variable
another = counter++; // here counter is incremented after assigning to variable

The while() statement you have does not check buffer size.  A better implementation is:

void lcd_outstr(const char *buffer)
{
int   ii=0;
int   maxSize=100;  // hard coded max, but can be parameter driven
   while(*buffer && ++ii<maxSize)   // Write upto terminator or maxSize bytes of data to LCD
   {
       lcd_char(*buffer);       // Write character to LCD
       buffer++;               // Increment buffer
   }
}

I added a counter so if a proper string terminator is NOT found, it will not output more than 100 characters.  Lacking the counter and if the string is not properly terminated, it will just keep printing until the CPU hangs or when other bad things happens.

Also, I never use single character variables.  If you have “int i;”, generic string search is useless.  You will find tons of ‘i’ unless you use full word/variable-name type of search.  If you use ‘ii’ or ‘jj’ or ‘zz’, they are much easier to find.

Rick
 

Offline David_AVDTopic starter

  • Super Contributor
  • ***
  • Posts: 2797
  • Country: au
Re: C function like printf but not sending to stdout? (XC8)
« Reply #65 on: May 11, 2013, 12:52:07 am »
Good stuff Rick.  Thanks.
 

Offline Rick Law

  • Super Contributor
  • ***
  • Posts: 3423
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #66 on: May 11, 2013, 12:57:30 am »
I think I'm on the right track now thanks to you guys.

One more question; If there is only one statement to execute in the for loop, are the braces around that statement still required?

David_AVD,

c4757p already pointed out that you don't really need the { } except for multiple statements.  An additional useful technique is comma which continues the calculation statement.  When comma is used, the last statement has the final value and that is used.
ii = 1, 2, 3, 4 ;   // ii is assigned 4, the last evaluation done

original:
while (*buffer) {
    lcd_char(*buffer);
    buffer++;
}


another version with comma to make that one statement:
while (*buffer) lcd_char(*buffer), buffer++;

another version with for ( ; ; )
for ( ;*buffer; lcd_char(*buffer), ++buffer ) ;
Explaination
for ( ; // this first semicolon with stuff between ( ; means no initialization needed
for ( ; *buffer;  // between the first and second semicolon is the condition to
                      // remain looping, so here is checking to see if current buffer char is
                      // not null before continue loop
for ( ;*buffer; lcd_char(*buffer), ++buffer )
                     // after the last semicolon, there is the end of a loop closing statement
                     // before end of the loop, do the lcd_char.  Since it is a comma, it continue
                     // the calculation and do the buffer pointer increment to point at the next char
for ( ;*buffer; lcd_char(*buffer), ++buffer ) ;
                     // the semicolon after the ) saids I have an empty statement
                     // there is nothing to left to do


Now one with size limit:

int  ii,newSize;
for (ii=0, newSize=100 ; *buffer & ii<newSize; ii++, lcd_char(*buffer), ++buffer);

I leave it for you to figure out the intialization, the continue-loop checking, and the loop end check.

Good luck with your C learning.  It will be a fun ride.

By the way, note this joke, it highlights some C programming facts:

When a C programmer brought a dozen eggs, how many eggs did he just buy?
The answer is 11.

0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11
Only 11.  C programmer always start counting with zero.

Rick
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: C function like printf but not sending to stdout? (XC8)
« Reply #67 on: May 11, 2013, 02:02:18 am »

c4757p already pointed out that you don't really need the { } except for multiple statements.  An additional useful technique is comma which continues the calculation statement.

I have been writing C for nearly 30 years and don't remember the last time I used a comma operator. I might have kludged an extra assignment into a for statement or macro using it but I can't think of an example. It is hardly useful.

I would recommend keeping braces around single statements because a consistent format is easier to read and forgetting to add braces if you add another statement screws up so badly without looking obvious.
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: C function like printf but not sending to stdout? (XC8)
« Reply #68 on: May 11, 2013, 02:10:38 am »
The only use that I can see for a comma operator is combining expressions in a for loop. (I wouldn't really call it a kludge. I've seen some very clear and elegant uses of that to emulate more sophisticated looping systems like in higher-level languages, and they didn't seem kludgey at all.) The only other use I can think of for the comma operator is intentional obfuscation.

I would recommend keeping braces around single statements because a consistent format is easier to read and forgetting to add braces if you add another statement screws up so badly without looking obvious.

I disagree. If it doesn't look obvious when you forget to add braces you need to use a better editor. There's nothing wrong with doing it if you find that it makes future extension easier, but I often find myself making enough single-statement blocks that it saves me more time to omit them than otherwise. Obviously, the time saved either way is minuscule.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline megajocke

  • Regular Contributor
  • *
  • Posts: 191
  • Country: 00
Re: C function like printf but not sending to stdout? (XC8)
« Reply #69 on: May 11, 2013, 02:19:06 am »
An additional useful technique is comma which continues the calculation statement.  When comma is used, the last statement has the final value and that is used.
ii = 1, 2, 3, 4 ;   // ii is assigned 4, the last evaluation done

Actually, that's not the result. Assignment has higher precedence than the comma operator, so this is the same as doing:

(ii = 1), 2, 3, 4 ;

which has the result that ii is assigned the value of 1, whereafter the expressions 2, 3 and 4 are evaluated with the results being thrown away, essentially becoming no-ops. The compiler might give you a warning that you have expressions without side effects if compiling this code.

However, if you write

ii = (1, 2, 3, 4) ;

you get the result claimed before.
« Last Edit: May 11, 2013, 02:22:07 am by megajocke »
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: C function like printf but not sending to stdout? (XC8)
« Reply #70 on: May 11, 2013, 02:37:41 am »
I disagree. If it doesn't look obvious when you forget to add braces you need to use a better editor. There's nothing wrong with doing it if you find that it makes future extension easier, but I often find myself making enough single-statement blocks that it saves me more time to omit them than otherwise. Obviously, the time saved either way is minuscule.

My editor of choice adds braces as I type so I would have to delete them for single statements. Time is insignificant, screen space less so but I value consistent appearance over space.

As for for loops they are just shorthand for a commonly used form of while statement, kludging less common forms of while into them with comma operators is more confusing than just using while.
 

Offline glatocha

  • Regular Contributor
  • *
  • Posts: 114
Re: C function like printf but not sending to stdout? (XC8)
« Reply #71 on: May 11, 2013, 03:01:39 am »
I am always scared to use library functions.
They might cover much more than I need and cause a high memory usage.
I prepare most of my routines on myself. For example for LCD Integer display:

Code: [Select]
void LCD_Int(int16_t num)
{
    int16_t div;
    uint16_t i;
    if (num <0)
    {
        LCD_SendCHAR_4b('-');
        num = num * -1;
    }
    for (i=10000;i>=10;i=i/10)
    {
        if (num>=i)
        {
            div = num/i;
            num = num%i;
            LCD_SendCHAR_4b(div + '0');
        }
    }
    LCD_SendCHAR_4b(num + '0');
}

Same I have for doubles.
I am also planning to rewrite most of it in asm and maybe get rid of dividing.

I started programming with atmel 2051 with 2kb Flash and 128 RAM bytes. So still have in mind always optimize.
But maybe nowadays much cheaper is to just update the chip with higher memory version of 0,10$.
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: C function like printf but not sending to stdout? (XC8)
« Reply #72 on: May 11, 2013, 03:08:23 am »
As for for loops they are just shorthand for a commonly used form of while statement, kludging less common forms of while into them with comma operators is more confusing than just using while.

I've always seen a "for" loop as being iterative over a sequence, and a "while" loop as being repetitive conditional on an external state - for example, while the input given is invalid, continue giving an error message and requesting it again. I'd rather use a "for" loop to loop over some sort of container, even if the top of the loop is a little bit ugly, as it makes the intention clear.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 11790
  • Country: us
Re: C function like printf but not sending to stdout? (XC8)
« Reply #73 on: May 11, 2013, 03:12:04 am »
void lcd_outstr(const char *buffer)
{
int   ii=0;
int   maxSize=100;  // hard coded max, but can be parameter driven
   while(*buffer && ++ii<maxSize)   // Write upto terminator or maxSize bytes of data to LCD
   {
       lcd_char(*buffer);       // Write character to LCD
       buffer++;               // Increment buffer
   }
}

A good practice when writing code is to replay the code in your mind and see if it does what you think it ought to do.

In the above example, suppose the buffer has size 1 (a thought experiment). What will happen?

First pass through the loop: buffer points to the one and only character so the while() test is successful and the character is printed.

Second pass through the loop: buffer was incremented and now points beyond the end of the buffer. It will print some unknown character to the LCD.

Note how the ++ii<maxSize test will not save it, since the && operator short circuits and if the first condition is true the second condition will never be evaluated. If this logic fails with a buffer of size 1, it will fail with a buffer of size 100.

Another goal of good code is to be clear and readable, so that human readers who encounter the code later on are readily able to understand what it is intended to do.  In light of that, I suggest this variant:

Code: [Select]
void lcd_outstr(const char *buffer, int buflen)
{
const char *endbuf = buffer + buflen;
while(buffer < endbuf && *buffer)
{
    lcd_char(*buffer);
    ++buffer;
}
}
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: C function like printf but not sending to stdout? (XC8)
« Reply #74 on: May 11, 2013, 03:23:21 am »
And I would suggest this variant:

Code: [Select]
void lcd_outstr(const char *buffer, size_t buflen)
{
size_t i;
        for (i = 0; i < buflen && buffer[i]; ++i) {
                lcd_char (buffer[i]);
        }
}

Or if you're tight on RAM in a microcontroller application, slightly less readable but saving a variable (that the compiler ought to optimize out anyway, but some compilers suck):
Code: [Select]
void lcd_outstr(const char *buffer, size_t buflen)
{
        for (i = 0; buflen && *buffer; ++buffer, --buflen) {
                lcd_char (*buffer);
        }
}

I wouldn't really recommend the second one, but it's not awful, I suppose.
« Last Edit: May 11, 2013, 03:24:59 am by c4757p »
No longer active here - try the IRC channel if you just can't be without me :)
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf