Размер страницы EEPROM у AVR семейства ATmega (E2PAGESIZE)

Как всем известно, EEPROM разных производителей имеют страничное стирание и перезапись страницы (даже если меняется один байт).
Конечно кроме самых мелких, там одна страница == один байт, но этот случай мы сейчас не рассматриваем.
Интересно выяснить какой же размер страницы внутреннего EEPROM у AVR семейства ATmega.

Запись байта по какому-то адресу автоматом запускает последовательность действий: копирование страницы EEPROM (той, где расположена записываемая ячейка) во временную RAM-страницу; модификацию записываемого байта в RAM-странице; очистку всей страницы EEPROM, и, наконец, запись RAM-страницы в EEPROM.
В современных атмелах даташит говорит нам о том что:
The EEPROM array is programmed one byte at a time by supplying the address and data together with the appropriate Write instruction. An EEPROM memory location is first automatically erased before new data is written.


У семейства AVR ATmega размер страницы EEPROM 4 или 8 байт
Это значение хранится в хедер файлах, в дефайне E2PAGESIZE (The size of the EEPROM page)

Например начиная с 64 меги #define E2PAGESIZE 8 байт, а у 32 меги и других младших моделей — 4 байта. У тинек 1/2/4 байта (E2PAGESIZE 0 судя по всему одначает «одна страница == один байт»)
Но что самое интересное E2PAGESIZE 0 было и у некоторых устаревших мег (atmega323, mega103 и т.п.)

Чтобы не засорять тему я приведу E2PAGESIZE только некоторых моделей:
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom8.h
Line 590: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom88pa.h
Line 858: #define E2PAGESIZE (4)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom8515.h
Line 610: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom8535.h
Line 693: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom2560.h
Line 46: #define E2PAGESIZE 8
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom128a.h
Line 1017: #define E2PAGESIZE 8
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom1280.h
Line 46: #define E2PAGESIZE 8
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom1284p.h
Line 1165: #define E2PAGESIZE 8
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom16.h
Line 592: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom32a.h
Line 639: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom161.h
Line 664: #define E2PAGESIZE 0
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom163.h
Line 620: #define E2PAGESIZE 0
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom164pa.h
Line 962: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom168pa.h
Line 789: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom16m1.h
Line 1514: #define E2PAGESIZE (4)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom328p.h
Line 888: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom48pa.h
Line 789: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iom64.h
Line 1214: #define E2PAGESIZE 8
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn10.h
Line 387: #define E2PAGESIZE (0)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn4.h
Line 352: #define E2PAGESIZE (0)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn11.h
Line 212: #define E2PAGESIZE 2
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn13a.h
Line 352: #define E2PAGESIZE (4)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn2313a.h
Line 647: #define E2PAGESIZE (4)
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn24.h
Line 45: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn88.h
Line 757: #define E2PAGESIZE 4
C:\Atmel\Studio\7.0\...gnu-toolchain\avr\include\avr\iotn9.h
Line 352: #define E2PAGESIZE (0)

Небольшое исследование подтвердило мои догадки:
AVR C Runtime Library — bug #22240
The factory has confirmed, via my FAE, that the listed EEPROM Endurance (100,000 Write/Erase Cycles) figures are on a page bases, and not a byte bases.

If you need to know how far to spread the bytes so that you can archive higher endurance figures, for example if you need a byte, but for 200,000 cycles you need to use two pages, or 16 bytes in the CAN64 part.

The most important efficiency with EEPROM is to read first, and only update the EEPROM byte if changed. avr-gcc now has 'eeprom_update_xxx()' macros. CodeVision has always done update rather than write. I would assume that ICC, IAR and everyone else behaves the same.

Но похоже автор комментария был не прав.
Документ AVR103: Using the EEPROM Programming Modes уточняет что программирование действительно производится побайтно, даже у самых свежих мег вроде ATmega256x и ATmega48/88/168:
Как устроена память EEPROM. Память EEPROM состоит из независимых ячеек, каждая представляет 1 бит. Принцип работы ячеек основан на технологии транзисторов с плавающим затвором: электрический заряд, захваченный затвором транзистора, определяет хранимый в ячейке логический уровень. Слегка упрощая, работу ячейки можно описать следующим образом: когда ячейка стирается (erase), заряд помещается на затвор и ячейка будет читаться как лог. 1 (состояние памяти по умолчанию). Программирование ячейки эквивалентно снятию разряда с затвора, что переводит логическое значение ячейки в 0. Можно запрограммировать (разрядить) только ту ячейку, которая ранее была стерта (заряжена).

Обратите внимание, что хотя к памяти EEPROM осуществляется доступ по байтам, биты все равно могут быть запрограммированы по отдельности. Поскольку только запрограммированные биты разряжены (находятся в состоянии лог. 0), оставшиеся незапрограммированными биты все еще остаются заряженными (лог. 1). Любой незапрограммированный бит может быть впоследствии запрограммирован. Таким образом, уже запрограммированный байт можно программировать без его предварительной очистки, в результате получится значение побитной операции AND между старым значением и новым (пока все биты в байте не обнулятся).

Режимы программирования EEPROM. Изначально в микроконтроллерах AVR не было выбираемых режимов программирования EEPROM, при этом операция записи в действительности является комбинацией очистки и программирования. Записываемый в память байт всегда очищается перед программированием нового значения, даже если не требуется стирать какие-либо биты. Например, когда программируется значение 0x00 в ячейку EEPROM, в которой уже содержится значение 0x0F, не нужно выполнять предварительную очистку байта. Достаточно было бы обнулить оставшиеся биты, потому что не требуется переводить значение битов из 0 в 1. Для этого случая операция очистки не даст никакого эффекта, она просто впустую потратит время и потребляемую энергию. Комбинированные операции очистки и программирования удваивают время выполнения по сравнению с простой операцией программирования.

Когда были разработаны AVR, в которых есть выбор режима программирования, например ATmega48/88/168, стало возможным разделить доступ на запись в EEPROM на отдельные операции очистки и программирования. Это уменьшит время обновления EEPROM на 50% в случаях, когда не требуется очистка.

Примечание: в этом документе слово «программирование» используется для описания обновления без очистки, и слово «обновление» используется для любой операции, которая изменит содержимое EEPROM.


Но вообще там где нужна высокая надежность:
1. Нужно использовать внешнюю память, например FeRAM или хотя бы еепром 25LCхх + постраничную запись (обещают 10 миллионов циклов)
2. Располагать данные во внутренней EEPROM используя только по одному полезному байту из каждой страницы (а в остальных можно хранить контрольную сумму или код для коррекции ошибок), не забывая при этом пользоваться макросами eeprom_update_xxx().
3. Список будет пополняться по мере поступления ценных идей в комментариях

П.С. На разных форумах ембеддеров в разное время проводили эксперименты и реально одна ячейка EEPROM у мег умирала только после 230тыс. перезаписей, а в некоторых экспериментах получалось более миллиона перезаписей! (Но стоит заметить что перезаписывался только один байт, если перезаписывать последовательно четыре байта (для хранения числа типа int), скорее всего никакого миллиона мы не получим).
  • +3
  • 22 января 2016, 21:52
  • UR5SIX

Комментарии (3)

RSS свернуть / развернуть
Если нужно хранить немного данных, то можно равномерно размазать по всем страницам. Например, если нужно хранить количество каки-либо событий, то можно всегда писать в случайную странцу, а при считывании просто выбирать максимальное значение.
0
  • avatar
  • m0xf
  • 22 января 2016, 22:41
Да, похоже я был немного неправ.
DIHALT, наверно стоит забрать статью в личный блог или пусть тут полежит?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.