Носіть вирівнювання на EEPROM мікроконтролера


15

Наприклад: Лист даних ATtiny2313 (як і більшість таблиць даних Atmel AVR) визначає:

128-байтний програмований EEPROM витривалість у системі: 100 000 циклів запису / видалення

Уявіть, що для зберігання деякої конфігурації програмі потрібні лише два байти, інші 126 байтів фактично витрачені на максимум. Мене хвилює те, що регулярні оновлення двох байтів конфігурації можуть зношувати EEPROM пристрою та робити його марним. Весь пристрій став би ненадійним, оскільки в певний момент ви просто не можете відслідковувати, які байти в EEPROM є ненадійними.

Чи є розумний спосіб зробити вирівнювання зносу на EEPROM мікроконтролера, коли ви ефективно використовуєте лише один або два байти із доступних 128?


1
Якщо цикли запису на 100 тис. Були обмеженням, чи має сенс замість цього використовувати якусь іншу технологію? Чи механізм, який включає внутрішнє вирівнювання, або щось із порядком чи більшою витривалістю?
Аніндо Гош

1
@AnindoGhosh Я просто не хочу витрачати мій невеликий запас мікроконтролерів лише через те, що зношується EEPROM через тестування доказів концепції. Я не хочу турбуватися про те, який байт я використовував у попередньому проекті при повторному використанні контролера. І мені просто добре знати, що я оптимально використовую наявне обладнання.
jippie


1
Можливо, подивіться на мою відповідь на stackoverflow .
JimmyB

Подивіться на серії TAM MSP430 FRAM ... 10 ^ 13 пише !!!
геометрикал

Відповіді:


19

Метод, який я зазвичай використовую, - це префіксувати дані за допомогою 4-байтового послідовного прокату, де найбільше число представляє останнє / поточне значення. У випадку зберігання 2-х байт фактичних даних, які давали б 6 байтів, а потім я формую кругову чергу черг, тому для 128 байт EEPROM вона міститиме 21 запис та збільшує витривалість у 21 раз.

Тоді під час завантаження найбільший номер послідовності може бути використаний для визначення як наступного порядкового номера, який буде використовуватися, так і поточного хвоста черги. Наступний псевдокод C демонструє, це передбачає, що при початковому програмуванні область EEPROM була стерта до значень 0xFF, тому я ігнорую порядковий номер 0xFFFF:

struct
{
  uint32_t sequence_no;
  uint16_t my_data;
} QUEUE_ENTRY;

#define EEPROM_SIZE 128
#define QUEUE_ENTRIES (EEPROM_SIZE / sizeof(QUEUE_ENTRY))

uint32_t last_sequence_no;
uint8_t queue_tail;
uint16_t current_value;

