The image above is a photo of static RAM (SRAM) cells of a STM32F103VGT6 microcontroller, taken by a scanning electron microscope. By ZeptoBars - http://zeptobars.ru/en/read/open-microchip-asic-what-inside-II-msp430-pic-z80, CC BY 3.0, https://commons.wikimedia.org/w/index.php?curid=25044206
The Arduino Uno's ATmega328P only has 2048 bytes of SRAM (static RAM). It also has 32 KBytes of flash memory.
The flash memory is where your sketch is stored when you upload it. Flash memory persists when you turn the Arduino off.
The SRAM is where the various variables in your sketch are stored during execution. Unlike flash memory, the contents of the SRAM do not persist when you turn the Arduino off.
In the Arduino Uno, there is a lot more flash than SRAM space.
Let's look at an example of how SRAM space is used, and how easy it is to misuse it.
Take this simple instruction:
Serial.println("Hello world!");
It doesn't do anything fancy. It will simply print a short string of text to the serial monitor.
The string of text in the double quotes will occupy 12 bytes in the SRAM. If you have a few additional println() statements like that, your sketch will easily fill the SRAM with static strings of text.
If your program is large with a lot of variables like this, there is a good chance you will run out of RAM, and your program will crash.
Because the string in this example is static, i.e. it does not change, you can easily optimize your sketch by storing the string in the plentiful flash memory instead of the SRAM.
Serial.println(F("Hello world!"));
I have marked the F() macro used in the println() instruction in bold.
The F() macro tells the compiler that the contents of the parameter should be stored in the flash memory (program memory or PROGMEM), instead of the SRAM, hence saving you this valuable resource for better use.
The F() macro provides us an easier syntax for marking compatible data type values to be stored in program memory, in place of the PROGMEM keyword modifier. If you wanted to use the raw PROGMEM modifier instead of the F() macro, you would do it like this:
const char a_string[] PROGMEM = "Hello world!";
void setup()
{
Serial.begin(9600);
for (byte k = 0; k < strlen_P(a_string); k++) {
myChar = pgm_read_byte_near(a_string + k);
Serial.print(myChar);
}
}
I find the F() macro much easier compared to manually marking a constant string for storage in program memory and then reading it back one byte at a time.