Memory usage of a simple project with POW function

This is my first post here, so in the beginning I'd like to say 'Hi' to all of you :wink:

I'm trying to complete a simple project, which includes controlling LED brightness. I noticed, that using writeAnalog with linear values doesn't give me a linear increase/decrease of the brightness. Quick internet search gave me a few answers, and I decided to try one described here

I wrote a quick test, but was surprised when saw upload error related too using too much memory (I'm using ATtiny13a). After some investigation, this time on my Uno, I was able to prepare minimal program, where the issue occurs. This is of course some artificial code, which doesn't do anything useful - it just shows the problem.

#define LED_PIN 0

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  analogWrite(LED_PIN, getValue(1));
  analogWrite(LED_PIN, getValue(2));
  analogWrite(LED_PIN, getValue(3));
  //analogWrite(LED_PIN, getValue(4));
}

int getValue(int value)
{
  return pow(2, value);
}

This code compiles and according to info, uses 922 bytes. After uncommenting the last analogWrite, usage jumps to 2952 bytes. If I remove pow function from getValue, and just return value, issue doesn't occur.

In addition, I observed that changing the loop to:

void loop() {
  for(int i = 0; i < 1; i++)
  {
    analogWrite(LED_PIN, getValue(i));
  }

  //analogWrite(LED_PIN, getValue(1));
}

compiles to 880 bytes. Changing 1 to 2 in for condition, causes usage jump to 2914 bytes, and then, uncommenting analogWrite, causes it to go to 922 bytes.

I have no idea what is going on here, and I really hope someone here can help me understand it...

I'm using Arduino IDE 1.8.13

Thanks!

looks like the ATtiny13a has only 1k of prog memory. pow() may involve log values requiring a lot of code

a power of 2 is easily determined using a shift operator

    return 2 << value;

Yup - I know this, but pow(2,value) is just a stub - in my real program I require e raised to a power depending on value.

Anyway - I just want to know what causes this specific memory issue.

Because you have to pull in floating-point handling.

I don't understand. You mean, that first three analogWrite(getValue(x)) differs from the 4th one? These use pow as well... Does compiler somehow optimizes these three calls, but the next one requires totally different handling according to compiler logic?

The ATtiny13a is not an official Arduino microcontroller. Are you sure that you want to use that ?

When testing with a Arduino Uno, you can try my millis_soft_pulsating_led.ino example and see if that seems to be a sine to the human eye.
There is a pow( 10, t) between the sine and the analogWrite().
That is not a power of 2, not even a power of e, but the full 10log. I think it looks good :sunglasses:

In the ATtiny13a you should avoid any floating point. A lookup table might work, perhaps between 10 and 20 bytes should be enough. Or use something mathematical, for example shifting as gcjr wrote.

About your question: I think you underestimate the compiler. When using float calculations with constants, the compiler can decide to do those calculations for you :stuck_out_tongue: There is a point that the compiler decides to calculate it runtime, and that is when the floating point library and the pow function comes in. You can not afford that in a ATTiny13a.

The optimiser probably pre-calculates the low values.

You could look at the generated code with avr-objdump

I thought about this as well, but replacing numbers 1,2,3,4 to any other numbers, doesn't make any difference.

Thanks for the suggestion though. I'm a total newbie to microprocessors programming, and I never used that decompiler, but I hope this won't be hard :slight_smile:

Koepel:
The ATtiny13a is not an official Arduino microcontroller. Are you sure that you want to use that ?

Yeah - I bought this uc just to check if I can squeeze something into it, so I'm desperate to do this :sunglasses:

Koepel:
When testing with a Arduino Uno, you can try my millis_soft_pulsating_led.ino example and see if that seems to be a sine to the human eye.

Thanks - I'll give it a try :wink:

Koepel:
In the ATtiny13a you should avoid any floating point. A lookup table might work, perhaps between 10 and 20 bytes should be enough. Or use something mathematical, for example shifting as gcjr wrote.

About your question: I think you underestimate the compiler. When using float calculations with constants, the compiler can decide to do those calculations for you :stuck_out_tongue: There is a point that the compiler decides to calculate it runtime, and that is when the floating point library and the pow function comes in. You can not afford that in a ATTiny13a.

I thought about lookup table with 16 entries as well and I believe I'll finish this program, maybe with some workarounds. But was just curious about why this happens. Especially the second case, where adding a line causes decrease in memory size.

But I guess that both you and TheMemberFormerlyKnownAsAWOL might be right, and that for specific cases compiler can pre-calculate this, without actually linking pow-related code.

arkanex:
Yup - I know this, but pow(2,value) is just a stub - in my real program I require e raised to a power depending on value.

Like exp(value)?

aarg:
Like exp(value)?

Yes, but unfortunately using exp goes over available memory as well... I see I have to skip using "fancy" floating-point functions for this baby :wink:

I checked the decompiled sketch and it looks like the problem is indeed related to linking the pow-related code, though I still don't understand why fourth getValue, with const argument, causes it to link. Nevertheless, I skip this investigation, and go to finish the project.

Thank you very much for help!

at bell labs, i've used limited accuracy log() functions that are table based. i was using a fixed point dsp.

This might be a good idea, although this attiny's memory might not be enough even for some predefined tables... Depends on specific needs of course.

Most people use a log brightness table for LED brightness. It doesn't take up much memory.

Here are all the values for a 256 byte table intended for PWM control. It could be put in program memory, but the human eye can distinguish only about 50-60 levels at best, so you might take every fourth one, for a 64 byte table.

byte ledLookupTable[] = {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30,30,31,32,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,46,47,47,48,49,50,51,52,53,54,55,56,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,91,92,93,94,95,97,98,99,100,102,103,104,105,107,108,109,111,112,113,115,116,117,119,120,121,123,124,126,127,128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151,152,154,155,157,158,160,162,163,165,166,168,170,171,173,175,176,178,180,181,183,185,186,188,190,192,193,195,197,199,200,202,204,206,207,209,211,213,215,217,218,220,222,224,226,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255}

...don't forget to leave it in PROGMEM

Yup - I ended with using similar, 32 bytes table.