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

EEPROM AVR: раскладка, ресурс записи и код

Опишите переменные, которые хранятся в EEPROM, и как часто программа их сохраняет — калькулятор разложит их по адресам, посчитает время записи и ресурс с кольцом копий и без него, проверит порог BOD для вашей частоты и питания и выдаст код на C для avr-libc или на регистрах, а также файл .eep с начальными значениями.

Исходные данные

uint8_t, int8_t, char, uint16_t, int16_t, uint32_t, int32_t, float; массивы: uint8_t cal[4] = {1, 2, 3, 4}, char name[8] = "abc"

с
Б

пусто — вся EEPROM, 1024 Б

счётчик-метка новой копии

Питание и детектор BOD

МГц
В

Результат

Ресурс при кольце из 56 копий (оценка)
≈ 10,6 года
5 600 000 сохранений до 100 000 циклов ячейки; копия 18 Б
Без выравнивания износа
69 сут 11 чодна копия перезаписывается на месте
EEPROM
1024 Б, 0x000…0x3FFстраницы программатора 4 Б × 256; из программы — по байту
Запись байта из программы
3,4 мсстирание + запись; по отдельности 1,8 мс + 1,8 мс; 26 368 тактов RC — 3,3 мс
Запись всей структуры (17 Б)
57,8 мсесли изменились все байты; столько должно держаться питание
Запись всей EEPROM
3,48 спрограмматором без опроса готовности: по байту 3,69 с, страницами ≈ 922 мс (tWD_EEPROM 3,6 мс)

Оценка: 100 000 циклов — минимум по даташиту для каждого байта; считается, что при каждом сохранении меняются все байты. eeprom_update_* не перезаписывают совпадающие байты, поэтому на деле ресурс не меньше.

ATmega328P на 16 МГц нужно не меньше 3,78 В, а BOD 101 может сработать только при 2,5 В: между ними процессор работает вне гарантированной области и может испортить EEPROM. Выберите порог выше или снизьте частоту.

Раскладка структуры: 17 Б из 1024 Б

АдресПолеБайтНачальные байты
0x000uint16_t magic25A A5
0x002uint8_t brightness180
0x003int16_t offset2F4 FF
0x005float k400 00 80 3F
0x009char name[8]867 61 77 00 00 00 00 00
Код

Код на C (avr-libc)

#include <stdint.h>
#include <avr/eeprom.h>

typedef struct {
    uint16_t magic;        /* 0x000, 2 B */
    uint8_t brightness;    /* 0x002, 1 B */
    int16_t offset;        /* 0x003, 2 B */
    float k;               /* 0x005, 4 B */
    char name[8];          /* 0x009, 8 B */
} settings_t;                       /* 17 B, no padding on AVR */

/* Initial values go to the .eeprom section, that is, to the .eep file. */
settings_t EEMEM ee_settings = {
    .magic = 0xA55A,
    .brightness = 128,
    .offset = -12,
    .k = 1.0,
    .name = "gaw",
};

settings_t settings;                /* working copy in RAM */

void settings_load(void)
{
    eeprom_read_block(&settings, &ee_settings, sizeof settings);
}

void settings_save(void)
{
    /* rewrites only the bytes that changed: less wear, ~3.4 ms per changed byte */
    eeprom_update_block(&settings, &ee_settings, sizeof settings);
}

/* One field without the whole struct: */
void settings_magic_example(void)
{
    uint16_t magic = eeprom_read_word(&ee_settings.magic);
    eeprom_update_word(&ee_settings.magic, magic);
}

Файл .eep (Intel HEX)

Скачать eeprom.eep
:100000005AA580F4FF0000803F6761770000000080
:0100100000EF
:00000001FF

Содержимое EEPROM после прошивки .eep

0000  5A A5 80 F4 FF 00 00 80  3F 67 61 77 00 00 00 00  |Z.......?gaw....|
0010  00                                                |.               |

Расчёт выполнен по формулам и данным из источников, приведённых на этой странице, и не заменяет проверку: параметры компонентов у разных производителей и даже партий различаются. Перед применением сверяйте результат с документацией на конкретные детали.

