1

So I have been diving deep into AVR programming and have seen several different ways of defining constants. I know that some of these methods are from "C" and others are from more modern "C++" methods. So my question here is what is the preferred method?

Method 1:

#define MAX_ARRAY_LEN 3

int my_array[MAX_ARRAY_LEN] = {1, 2, 3};

Method 2:

const int MAX_ARRAY_LEN = 3;

int my_array[MAX_ARRAY_LEN] = {1, 2, 3};

In my searches online I have ran across several pages that mention using PROGMEM in the declarations to load the variable into Flash memory instead of SRAM. The thing I am unclear about using PROGMEM is what should be loaded into Flash and what should be loaded into SRAM? Should I be loading all const into Flash? Are there any performance hits using Flash?

Edit

I guess I was not clear on what I was asking. I understand that #define and PROGMEM are not related. My question is there are a couple of methods that can be used to define constants. One is #define (Method 1) and the other is const (Method 2), with the const type comes the PROGMEM, when using Method 2 there are now 2 areas where you can store variables, Flash and SRAM so when do you want to use PROGMEM and when do you want to just leave it, when using method 2? Is there any performance hits when reading from Flash vs SRAM?

I understand that when using method 2 it is now scoped and typed, but are there any other advantages? Are the programs smaller using one vs the other? Are there times that you would want to use one over the other?

1
  • 1
    Well, as far as I know every compiler is smart enough to understand that the const int field should not be allocated into memory, so I THINK both are the same... You can try to compile both versions and see if the size and the memory is the same or not..
    – frarugi87
    May 13, 2016 at 13:04

1 Answer 1

4

(Duplicate of this.)

In your example, neither definition of MAX_ARRAY_LEN will use program space nor RAM. If you were to use the definition somewhere, it will certainly use program space in the instruction that uses that constant:

for (int i=0 i<MAX_ARRAY_LEN, i++)
  Serial.print( my_array[i] );

In this case, a CPU instruction will load the constant 3 into a register. So you can say that the 3 "used" a byte in the CPU instruction, and is part of the program size. Most people ignore that, because a loading a variable into i uses both RAM and program space.

The primary advantage of using const instead of #define is that the former allows additional compiler type-checking.

C++ has introduced another keyword, constexpr, which tells the compiler that an entire expression can be evaluated at compile time and reduced to a single constant value. Read all about it here.

To answer your comment, #define's are not stored anywhere. They are a simple string substituion that happens before the compiler processes the C++ code.

When you build your sketch, the source files are processed in several phases. First, there's a preprocessor phase that consumes all the lines that start with '#'. The results of the preprocessing are sent to the C++ compiler. The compiler will see all the included files inline, and all #define's will be substituted.

UPDATE

For example, #define XXX 3 will cause the preprocessor to replace all occurrences of XXX in the C++ file with 3. The C++ compiler will never see XXX, it only sees 3. You could do a search and replace with your editor, and the C++ compiler would not know the difference. (Well, there are a few more tricks the preprocessor can do for you, but you only asked about defining a numeric constant.) The generated code would be identical.

There is no "global space" for #define symbols. There is a "dictionary" in the preprocessor that is used to perform the substitution, but that is ephemeral.

PROGMEM is not related to #define or const at all. It is an attribute of a variable that indicates it is to be stored in FLASH, not RAM. I think the key is that #define is just string substitution, but const is a C++ attribute of a C++ variable. All of these are equivalent:

#define MAX_ARRAY_SIZE 3
const uint8_t maxArraySize = MAX_ARRAY_SIZE;
int array[ maxArraySize ] = { 1,2,3 };

#define FOO 1,2,3
int array[] = { FOO };
const uint8_t MAX_ARRAY_SIZE = (uint8_t) sizeof(array)/sizeof(array[0]);

#define MAX_ARRAY_SIZE goofyNameThatMeansNothing
const uint8_t MAX_ARRAY_SIZE = 3;
int array[ MAX_ARRAY_SIZE ] = { 1,2,3 };

#define BAR = { 1,2,
int array[] BAR,3};
#define MAX_ARRAY_SIZE sizeof(array)/sizeof(array[0])

I hope that last 2 goofy examples help illustrate what a #define is... string substitution.

The PROGMEM attribute is a separate thing. It can only be added to C++ variables, and it now requires the variable to also be const. Extending the above, you could do this:

#define MAX_ARRAY_SIZE 3
const uint8_t maxArraySize = MAX_ARRAY_SIZE;
const int array[ maxArraySize ] PROGMEM = { 1,2,3 }; // a flash array, not RAM

#define FOO 1,2,3
const int array[] PROGMEM = { FOO };
const uint8_t MAX_ARRAY_SIZE = (uint8_t) sizeof(array)/sizeof(array[0]);

#define MAX_ARRAY_SIZE goofyNameThatMeansNothing
const uint8_t MAX_ARRAY_SIZE = 3;
int array[ MAX_ARRAY_SIZE ] PROGMEM = { 1,2,3 };

#define BAR PROGMEM = { 1,2,
const int array[] BAR,3};
#define MAX_ARRAY_SIZE 14

Again, in that last goofy example I used PROGMEM in the #define BAR, but it's just some characters as far as the #define is concerned. And the last define is just plain wrong, but it will compile.

Because there is no validation of a string substitution, it is usually better to use a const type var = value; that can be validated by the compiler, both when it is defined and again when var is used in some context.

