Посмотр рубрик

Отличия ESP от Arduino

ESP8266 и ESP32 поддерживают Arduino-фреймворк, но с некоторыми отличиями и добавлениями. Рассмотрим некоторые из них.

Математика #

Деление на 0 #

В отличие от AVR, деление на 0 приводит к критической ошибке и перезагрузке МК - стараемся этого избегать.

Типы данных #

  • int, size_t - 32 бит
  • double - 64 бит
  • Указатель/ссылка - 32 бит
  • Выравнивание памяти - 4 байта

При передаче данных между ESP и условно AVR, где int 16 битный, лучше использовать явные типы данных - short/long, а лучше int16_t/int32_t и так далее

Arduino-функции #

map() #

В функции map(val, min, max, to_min, to_max) нет защиты от деления на 0, поэтому если min равен max - МК перезагрузится. Если min и max задаются какими-то внешними условиями - проверяйте их равенство вручную и исключайте вызов map() с такими аргументами.

min() и max() #

Функции min() и max() реализованы как функции, а не как макросы, поэтому должны использоваться с данными одного типа. Использование переменных разного типа приведёт к ошибке компиляции:

unsigned long v;
v = max(v, 123);    // ошибка
v = max(v, 123ul);  // ОК

analogWrite #

У ESP32 (тестировалось на версии SDK 2.x) после вызова analogWrite пин перестаёт работать как обычный GPIO - не реагирует на digitalWrite, что не соответствует Ардуино-фреймворку, где digitalWrite отключает ШИМ. Для отключения генерации ШИМ нужно вручную вызвать ledcDetachPin(пин).

printf #

В отличие от классических Arduino, в ESP к классу Print добавлен метод printf - форматированный вывод (см. урок про него). Это означает, что можно печатать с форматированием в разные интерфейсы, например выводить в монитор порта как

Serial.printf("Hello %d world", 12);    // Hello 12 world

Прерывания #

  • В обработчике нельзя использовать динамическое выделение и перераспределение памяти (new, malloc, realloc), соответственно работать со String тоже нельзя
  • В прерывании нельзя использовать задержки
  • Функция-обработчик должна быть объявлена со специальным атрибутом, он размещает код функции в оперативной памяти:
    • ESP8266 - IRAM_ATTR
    • ESP32 - ARDUINO_ISR_ATTR
// IRAM_ATTR void isr() {}          // ESP8266
// void ARDUINO_ISR_ATTR isr() {}   // ESP32

void setup() {
    attachInterrupt(1, isr, RISING);
}

Есть ещё атрибут DRAM_ATTR - у объявленной с ним функции находящиеся внутри static const переменные также будут размещены в оперативной памяти. При использовании IRAM_ATTR такие переменные компилятор может разместить во Flash памяти.

EEPROM #

Основной урок про EEPROM

EEPROM является эмуляцией из Flash памяти, поэтому мы можем выбрать нужный размер:

  • Перед началом работы нужно вызвать EEPROM.begin(4.. 4096) с указанием размера области памяти в байтах
  • Для применения изменений в памяти нужно вызвать EEPROM.commit()
  • В некоторых версиях SDK отсутствует EEPROM.update() и EEPROM.length()
  • У Flash памяти небольшой ресурс - всего около 10'000 перезаписей. У фирменной памяти Winbond (можно найти на некоторых моделях ESP-12 и прочих) - около 50'000 перезаписей

Важно: EEPROM реализован следующим образом: после запуска EEPROM.begin(4.. 4096) содержимое EEPROM указанного размера дублируется в оперативной памяти. После любого изменения и вызова EEPROM.commit() стирается весь блок Flash памяти (4 кБ) и записывается заново. Таким образом ресурс "EEPROM" памяти у ESP вырабатывается довольно быстро и весь сразу, а не по ячейкам.

Вместо EEPROM используйте библиотеку FileData для удобного хранения любых данных в файловой системе

PROGMEM #

Основной урок про PROGMEM

ESP8266 #

PROGMEM работает как на AVR, без него данные могут располагаться в оперативной памяти. Для совместимости есть даже _P и pgm_-функции из avrlibs.

ESP32 #