EEPROM у классических AVR — отдельное от флеш-памяти пространство в сотни байт или несколько килобайт, которое читается и пишется по одному байту и не теряет данные без питания. В нём держат калибровки, настройки и счётчики. Калькулятор отвечает на три вопроса, которые возникают при работе с ним: где лягут переменные, на сколько хватит ресурса и что сделать, чтобы данные не портились.

Объём и время записи

МикроконтроллерEEPROMЗапись байта из программыОжидание у программатора (tWD_EEPROM)
ATmega328P1 КБ3,4 мс (3,3 мс в табл. 8-2)3,6 мс
ATmega32U41 КБ3,4 мс9,0 мс
ATmega25604 КБ3,4 мс3,6 мс
ATmega8, ATmega16512 байт8,5 мс9,0 мс
ATmega321 КБ8,5 мс9,0 мс
ATtiny85512 байт3,4 мс4,0 мс
ATtiny13A64 байта3,4 мс4,0 мс
ATtiny2313A128 байт3,4 мс4,0 мс

Время записи отсчитывает внутренний калиброванный RC-генератор, поэтому от тактовой частоты процессора оно не зависит. У ATmega328P даташит даёт два близких значения: 3,4 мс в таблице режимов EEPM и 3,3 мс — 26 368 тактов генератора 8 МГц — в таблице времени программирования; калькулятор считает по большему. У новых AVR биты EEPM1:0 позволяют разделить стирание и запись по 1,8 мс: байт стирают заранее, а в спешке, например при пропадании питания, только записывают.

Размер страницы (4 или 8 байт) важен только программатору, который может загружать EEPROM страницами. Программа пишет по одному байту, и пока идёт запись, остальные обращения к EEPROM невозможны: следующий байт ждёт сброса бита EEPE.

Ресурс и выравнивание износа

Каждый байт EEPROM выдерживает не меньше 100 000 циклов стирания и записи. Ресурс тратит только запись, и тратит его тот байт, который перезаписывается чаще всего. Если сохранять 16-байтную структуру раз в минуту на одно место, 100 000 циклов закончатся примерно через 69 суток.

Простое выравнивание — кольцо копий. Область EEPROM делят на ячейки размером «запись + служебный байт», каждое сохранение кладут в следующую ячейку, а в служебный байт пишут счётчик, по которому после сброса находят самую новую копию. Если копий N, каждая ячейка перезаписывается в N раз реже. В 1 КБ помещается 60 копий по 17 байт, и та же структура раз в минуту проживёт около 11 лет.

Калькулятор показывает обе цифры и прямо помечает их как оценку: он берёт гарантированный минимум даташита и считает, что при каждом сохранении меняются все байты. Функции eeprom_update_* из avr-libc читают байт перед записью и не трогают совпадающие, так что неизменные поля не изнашиваются.

Запись по регистрам

Регистры EEPROM — адрес EEARH:EEARL (у ATtiny13A и ATtiny2313A один EEAR), данные EEDR и управление EECR. Порядок записи по даташиту ATmega328P:

  1. Дождаться, пока сбросится EEPE: предыдущая запись закончилась.
  2. Если программа сама пишет во флеш-память (загрузчик, SPM), дождаться сброса SPMEN.
  3. Записать адрес в EEAR и данные в EEDR.
  4. Установить EEMPE, сбросив при этом EEPE.
  5. Не позже чем через четыре такта установить EEPE.

EEMPE сам сбрасывается через четыре такта, поэтому прерывание между шагами 4 и 5 срывает запись — на это время прерывания запрещают. Окно в четыре такта выдерживает только оптимизированная сборка (например, -Os), где обе записи в EECR — по одной команде; без оптимизации вторая запись может опоздать. В сгенерированном коде шаг 4 записан как EECR = (1 << EEMPE): так заодно обнуляются биты EEPM1:0 (режим «стирание + запись») и EERIE, то есть прерывание готовности EEPROM выключается — если оно нужно, включите его снова после записи. Для чтения достаточно записать адрес и установить EERE: данные появляются в EEDR сразу, процессор при этом стоит четыре такта. У ATmega8, ATmega16 и ATmega32 те же биты называются EEMWE и EEWE, а режимов EEPM нет.

