/*********************************************************** Author: Bernard Borredon Version: 1.3 - Correct write(uint32_t address, int8_t data[], uint32_t length) for eeprom >= T24C32. Tested with 24C02, 24C08, 24C16, 24C64, 24C256, 24C512, 24C1025 on LPC1768 (mbed online and µVision V5.16a). - Correct main test. Date : 12 decembre 2013 Version: 1.2 - Update api documentation Date: 11 december 2013 Version: 1.1 - Change address parameter size form uint16_t to uint32_t (error for eeprom > 24C256). - Change size parameter size from uint16_t to uint32_t (error for eeprom > 24C256). - Correct a bug in function write(uint32_t address, int8_t data[], uint32_t length) : last step must be done only if it remain datas to send. - Add function getName. - Add function clear. - Initialize _name array. Date: 27 december 2011 Version: 1.0 ************************************************************/ #include "eeprom.h" #define BIT_SET(x, n) (x=x | (0x01< 7) { _errnum = EEPROM_BadAddress; } _address = _address << 1; _page_write = 8; _page_number = 1; break; case T24C04 : if (address > 7) { _errnum = EEPROM_BadAddress; } _address = (_address & 0xFE) << 1; _page_write = 16; _page_number = 2; break; case T24C08 : if (address > 7) { _errnum = EEPROM_BadAddress; } _address = (_address & 0xFC) << 1; _page_write = 16; _page_number = 4; break; case T24C16 : _address = 0; _page_write = 16; _page_number = 8; break; case T24C32 : case T24C64 : if (address > 7) { _errnum = EEPROM_BadAddress; } _address = _address << 1; _page_write = 32; _page_number = 1; break; case T24C128 : case T24C256 : if (address > 3) { _errnum = EEPROM_BadAddress; } _address = _address << 1; _page_write = 64; _page_number = 1; break; case T24C512 : if (address > 3) { _errnum = EEPROM_BadAddress; } _address = _address << 1; _page_write = 128; _page_number = 1; break; case T24C1024 : if (address > 7) { _errnum = EEPROM_BadAddress; } _address = (_address & 0xFE) << 1; _page_write = 128; _page_number = 2; break; case T24C1025 : if (address > 3) { _errnum = EEPROM_BadAddress; } _address = _address << 1; _page_write = 128; _page_number = 2; break; } // Size in bytes _size = _type; if (_type == T24C1025) _size = T24C1024; // Set I2C frequency _i2c.frequency(400000); } /** * void write(uint32_t address, int8_t data) * * Write byte * @param address start address (uint32_t) * @param data byte to write (int8_t) * @return none */ void EEPROM::write(uint32_t address, int8_t data) { uint8_t page; uint8_t addr; uint8_t cmd[3]; int len; int ack; // Check error if (_errnum) return; // Check address if (!checkAddress(address)) { _errnum = EEPROM_OutOfRange; return; } // Compute page number page = 0; if (_type < T24C32) page = (uint8_t) (address / 256); // Device address addr = EEPROM_Address | _address | (page << 1); if (_type < T24C32) { len = 2; // Word address cmd[0] = (uint8_t) (address - page * 256); // Data cmd[1] = (uint8_t) data; } else { len = 3; // First word address (MSB) cmd[0] = (uint8_t) (address >> 8); // Second word address (LSB) cmd[1] = (uint8_t) address; // Data cmd[2] = (uint8_t) data; } ack = _i2c.write((int) addr, (char *) cmd, len); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); } /** * void write(uint32_t address, int8_t data[], uint32_t length) * * Write array of bytes (use the page mode) * @param address start address (uint32_t) * @param data bytes array to write (int8_t[]) * @param size number of bytes to write (uint32_t) * @return none */ void EEPROM::write(uint32_t address, const int8_t data[], uint32_t length) { uint8_t page; uint8_t addr = 0; uint8_t blocs, remain; uint8_t fpart, lpart; uint8_t i, j, ind; uint8_t cmd[129]; int ack; // Check error if (_errnum) return; // Check address if (!checkAddress(address)) { _errnum = EEPROM_OutOfRange; return; } // Check length if (!checkAddress(address + length - 1)) { _errnum = EEPROM_OutOfRange; return; } // Compute blocs numbers blocs = length / _page_write; // Compute remaining bytes remain = length - blocs * _page_write; for (i = 0; i < blocs; i++) { // Compute page number page = 0; if (_type < T24C32) page = (uint8_t) (address / 256); // Device address addr = EEPROM_Address | _address | (page << 1); if (_type < T24C32) { // Word address cmd[0] = (uint8_t) (address - page * 256); if ((uint8_t) ((address + _page_write) / 256) == page) { // Data fit in the same page // Add data for (j = 0; j < _page_write; j++) cmd[j + 1] = (uint8_t) data[i * _page_write + j]; // Write data ack = _i2c.write((int) addr, (char *) cmd, _page_write + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); // Increment address address += _page_write; } else { // Data on 2 pages. We must split the write // Number of bytes in current page fpart = (page + 1) * 256 - address; // Add data for current page for (j = 0; j < fpart; j++) cmd[j + 1] = (uint8_t) data[i * _page_write + j]; // Write data for current page ack = _i2c.write((int) addr, (char *) cmd, fpart + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); // Increment address address += fpart; if (page < _page_number - 1) { // Increment page page++; // Device address addr = EEPROM_Address | _address | (page << 1); // Word address cmd[0] = (uint8_t) (address - page * 256); // Data index ind = i * _page_write + fpart; // Number of bytes in next page lpart = _page_write - fpart; // Add data for next page for (j = 0; j < lpart; j++) cmd[j + 1] = (uint8_t) data[ind + j]; // Write data for next page ack = _i2c.write((int) addr, (char *) cmd, lpart + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); // Increment address address += lpart; } } } else { // First word address (MSB) cmd[0] = (uint8_t) (address >> 8); // Second word address (LSB) cmd[1] = (uint8_t) address; // Add data for (j = 0; j < _page_write; j++) cmd[j + 2] = (uint8_t) data[i * _page_write + j]; // Write data ack = _i2c.write((int) addr, (char *) cmd, _page_write + 2); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); // Increment address address += _page_write; } } if (remain) { // Compute page number page = 0; if (_type < T24C32) page = (uint8_t) (address / 256); // Device address addr = EEPROM_Address | _address | (page << 1); if (_type < T24C32) { // Word address cmd[0] = (uint8_t) (address - page * 256); if ((uint8_t) ((address + remain) / 256) == page) { // Data fit in the same page // Add data for the current page for (j = 0; j < remain; j++) cmd[j + 1] = (uint8_t) data[blocs * _page_write + j]; // Write data for the current page ack = _i2c.write((int) addr, (char *) cmd, remain + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); } else { // Data on 2 pages. We must split the write // Number of bytes in current page fpart = (page + 1) * 256 - address; // Add data for current page for (j = 0; j < fpart; j++) cmd[j + 1] = (uint8_t) data[blocs * _page_write + j]; // Write data for current page ack = _i2c.write((int) addr, (char *) cmd, fpart + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); // Increment address address += fpart; if (page < _page_number - 1) { // Increment page page++; // Device address addr = EEPROM_Address | _address | (page << 1); // Word address cmd[0] = (uint8_t) (address - page * 256); // Data index ind = blocs * _page_write + fpart; // Number of bytes in next page lpart = remain - fpart; // Add data for next page for (j = 0; j < lpart; j++) cmd[j + 1] = (uint8_t) data[ind + j]; // Write data for next page ack = _i2c.write((int) addr, (char *) cmd, lpart + 1); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); } } } else { // Fist word address (MSB) cmd[0] = (uint8_t) (address >> 8); // Second word address (LSB) cmd[1] = (uint8_t) address; // Add data for the current page for (j = 0; j < remain; j++) cmd[j + 2] = (uint8_t) data[blocs * _page_write + j]; // Write data for the current page ack = _i2c.write((int) addr, (char *) cmd, remain + 2); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Wait end of write ready(); } } } /** * void write(uint32_t address, int16_t data) * * Write short * @param address start address (uint32_t) * @param data short to write (int16_t) * @return none */ void EEPROM::write(uint32_t address, int16_t data) { int8_t cmd[2]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 1)) { _errnum = EEPROM_OutOfRange; return; } memcpy(cmd, &data, 2); write(address, cmd, 2); } /** * void write(uint32_t address, int32_t data) * * Write long * @param address start address (uint32_t) * @param data long to write (int32_t) * @return none */ void EEPROM::write(uint32_t address, int32_t data) { int8_t cmd[4]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 3)) { _errnum = EEPROM_OutOfRange; return; } memcpy(cmd, &data, 4); write(address, cmd, 4); } /** * void write(uint32_t address, float data) * * Write float * @param address start address (uint32_t) * @param data float to write (float) * @return none */ void EEPROM::write(uint32_t address, float data) { int8_t cmd[4]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 3)) { _errnum = EEPROM_OutOfRange; return; } memcpy(cmd, &data, 4); write(address, cmd, 4); } /** * void write(uint32_t address, void *data, uint32_t size) * * Write anything (use the page write mode) * @param address start address (uint32_t) * @param data data to write (void *) * @param size number of bytes to write (uint32_t) * @return none */ void EEPROM::write(uint32_t address, void *data, uint32_t size) { int8_t *cmd = nullptr; // Check error if (_errnum) return; // Check address if (!checkAddress(address + size - 1)) { _errnum = EEPROM_OutOfRange; return; } cmd = (int8_t *) malloc(size); if (cmd == nullptr) { _errnum = EEPROM_MallocError; return; } memcpy(cmd, (uint8_t *) data, size); write(address, cmd, size); free(cmd); } /** * void read(uint32_t address, int8_t& data) * * Random read byte * @param address start address (uint32_t) * @param data byte to read (int8_t&) * @return none */ void EEPROM::read(uint32_t address, int8_t &data) { uint8_t page; uint8_t addr; uint8_t cmd[2]; uint8_t len; int ack; // Check error if (_errnum) return; // Check address if (!checkAddress(address)) { _errnum = EEPROM_OutOfRange; return; } // Compute page number page = 0; if (_type < T24C32) page = (uint8_t) (address / 256); // Device address addr = EEPROM_Address | _address | (page << 1); if (_type < T24C32) { len = 1; // Word address cmd[0] = (uint8_t) (address - page * 256); } else { len = 2; // First word address (MSB) cmd[0] = (uint8_t) (address >> 8); // Second word address (LSB) cmd[1] = (uint8_t) address; } // Write command ack = _i2c.write((int) addr, (char *) cmd, len, true); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Read data ack = _i2c.read((int) addr, (char *) &data, sizeof(data)); if (ack != 0) { _errnum = EEPROM_I2cError; return; } } /** * void read(uint32_t address, int8_t *data, uint32_t size) * * Sequential read byte * @param address start address (uint32_t) * @param data bytes array to read (int8_t[]&) * @param size number of bytes to read (uint32_t) * @return none */ void EEPROM::read(uint32_t address, int8_t *data, uint32_t size) { uint8_t page; uint8_t addr; uint8_t cmd[2]; uint8_t len; int ack; // Check error if (_errnum) return; // Check address if (!checkAddress(address)) { _errnum = EEPROM_OutOfRange; return; } // Check size if (!checkAddress(address + size - 1)) { _errnum = EEPROM_OutOfRange; return; } // Compute page number page = 0; if (_type < T24C32) page = (uint8_t) (address / 256); // Device address addr = EEPROM_Address | _address | (page << 1); if (_type < T24C32) { len = 1; // Word address cmd[0] = (uint8_t) (address - page * 256); } else { len = 2; // First word address (MSB) cmd[0] = (uint8_t) (address >> 8); // Second word address (LSB) cmd[1] = (uint8_t) address; } // Write command ack = _i2c.write((int) addr, (char *) cmd, len, true); if (ack != 0) { _errnum = EEPROM_I2cError; return; } // Sequential read ack = _i2c.read((int) addr, (char *) data, static_cast(size)); if (ack != 0) { _errnum = EEPROM_I2cError; return; } } /** * void read(int8_t& data) * * Current address read byte * @param data byte to read (int8_t&) * @return none */ void EEPROM::read(int8_t &data) { uint8_t addr; int ack; // Check error if (_errnum) return; // Device address addr = EEPROM_Address | _address; // Read data ack = _i2c.read((int) addr, (char *) &data, sizeof(data)); if (ack != 0) { _errnum = EEPROM_I2cError; return; } } /** * void read(uint32_t address, int16_t& data) * * Random read short * @param address start address (uint32_t) * @param data short to read (int16_t&) * @return none */ void EEPROM::read(uint32_t address, int16_t &data) { int8_t cmd[2]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 1)) { _errnum = EEPROM_OutOfRange; return; } read(address, cmd, 2); memcpy(&data, cmd, 2); } /** * void read(uint32_t address, int32_t& data) * * Random read long * @param address start address (uint32_t) * @param data long to read (int32_t&) * @return none */ void EEPROM::read(uint32_t address, int32_t &data) { int8_t cmd[4]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 3)) { _errnum = EEPROM_OutOfRange; return; } read(address, cmd, 4); memcpy(&data, cmd, 4); } /** * void read(uint32_t address, float& data) * * Random read float * @param address start address (uint32_t) * @param data float to read (float&) * @return none */ void EEPROM::read(uint32_t address, float &data) { int8_t cmd[4]; // Check error if (_errnum) return; // Check address if (!checkAddress(address + 3)) { _errnum = EEPROM_OutOfRange; return; } read(address, cmd, 4); memcpy(&data, cmd, 4); } /** * void read(uint32_t address, void *data, uint32_t size) * * Random read anything * @param address start address (uint32_t) * @param data data to read (void *) * @param size number of bytes to read (uint32_t) * @return none */ void EEPROM::read(uint32_t address, void *data, uint32_t size) { int8_t *cmd = nullptr; // Check error if (_errnum) return; // Check address if (!checkAddress(address + size - 1)) { _errnum = EEPROM_OutOfRange; return; } cmd = (int8_t *) malloc(size); if (cmd == nullptr) { _errnum = EEPROM_MallocError; return; } read(address, (int8_t *) cmd, size); memcpy(data, cmd, size); free(cmd); } /** * void clear(void) * * Clear eeprom (write with 0) * @param none * @return none */ void EEPROM::clear() { int32_t data; uint32_t i; data = 0; for (i = 0; i < _size / 4; i++) { write((uint32_t) (i * 4), data); } } /** * void ready(void) * * Wait eeprom ready * @param none * @return none */ void EEPROM::ready() { int ack; uint8_t addr; uint8_t cmd[2]; // Check error if (_errnum) return; // Device address addr = EEPROM_Address | _address; cmd[0] = 0; // Wait end of write do { ack = _i2c.write((int) addr, (char *) cmd, 0); ThisThread::sleep_for(1ms); } while (ack != 0); } /** * uint32_t getSize(void) * * Get eeprom size in bytes * @param none * @return size in bytes (uint32_t) */ uint32_t EEPROM::getSize() { return (_size); } /** * const char* getName(void) * * Get eeprom name * @param none * @return name (const char*) */ const char *EEPROM::getName() { uint8_t i = 0; switch (_type) { case T24C01 : i = 0; break; case T24C02 : i = 1; break; case T24C04 : i = 2; break; case T24C08 : i = 3; break; case T24C16 : i = 4; break; case T24C32 : i = 5; break; case T24C64 : i = 6; break; case T24C128 : i = 7; break; case T24C256 : i = 8; break; case T24C512 : i = 9; break; case T24C1024 : i = 10; break; case T24C1025 : i = 11; break; } return (_name[i]); } /** * uint8_t getError(void) * * Get the current error number (EEPROM_NoError if no error) * @param none * @return none */ uint8_t EEPROM::getError() { return (_errnum); } /** * bool checkAddress(uint32_t address) * * Check if address is in the eeprom range address * @param address address to check (uint32_t) * @return true if in eeprom range, overwise false (bool) */ bool EEPROM::checkAddress(uint32_t address) { bool ret = true; switch (_type) { case T24C01 : if (address >= T24C01) ret = false; break; case T24C02 : if (address >= T24C02) ret = false; break; case T24C04 : if (address >= T24C04) ret = false; break; case T24C08 : if (address >= T24C08) ret = false; break; case T24C16 : if (address >= T24C16) ret = false; break; case T24C32 : if (address >= T24C32) ret = false; break; case T24C64 : if (address >= T24C64) ret = false; break; case T24C128 : if (address >= T24C128) ret = false; break; case T24C256 : if (address >= T24C256) ret = false; break; case T24C512 : if (address >= T24C512) ret = false; break; case T24C1024 : if (address >= T24C1024) ret = false; break; case T24C1025 : if (address >= T24C1025 - 1) ret = false; break; } return (ret); }