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

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

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Ключевое слово 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"));

См.также

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