Проверка/Оформление/Редактирование: Мякишев Е.А.
|
|
|
Черновик
|
Руководство по PROGMEM в аддоне ESP8266 для IDE Arduino[1]
PROGMEM – это функция AVR-процессоров Arduino, портированная на ESP8266 для сохранения совместимости с библиотеками Arduino, а также для экономии RAM-памяти. На ESP8266 объявление строки вроде const char * xyz = "это строка" поместит эту строку в RAM, а не во flash-память. Впрочем, есть способ поместить строку сначала во flash-память, а затем, если понадобится, в RAM. На 8-битных процессорах ARM этот процесс очень прост. На 32-битном ESP8266 для считывания с flash-памяти должны быть соблюдены определенные условия.
На ESP8266 для PROGMEM используется следующий макрос:
#define PROGMEM ICACHE_RODATA_ATTR
ICACHE_RODATA_ATTR задается при помощи:
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
Это поместит переменную в секцию «.irom.text» во flash-памяти. Для сохранения строки во flash-память можно использовать оба этих метода.
Чтобы объявить глобальную переменную, сохраняемую во flash-памяти, нужно написать следующее:
static const char xyz[] PROGMEM = "Это строка, сохраненная на flash";
Как объявить flash-строку внутри кода
Для этого можно воспользоваться макросом PSTR(). Он задан в файле pgmspace.h.
#define PGM_P const char *
#define PGM_VOID_P const void *
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
На практике:
void myfunction(void) {
PGM_P xyz = PSTR("Сохраняем эту строку во flash-памяти");
const char * abc = PSTR("Эту строку тоже сохраняем во flash-памяти");
}
Вот два способа, с помощью которых строки можно сохранять во flash-памяти. Чтобы считывать и манипулировать этими flash-строками, они должны быть в виде 4-байтных слов. В аддоне ESP8266 для IDE Arduino есть несколько функций, которые позволяют извлекать из flash-памяти строки, сохраненные туда при помощи PROGMEM. Оба примера выше возвращают данные типа const char *. Впрочем, если использовать эти указатели без правильного 32-битного выравнивания, это повлечет ошибку сегментации, и ESP8266 крашнется. Данные, считываемые из flash-памяти, должны быть выровнены под 32 бита.
Функции для считывания из PROGMEM
Все эти функции заданы в заголовочном файле pgmspace.h.
int memcmp_P(const void* buf1, PGM_VOID_P buf2P, size_t size);
void* memccpy_P(void* dest, PGM_VOID_P src, int c, size_t count);
void* memmem_P(const void* buf, size_t bufSize, PGM_VOID_P findP, size_t findPSize);
void* memcpy_P(void* dest, PGM_VOID_P src, size_t count);
char* strncpy_P(char* dest, PGM_P src, size_t size);
char* strcpy_P(dest, src)
char* strncat_P(char* dest, PGM_P src, size_t size);
char* strcat_P(dest, src)
int strncmp_P(const char* str1, PGM_P str2P, size_t size);
int strcmp_P(str1, str2P)
int strncasecmp_P(const char* str1, PGM_P str2P, size_t size);
int strcasecmp_P(str1, str2P)
size_t strnlen_P(PGM_P s, size_t size);
size_t strlen_P(strP)
char* strstr_P(const char* haystack, PGM_P needle);
int printf_P(PGM_P formatP, ...);
int sprintf_P(char *str, PGM_P formatP, ...);
int snprintf_P(char *str, size_t strSize, PGM_P formatP, ...);
int vsnprintf_P(char *str, size_t strSize, PGM_P formatP, va_list ap);
Как видите, здесь много функций, но фактически это _P версии стандартных C-функций, адаптированные для считывания выровненных данных из 32-битной flash-памяти ESP8266. Все они используют тип данных PGM_P, который в сущности является const char *. Под «капотом» всех этих функций работает процесс, который считывает из flash-памяти 4 байта, но возвращает только тот байт, что был запрошен пользователем.
Но этот процесс исправно работает, только если ваша функция создана аналогично тем, что показаны выше. Все они адаптированы для работы с указателями PROGMEM, но в них нет проверки на тип данных, за исключением проверки на соответствие с const char*. Таким образом, вы вполне можете (пока разрешает компилятор) использовать в этих функциях строки с указателями типа const char *, но штука в том, что из-за этого программа может начать вести себя странно. Вы не сможете создавать перегруженные функции, способные использовать flash-строки, когда они заданы как PGM_P. Если попытаться сделать это, вы получите двусмысленную ошибку перегрузки, т.к. тип данных PGM_P, в сущности, идентичен типу данных const char*.
Но тут на помощь приходит __FlashStringHelper. Это класс-обертка, позволяющий использовать flash-строки как класс. Благодаря ему с flash-строками можно делать и проверку типа данных, и перегрузку функций. Думаю, многие знакомы с макросом F() и, возможно, с макросом FPSTR(). Оба этих макроса заданы в файле WString.h.
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
Итак, макрос FPDTR() берет PROGMEM-указатель на строку и преобразует его в класс __FlashStringHelper. Следовательно, если вы задали строку как xyz, то можете воспользоваться FPSTR() для преобразования этой строки в класс __FlashStringHelper, чтобы ее можно было использовать в функциях, совместимых с этих классом.
static const char xyz[] PROGMEM = "Это строка, сохраненная на flash";
Serial.println(FPSTR(xyz));
Макрос F() объединяет в себе оба этих метода, создавая простой и быстрый способ для записи встраиваемых строк на flash-память, а также возвращения типа __FlashStringHelper.
Например:
Serial.println(F("Это строка, сохраненная на flash"));
Хотя обе эти функции выполняют одну и ту же задачу, роли у них разные. Если FPSTR() позволяет задать глобальную flash-строку, а затем использовать ее в функции, принимающей класс __FlashStringHelper, то F() позволяет задать flash-строку лишь локально (т.е. ее нельзя будет использовать больше нигде). В результате FPSTR() позволяет обмениваться общими строками, а F() – нет. Класс String использует __FlashStringHelper, чтобы перегрузить свой конструктор (т.е. создать его разные версии).
String(const char *cstr = ""); // конструктор из const char *
String(const String &str); // копирующий конструктор
String(const __FlashStringHelper *str); // конструктор для flash-строк
В результате в коде можно написать следующее:
String mystring(F("Эта строка сохранена во flash-памяти"));
Как я написал функцию, использующую __FlashStringHelper? Я преобразовал указатель обратно в PGM_P и воспользовался функциями _P, показанными выше. Ниже это показано на примере класса String и функции конкатенации:
unsigned char String::concat(const __FlashStringHelper * str) {
if (!str) return 0; // это значение возвращается,
// если указатель пуст
int length = strlen_P((PGM_P)str); // преобразуем обратно
// в PGM_P, который в сущности
// является const char *,
// и измеряем при помощи
// _P версии функции strlen()
if (length == 0) return 1;
unsigned int newlen = len + length;
if (!reserve(newlen)) return 0; // создаем буфер
// корректной длины
strcpy_P(buffer + len, (PGM_P)str); // копируем строку в буфер
// при помощи strcpy_P()
len = newlen;
return 1;
}
Как объявить глобальную flash-строку и использовать ее?
static const char xyz[] PROGMEM = "Эта строка сохранена на flash. Len = %u";
void setup() {
Serial.begin(115200); Serial.println();
Serial.println( FPSTR(xyz) ); // просто печатаем строку,
// но сначала конвертируем ее
// в класс FlashStringHelper
// при помощи FPSTR()
Serial.printf_P( xyz, strlen_P(xyz)); // используем printf_P()
// со строкой PROGMEM
}
Как использовать встраиваемые flash-строки?
void setup() {
Serial.begin(115200); Serial.println();
Serial.println( F("Это встраиваемая строка")); //
Serial.printf_P( PSTR("Это встраиваемая строка при помощи printf %s"), "привет");
}
Как объявить и использовать данные в PROGMEM?
const size_t len_xyz = 30;
const uint8_t xyz[] PROGMEM = {
0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00};
void setup() {
Serial.begin(115200); Serial.println();
uint8_t * buf = new uint8_t[len_xyz];
if (buf) {
memcpy_P(buf, xyz, len_xyz);
Serial.write(buf, len_xyz); // выгружаем буфер
}
}
Как объявить данные в PROGMEM, а затем извлечь оттуда один байт?
Об объявлении данных говорилось выше. Чтобы извлечь данные, воспользуйтесь pgm_read_byte.
const size_t len_xyz = 30;
const uint8_t xyz[] PROGMEM = {
0x53, 0x61, 0x79, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
0x74, 0x6f, 0x20, 0x4d, 0x79, 0x20, 0x4c, 0x69, 0x74, 0x74,
0x6c, 0x65, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x00
};
void setup() {
Serial.begin(115200); Serial.println();
for (int i = 0; i < len_xyz; i++) {
uint8_t byteval = pgm_read_byte(xyz + i);
Serial.write(byteval); // выгружаем буфер
}
}
Итого
В использовании PROGMEM и PSTR() для сохранения строк на flash-память нет ничего сложного. Но PROGMEM и PSTR() генерируют указатели, поэтому вам понадобятся функции, которые специальным образом используют эти указатели, т.к. они являются, по сути, const char*. С другой стороны, FPSTR() и F() дают вам класс, из которого можно сделать неявные преобразования, что очень полезно при перегрузке функций. Стоит добавить, что если вам нужно сохранить int, float или указатель, то их можно сохранять и считывать напрямую, потому что их размер составляет 4 байта (т.е. они всегда остаются выровненными).
Надеюсь, эта статья была для вас полезна.
См.также
Партнерские ресурсы |
---|
Криптовалюты |
|
---|
Магазины |
|
---|
Хостинг |
|
---|
Разное |
- Викиум - Онлайн-тренажер для мозга
- Like Центр - Центр поддержки и развития предпринимательства.
- Gamersbay - лучший магазин по бустингу для World of Warcraft.
- Ноотропы OmniMind N°1 - Усиливает мозговую активность. Повышает мотивацию. Улучшает память.
- Санкт-Петербургская школа телевидения - это федеральная сеть образовательных центров, которая имеет филиалы в 37 городах России.
- Lingualeo.com — интерактивный онлайн-сервис для изучения и практики английского языка в увлекательной игровой форме.
- Junyschool (Джунискул) – международная школа программирования и дизайна для детей и подростков от 5 до 17 лет, где ученики осваивают компьютерную грамотность, развивают алгоритмическое и креативное мышление, изучают основы программирования и компьютерной графики, создают собственные проекты: игры, сайты, программы, приложения, анимации, 3D-модели, монтируют видео.
- Умназия - Интерактивные онлайн-курсы и тренажеры для развития мышления детей 6-13 лет
- SkillBox - это один из лидеров российского рынка онлайн-образования. Среди партнеров Skillbox ведущий разработчик сервисного дизайна AIC, медиа-компания Yoola, первое и самое крупное русскоязычное аналитическое агентство Tagline, онлайн-школа дизайна и иллюстрации Bang! Bang! Education, оператор PR-рынка PACO, студия рисования Draw&Go, агентство performance-маркетинга Ingate, scrum-студия Sibirix, имидж-лаборатория Персона.
- «Нетология» — это университет по подготовке и дополнительному обучению специалистов в области интернет-маркетинга, управления проектами и продуктами, дизайна, Data Science и разработки. В рамках Нетологии студенты получают ценные теоретические знания от лучших экспертов Рунета, выполняют практические задания на отработку полученных навыков, общаются с экспертами и единомышленниками. Познакомиться со всеми продуктами подробнее можно на сайте https://netology.ru, линейка курсов и профессий постоянно обновляется.
- StudyBay Brazil – это онлайн биржа для португалоговорящих студентов и авторов! Студент получает уникальную работу любого уровня сложности и больше свободного времени, в то время как у автора появляется дополнительный заработок и бесценный опыт.
- Автор24 — самая большая в России площадка по написанию учебных работ: контрольные и курсовые работы, дипломы, рефераты, решение задач, отчеты по практике, а так же любой другой вид работы. Сервис сотрудничает с более 70 000 авторов. Более 1 000 000 работ уже выполнено.
- StudyBay – это онлайн биржа для англоязычных студентов и авторов! Студент получает уникальную работу любого уровня сложности и больше свободного времени, в то время как у автора появляется дополнительный заработок и бесценный опыт.
|
---|
Внешние ссылки
ESP8266 AT-команды |
---|
Список AT-команд |
---|
Базовые команды |
- AT - Проверка запуска
- AT+RST - Рестарт
- AT+GMR - Просмотр информации о версиях
- AT+GSLP - Активация режима глубокого сна
- ATE - Активация/деактивация эха
- AT+RESTORE - Сброс к заводским настройкам
- AT+UART Настройка UART [Устарела]
- AT+UART_CUR - Настройка UART в текущей сессии
- AT+UART_DEF - Дефолтная настройка UART (записывается на FLASH)
- AT+SLEEP - Режим сна
|
---|
Команды для WiFi |
- AT+CWMODE - WiFi-режим (клиент / точка доступа / клиент + точка доступа).
- AT+CWMODE_CUR - WiFi-режим (клиент / точка доступа / клиент + точка доступа). Запись на FLASH не идет.
- AT_CWMODE_DEF - WiFi-режим (клиент / точка доступа / клиент + точка доступа). Запись идет на FLASH.
- AT+CWJAP - Подключение к точке доступа.
- AT+CWJAP_CUR - Подключение к точке доступа. Запись на FLASH не идет.
- AT+CWJAP_DEF - Подключение к точке доступа. Запись идет на FLASH.
- AT+CWLAP - Вывод списка доступных точек доступа.
- AT+CWQAP - Отключение от точки доступа
- AT+CWSAP - Настройка параметров для режима точки доступа
- AT+CWSAP_CUR - Настройка параметров для режима точки доступа. На FLASH запись не идет.
- AT+CWSAP_DEF - Настройка параметров для режима точки доступа. Запись идет на FLASH.
- AT+CWLIF - Получение IP-адресов клиентов, подключенных к точке доступа ESP8266.
- AT+CWDHCP - Включение/выключение DHCP. [Эта команда устарела].
- AT+CWDHCP_CUR - Включение/выключение DHCP. На FLASH не записывается.
- AT+CWDHCP_DEF - Включение/выключение DHCP. Сохранение идет на FLASH.
- AT+CWAUTOCONN - Автоматическое подключение к точке доступа при включении ESP8266.
- AT+CIPSTAMAC - Задает MAC-адрес для клиента ESP8266
- AT+CIPSTAMAC_CUR - Задает MAC-адрес для клиента ESP8266. На FLASH запись не идет.
- AT+CIPSTAMAC_DEF - Задает MAC-адрес для клиента ESP8266. Запись идет на FLASH.
- AT+CIPAPMAC - Задает MAC-адрес для точки доступа ESP8266.
- AT+CIPAPMAC_CUR - Задает MAC-адрес для точки доступа ESP8266. Запись на FLASH не идет.
- AT+CIPAPMAC_DEF - Задает MAC-адрес для точки доступа ESP8266. Запись идет на FLASH.
- AT+CIPSTA - Задает IP-адрес клиента ESP8266.
- AT+CIPSTA_CUR - Задает IP-адрес клиента ESP8266. Запись на FLASH не идет.
- AT+CIPSTA_DEF - Задает IP-адрес клиента ESP8266. Запись идет на FLASH.
- AT+CIPAP - Задает IP-адрес точки доступа ESP8266
- AT+CIPAP_CUR - Задает IP-адрес точки доступа ESP8266. На FLASH запись не идет.
- AT+CIPAP_DEF - Задает IP-адрес точки доступа ESP8266. Запись идет на FLASH.
- AT+CWSTARTSMART - Запуск SmartConfig
- AT+CWSTOPSMART - Остановка SmartConfig
|
---|
Команды для TCP/IP |
|
---|
Телепорт |
---|
Arduino |
Примеры Arduino |
---|
Стандартные функции |
---|
Основы |
|
---|
Цифровой сигнал |
|
---|
Аналоговый сигнал |
|
---|
Связь |
- ReadASCIIString - Анализ строки, состоящей из разделенных запятыми int-значений, и их последующее использование для управления RGB-светодиодом.
- ASCII Table - Демонстрирует продвинутые способы вывода данных на Serial Monitor.
- Dimmer - Изменение яркости светодиода при помощи движения мышкой.
- Graph - Отправка данных на компьютер и их графическое отображение в скетче Processing.
- Physical Pixel - Включение/выключение светодиода путем отправки данных со скетча Processing (или Max/MSP) на Arduino.
- Virtual Color Mixer - Отправка с Arduino на компьютер сразу нескольких значений, а затем их считывание при помощи скетча для Processing или Max/MSP.
- Serial Call Response - Многобайтная передача данных при помощи метода вызова и ответа (метода «рукопожатия»).
- Serial Call Response ASCII - Многобайтная передача данных при помощи метода вызова и ответа (метода «рукопожатия»). До передачи данные зашифровываются в ASCII.
- SerialEvent - Демонстрирует использование SerialEvent().
- Serial input (Switch (case) Statement) - Как совершать различные действия, беря за основу символы, присланные через последовательный порт.
- MIDI - Передача через последовательный порт сообщений с MIDI-нотами.
- MultiSerialMega - Использование двух последовательных портов на Arduino Mega.
|
---|
Управляющие структуры |
- If Statement - Как использовать оператор «if» для создания условий, опирающихся на входные аналоговые данные, при которых светодиод будет либо включаться, либо оставаться выключенным.
- For Loop - Управление несколькими светодиодами, чтобы они мигали, как LED-полоска у автомобиля Китт из сериала «Рыцарь дорог».
- Array - Вариация примера «For Loop», но с использованием массива.
- While Loop - Использование цикла while() для калибровки датчика. Калибровка включается при нажатии на кнопку.
- Switch Case - Как совершать какие-либо действия в зависимости от значений, полученных от датчика. Эквивалент примера «If Statement», но если бы условий было не два, а четыре. Этот пример демонстрирует, как дробить диапазон данных от датчика на четыре «суб-диапазона», а затем в зависимости от полученных результатов совершать одно из четырех действий.
- Switch Case 2 - Второй пример, демонстрирующий использование оператора switch. Показывает, как совершать различные действия в зависимости от определенных символов, полученных через последовательный порт.
|
---|
Датчики |
- ADXL3xx - Считывание данных с акселерометра ADXL3xx.
- Knock - Определение стука при помощи пьезоэлемента.
- Memsic2125 - Считывание данных с 2-осевого акселерометра Memsic2125.
- Ping - Определение объектов при помощи ультразвукового дальномера.
|
---|
Дисплей |
Примеры, объясняющие основы управления дисплеем:
|
---|
Строки |
|
---|
USB (для Leonardo, Micro и Due плат) |
В этой секции имеют место примеры, которые демонстрируют использование библиотек, уникальных для плат Leonardo, Micro и Due.
|
---|
Клавиатура |
- KeyboardMessage - Отправка текстовой строки при нажатии на кнопку.
- KeyboardLogout - Выход из текущей пользовательской сессии при помощи клавиатурных комманд.
- KeyboardSerial - Считывает байт, присланный через последовательный порт, а в ответ отсылает другой байт.
- KeyboardReprogram - Открывает новое окно в среде разработки Arduino, а затем перешивает Leonardo скетчем «Моргание».
|
---|
Мышь |
|
---|
Разное |
---|
|
---|
Espruino |
|
---|
ESP8266 |
ESP8266 AT-команды |
---|
Список AT-команд |
---|
Базовые команды |
- AT - Проверка запуска
- AT+RST - Рестарт
- AT+GMR - Просмотр информации о версиях
- AT+GSLP - Активация режима глубокого сна
- ATE - Активация/деактивация эха
- AT+RESTORE - Сброс к заводским настройкам
- AT+UART Настройка UART [Устарела]
- AT+UART_CUR - Настройка UART в текущей сессии
- AT+UART_DEF - Дефолтная настройка UART (записывается на FLASH)
- AT+SLEEP - Режим сна
|
---|
Команды для WiFi |
- AT+CWMODE - WiFi-режим (клиент / точка доступа / клиент + точка доступа).
- AT+CWMODE_CUR - WiFi-режим (клиент / точка доступа / клиент + точка доступа). Запись на FLASH не идет.
- AT_CWMODE_DEF - WiFi-режим (клиент / точка доступа / клиент + точка доступа). Запись идет на FLASH.
- AT+CWJAP - Подключение к точке доступа.
- AT+CWJAP_CUR - Подключение к точке доступа. Запись на FLASH не идет.
- AT+CWJAP_DEF - Подключение к точке доступа. Запись идет на FLASH.
- AT+CWLAP - Вывод списка доступных точек доступа.
- AT+CWQAP - Отключение от точки доступа
- AT+CWSAP - Настройка параметров для режима точки доступа
- AT+CWSAP_CUR - Настройка параметров для режима точки доступа. На FLASH запись не идет.
- AT+CWSAP_DEF - Настройка параметров для режима точки доступа. Запись идет на FLASH.
- AT+CWLIF - Получение IP-адресов клиентов, подключенных к точке доступа ESP8266.
- AT+CWDHCP - Включение/выключение DHCP. [Эта команда устарела].
- AT+CWDHCP_CUR - Включение/выключение DHCP. На FLASH не записывается.
- AT+CWDHCP_DEF - Включение/выключение DHCP. Сохранение идет на FLASH.
- AT+CWAUTOCONN - Автоматическое подключение к точке доступа при включении ESP8266.
- AT+CIPSTAMAC - Задает MAC-адрес для клиента ESP8266
- AT+CIPSTAMAC_CUR - Задает MAC-адрес для клиента ESP8266. На FLASH запись не идет.
- AT+CIPSTAMAC_DEF - Задает MAC-адрес для клиента ESP8266. Запись идет на FLASH.
- AT+CIPAPMAC - Задает MAC-адрес для точки доступа ESP8266.
- AT+CIPAPMAC_CUR - Задает MAC-адрес для точки доступа ESP8266. Запись на FLASH не идет.
- AT+CIPAPMAC_DEF - Задает MAC-адрес для точки доступа ESP8266. Запись идет на FLASH.
- AT+CIPSTA - Задает IP-адрес клиента ESP8266.
- AT+CIPSTA_CUR - Задает IP-адрес клиента ESP8266. Запись на FLASH не идет.
- AT+CIPSTA_DEF - Задает IP-адрес клиента ESP8266. Запись идет на FLASH.
- AT+CIPAP - Задает IP-адрес точки доступа ESP8266
- AT+CIPAP_CUR - Задает IP-адрес точки доступа ESP8266. На FLASH запись не идет.
- AT+CIPAP_DEF - Задает IP-адрес точки доступа ESP8266. Запись идет на FLASH.
- AT+CWSTARTSMART - Запуск SmartConfig
- AT+CWSTOPSMART - Остановка SmartConfig
|
---|
Команды для TCP/IP |
|
---|
|
---|
Node-RED |
|
---|
Processing |
Справочник языка Processing |
---|
Конструкции языка |
|
---|
Окружение |
|
---|
Данные |
|
---|
Управление |
|
---|
Форма |
|
---|
Ввод |
|
---|
Вывод |
|
---|
Преобразование |
|
---|
Свет, камера |
|
---|
Цвет |
|
---|
Изображение |
|
---|
Рендер |
|
---|
Типография |
|
---|
Математика |
|
---|
Константы |
|
---|
Примеры на Processing |
---|
Основы |
- Структуры и конструкции:
- Фигуры:
- Данные:
- Массивы:
- Управляющие конструкции:
- Работа с изображением:
- Работа с цветом:
- Применение математических функций:
|
---|
Продвинутые графические эффекты |
- Рисование:
- Анимация:
- Графический интерфейс пользователя:
- Движение:
- Взаимодействие:
- Обработка изображения:
- Advanced Data:
- File IO:
- Simulate:
- Vectors:
- Fractals and L-Systems:
- Cellular Automata:
|
---|
Примеры из сторонних библиотек |
|
---|
|
---|
Электроника |
|
---|