마이크로 컨트롤러의 EEPROM에서웨어 레벨링


15

예 : ATtiny2313에 대한 데이터 시트 (대부분의 Atmel AVR 데이터 시트와 동일)는 다음과 같습니다.

128 바이트 시스템 내 프로그램 가능 EEPROM 내구성 : 100,000 쓰기 / 삭제주기

프로그램이 일부 구성을 저장하는 데 2 ​​바이트 만 필요하고 다른 126 바이트가 효과적으로 낭비된다고 상상해보십시오. 두 가지 구성 바이트를 정기적으로 업데이트하면 장치의 EEPROM이 마모되어 쓸모 없게 될 수 있습니다. 특정 순간에 EEPROM의 어떤 바이트를 신뢰할 수 없는지 추적 할 수 없기 때문에 전체 장치를 신뢰할 수 없게됩니다.

사용 가능한 128에서 1 또는 2 바이트 만 효과적으로 사용할 때 마이크로 컨트롤러의 EEPROM에서웨어 레벨링을 수행하는 현명한 방법이 있습니까?


1
100k 쓰기주기가 제약 조건 인 경우 다른 기술을 대신 사용하는 것이 합리적입니까? 내부적으로 레벨링을 포함하는 메커니즘이거나, 또는 그보다 더 큰 내구성을 가진 메커니즘입니까?
Anindo Ghosh

1
@AnindoGhosh 저는 개념 증명 테스트로 인해 EEPROM이 마모되어 작은 마이크로 컨트롤러를 낭비하고 싶지 않습니다. 컨트롤러를 재사용 할 때 이전 프로젝트에서 사용한 바이트에 대해 걱정하고 싶지 않습니다. 사용 가능한 하드웨어를 최적으로 사용한다는 사실을 알게되어 기쁩니다.
jippie


1
아마도 stackoverflow 에서 내 대답을 살펴보십시오 .
JimmyB

TI의 MSP430 FRAM 시리즈를 살펴보십시오.
geometrikal

답변:


19

내가 일반적으로 사용하는 기술은 데이터 앞에 4 바이트 롤링 시퀀스 번호를 붙여서 가장 큰 숫자가 가장 최근 / 현재 값을 나타내는 것입니다. 총 6 바이트를 제공하는 2 바이트의 실제 데이터를 저장 한 다음 순환 큐 배열을 형성하여 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, 좋은 접근. 더 적은 "태그"바이트를 사용하고 추가 배포를 제공하는 해시 버킷 메커니즘의 형태에 따라 스토리지를 약간 최적화 할 수 있습니까? 평준화와 접근 방식이 혼용되어 있습니까?
Anindo Ghosh

@ AnindoGhosh, 네, 가능하다고 생각합니다. 필자는 일반적으로 코드 단순성을 위해 작은 마이크로에서이 방법을 사용했으며 주로 DataFLASH와 같은 더 큰 장치에서 주로 사용했습니다. 명심해야 할 또 다른 간단한 아이디어는 시퀀스 번호를 주기적으로 낮추어 더 작은 값으로 유지할 수 있다는 것입니다.
PeterJ

@ m.Alin이 언급 한 Atmel 애플리케이션 노트는 현명하게 단순화되었습니다. RESET 후 [...] 버퍼를 살펴보고 마지막 [...] 버퍼 요소를 찾은 위치 는 버퍼 요소와 다음 버퍼 요소의 차이가 1보다 큽니다 .
jippie

write_value ()가 queue_tail * sizeof (QUEUE_ENTRY)에 항목을 넣지 않아야합니까? 나는 처음에는 맞을 것이지만 여러 번 쓰는 것이 계속 진행되어서는 안됩니까? load_queue () 외부에서 증가하지 않습니다.
마샬 유 뱅크스

2
@ DWORD32 : 예, 기술적으로 정확하지만 실제로는 관련이 없습니다. 그럴 때까지 EEPROM의 마모 한계는 2000 배까지 초과되었습니다!
Dave Tweed

5

