[Перевод] Хранение данных во флеше вместо RAM

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

PROGMEM - это часть библиотеки pgmspace.h, которая доступна только для архитектуры AVR.

 Для начала вам нужно подключить библиотеку к своему скетчу:

1
#include <avr/pgmspace.h>

Синтаксис

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

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

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

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

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

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

 

Пример

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <avr/pgmspace.h>
 
// сохраним несколько беззнаковых 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; // counter variable
char myChar;
 
 
void setup() {
  Serial.begin(9600);
  while (!Serial);
  
  // читаем двухбайтовый int
  for (k = 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();
  
  // читаем однобайтовый char
  int len = strlen_P(signMessage);
  for (k = 0; k < len; k++)
  {
    myChar = pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }
  
  Serial.println();
}
 
void loop() {
 
}

 

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

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

Такие большие структуры предпочтительнее складывать во флеш память. Следующий код иллюстрирует идею:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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);
  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]))); // Необходимые вызов и расшифровка ссылки. Просто скопируйте это :D
    Serial.println(buffer);
    delay( 500 );
  }
}

 

Замечание

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

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

1
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()

Когда используется выражение типа

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

строка, которая выводится на печать, хранится в оперативной памяти. Если ваш скет печатает много всего в монитор порта, можно очень легко переполнить оперативную память. Если у вас есть свободное место во флеше, то вы можете поместить строку в него, используя макрос F() следующим образом:

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

Оригинал статьи: https://www.arduino.cc/en/Reference/PROGMEM

 

Метки: программирование, Arduino, hack, код, хардкор, память

Добавить комментарий


Защитный код
Обновить