An easier way of doing this?

Go To Last Post
24 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For diagnostic purposes I need to print the supply voltage to the board before the regulator which has been arranged to read in ADCH as voltage*10 by a voltage divider.

I tried making supply_voltage double and do the nice %2.1f thingy but the code grows,expectedly, by about 2K. :(

This works but seems unecessarilly clumsy.

	printf("Supply voltage %2d.%1dV\n\r",(supply_voltage/10),(supply_voltage%10));

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You've looked at the output listing and have seen that the 2k increase is from printf? If so, why not avoid printf and use utoa() instead?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
use utoa() instead
ah?? No wonder I could not find it in "the book". It is part of "Non-standard (i.e. non-ISO C) functions. :shock: :shock:

Ok I'll look into that, see how it works.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using printf() anyway there is no advantage in trying to avoid it.

   printf("Supply voltage %2d.%1dV\n\r",(supply_voltage/10),(supply_voltage%10));

If you are using floating point printf() elsewhere then this is the neatest solution. It is clear, readable
and does the appropriate casting.

   printf("Supply voltage %4.1fV\n\r", 0.1 * supply_voltage);

The alternative is a helper function that scales and formats into a static buffer.

   printf("Supply voltage %sV\n\r", float_display(supply_voltage));

If your program will fit into your selected AVR there is little point in contortions to minimise size. It only matters if you have to change to a bigger device.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not using floaties anywhere but using printf everywhere, at least in the setup/diagnostic interface to a terminal program.

Don't know how much the real, final code will be. So far just under 8K of a M164p, goes to about 9.7K if the floating point printf is used. The setup interface is pretty much finished (lotsa strings). The main program is running in bare minimum mode, now will need to add all the bells and whistles. And to this I will need to fit the bootloader (other thread running elsewhere).

I think I will use a M324p anyway, just could not get one in time when I built the prototype. I can see the usual feature creeps already. I make provisions for something, just in case, and the "case" seems a good idea that needs to be implemented. :?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's an example how I did it in a project of mine. Hope my comments speak for themself. If not, don't hesitate to ask.

// Parameter value_E1 Must Be In The Range Of -999 To +9999
// Examples:
// 1234 Is Converted To "123.4"
//  321 Is Converted To " 32.1"
//   12 Is Converted To "  1.2"
//    2 Is Converted To "  0.2"
//   -1 Is Converted To " -0.1"
//  -21 Is Converted To " -2.1"
// -123 Is Converted To "-12.3"
void int16_E1_to_string (int16_t value_E1, char *destination)
{
    // Check  If int16 Value Is Negative
    if (value_E1 < 0)
    {
        // Right Alignment: Add ' ' If Value Is > -10.0
        if (value_E1 > -100)
        {
            *destination++ = ' ';
        }
        
        // Add Leading Zero If Value > -1.0
        if (value_E1 > -10)
        {
            *destination++ = '0';
        }
    }
    // int16 Value Is Positive
    else
    {
        // Right Alignment: Add ' ' If Value Is < 100.0
        if (value_E1 < 1000)
        {
            *destination++ = ' ';
        }
        
        // Right Alignment: Add ' ' If Value Is < 10.0
        if (value_E1 < 100)
        {
            *destination++ = ' ';
        }
        
        // Add Leading Zero If Value < 1.0
        if (value_E1 < 10)
        {
            *destination++ = '0';
        }
    }
    
    // Convert int16 Value Into A String (Base = 10)
    itoa (value_E1, destination, 10);
    
    // Set Pointer To String Termination Character '\0' + 1
    while (*destination++ != '\0');
    
    // Insert Decimal Point
    destination[-1] = destination[-2];
    destination[-2] = '.';
    destination[0] = '\0';
}

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are intending to use a 324p then there is no problem with hauling in the float printf(). It will make your code clearer and easier to maintain.

Sebastien's example shows how easy it is to make and use a helper function.

You could also use dstrtof()

#include 
char buf[10]; // need a big enough buffer for dstrtof()
printf("Supply voltage %sV\n\r", dstrtof(0.1*supply_voltage, 4, 1, buf));

This avoids using the float printf(). But quite honestly your original code is neater in my opinion.

You can always put your debug code inside a D((anything)) macro. Then you just compile with the debug code expanding to nothing in your production version.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
your original code is neater in my opinion.
:oops: awhh shucks..:oops:

I hope it is not something like when my neighbours say "your garden looks nice John" when they mean "you finally mowed the lawn after six months, you sloth." :lol:

As I only need this thing in one location I may stick with what I have. I looked at the utoa() thingy but the manual says that:

Quote:
Note that these functions are not located in the default library, libc.a, but in the
mathematical library, libm.a. So when linking the application, the -lm option needs
to be specified.
Does this mean loading up another largish library? Or just one small function of that library?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I hope it is not something like when my neighbours say "your garden looks nice John" when they mean "you finally mowed the lawn after six months, you sloth." Laughing

Well, personally, I'd be with your neighbor in pulling in floating point just for one displayed value.

Re the / and % --it does kind of irk ones sensibilities and look wasteful, doesn't it? Note that nearly all division routines will have the remainder floating about in a register just aching to be used. If you go through the hoops of div_t structure return you can get both pieces with one division.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
all division routines will have the remainder floating about in a register just aching to be used.
But that would be like working in asm, how arcane. :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
Does this mean loading up another largish library? Or just one small function of that library?
Just the smallest function gets extracted from the library and linked to your output file.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
But quite honestly your original code is neater in my opinion.
I think his original code is neater as well. And while the subject is "an easier way of doing this", the post talked more about concerns with binary size, not the simplest source code. Simplest, as has beenbeen shown is using floating point, but would also be the largest (assuming John's not using printf or floating point for anything else in his application). Given John's long and successful assembly language history, my suspicion is that he wants the smallest binary. But, he's gotten a lot of options from this thread where he can balance source code simplicity and output size.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
he can balance source code simplicity and output size.

...and portability...the KEY factor with C :-)

I think the original code, unsightly as it may be, should be usable with all compilers???

Anyway, I'll experiment with other ways later on as a learning exercise. I thought there might have been some magical way of doing it that I was not aware of, well apart from using floaties.

I hope you all appreciate the hystorical (or is it hysterical) significance of this thread. My first program fully in C! It will be remembered with the likes of the invention of the electric bulb etc... :lol:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
My first program fully in C! It will be remembered with the likes of the invention of the electric bulb etc...

And then the Assembly Police are going to come to your house, tie you up, and beat you with that garden hose.

You'll walk by a Coke machine and the "sold out" light will come on.

And all your old friends, Mr. Push, Mr. Pop, Mr. Vector, and Ms. Take will shun you as you sit on the park bench, alone, confused, and lost.

But don't worry. You'll be portable. Sort of. Like a pig sort of flies.

[This message brought to you by your guilty conscience]

ps: congratulations!

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   printf("Supply voltage %2d.%1dV\n\r",(supply_voltage/10),(supply_voltage%10));

Quote:
I think the original code, unsightly as it may be, should be usable with all compilers???

Yes, John. This should work with any compiler.

So should the floating point method but is going to require a "full featured printf()" being linked.

Using dstrtof() or utoa() may not always be available. But these functions are not difficult to write yourself.

I am impressed that your approach to C is to achieve portability. Hardly the correct approach from an ASM programmer.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I am impressed that your approach to C is to achieve portability.
Actually I'm having a bit of fun with the esponents of C as being portable. :mrgreen: ...but they know that and will not get mad at me for bringing this up again.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
[Actually I'm having a bit of fun with the esponents of C as being portable.
When people refer to C as a portable, high-level assembler, as I think that you know, they are they use the word "portable" as meaning "much more portable than assembler itself".

BTW, if you need a version to utoa written in pure, portable C, I have a version I wrote that I can post for you in case you want your own version for portability sake. As you're using avr-gcc, at the moment though, it has its own optimized, assembly language versions that are included in its library.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
Quote:
all division routines will have the remainder floating about in a register just aching to be used.
But that would be like working in asm, how arcane. :wink:

No, John--I'm not wishing that upon you or anyone else.

One must be aware of what is provided in the standard libraries of your C compiler. ;) In this case, examine the div() family of functions, which live in stdlib.h IIRC.

Quote:
7.20.6.2 The div, ldiv, and lldiv functions
Synopsis
1 #include
div_t div(int numer, int denom);
ldiv_t ldiv(long int numer, long int denom);
lldiv_t lldiv(long long int numer, long long int denom);
Description
2 The div, ldiv, and lldiv, functions compute numer / denom and numer %
denom in a single operation.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So is there "An easier way of doing this? " The code increases by 68 bytes (or an entire program in asm... :? )

	read_supply_voltage();
//	printf("Supply voltage %2d.%1dV\n\r",(supply_voltage/10),(supply_voltage%10));  //Program:    7940 bytes (48.5% Full)

	div_t volts;
    volts = div(supply_voltage, 10);
	printf("Supply voltage %2d.%1dV\n\r",volts.quot,volts.rem);  //Program:    8008 bytes (48.9% Full)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So things seem to get worse and not easier. :(

This prints supply_voltage but not in 2.1 format, obviously. I guess I would need to do a bit of string manipulation or do a for loop to print 2 chars then a full stop then the last char, making the whole thing even messier.

	char buffer[10];
	printf( "Supply voltage %s", utoa(supply_voltage,buffer, 10 ); //Program:    8040 bytes (49.1% Full))

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
This prints supply_voltage but not in 2.1 format, obviously. I guess I would need to do a bit of string manipulation or do a for loop to print 2 chars then a full stop then the last char, making the whole thing even messier.
There's no doubt it takes more source code to use utoa (or its relatives itoa, ltoa, ultoa) vs. printf. But, since you're still using printf to print the string in your last example, you're not saving any space but using more. If you really want to save space (and you're not otherwise going to use printf() anywhere else in your program, you'd want to do something like this (simplified to knowing there are 2 integer digits and one fractional digit:
char buf[6]; // large enough so that utoa will never overflow it
utoa(val, buf, 10); // WARNING: output must be 3 digits, LSB behind decimal point
lcd_puts("The value is ");
lcd_putc(buf[0]);
lcd_putc(buf[1]);
lcd_putc('.');
lcd_putc(buf[2]);
lcd_puts(" units");

Congratulations on your first C program. Personally, as your first C program, unless you really want to conserve every byte, go ahead and by crazy and use the convenience of printf (unless you are trying to fit in some tiny AVR)..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
unless you really want to conserve every byte,
You are so wasteful. BITS you MUST conserve bits. :lol: I can't help it, it's like some people of some nationality known for their frugality and a cent is a LOT of money.

printf is being used quite a lot in the same module, I guess you can see for yourself. :) (discard the dB reading as it is just the ADC read off the mic amp and not adjusted yet)

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I see a lot of output, but you can get that with puts and putc as well as well. As I said, unless you really need to conserve every bit, go ahead and use printf and enjoy the convenience.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John, are you becoming a decadent C junkie? :lol:
What's coming next? Switching from ATtiny13 to ATmega2560 just because you want to use a OS? :lol: :lol: :lol:
Enjoy, the luxury of the C language! You had to crawl far too long!!! :wink:

Regards
Sebastian