wtorek, 18 kwietnia 2017

[4] ATxmega - Zapis danych do pamięci EEPROM

W tym poście chciałbym opisać sposób zapisu oraz odczytu danych z pamięci EEPROM mikrokontrolera ATXmega.

Krótki dokument od Atmela można znaleźć pod tym linkiem.

Dostęp do EEPROMu jest zapewniony przez kontroler NVM. Jest on także wykorzystywany aby uzyskać dostęp do fuse oraz lock bitów. Podczas korzystania z kontrolera należy się upewnić, że nie wykonuje on innych operacji. Wykonuje się to poprzez sprawdzenie bitu NVMBUSY w rejestrze STATUS.

Cały proces dostępu do pamięci wygląda następująco:

  • Czekanie na gotowość NVM;
  • Obliczenie adresu;
  • Ładowanie danych do rejestru z adresem;
  • Ładowanie komendy do rejestru CMD;
  • Załadowanie wartości do rejestru CCP, co wyłącza przerwania na następne 4 cykle;
  • Zakończenie operacji po wyczyszczeniu bitu NVMBUSY.

Funkcja AtomicWrite pozwala na zapis oraz odczyt podczas jednej operacji. Przy czyszczeniu dane w rejestrze są ustawiane na 1, programuje się poprzez zmianę z jedynki na 0. Dane które zostały wybrane do zmiany zostaną najpierw wyczyszczone, po czym zapisana do nich zostanie ustawiona wartość.

Należy pamiętać, że zapis oraz odczyt danych operuje na całych stronach danych. Dane są przed wpisaniem przekazywane do buforu tymczasowego.

Więcej informacji można znaleźć w dokumencie przedstawionym powyżej oraz w:
AVR101: High Endurance EEPROM storage - odnoście bardzo częstego korzystania z EEPROMU, jak zwiększyć jego żywotność.

AVR1304: Using the Xmega dma controller - wykorzystanie DMA ogólnie, oraz dla pamięci EEPROM.

Przykłady programów w następujących plikach: eeprom_driver.c; eeprom_driver.h; eeprom_example.c

Na początku należy pamiętać o włączeniu mapowania EEPROMU:

  1. //EEpromu map do Data Memory, Data address 0x1000 to 0x1FFF
  2. //is reserved for memory mapping of EEPROM
  3. NVM_CTRLB = NVM_EEMAPEN_bm;

Dane dotyczące wielkości pamięci, rozmiaru strony są zdefiniowane jako deklaracje preprocesora:

  1. #define EEPROM_START     (0x0000)
  2. #define EEPROM_SIZE      (2048)
  3. #define EEPROM_PAGE_SIZE (32)
  4. #define EEPROM_END       (EEPROM_START + EEPROM_SIZE - 1)

Zapis strony z danymi można wykonać poprzez następującą  funkcje:

  1. void EEPROM_AtomicWritePage( uint8_t pageAddr )
  2. {
  3.  EEPROM_WaitForNVM();                                       /* Wait until NVM is not busy */
  4.  uint16_t address = (uint16_t)(pageAddr*EEPROM_PAGE_SIZE);  /* Calculate page address */
  5.  /* Set address */
  6.  NVM.ADDR0 = address & 0xFF;
  7.  NVM.ADDR1 = (address >> 8) & 0x1F;
  8.  NVM.ADDR2 = 0x00;
  9.  /* Issue EEPROM Atomic Write (Erase&Write) command */
  10.  NVM.CMD = NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc;
  11.  NVM_EXEC();
  12. }

Odczekanie na gotowość realizuje się poprzez tą funkcje:

  1. void EEPROM_WaitForNVM( void )
  2. {
  3.  do {
  4.   /* Block execution while waiting for the NVM to be ready */
  5.  } while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm);
  6. }

Sprawdza ona czy odpowiednie bity w rejestrach zostały wyczyszczone.

Aby zapisać pojedynczy bajt danych należy wykonać następujące operacje:

  1. EEPROM(AddrToWrite) = 0x01;
  2. EEPROM_AtomicWritePage(AddrToWrite/PAGE_SIZE_EEPROM);    //Write one bit of data
  3. EEPROM_WaitForNVM();                                        //Waits for finishing all operations

W tym przypadku dane potrzebne do wpisania są dzielone przez wielkość danych pojedynczej strony w pamięci EEPROM. Zapis całego bufora wykonuje się w następujący sposób:

  1. for (i=0; i<MAX; i++)
  2. {  
  3.     EEPROM(AddrToWrite + i) = Buffer[i];
  4. }
  5. EEPROM_AtomicWritePage(AddrToWrite);
  6. EEPROM_WaitForNVM();

Odczyt danych z danego adresu wykonuje się następującą komendą:

  1. #define EEPROM(Addr) ((uint8_t *) MAPPED_EEPROM_START)[ Addr ]

Wartość tą można przypisać do zmiennej, która będzie przechowywać dane znajdujące się pod podanym adresem.