Начиная с 2.x версии, const-данные (строки, массивы и т.д.) автоматически располагаются во Flash памяти и сами читаются оттуда. Arduino PROGMEM API полностью поддерживается для совместимости со скетчами от AVR/ESP8266, но по сути ничего не делает.

UART/Serial #

Основной урок про Serial

ESP8266 #

Имеет 1.5 (полтора) аппаратных UART:

  • Serial (UART0) - подключен к USB-UART для прошивки и отладки
    • RX - 3
    • TX - 1
    • При вызове Serial.swap() (после Serial.begin()) пины переместятся на TX 15 и RX 13. После повторного вызова - обратно
  • Serial1 - доступен только на отправку
    • RX - нет
    • TX - 2

ESP32 #

Имеет 3 аппаратных UART:

UART Serial RX TX
UART0 Serial 3 1
UART1 Serial1 9* 10*
UART2 Serial2 16 17
  • *Serial1 по умолчанию использует системные пины и не будет работать!

На ESP32 пины аппаратных UART можно переназначить на любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения):

void begin(uint32_t baud);  // стандартные пины
void begin(uint32_t baud, uint32_t config = SERIAL_8N1, int8_t rxPin, int8_t txPin);    // свои пины
void setup() {
    Serial.begin(9600, SERIAL_8N1, 1, 2);
    Serial1.begin(9600, SERIAL_8N1, 3, 4);
    Serial2.begin(115200, SERIAL_8N1, 5, 6);
}

I2C/Wire #

Основной урок про Wire

ESP8266 #

Используется программная реализация I2C, стандартные пины как на Arduino Nano:

  • SDA - 4
  • SCL - 5

При запуске можно переназначить пины на любые другие:

void begin();   // стандартные пины
void begin(int sda, int scl);   // свои пины
void begin(int sda, int scl, uint8_t addr); // свои пины, свой адрес (режим slave)

ESP32 #

Имеет 2 аппаратных I2C: системные объекты Wire и Wire1. При вызове begin() устанавливаются стандартные пины:

  • SDA - 21
  • SCL - 22

При запуске можно переназначить пины на любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения). Порядок аргументов отличается от ESP8266:

void begin();   // стандартные пины
bool begin(int sda, int scl, uint32_t frequency = 0);   // свои пины, частота
bool begin(uint8_t addr, int sda, int scl, uint32_t frequency); // адрес (slave), пины, частота

Для использования Wire1 нужно обязательно указать ему пины!

void setup() {
    Wire.begin();           // Wire на пинах 21 22
    Wire1.begin(23, 24);    // Wire1 на пинах 23 24
}

SPI #

Основной урок про SPI

ESP8266 #

Один аппаратный SPI, пины привязаны к стандартным:

MISO MOSI SCK SS
12 13 14 15

Пин SS должен работать в режиме OUTPUT. Если он будет INPUT с сигналом LOW - SPI перейдёт в режим Slave.

void setup() {
    SPI.begin();
    pinMode(SS, OUTPUT);
}

ESP32 #

Несколько аппаратных SPI, но доступны только два - HSPI и VSPI, их пины по умолчанию (см. распиновку конкретной платы, на некоторых моделях пины отличаются):

SPI MISO MOSI SCK SS
VSPI 19 23 18 5
HSPI 12 13 14 15

Системный объект SPI - это VSPI. При вызове begin() будет работать на указанных выше пинах. Пины можно указать любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения):

void begin();   // запуск на стандартных пинах
void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss);    // свои пины

Для использования второго SPI (HSPI) нужно самостоятельно создать объект класса SPIClass и передать ему константу HSPI:

SPIClass hSPI(HSPI);    // создание HSPI

// точно так же можно создать VSPI - "синоним" системного объекта SPI
// SPIClass vSPI(VSPI);     // == SPI

void setup() {
    // VSPI, стандартные пины
    SPI.begin();

    // VSPI, свои пины
    SPI.begin(4, 5, 6, 7);
    pinMode(7, OUTPUT);     // обязательно для SS (SPI Master)

    // HSPI, стандартные пины
    hSPI.begin()

    // HSPI, свои пины
    hSPI.begin(8, 9, 10, 11);
    pinMode(11, OUTPUT);    // обязательно для SS (SPI Master)
}

Полезные страницы #

Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Прокрутить вверх