PROGMEM

Храните данные во флэш-памяти (программе) вместо SRAM. Существует описание различных типов памяти, доступных на плате Arduino.

PROGMEM - ключевое слово, является модификатором переменной, оно должно быть использовано только с типами данных , определенными в pgmspace.h. Оно говорит компилятору «поместить эту информацию во флэш-память», а не в SRAM, куда она обычно отправляется.

PROGMEM является частью библиотеки pgmspace.h. Он автоматически включается в современные версии IDE, однако, если вы используете версию IDE ниже 1.0 (2011), вам сначала нужно включить библиотеку в верхнюю часть скетча, например:

#include <avr/pgmspace.h>

Синтаксис

const dataType variableName [] PROGMEM = {данные0, данные1, данные3...};

dataType - любой тип переменной 

variableName - имя для вашего массива данных

Обратите внимание, что, поскольку PROGMEM является модификатором переменной, не существует жесткого и правила относительно того, в какое место его следует поместить, поэтому компилятор Arduino принимает все приведенные ниже определения, которые также являются синонимами. Однако эксперименты показали, что в различных версиях Arduino (имеющих отношение к версии GCC) PROGMEM может работать в одном месте, а не в другом. Приведенный ниже пример «таблицы строк» ​​был протестирован для работы с Arduino 13. Более ранние версии IDE могут работать лучше, если после имени переменной включен PROGMEM.

const dataType variableName[] PROGMEM = {}; // Используйте этот пример. 
const PROGMEM dataType variableName[] = {}; // Либо этот.
const dataType PROGMEM variableName[] = {}; // Не используйте этот пример.

Хотя PROGMEM можно использовать для одной переменной, на самом деле это стоит того, если у вас есть больший блок данных, который необходимо сохранить, что обычно проще всего в массиве (или другой структуре данных C ++ за пределами нашего нынешнего обсуждения).

Использование PROGMEM - двухэтапная процедура. После получения данных во флэш-память требуются специальные методы (функции), также определенные в библиотеке pgmspace.h , для считывания данных из памяти программ обратно в SRAM, чтобы мы могли сделать что-то полезное с этим.

Пример кода

Следующие фрагменты кода иллюстрируют, как читать и записывать символы без знака (байты) и целые числа (2 байта) в PROGMEM.

const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};

const char signMessage[] PROGMEM = {"I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k; // Счетчик. 
char myChar;

void setup() {
	Serial.begin(9600);
	while (!Serial); // Ждем соединения с последовательным портом. Нужвен свободный USB.

// Поместите свой код для настроек здесь, команды выполнятся единожды.

	for (k = 0; k < 5; k++) {
		displayInt = pgm_read_word_near(charSet + k);
		Serial.println(displayInt);
	}
	Serial.println();


	for (k = 0; k < strlen_P(signMessage); k++) {
		myChar = pgm_read_byte_near(signMessage + k);
		Serial.print(myChar);
	}

	Serial.println();
}

void loop() {
// Поместите ваш основной код здесь, команды будут повторяться в цикле.
}

Массивы строк

Часто при работе с большими объемами текста, такими как проект с ЖК-дисплеем, удобно настраивать массив строк. Поскольку сами строки являются массивами, это фактически является примером двумерного массива.

Как правило, это большие структуры, поэтому часто желательно помещать их в программную память. Код ниже иллюстрирует идею.

/*
PROGMEM демо скетч.
Как хранить таблицу строк в памяти программы (flash),
и извлекать их.

Информация законспектирована отсюда:
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
 
Настройка таблицы (массива) строк в памяти программ немного сложна, но
вот хороший шаблон.

Настройка строк - двухэтапный процесс. Сначала определите строки.
*/

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0"; // "String 0" и другие - строки, с которыми ведется работа, их можно изменить по необходимости.
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";


// Настраиваем таблицу для обращения к строкам.

const char *const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};

char buffer[30]; // Убедитесь, что размерности массива хватает для самой длинной строки.

void setup() {
	Serial.begin(9600);
	while (!Serial); // Ждем соединения с последовательным портом. Нужвен свободный USB.
	Serial.println("OK");
}


void loop() {
	/* Использование таблицы строк в памяти программы требует использования специальных функций для извлечения данных.
Функция strcpy_P копирует строку из программного пространства в строку в ОЗУ ("buffer").
Убедитесь, что ваша принимающая строка в ОЗУ достаточно велика, чтобы вместить все, что
вы извлекаете из программного пространства. */


	for (int i = 0; i < 6; i++) {
		strcpy_P(buffer, (char *)pgm_read_word(&(string_table[i]))); // Необходимые приведения и разыменования, просто скопируйте.
		Serial.println(buffer);
		delay(500);
	}
}

Примечания и предупреждения

Обратите внимание, что для работы с PROGMEM переменные должны быть либо глобально определены, либо определены с помощью ключевого слова static.

Следующий код НЕ будет работать внутри функции:

const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";

Следующий код будет работать, даже если он определен локально в функции:

const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"


Когда используется такая конструкция:

Serial.print("Write something on the Serial Monitor");

строка для печати обычно сохраняется в ОЗУ. Если ваш скетч напечатает много материала в последовательный монитор, вы можете легко заполнить оперативную память. Если у вас есть свободное место во флэш-памяти, вы можете легко указать, что строка должна быть сохранена во флэш-памяти, используя синтаксис:

Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));