poniedziałek, 24 grudnia 2018

LPC1769 - Obsługa pamięci EEPROM 24AA02E48

W tym poście chciałbym opisać sposób obsługi pamięci EEPROM 24AA02E48 sterowej przez interfejs I2C.

Znalezione obrazy dla zapytania lpc1769
[Źródło: https://www.nxp.com]


EEPROM:


Pamięć 24AA02E48 jest to 2 Kbit pamięć PROM. Pamięć zorganizowana jest w dwóch blokach 128 x 8 bit. Komunikacja odbywa się przez interfejs I2C. Zaletą tej pamięci jest przechowywanie danych z adresem MAC w osobnym miejscu w pamięci. Przez co uzyskuje się możliwość posiadania unikalnego adresu MAC urządzenia.

Wyprowadzenia układu są następujące:

A0, A1, A2 - piny nieużywane, można je zostawić wiszące lub podłączyć do masy bądź zasilania. Ja wspomniane piny podłączyłem do masy,
VSS - do masy,
SDA - linia danych I2C, podłączana pod mikrokontroler,
SCL - linia zegara I2C, podłączana pod mikrokontroler,
NC - jeden pin w obudowie 8 nóżkowej zostaje nie podłączony do niczego.
VCC - do zasilania, układ akceptuje napięcie od 1.7V do 5.5V

Program:


Inicjalizacja interfejsu I2C w celu rozpoczęcia komunikacji z pamięcią. Podłączenie kości pod mikrokontroler LPC1769 jest następujące:

  1. /*
  2.  * P0[0] - EEPROM SDA
  3.  * P0[1] - EEPROM SCL
  4.  * */
  5. #define EEPROM_I2C_PORT_SDA     0
  6. #define EEPROM_I2C_PIN_SDA      0
  7. #define EEPROM_PORT_I2C_SCL     0
  8. #define EEPROM_PIN_I2C_SCL      1
  9. #define EEPROM_I2C              LPC_I2C1
  10. #define EEPROM_FUNC             PINSEL_FUNC_3

Pozostałe stałe definicje:

  1. #define I2CDEV              LPC_I2C1   
  2. #define EEPROM_I2C_ADDR1    (0x57)
  3. #define EEPROM_TOTAL_SIZE  256
  4. #define EEPROM_BLOCK_SIZE  256
  5. #define EEPROM_PAGE_SIZE   8
  6. #define READ_MAC_ADDR_OFFSET 0xfa

Inicjalizacja interfejsu I2C wygląda następująco:

  1. void EEPROM_Initial(void) {
  2.     PINSEL_CFG_Type PinCfg;
  3.     PinCfg.Funcnum = EEPROM_FUNC;
  4.     PinCfg.Pinnum = EEPROM_I2C_PIN_SDA;
  5.     PinCfg.Portnum = EEPROM_I2C_PORT_SDA;
  6.     PINSEL_ConfigPin(&PinCfg);
  7.     PinCfg.Pinnum = EEPROM_PIN_I2C_SCL;
  8.     PINSEL_ConfigPin(&PinCfg);
  9.     I2C_Init(EEPROM_I2C, 100000);
  10.     I2C_Cmd(EEPROM_I2C, ENABLE);
  11. }

Funkcję opisaną powyżej należy wywołać na samym początku, przed rozpoczęciem wykonywania jakichkolwiek operacji na pamięci.

Odczyt danych po I2C:

  1. static int I2CRead(uint8_t addr, uint8_t* buf, uint32_t len) {
  2.     I2C_M_SETUP_Type i2cReadSetup;
  3.     i2cReadSetup.sl_addr7bit = addr;
  4.     i2cReadSetup.tx_data = NULL;
  5.     i2cReadSetup.tx_length = 0;
  6.     i2cReadSetup.rx_data = buf;
  7.     i2cReadSetup.rx_length = len;
  8.     i2cReadSetup.retransmissions_max = 3;
  9.     if(I2C_MasterTransferData(I2CDEV, &i2cReadSetup, I2C_TRANSFER_POLLING)== SUCCESS) {
  10.         return (0x00);
  11.     }
  12.     else {
  13.         return (-1);
  14.     }
  15. }

Zapis danych po I2C:

  1. static int I2CWrite(uint8_t addr, uint8_t* buf, uint32_t len) {
  2.     I2C_M_SETUP_Type i2cWriteSetup;
  3.     i2cWriteSetup.sl_addr7bit = addr;
  4.     i2cWriteSetup.tx_data = buf;
  5.     i2cWriteSetup.tx_length = len;
  6.     i2cWriteSetup.rx_data = NULL;
  7.     i2cWriteSetup.rx_length = 0;
  8.     i2cWriteSetup.retransmissions_max = 3;
  9.     if(I2C_MasterTransferData(I2CDEV, &txsetup, I2C_TRANSFER_POLLING)== SUCCESS){
  10.         return (0x00);
  11.     }
  12.     else{
  13.         return (-1);
  14.     }
  15. }

Odczytanie danych z pamięci EEPROM i ich zapisanie do zdefiniowanego bufora przekazywanego jako wskaźnik:

  1. int16_t EEPROM_READFROM_MEM(unsigned char* readBufPtr, unsigned short readStartPosition, unsigned short readDataLength) {
  2.     uint8_t addr = 0;
  3.     uint16_t readDataOffset = readStartPosition;
  4.     if (readDataLength > EEPROM_TOTAL_SIZE || offset + readDataLength > EEPROM_TOTAL_SIZE) {
  5.         return -1;
  6.     }
  7.     addr = EEPROM_I2C_ADDR1 + (readStartPosition/ EEPROM_BLOCK_SIZE);
  8.     readDataOffset = readStartPosition % EEPROM_BLOCK_SIZE;
  9.     I2CWrite((addr), (uint8_t*)&readDataOffset , 1);
  10.     EEPROM_DELAY();
  11.     I2CRead((addr), readBufPtr, readDataLength);
  12.     return readDataLength;  /* Ret number of readed bytes */
  13. }

Zapisanie danych do pamięci EEPROM:

  1. int16_t EEPROM_WRITE_TO_MEM(unsigned char* readBufPtr, unsigned short writeStartPosition, unsigned short writeDataLength) {
  2.     uint8_t addr = 0;
  3.     int16_t writtenDataCounter = 0;
  4.     uint16_t wLen = 0;
  5.     uint16_t off = writeStartPosition;
  6.     uint8_t tmp[9];
  7.     if (writeDataLength> EEPROM_TOTAL_SIZE || writeStartPosition + writeDataLength> EEPROM_TOTAL_SIZE) {
  8.         return -1;
  9.     }
  10.     addr = EEPROM_I2C_ADDR1 + (writeStartPosition / EEPROM_BLOCK_SIZE);
  11.     off = writeStartPosition % EEPROM_BLOCK_SIZE;
  12.     wLen = ((((off >> 3) + 1) << 3) - off);=
  13.     wLen = MIN(wLen, writeDataLength);
  14.     while (writeDataLength) {
  15.         tmp[0] = off;
  16.         memcpy(&tmp[1], (void*) &readBufPtr[writtenDataCounter ], wLen);
  17.         I2CWrite((addr), tmp, wLen + 1);
  18.         /* delay to wait for a write cycle */
  19.         EEPROM_DELAY();
  20.         writeDataLength-= wLen;
  21.         writtenDataCounter += wLen;
  22.         off += wLen;
  23.         wLen = MIN(EEPROM_PAGE_SIZE, writeDataLength);
  24.         addr += off / EEPROM_BLOCK_SIZE;
  25.         off = off % EEPROM_BLOCK_SIZE;
  26.     }
  27.     return writtenDataCounter ;
  28. }

Odczyt zapisanego adresu MAC:

  1. void Read_MacAddress(uint8_t *MACbuf) {
  2.     eeprom_read(MACbuf, READ_MAC_ADDR_OFFSET, 6);
  3. }

Reset pamięci EEPROM:

  1. uint8_t EEPROM_Reset() {
  2.     uint8_t i = 0
  3.     uint8_t len = 255;
  4.     uint8_t dataBuffer[len];
  5.  
  6.     for (= 0; i < len; i++) {
  7.         if(< 0x80)        { dataBuffer[i] = 0x00; }
  8.         else if(< 0xfa)   { dataBuffer[i] = 0xff; }
  9.     }
  10.    
  11.     for(uint8_t j = 0; j<6; j++)
  12.     {
  13.         dataBuffer[249 + i] = (+ 1);
  14.     }
  15.  
  16.     if(EEPROM_WRITE_TO_MEM((uint8_t *)&dataBuffer, 0, len) != (-1))
  17.     {
  18.         return 0xFF;
  19.     }
  20.  
  21.     return (0x00);
  22. }

Odczyt pojedynczego bajtu z wybranego adresu:

  1. uint8_t readByte(uint8_t readAddress) {
  2.     uint8_t readedData = 0;
  3.     EEPROM_READFROM_MEM(&odczyt, readAddress, 1);
  4.  
  5.     return readedData;
  6. }

Teraz krótka funkcje pozwalające na ustawienie bądź czyszczenie wybranego bitu:

  1. void ClearBit(uint8_t dataAddress, uint8_t bitToClear) {
  2.     uint8_t dataBuffer = 0;
  3.     eeprom_read(&dataBuffer, dataAddress, 1);
  4.     dataBuffer&= ~bitToClear;
  5.     eeprom_write(&dataBuffer, dataAddress, 1);
  6. }
  7.  
  8. void SetBit(uint8_t dataAddress, uint8_t bitToSet) {
  9.     uint8_t dataBuffer = 0;
  10.     eeprom_read(&dataBuffer, dataAddress, 1);
  11.     dataBuffer |= bitToSet;
  12.     eeprom_write(&dataBuffer, dataAddress, 1);
  13. }

Do funkcji podajemy bit, który ma być wyczyszczony lub ustawiony. Dane następnie są odczytywane z pamięci, dokonywana jest ich zmiana po czym ponownie wpisujemy je do pamięci.

Proste opóźnienie pomiędzy poszczególnymi operacjami:

  1. static void EEPROM_DELAY(void) {
  2.     volatile int i = 0;
  3.     for (= 0; i < 0x04000; i++) { ; }
  4. }