Порча данных при низком питании

Главная причина испорченной EEPROM — не износ, а работа при недостаточном напряжении: при медленном спаде питания процессор продолжает выполнять программу ниже допустимого для своей частоты и может записать мусор. Даташит советует держать микроконтроллер в сбросе, пока питания не хватает, — детектором BOD или внешним супервизором.

Калькулятор сравнивает порог BOD с напряжением, нужным для выбранной частоты. У ATmega328P допустимая частота растёт линейно от 4 МГц при 1,8 В до 10 МГц при 2,7 В и до 20 МГц при 4,5 В, так что для 16 МГц нужно не меньше 3,78 В. У Arduino Uno фьюзы выставляют BOD 2,7 В, а его нижняя граница по даташиту — 2,5 В: между 2,5 и 3,78 В процессор работает вне гарантированной области. Если EEPROM важна, на 16 МГц выбирают порог 4,3 В. Пороги и их коды для всех микроконтроллеров есть в калькуляторе фьюзов AVR.

Ещё одна неожиданность — стирание EEPROM программатором. Команда Chip Erase стирает и флеш-память, и EEPROM, если не запрограммирован фьюз EESAVE; у Arduino Uno он не запрограммирован.

Переменные EEMEM и файл .eep

Атрибут EEMEM из avr-libc кладёт переменную в секцию .eeprom. Порядок отдельных глобальных переменных в секции компилятор не обещает, поэтому калькулятор собирает поля в одну структуру: у AVR нет выравнивания, поля идут подряд, многобайтные значения хранятся младшим байтом вперёд, float — в формате IEEE 754 одинарной точности.

Начальные значения из инициализатора попадают в файл .eep — Intel HEX с адресами от начала EEPROM. Калькулятор показывает этот файл и дамп памяти после его записи; разобрать готовый файл можно в просмотрщике HEX-файлов, а как появляются .hex и .eep при сборке — в статье о файлах прошивки.

Частые вопросы

Сколько раз можно записать EEPROM ATmega328P?

Даташит гарантирует не меньше 100 000 циклов записи и стирания для каждого байта. Если сохранять одну и ту же переменную раз в минуту, этого хватит примерно на 69 суток; если раз в час — на 11 лет. Ресурс расходует только запись: чтение EEPROM не изнашивает. Чтобы растянуть ресурс, данные пишут по кругу в несколько копий, а eeprom_update_* не перезаписывают байты, которые не изменились.

Сколько длится запись байта в EEPROM AVR?

У ATmega328P, ATmega2560, ATmega32U4 и ATtiny — около 3,3–3,4 мс на байт: стирание и запись идут одной операцией, их отсчитывает внутренний калиброванный RC-генератор, поэтому время не зависит от тактовой частоты. У старых ATmega8, ATmega16 и ATmega32 — 8,5 мс. Структура из 17 байт записывается почти 60 мс, и всё это время питание должно держаться.

Почему портится EEPROM AVR при выключении питания?

Когда напряжение медленно падает, процессор какое-то время работает ниже допустимого для своей частоты и может выполнить команды неверно, в том числе записать мусор в EEPROM. Даташит советует держать микроконтроллер в сбросе при недостаточном питании — включить детектор BOD с подходящим порогом или поставить внешний супервизор. Начатая запись байта при сбросе доводится до конца, если напряжения хватает.

Как записать начальные значения EEPROM вместе с прошивкой?

