How to use PROGMEM in Arduino to store large immutable data?

ArduinoHardwareSoftware & Coding

PROGMEM is the keyword you use when you wish to store data in the program memory (flash) instead of the SRAM. While you can use PROGMEM for a single variable, it won't make much sense to do so. After all, the SRAM would have more than enough space to accommodate your single variable, and it will be faster to access the variable stored in SRAM.

PROGMEM is primarily used for large chunks of data (an array mostly), which can overwhelm the SRAM (which is generally much smaller in size than the flash memory, but faster to access). The implication of storing something in PROGMEM is that it cannot be modified dynamically at runtime. Therefore, people generally use PROGMEM to store large immutable text or data.

If you are using Arduino IDE version below 1.0 (why?), then you need to include

#include <avr/pgmspace.h>

at the top of your code to use the PROGMEM keyword. For versions of Arduino >= 1.0, you can use the PROGMEM keyword directly without any includes required.

Note that PROGMEM works only with global variables or the ones defined with a static keyword.

Syntax

The syntax is as follows −

const dataType arrayName[] PROGMEM = {data0, data1, data3…};

Any variable type is allowed for datatype and arrayName is the name of your array.

In order to access the data stored in the flash memory using PROGMEM, you use specialized functions −

  • strlen_P(arrayName) − This function returns the length of the array arrayName.

  • pgm_read_byte_near(address) − This function returns the value of one byte located at address.

  • pgm_read_word_near(address) − This function returns the value of one word (2 bytes on most microcontrollers) with location starting from address.

The usage of these functions will be clear in the example below. These aren't the only functions to be used with PROGMEM. There are several others, which you can find here.

Example

Let's begin with the example. We will use the example given in Arduino's documentation.

We will look at the first example. As you can see, there are two arrays defined in PROGMEM, one is an array of 16-bit integers (16-bit = 1 word on Arduino Uno), while the second is an array of characters (each character is 8-bit or 1 byte long)

// save some unsigned ints
const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};
// save some chars
const char signMessage[] PROGMEM = {"I AM PREDATOR, UNSEEN COMBATANT.
CREATED BY THE UNITED STATES DEPART"};

Later, there are two global variables defined, which will be used later.

unsigned int displayInt;
char myChar;

Within setup, we initialize Serial. Later, we read one word at a time, to print the integers of the charset array. Notice how we use charSet + k for the address we want to read. You may remember that the name of an array is also the pointer to its first element. That same property is being used here.

void setup() {
   Serial.begin(9600);
   while (!Serial); // wait for serial port to connect. Needed for native USB
   // put your setup code here, to run once:
   // read back a 2-byte int
   for (byte k = 0; k < 5; k++) {
      displayInt = pgm_read_word_near(charSet + k);
      Serial.println(displayInt);
   }
   Serial.println();

Later, we read one byte each from the signMessage array and print it to the Serial Monitor. Please note that we are using the strlen_P function to get the length of the array, to determine the termination condition of the for loop.

   // read back a char
   for (byte k = 0; k < strlen_P(signMessage); k++) {
      myChar = pgm_read_byte_near(signMessage + k);
      Serial.print(myChar);
   }
   Serial.println();
}

Nothing happens inside the loop.

raja
Updated on 24-Jul-2021 12:45:54

Advertisements