다음은 버킷과 버킷 당 약 1 개의 오버 헤드 바이트를 사용하는 방법입니다. 버킷 바이트와 오버 헤드 바이트는 거의 같은 마모량을 갖습니다. 현재의 예에서, 128 개의 EEPROM 바이트가 주어지면이 방법은 42 개의 2 바이트 버킷과 44 개의 상태 바이트를 할당하여 약 42 배의 마모 성능을 제공합니다.

방법:

EEPROM 주소 공간을 k 버킷 으로 나누십시오. 여기서 k = ⌊ E / ( N + 1) ⌋와 N = 설정 데이터 어레이 크기 = 버킷 크기, E = EEPROM 크기 (또는,보다 일반적으로는, EEPROM의 개수 이 데이터 구조에 전념 할 셀).

m 바이트 배열을 모두 다음으로 설정하여 디렉토리를 초기화하십시오. K 로, m = 욕실 · K . 장치가 시작되면 현재 항목을 찾을 때까지 디렉토리를 읽습니다. 이는 k와 다른 바이트 입니다. [모든 디렉토리 항목이 k 인 경우 첫 번째 디렉토리 항목을 0으로 초기화 한 다음 계속하십시오.]

현재 디렉토리 항목에 j 가 포함 된 경우 경우 버킷 j 에 현재 데이터가 포함됩니다. 새로운 설정 데이터 항목을 작성해야 할 경우 j +1을 현재 디렉토리 항목에 저장합니다. 그것이 k 와 같으면 다음 디렉토리 항목을 0으로 초기화하고 거기서 시작하십시오.

디렉토리 바이트는 버킷 바이트와 거의 같은 마모량을 가지므로 2 · k > mk .

( Arduino SE 질문 34189 , EEPROM의 수명을 늘리는 방법에 대한 답변 에서 위의 내용을 수정했습니다 . .)


2

나는 이것을 위해 롤링 시퀀스 번호를 사용했다 (Peter의 대답과 유사). 큐의 요소 수가 홀수 인 경우 시퀀스 번호는 실제로 1 비트만큼 작을 수 있습니다. 그런 다음 머리와 꼬리는 2 연속 1 또는 0으로 표시됩니다.

예를 들어 5 개의 요소를 롤오버하려면 시퀀스 번호는 다음과 같습니다.

{01010} (0에 쓰기) {11010} (1에 쓰기) {10010} (2에 쓰기) {10110} (3에 쓰기) {10100} (4에 쓰기) {10101} (5에 쓰기)


1

보유하고있는 EEPROM의 종류와 데이터 크기에 따라 몇 가지 옵션이 있습니다.

  1. EEPROM에 개별적으로 지울 수있는 페이지가 있고 1 페이지 이상을 사용하는 경우 사용중인 페이지를 제외한 모든 페이지를 지우고 순환 방식으로 페이지를 재사용하십시오.

  2. 한 번에 지워야하는 일부 페이지 만 사용하는 경우 해당 페이지를 데이터 항목으로 분할하십시오. 쓸 때마다 깨끗한 항목을 사용하고 깨끗한 항목이 없으면 삭제하십시오.

"더티 (dirty)"비트를 사용하여 필요한 경우 클린 항목과 더티 항목 사이에 알립니다 (보통, 더티 항목을 추적하는 데 사용할 수있는 0xFF와 다른 1 바이트 이상이 보장됨).

EEPROM 라이브러리가 Arduino와 같은 지우기 기능을 표시하지 않는 경우 알고리즘 # 2에 대한 유용한 트릭 이 있습니다. 첫 번째 EEPROM 항목이 항상 사용되므로 "더티"비트의 값을 읽어서 결정할 수 있습니다. 그런 다음 정리 된 항목이 부족하면 첫 번째 항목부터 다시 시작하여 "더러운"비트를 반전시키고 나머지 항목은 자동으로 "깨끗한"으로 표시됩니다.

불량 페이지를 추적하거나 EEPROM 데이터의 다른 부분을 독립적으로 업데이트하지 않으려는 경우 시퀀스 번호와 카탈로그는 공간을 낭비합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.