Переменные с атрибутом EEMEM и инициализатором попадают в секцию .eeprom, из которой при сборке получается отдельный файл .eep в формате Intel HEX. Его записывают программатором: avrdude -U eeprom:w:firmware.eep:i. Загрузка скетча через загрузчик Arduino этот файл не записывает.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 8.4 — 1 КБ EEPROM, не меньше 100 000 циклов записи и стирания; 8.4.1 — при чтении процессор стоит 4 такта, при записи 2; 8.4.2 — порча EEPROM при низком VCC, совет держать сброс через BOD; 8.6.3 — EECR, порядок записи и EEMPE → EEPE за 4 такта; табл. 8-1 — EEPM: 3,4 мс стирание с записью, 1,8 мс по отдельности; табл. 8-2 — 26 368 тактов RC-генератора, 3,3 мс; табл. 28-12 — страницы по 4 байта; табл. 28-18 — tWD_EEPROM 3,6 мс; табл. 29-12 — пороги BOD; 29.3 — частота от напряжения линейна между 1,8, 2,7 и 4,5 В; фьюз EESAVE
  2. Microchip (Atmel). ATmega640/1280/1281/2560/2561 datasheet (2549Q) — табл. 8-1 — 26 368 тактов, 3,3 мс; табл. 9-1 — режимы EEPM; табл. 30-8 — 4 КБ страницами по 8 байт; табл. 30-16 — tWD_EEPROM 3,6 мс; табл. 31-6 — пороги BOD; 31.2 — исполнения 2560 и 2560V
  3. Microchip (Atmel). ATmega16U4/32U4 datasheet (Atmel-7766J) — табл. 5-2 и 5-3 — режимы EEPM и 3,3 мс; табл. 28-12 — страницы; табл. 28-15 — tWD_EEPROM 9,0 мс; табл. 8-1 — семь порогов BOD от 2,0 до 4,3 В; 29.6 — 8 МГц при 2,7 В и 16 МГц при 4,5 В
  4. Microchip (Atmel). Даташиты ATmega8(L) 2486AA, ATmega16(L) 2466T, ATmega32(L) 2503Q — табл. 1 — 8448 тактов генератора 1 МГц, 8,5 мс; биты EEMWE и EEWE без режимов EEPM; табл. 90, 108, 107 — страницы по 4 байта; tWD_EEPROM 9,0 мс (табл. 97, 115, 114); табл. 15 — BODEN и BODLEVEL: 2,7 или 4,0 В; исполнения L (8 МГц от 2,7 В) и обычные (16 МГц от 4,5 В)
  5. Microchip (Atmel). Даташиты ATtiny25/45/85 (2586Q), ATtiny13A (DS40002307A), ATtiny2313A/4313 (8246B) — режимы EEPM — 3,4 и 1,8 мс (табл. 5-1, 5-1, 5-4); страницы по 4 байта (табл. 20-9, 17-6, 21-2); tWD_EEPROM 4,0 мс; пороги BOD 1,8/2,7/4,3 В (табл. 21-7, 18-6, 22-5 — у ATtiny2313A в таблице напечатаны двухбитные коды, а фьюзов BODLEVEL три)
  6. avr-libc. avr/eeprom.h и заголовки устройств (репозиторий avrdudes/avr-libc) — EEMEM — секция .eeprom; данные хранятся младшим байтом вперёд без выравнивания; eeprom_update_* сначала читают байт и не пишут совпадающий; функции ждут EEPE, но не SPMEN; не реентерабельны; пишут в режиме «стирание + запись». E2END и E2PAGESIZE в iom328p.h, iomxx0_1.h, iotnx5.h и других — второй источник размеров для тестов
  7. Arduino. ArduinoCore-avr, boards.txt — Arduino Uno: 16 МГц, фьюзы high 0xDE (EESAVE не запрограммирован) и extended 0xFD (BOD 2,7 В)

Где пригодится

Встроить калькулятор на свой сайт

Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах

<iframe src="https://gaw.ru/embed/avr-eeprom/" title="EEPROM AVR: раскладка, ресурс записи и код" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/avr-eeprom/">EEPROM AVR: раскладка, ресурс записи и код</a> — GAW.ru</p>
Автоподстройка высоты рамки

Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.

<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>

Обновлено .

Нашли ошибку в расчёте?