Reducing code size on M48

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

Hi Freaks,
I have completed a program for an app. on the Atmega48 and my code size is 110.7%.

I have added the libm.a library to my linker.

I use a floating point calculation as well as a printf in my program. I am measuring a <0 ADC value and sending it to the UART through a printf statement. I am also using a division statement to get the floating point value as follows:

  ADC_buffer_dummy = (float)(ADC_dummy * 5)/1023;

  a = ADC_buffer_dummy/1;
    if(a == 1) 
    // if ADC_buffer_dummy value is less than one
   //say for e.g. ADC_buffer_dummy = 0.547           //then a = 0, b = 0.547 * 1000 = 547
  //hence the else statement

         b = (ADC_buffer_dummy - 1)*1000;
         else
	 b = ADC_buffer_dummy * 1000;

For the printf statement I am using the FDEV_SETUP_STREAM routine. It is working fine and I would like to try and stick to this rather than make any last minute changes.

When I remove the division statement:

ADC_buffer_dummy = (float)(ADC_dummy * 5)/1023;

my code size drops to 85%.When I remove the division it does not change much. When I remove the float it does not change much either, it is only when I completely remove the statement above, I get the code size drop to 85%.

Now I am trying to rewrite this logic to eliminate the divide and the float but I am looking for three decimal places for my values (0.xxx).

Any ideas of how I can reduce code size? Getting another chip with larger memory is also an option but that will be my last resort.

Thanks.

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

do all calculations using integers. then convert to a string and add the comma before sending the data over.

calculations inside the avr you can do by making clever formulas that only use the int....

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

Quote:

When I remove the float it does not change much either, it is only when I completely remove the statement above, I get the code size drop to 85%.

That means float point operations are not performed and your code size is smaller.

My advice is try to use different printf library without all the features of the standard printf.
Add:

-Wl,-u,vfprintf -lprintf_min

to the linker parameters. This is minimal version only integers and strings, so to print float you must use appropriate functions to get what is before and after decimal point.

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

In a 4K microcontroller are you sure you want to use either float or printf() anyway? It is very rare that you cannot simply get away with scaled integers and some simple charcter/string/number output routines.

If you want 3 decimal places just multiply everything up by 1,000. So 3141 really means "3.141". All you need do is place the '.' 3 places from the end when you come to finally output the value.

Cliff

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

Try adding

-fdata-sections
-ffunction-sections

to compiler settings, and

-Wl,--gc-sections

to the linker Options. That might help reduce code size.

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

But those options won't help in this case. If I write the minimal mega48 program to use printf() with floats (using libprintf_flt.a):

#include 
#include 

int main(void) {
	printf("% f", 1.2345678);
}

I get:

Program:    3078 bytes (75.1% Full)

If I then force the FP lib to be used using:

#include 
#include 

int main(void) {
	printf("% f", PINB * 1.2345678);
}

It gets worse:

Program:    4870 bytes (118.9% Full)

It makes no difference to those figures using the -section options.

In fact my guess is that npat_avr was not linking with libprintf_flt.a so his figures would actually be worse.

Cliff

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

Quote:

If I then force the FP lib to be used using:
...
It gets worse:
Code:
Program: 4870 bytes (118.9% Full)

As we've gone through similar before with "Hello, world!" and others, I popped the program into CodeVision and got similar results: 4500 bytes. In CV the float requires the largest printf() flavour.

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

I tried to post today morning but somehow I couldn't post. Anyway I changed my code to remove the float and the division but I do have the printf statement. The program memory is at a happy 82% now. I know I can reduce it even more but this is ok for now. I changed the following:

ADC_buffer_dummy = (float)(ADC_dummy * 5)/1023;

  a = ADC_buffer_dummy/1;
    if(a == 1)
    // if ADC_buffer_dummy value is less than one
   //say for e.g. ADC_buffer_dummy = 0.547           //then a = 0, b = 0.547 * 1000 = 547
  //hence the else statement

         b = (ADC_buffer_dummy - 1)*1000;
         else
    b = ADC_buffer_dummy * 1000; 

to

		ADC_buffer_dummy = ADC_dummy * 5; // both are ints so no problem

                   a = ADC_buffer_dummy/1000;
 	           b = ADC_buffer_dummy%1000;

This works fine the way I have set up my ADC and my reference voltage.Thanks for your help,guys.

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

Use the utoa() then a char ptr to the buffer in a loop that prints 1st # then '.' then the rest of the # and it's REALLY done ( about 200 bytes ttl ! ). With what you have now you can't do anything else because of the printf().

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Thu. Oct 20, 2011 - 10:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For formatted output of an integer like float you can use this:

Peter

Attachment(s): 

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

Noting that 5/1023 = 5.0049/1024, a way to do such a calculation using integer math might be

long val = (long)ADC_reading * 50049;
val += 512;  // if you want to round
val >>= 10;  // divide by 1024

Now you have an integer value with 4 digits after the decimal point. For example, an ADC reading of 500 (out of 1023) gives a result of 24437, or a reading @5V Aref of 2.4437v, with no floating point libraries required.

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

In assembly you would simply drop the lower byte and shift only two bits. I wonder whether the compiler is that smart too.

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

Quote:

Use the utoa() then a char ptr to the buffer in a loop that prints 1st # then '.' then the rest of the # and it's REALLY done ( about 200 bytes ttl ! ). With what you have now you can't do anything else because of the printf().

I gotta ask - why did you reply to a TWO YEAR OLD thread to resurrect it from the dead??

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

Oops, sorry crew !! I HATE when that happens...!!!

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

jayjay1974 wrote:
In assembly you would simply drop the lower byte and shift only two bits. I wonder whether the compiler is that smart too.

Actually you can set your multiply constant for >> 8 or even no shift at all.

5/1023 * 256 = 1.2512, so multipy by 12512 and >> 8.

5/1023 = 0.0048876, so multipy by 48876 with no shift. Now your result will have 7 digits right of the decimal point, and you can ignore 3 or 4 of them given the resolution of the original ADC value.

500 * 48876 = 24438000 (2.4438000)

My choice in this case would probably be the >> 8 constant, so I wouldn't have to deal with discarding the extra digits before displaying the result.

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

I tried >> 10 and >> 8 in Studio, and >> 10 is awful, doing 10 shifts of 4 bytes. >> 8 does the smart moving of the bytes and zeroing the high byte.