Как записать структуру в 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 циклов закончатся за минуты. Сохраняйте по событию — нажатию кнопки или раз в несколько минут, — а для частых записей используйте кольцо копий (наш вывод).
Источники
- 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
- avr-libc. avr/eeprom.h (репозиторий avrdudes/avr-libc) — EEMEM — атрибут секции .eeprom; eeprom_read_block(dst, src, n), eeprom_update_block(src, dst, n): байт, совпадающий с записанным, не перезаписывается
- Arduino. ArduinoCore-avr, libraries/EEPROM/src/EEPROM.h (ветка master, 2026-10-24) — put() перебирает sizeof(T) байт и для каждого вызывает update(): запись только при отличии; get() читает sizeof(T) байт в объект; length() = E2END + 1
Нашли ошибку?
Смотрите также
- EEPROM AVR: ресурс и кодРаскладка EEMEM, время записи, ресурс и износ, BOD, код и .eep
- EEPROMЭнергонезависимая память для настроек и калибровки
- ATmega328PРаспиновка, фьюзы, Optiboot, периферия, питание, схема и код на C
- Почему стирается EEPROM при прошивке Arduino?ISP делает Chip Erase, а EESAVE у Uno не запрограммирован