Arduino:Справочник языка Arduino/PROGMEM

Материал из Онлайн справочника
Перейти к: навигация, поиск

{{#setlogo:ArduinoCommunityLogo.png}}

Перевод: Максим Кузьмин (Cubewriter)
Перевел 1837 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Ключевое слово PROGMEM[1]

Записывает данные не в SRAM, а во flash-память (т.е. в программную память). Описание типов памяти, доступных на Arduino, можно найти тут.

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

PROGMEM является частью библиотеки pgmspace.h, которая доступна только для архитектуры ARM. Поэтому, если вы собираетесь использовать PROGMEM, то начать скетч нужно именно с подключения этой библиотеки. Примерно так:

#include <avr/pgmspace.h>

Синтаксис

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

dataType — это тип переменной
variableName — название массива данных

Поскольку PROGMEM — это модификатор переменных, то какого-то четкого правила о том, куда его поставить, нет. Следовательно, компилятор Arduino примет любую из конструкций, описанных ниже. Они, по сути, являются синонимами.

Впрочем, ряд экспериментов выявил, что в разных версиях Arduino (в зависимости от версии GCC) PROGMEM нужно ставить по-разному, потому что если поставить его в одном месте, то он будет работать, а если в другом — нет. К примеру, конструкции, указанные ниже, протестированы для работы с Arduino 13 (1 и 2 можно использовать, а 3 лучше не надо). Кроме того, в ранних версиях IDE модификатор будет работать лучше, если поставить его после имени переменной.

const dataType variableName[] PROGMEM = {};   // используйте эту форму
const PROGMEM  dataType  variableName[] = {}; // или эту
const dataType PROGMEM variableName[] = {};   // но не эту

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

Процедуру использования PROGMEM можно поделить на два этапа. Сначала данные размещаются во flash-память, а после этого — из программной памяти обратно в SRAM, для чего потребуются специальные функции, тоже имеющиеся в библиотеке pgmspace.h.

Пример

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

  1. #include <avr/pgmspace.h>
  2.  
  3. // сохраняем несколько беззнаковых целых чисел
  4. const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};
  5.  
  6. // сохраняем несколько символов
  7. const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};
  8.  
  9. unsigned int displayInt;
  10. int k;    // переменная-счечтик
  11. char myChar;
  12.  
  13. void setup() {
  14.   Serial.begin(9600);
  15.   while (!Serial);
  16.  
  17.   // Тут размещаем настройки — их понадобится запустить всего раз.
  18.   // Повторно считываем 2-байтное целое:
  19.   for (k = 0; k < 5; k++)
  20.   {
  21.     displayInt = pgm_read_word_near(charSet + k);
  22.     Serial.println(displayInt);
  23.   }
  24.   Serial.println();
  25.  
  26.   // Повторно считываем символ:
  27.   int len = strlen_P(signMessage);
  28.   for (k = 0; k < len; k++)
  29.   {
  30.     myChar =  pgm_read_byte_near(signMessage + k);
  31.     Serial.print(myChar);
  32.   }
  33.  
  34.   Serial.println();
  35. }
  36.  
  37. void loop() {
  38.   // Здесь размещаем главный код, который будет постоянно повторяться:
  39.  
  40. }

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

При работе с большим количеством текста (например, для вывода на LCD-экран), как правило, удобно пользоваться массивом строк. Поскольку строки сами по себе являются массивами, это, по сути, будет примером двухмерного массива.

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

  1. /*
  2. Демонстрация работы со строками при помощи PROGMEM
  3. Как записывать таблицу строк в программную память (flash-память),  а затем доставать их оттуда.
  4.  
  5. Это выжимка отсюда:
  6. http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
  7.  
  8. Размещение таблицы (массива) строк в программную память — не самая простая задача,
  9. но данный скетч, будем надеться, поможет разобраться, как это сделать.
  10.  
  11. Запись строк — это 2-этапный процесс. Для начала определяем строки.
  12. */
  13.  
  14. #include <avr/pgmspace.h>
  15. const char string_0[] PROGMEM = "String 0";   // "String 0" и т.д. - это содержимое строк; если необходимо меняйте его
  16. const char string_1[] PROGMEM = "String 1";
  17. const char string_2[] PROGMEM = "String 2";
  18. const char string_3[] PROGMEM = "String 3";
  19. const char string_4[] PROGMEM = "String 4";
  20. const char string_5[] PROGMEM = "String 5";
  21.  
  22.  
  23. // Теперь создаем таблицу с отсылками к этим строкам:
  24. const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};
  25.  
  26. char buffer[30];    // массив был достаточно велик, чтобы вместить даже самую большую строку
  27.  
  28. void setup()
  29. {
  30.   Serial.begin(9600);
  31.   while(!Serial);
  32.   Serial.println("OK");
  33. }
  34.  
  35.  
  36. void loop()
  37. {
  38.   /*
  39. Теперь, если мы хотим достать данные их программной памяти, нам потребуется специальная функция.
  40. Это strcpy_P — она копирует строку из программной памяти в строку, находящуюся в RAM («буфер»).
  41. Убедитесь, что принимающая строка (т.е. находящаяся в RAM) достаточно велика для того,
  42. чтобы вместить данные из программной памяти.
  43.  */
  44.  
  45.  
  46.   for (int i = 0; i < 6; i++)
  47.   {
  48.     strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
  49.     Serial.println(buffer);
  50.     delay( 500 );
  51.   }
  52. }

Примечание

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

Вот этот код внутри функции работать НЕ БУДЕТ:

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

А этот код работать БУДЕТ, даже если определение выполнено на локальном уровне:

  1. 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. Таким образом, если в вашем скетче много подобных строк, которые выводятся на Serial Monitor, то ресурс RAM-памяти может закончиться очень быстро. Однако если у вас есть свободное место во flash-памяти, то вышеуказанную инструкцию можно изменить таким образом, чтобы строка сохранялась именно туда. Для этого используется следующий синтаксис:

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

См.также

Внешние ссылки

  1. PROGMEM