PROGMEM / Arduino

Добавлено 12 ноября 2018 в 18:10

Описание

Хранит данные во флэш-памяти (памяти программ) вместо SRAM.

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

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

#include <avr/pgmspace.h>

Синтаксис

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

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 является также двухэтапной процедурой. После получения данных во флэш-память для чтения данных из памяти программ обратно в SRAM требуются специальные методы (функции), также определенные в библиотеке pgmspace.h, поэтому мы можем сделать с ними что-нибудь полезное.

Пример кода

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

// сохранить какие-то значения unsigned int
const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// сохранить какие-то значения char
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

  // прочитать 2-байтовые значения int
  for (k = 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // прочитать значения char
  for (k = 0; k < strlen_P(signMessage); k++)
  {
    myChar =  pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }
  Serial.println();
  
}

void loop() 
{

}

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

Часто при работе с большими объемами текста, например, в проекте с LCD дисплеем, удобно устанавливать массив строк. Поскольку сами строки являются массивами, на самом деле это пример работы с двумерным массивом.

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

/*
 Демонстрация работы PROGMEM со строками
 Как сохранить таблицу строк в программной памяти (флэш)
 и извлечь ее.

 Настройка таблицы (массива) строк в программной памяти немного сложна, но
 ниже приведен хороший пример.

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

#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 копирует строку из пространства программы в строку в RAM (буфер).
     Убедитесь, что ваша принимающая строка в RAM достаточно велика, чтобы удерживать
     всё, что вы извлекаете из пространства программы. */

  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"

Макрос F()

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

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

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

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

 

Теги

ArduinoПамять программПрограммированиеЭнергонезависимая память

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.