UPDATE 2

You say,

with the const type comes the PROGMEM

Not really. const is the "read-only" attribute, and the compiler enforces that.

PROGMEM tells the AVR compiler that, if storage is required for this variable, put it in FLASH memory (a special address range) instead of RAM (a different address range). Because of the AVR architecture, you must access FLASH differently, with special instructions. The avr.pgmspace.h include file has some routines (macros and inline assembler) for reading from those address spaces.

I emphasized the "storage space required", because the compiler can optimize away the storage. The literal 3 constant MAX_ARRAY_LEN does not need any storage space. For this example program:

const int MAX_ARRAY_LEN PROGMEM = 3;

void setup()
{
  Serial.begin( 9600 );

  Serial.println( MAX_ARRAY_LEN );
}

void loop() {}

This is the generated code:

  Serial.println( MAX_ARRAY_LEN );
156:    50 e0           ldi r21, 0x00   ; 0
158:    63 e0           ldi r22, 0x03   ; 3
15a:    70 e0           ldi r23, 0x00   ; 0
15c:    82 e2           ldi r24, 0x22   ; 34
15e:    92 e0           ldi r25, 0x02   ; 2
160:    f7 c2           rjmp    .+1518  ; 0x750 <_ZN5Print7printlnEii>

The compiler knows that storage is not required. The value "3" is pushed directly onto the stack at program location 0x158. It did not go to RAM nor FLASH (PROGMEM) to load a 3 into a register, and then push that register's value onto the stack.

The compiler knows the value, so it uses the "immediate" form of the instruction to push the 3.

However, for this program:

const int MAX_ARRAY_LEN PROGMEM = 3;

void setup()
{
  Serial.begin( 9600 );

  Serial.println( (uint16_t) &MAX_ARRAY_LEN, 16 );
}

void loop() {}

The following code is generated:

  e4:   03 00           .word   0x0003  ; ????
     .
     .
     .
  Serial.println( (uint16_t) &MAX_ARRAY_LEN, 16 );
 158:   50 e0           ldi r21, 0x00   ; 0
 15a:   64 ee           ldi r22, 0xE4   ; 228
 15c:   70 e0           ldi r23, 0x00   ; 0
 15e:   82 e2           ldi r24, 0x22   ; 34
 160:   92 e0           ldi r25, 0x02   ; 2
 162:   b5 c2           rjmp    .+1386      ; 0x6ce <_ZN5Print7printlnEji>

Because I took the address of MAX_ARRAY_LEN, the compiler knew that it had to have storage... in order for it to have an address. The compiler dutifully locates it in the program space, at 0xE4. You can see that address being pushed onto the stack at 0x15A.

Without the PROGMEM attribute, the following code is generated:

  Serial.println( (uint16_t) &MAX_ARRAY_LEN, 16 );
 156:   50 e0           ldi r21, 0x00   ; 0
 158:   60 e0           ldi r22, 0x00   ; 0
 15a:   72 e0           ldi r23, 0x02   ; 2
 15c:   84 e2           ldi r24, 0x24   ; 36
 15e:   92 e0           ldi r25, 0x02   ; 2
 160:   b5 c2           rjmp    .+1386      ; 0x6cc <_ZN5Print7printlnEji>

This is a RAM address, 0x200. Long before setup, a 3 is copied from the program space into RAM. It is using both program space and RAM.

But this is only because I took the address of MAX_ARRAY_LEN. In almost all cases, you will be using these as scalar values. The compiler will not allocate any storage space for them, and the PROGMEM attribute is irrelevant.

You have another question:

Is there any performance hits when reading from Flash vs SRAM?

Yes. Reading from flash takes twice as many CPU cycles. It's always a juggling act. If you need to squeeze every cycle out, make sure your constants do not even need storage space (i.e., don't take their address). And if it's a PROGMEM array or structure, consider using a cached value (i.e., loaded into a RAM array or structure). Sometimes you don't have a choice. If you are out of RAM, you will have to load them from PROGMEM every time you need them, with a small penalty. RAM, SPEED, SIZE... pick two.

6
  • Thanks, I saw the link that you posted but what that did not touch on was the usage of PROGMEM and what should be stored where to utilize memory as efficiently as possible. #define's are stored in global space but there is no way I know of to tell where to put that memory when "used" May 13, 2016 at 13:48
  • @AndyBraham, the link did not touch on PROGMEM because it is not related to #define. UPDATE section added above to explain the difference between preprocessing (string substitution) and compiling (variables, storage and PROGMEM).
    – slash-dev
    May 13, 2016 at 14:28
  • Thanks a bunch for trying to help, I guess I was just not clear on what I was trying to get at. I updated my question hopefully you can shed a little light.... May 13, 2016 at 20:17
  • @AndyBraham, UPDATE 2 added. Basically, constants rarely use storage space. PROGMEM only applies to things that actually use storage.
    – slash-dev
    May 13, 2016 at 21:27
  • Awesome, now you hit on what I was trying to get at. Now one more thing. While researching this topic there was some mention about LCD projects and storing the Strings that are displayed in a structure or array like you mentioned in your "Update 2". Do they suggest this because the String Arrays are large and when used they take up much more memory than other types? Or is this one of those things that "Work" but there are better ways? I am trying to find out what the best approach is. As you know the internet is full of obsolete information. May 14, 2016 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.