Arduino:Справочник языка Arduino/PROGMEM
Содержание | Знакомство с Arduino | Продукты | Основы | Справочник языка Arduino | Примеры | Библиотеки | Хакинг | Изменения | Сравнение языков Arduino и Processing |
Ключевое слово 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 байтов).
#include <avr/pgmspace.h>
// сохраняем несколько беззнаковых целых чисел
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);
// Тут размещаем настройки — их понадобится запустить всего раз.
// Повторно считываем 2-байтное целое:
for (k = 0; k < 5; k++)
{
displayInt = pgm_read_word_near(charSet + k);
Serial.println(displayInt);
}
Serial.println();
// Повторно считываем символ:
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-экран), как правило, удобно пользоваться массивом строк. Поскольку строки сами по себе являются массивами, это, по сути, будет примером двухмерного массива.
В итоге формируются, как правило, довольно большие структуры, поэтому их зачастую размещать именно в программную память. Вся эта концепция проиллюстрирована в примере ниже.
/*
Демонстрация работы со строками при помощи PROGMEM
Как записывать таблицу строк в программную память (flash-память), а затем доставать их оттуда.
Это выжимка отсюда:
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
Размещение таблицы (массива) строк в программную память — не самая простая задача,
но данный скетч, будем надеться, поможет разобраться, как это сделать.
Запись строк — это 2-этапный процесс. Для начала определяем строки.
*/
#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 — она копирует строку из программной памяти в строку, находящуюся в 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. Таким образом, если в вашем скетче много подобных строк, которые выводятся на Serial Monitor, то ресурс RAM-памяти может закончиться очень быстро. Однако если у вас есть свободное место во flash-памяти, то вышеуказанную инструкцию можно изменить таким образом, чтобы строка сохранялась именно туда. Для этого используется следующий синтаксис:
Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));
См.также
Внешние ссылки
Arduino продукты | |
---|---|
Начальный уровень | Arduino Uno • Arduino Leonardo • Arduino 101 • Arduino Robot • Arduino Esplora • Arduino Micro • Arduino Nano • Arduino Mini • Arduino Starter Kit • Arduino Basic Kit • MKR2UNO • TFT-дисплей Arduino |
Продвинутые функции | Arduino Mega 2560 • Arduino Zero • Arduino Due • Arduino Mega ADK • Arduino Pro • Arduino Motor Shield • Arduino USB Host Shield • Arduino Proto Shield • MKR Proto Shield • MKR Proto Large Shield • Arduino ISP • Arduino USB 2 Serial Micro • Arduino Mini USB Serial Adapter |
Интернет вещей | Arduino Yun • Arduino Ethernet • Arduino MKR1000 • Arduino WiFi 101 Shield • Arduino GSM Shield V2 • Arduino WiFi Shield • Arduino Wireless SD Shield • Arduino Wireless Proto Shield • Arduino Ethernet Shield V2 • Arduino Yun Shield • Arduino MKR1000 Bundle |
Носимые устройства | Arduino Gemma • Lilypad Arduino Simple • Lilypad Arduino Main Board • Lilypad Arduino USB • LilyPad Arduino SimpleSnap |
3D-печать | Arduino Materia 101 |
Устаревшие устройства | - |
Примеры Arduino | |
---|---|
Стандартные функции | |
Основы |
|
Цифровой сигнал |
|
Аналоговый сигнал |
|
Связь |
|
Управляющие структуры |
|
Датчики |
|
Дисплей |
Примеры, объясняющие основы управления дисплеем:
|
Строки |
|
USB (для Leonardo, Micro и Due плат) |
В этой секции имеют место примеры, которые демонстрируют использование библиотек, уникальных для плат Leonardo, Micro и Due.
|
Клавиатура |
|
Мышь |
|
Разное |