Перейти к содержанию

Как записать структуру в EEPROM AVR?

Обновлено Редакция GAW

EEPROM у ATmega328P — 1 КБ, который читается и пишется побайтно. Структуру удобнее всего записывать целиком: библиотеки разбивают её на байты сами.

Arduino

#include <EEPROM.h>

struct Settings {
  uint8_t version;   // layout version
  float k;
  int16_t offset;
  uint32_t counter;
};

Settings s;

void setup() {
  EEPROM.get(0, s);
  if (s.version != 1) s = {1, 1.0f, 0, 0};  // defaults
  s.counter++;
  EEPROM.put(0, s);  // writes changed bytes only
}

EEPROM.put() проходит по sizeof(T) байтам и для каждого вызывает update(), а он пишет байт, только если тот отличается от записанного.

avr-libc

Без Arduino переменную объявляют с атрибутом EEMEM — компилятор положит её в секцию .eeprom — и пользуются eeprom_update_block(&s, &ee_s, sizeof s) и eeprom_read_block(&s, &ee_s, sizeof s). Начальные значения из инициализатора попадут в файл .eep, а не во флеш. Как адреса полей ложатся в память, покажет калькулятор EEPROM AVR.

Сколько это стоит

ВеличинаСтруктура из примераИсточник
Размер1 + 4 + 2 + 4 = 11 байту AVR нет выравнивания полей
Время записидо 11 × 3,4 ≈ 37 мстабл. 8-1 DS40002061B
Ресурс100 000 циклов на байтразд. 8.4
Раз в минуту без кольца копийоколо 69 сутокнаш расчёт

Все 37 мс питание должно держаться: при просадке процессор может записать мусор, поэтому Microchip советует держать сброс детектором BOD.

Подводные камни

  • Смена прошивки. Если порядок или типы полей изменились, старые байты прочитаются как другие значения — отсюда поле версии.
  • Стирание при прошивке через программатор. Загрузка скетча через USB EEPROM не трогает, а через ISP — может стереть, см. «Почему стирается EEPROM при прошивке Arduino».
  • Ресурс. Как растянуть ресурс кольцом копий, разобрано в калькуляторе, а устройство памяти — в термине EEPROM. Остальные характеристики кристалла — на странице ATmega328P.

Похожие вопросы

Можно ли хранить в структуре String или указатель?

Нет. EEPROM.put копирует байты самого объекта, а у String и указателя это лишь адрес в ОЗУ: после перезагрузки он ни на что не указывает. Строки хранят в массиве char фиксированной длины внутри структуры (наш вывод из того, как put перебирает sizeof(T) байт).

Как понять, что в EEPROM ещё нет данных?

Стёртая EEPROM читается как 0xFF в каждом байте. Наш совет: первым полем держать номер версии структуры и сравнивать его при чтении; если не совпал — значит, данных нет или они от старой прошивки, и нужно записать значения по умолчанию. Для надёжности добавляют и контрольную сумму.

Не износится ли EEPROM, если вызывать put в loop()?

put пишет только изменившиеся байты, поэтому повтор одних и тех же данных ресурс не тратит. Но если значение действительно меняется каждый цикл, 100 000 циклов закончатся за минуты. Сохраняйте по событию — нажатию кнопки или раз в несколько минут, — а для частых записей используйте кольцо копий (наш вывод).

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 8.4 — 1 КБ EEPROM, не меньше 100 000 циклов записи и стирания; табл. 8-1 — стирание с записью 3,4 мс; 8.4.2 — порча данных при низком VCC, совет держать сброс через BOD
  2. avr-libc. avr/eeprom.h (репозиторий avrdudes/avr-libc) — EEMEM — атрибут секции .eeprom; eeprom_read_block(dst, src, n), eeprom_update_block(src, dst, n): байт, совпадающий с записанным, не перезаписывается
  3. Arduino. ArduinoCore-avr, libraries/EEPROM/src/EEPROM.h (ветка master, 2026-10-24) — put() перебирает sizeof(T) байт и для каждого вызывает update(): запись только при отличии; get() читает sizeof(T) байт в объект; length() = E2END + 1

Нашли ошибку?