// Called at startup
void load_queue()
{
  int i;

  last_sequence_no = 0;
  queue_tail = 0;
  current_value = 0;
  for (i=0; i < QUEUE_ENTRIES; i++)
  {
    // Following assumes you've written a function where the parameters
    // are address, pointer to data, bytes to read
    read_EEPROM(i * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
    if ((QUEUE_ENTRY.sequence_no > last_sequence_no) && (QUEUE_ENTRY.sequence_no != 0xFFFF))
    {
      queue_tail = i;
      last_sequence_no = QUEUE_ENTRY.sequence_no;
      current_value = QUEUE_ENTRY.my_data;
    }
  }
}

void write_value(uint16_t v)
{
  queue_tail++;
  if (queue_tail >= QUEUE_ENTRIES)
    queue_tail = 0;
  last_sequence_no++;
  QUEUE_ENTRY.sequence_no = last_sequence_no;
  QUEUE_ENTRY.my_data = v;
  // Following assumes you've written a function where the parameters
  // are address, pointer to data, bytes to write
  write_EEPROM(queue_tail * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
  current_value = v;
}

Для меншого EEPROM 3-байтова послідовність була б більш ефективною, хоча вимагала б трохи розрізання бітів замість використання стандартних типів даних.


+1, приємний підхід. Чи можна трохи оптимізувати сховище, використовуючи менше байтів "тегів" і, можливо, залежно від якоїсь форми механізму хеш-коду, щоб забезпечити додатковий розподіл? Гібрид між рівнем вирівнювання та вашим підходом?
Анніндо Гош

@AnindoGhosh, так, я вважаю, що це могло. Я зазвичай використовую цей підхід на невеликих мікросередовищах для простоти коду, а особисто я в основному використовую його на великих пристроях, таких як DataFLASH. Ще одна проста ідея, яка спадає на думку, полягає в тому, що номери послідовностей можна періодично знижувати, щоб зменшити їх до менших значень.
PeterJ

Примітка програми Atmel, згадана @ m.Alin, має розумне спрощення: після RESET можна переглянути за допомогою [...] буфера, знайшовши останній [...] буферний елемент, змінений шляхом пошуку місця, де різниця між буферним елементом та наступним буферним елементом більша за 1 .
джиппі

Чи не слід write_value () ставити запис у queue_tail * sizeof (QUEUE_ENTRY)? я виправлюсь з першого разу, але чи не слід продовжувати просуватися, якщо є кілька записів? i не збільшується за межами load_queue ().
Маршалл Юбанкс

2
@ DWORD32: Так, це технічно правильно, але практично не має значення. До моменту, коли це станеться, граничний знос EEPROM буде перевищено в 2000 році!
Трейд Дейв

5

Далі йде метод, який використовує відра і приблизно один накладний байт на відро. Байти відра і накладні байти отримують приблизно однакову кількість зносу. У наведеному прикладі, з урахуванням 128 байтів EEPROM, цей метод виділяє 42 2-байтові відра та 44 статусні байти, збільшуючи здатність до зносу приблизно в 42 рази.

Спосіб:

Розділіть адресний простір EEPROM на k відра, де k = ⌊ E / ( n +1) ⌋, з n = розмір масиву даних-параметрів = розмір відра, і E = розмір EEPROM (або, загалом, кількість EEPROM комірки, які будуть присвячені цій структурі даних).

Ініціалізуйте каталог, масив m байтів, усі встановлені на k , m = En · k . Коли ваш пристрій запускається, він читає каталог, поки не знайде поточний запис, який є байтом, не рівним k . [Якщо всі записи каталогів рівні k , ініціалізуйте перший запис у каталозі 0 та продовжуйте звідти.]

Коли поточний запис каталогу містить j , відро j містить поточні дані. Коли вам потрібно написати новий запис даних про налаштування, ви зберігаєте j +1 у поточному записі каталогу; якщо це дорівнює k , ініціалізуйте наступний запис каталогу в 0 і продовжуйте звідти.

Зауважте, що байти каталогів отримують приблизно стільки ж зносу, скільки й байти відра, оскільки 2 · k > mk .

(Я адаптував вище сказане з моєї відповіді на питання Arduino SE 34189 , Як збільшити термін служби EEPROM? )


2

Для цього я скористався порядковим номером (подібний до відповіді Петра). Послідовний номер насправді може становити всього 1 біт, якщо кількість елементів у киї є непарною. Тоді голова та хвіст позначаються двома послідовними 1 або 0

Наприклад, якщо ви хочете прокрутити через 5 елементів, порядкові номери будуть:

{01010} (записати на 0) {11010} (записати на 1) {10010} (написати до 2) {10110} (написати до 3) {10100} (написати до 4) {10101} (написати до 5)


1

Існує пара варіантів залежно від типу EEPROM, який ви маєте, та розміру ваших даних.

  1. Якщо у вашому EEPROM є сторінки, що стираються окремо, і ви використовуєте 1 сторінку (або більше), просто не стирайте всі сторінки, окрім тих, що використовуються, і повторно використовуйте сторінки циркулярно.

  2. Якщо ви використовуєте лише частину сторінки, яку потрібно стерти відразу, розділіть її на записи даних. Використовуйте чистий запис щоразу, коли ви пишете, і стирайте, коли у вас закінчуються чисті записи.

Використовуйте "брудний" біт, щоб вказати між чистими та брудними записами, якщо це необхідно (зазвичай, у вас є принаймні один байт, який гарантовано відрізняється від 0xFF, який можна використовувати для відстеження брудних записів).

Якщо ваша бібліотека EEPROM не відкриває функцію стирання (наприклад, Arduino), ось чіткий трюк для алгоритму №2: оскільки ваш перший запис EEPROM завжди використовується, ви можете визначити значення "брудного" біта, прочитавши його. Потім, коли у вас закінчуються чисті записи, ви просто починаєте знову з першого запису, перевертаючи "брудний" біт, а решта ваших записів автоматично позначаються як "чисті".

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.