UNO's RAM amount for variables - I'm stumped!

I really don't know if this is the right place for this issue, but...

I have a project using a display made up of RGB LEDs organized as 80 columns of 48 rows. I've defined an unsigned long array to store 4 bytes of data per LED, so if my math is correct I need 80x48x4 bytes of RAM, which is 15360 bytes (15.36kB).

Here's the code, which just initializes each array location to a unique unsigned long integer value that uses 4 bytes per location:

unsigned long data;
unsigned long DisplayArray[48][80]; //DisplayArray contains 48 rows of 80 columns of data

void setup() {
  Serial.begin(115200);
}

void loop() {
  for(int i=0; i<48; i++){              //For each row
    for(int j=0; j<80; j++){
      data = 4294967295-(j+80*i);
      DisplayArray[i][j] = data;
      Serial.print(DisplayArray[i][j]);
      Serial.print(" ");
    }
    
    Serial.println("*");
  }
  while(1);
}

It seems to me that the compiler should be complaining about exceeding storage space, but I get no warnings/errors (see the screenshot). The serial monitor output shows unique values as expected per the calculation (from 4294967295 down to 4294963456).

What's going on here? Am I misunderstanding the Uno's storage limits?

That will be hard to fit into the 2048 bytes actually available on the Uno!

The compiler is very smart, and eliminates the array during optimization, since all the posted code does with the values is print them out.

2 Likes

Welcome to the weird and wonderful world of optimizing compilers. Using one set of loops, the compiler is smart enough to realize that it doesn't have to allocate all that space, since only one value of data is in play at any time. Calculate it, print it, toss it.

If you put the assignments and printing in two separate loop sets, you'll force the compiler to actually try and store all the values, and you'll see what you likely were expecting to see.

unsigned long data;
static unsigned long DisplayArray[48][80]; //DisplayArray contains 48 rows of 80 columns of data

void setup() {
  Serial.begin(115200);
}

void loop() {
   
  for(int i=0; i<48; i++){              //For each row
    for(int j=0; j<80; j++){
      data = 4294967295-(j+80*i);
      DisplayArray[i][j] = data;
    }
  }
  for(int i=0; i<48; i++){              //For each row
    for(int j=0; j<80; j++){
      Serial.print(DisplayArray[i][j]);
      Serial.print(" ");
    }
    
    Serial.println("*");
  }
  while(1);
}
arduino-cli compile -b arduino:avr:uno --output-dir /home/me/tmp --no-color (in directory: /home/me/Documents/sketchbook/Uno_R3/ramtest)
Sketch uses 4628 bytes (14%) of program storage space. Maximum is 32256 bytes.
Global variables use 15552 bytes (759%) of dynamic memory, leaving -13504 bytes for local variables. Maximum is 2048 bytes.
Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
Used platform Version Path
arduino:avr   1.8.3   /home/me/.arduino15/packages/arduino/hardware/avr/1.8.3
Error during build: data section exceeds available space in board
Compilation failed.
2 Likes

You can get a 32K x 8 FRAM chip for a few dollars, either I2C or SPI. That will give you enough memory and it will not wear out like EEPROM.

Thanks for taking your time to reply, but I'm actually using a Teensy 4.1 that has sufficient RAM. My question was really about why the compiler didn't complain about insufficient space AND why the code seems to run fine despite my (possibly erroneous) belief that an UNO should not have the space for the array.

Thanks for the excellent reply! I suspected that compiler optimization was the problem, but didn't try to force the array filling and outputting in two different loops to try to force the compiler to actually attempt (and fail) to fill the entire array.

I'm using a Teensy 4.1 for the actual design, as it has LOTS of RAM. I was writing some simple code to play with struct and union on an Arduino UNO, but first wanted to see what error messages I would get by trying to load the whole array. Now that you've cleared that up for me, it's time to get the hang of struct and union. I want to be able to access the array as either 80 x 48 (lansdcape orientation) or 48 x 80 (portrait orientation), so that I can rotate the display and have the code reorganize everything automatically. To compound matters, the display is made up of a 5 x 3 arrangement of matrices of 16 x 16 RGB LEDs, themselves organized as 256 LEDs arranged in a serpentine fashion. I already have the address translation code working fine, but want to try accessing the array different ways so as to be as memory-efficient as practical.

I knew it was supposed to be impossible, so I was expecting that compiler optimization was responsible for making it appear to be possible. Thanks for confirming my suspicions!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.