This page demonstrates using data that is stored in ROM.
The background to this page was a project that used a LCD display where the bit patterns for each character were stored in memory. Ignoring "characters" 0x00 through 0x1F with 7 bit ASCII there are 96 different characters. On a 6x8 display with left and bottom columns/rows blank each character requires 5 bytes making a total of 480 bytes. Since the Arduino has only 2K bytes of RAM storing the bit patterns in RAM uses 25% of the RAM resources. A better solution is to use the patterns already in Flash (ROM) memory.
Briefly the Arduino IDE compiler will generate a memory image that will contain an image of the program and an image of the data (variables). As part of the program image there will be code that at start up transfers the data image from Flash (Read Only) Memory into RAM (Random Access Memory or write-read memory). This is illustrated below:
In the LCD example the data image includes the bit patterns ASCII[][] for all the characters. For fixed or constant data this implies there are two identical copies, one in the micro-controller flash and the other in RAM. Since the Arduino has limited RAM the ideal solution is have one copy only, that is in Flash (or Program Memory) and use only that copy.
In the LCD example an ASCII array will be defined
static const byte ASCII[96][5] PROGMEM =
{ {0,0,0,0,0}, // space {0,95,95,0,0}, //! {3,3,0,3,3}, //" {60,60,36,60,60}, //# {36,46,127,58,18}, //$ {99,19,8,100,99}, //% {54,127,73,127,126}, //& ...... 96 entries total};The crucial directive here is PROGMEM which tells the compiler that a copy of this memory is not to be made in dynamic memory (RAM) at runtime. The original array in flash will be used.
The C Language is designed for Von Neumann architectures where the code and data exist in the same address space. With Von Neumann architectures there is no fundamental difference to reading code or data memory. However, the Arduino uses an ATmega 328P micro-controller that has a Harvard architecture processor with a separate address bus for program memory and RAM. This implies changes/modifications/enhancements are necessary with the compiler to use program memory for constant data storage. (Some compilers use non-standard C language keywords, or they extend the standard syntax in non-standard ways.)
With the Arduino to use the copy of the character array in ROM memory is a two step process
1. Add the macro PROGMEM to the data. At run time a copy of the data will no longer be created in RAM.
2. Use the macro "pgm_read_byte( )" to read the data from the Program Memory (ROM/Flash) Note the parameter will be the address of the data.
The macro "pgm_read_byte( )" will read one byte from the the Program Memory (ROM/Flash)
For the LCD example possible code might be:
This method will take two arguments:
1. char c the character for which the bit pattern is required
2. byte pat[6] the 6 element array where the bit pattern will be returned.
The implementation first checks if the character is in the allowable range.
Since the ASCII table assumes the first column is a blank pat[0] is assigned to the pattern 0x00.
The "for loop" uses the special function/macro pgm_read_byte() to transfers the table contents in Flash/ROM to the array pat[index+1]. It uses as a pointer the value of the character less the value of a space (the first entry in the table.)
The F() macro can be used on small strings. For example Serial.println(F("Keep string in ROM")); This macro tells the compiler to leave the string in PROGMEM. At run time the data is copied to RAM at a time one byte at a time.
DSP_Train_State: Sends messages stored in Flash/ROM about the state of a model train to the Arduino Serial Monitor
MESS_2_ESP_AP: Uses PROGMEM and pgm_read_byte( ) to send WiFi message but also uses Strlen_P( ) to determine length of message.