Class to provide simple access to I2C EEPROM chiles like Microchip's 24LC range or AMTELS AT24C range. Chips up to 64Kb in size are directly supported. Updated to Mbed OS v 5.1
I2CEeprom.cpp
- Committer:
- skyscraper
- Date:
- 2020-03-28
- Revision:
- 12:d9a44fb3b9a6
- Parent:
- 4:d8f51b136dbd
File content as of revision 12:d9a44fb3b9a6:
/* Simple access class for I2C EEPROM chips like Microchip 24LC * Copyright (c) 2015 Robin Hourahane * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <iostream> #include <algorithm> #include "I2CEeprom.h" namespace { //return true if system is little-endian inline bool little_endian() { int n = 1; // little endian if true return(*(char *)&n == 1); } // Put EEprom address into 2 char array // in corrcet endiannness void convert_address(size_t const & addressIn, char* arOut) { constexpr size_t int_size = sizeof(size_t); union uu { uu(size_t a) : address{a} {} size_t address; const char ar[int_size]; } u{addressIn}; static_assert(sizeof(u) == int_size,""); if( little_endian()) { arOut[1] = u.ar[0]; arOut[0] = u.ar[1]; } else { // std::cout << "big endian\n"; arOut[1] = u.ar[int_size - 1]; // lsb arOut[0] = u.ar[int_size - 2]; //nextsb } } } I2CEeprom::I2CEeprom( I2C & i2c, int address, size_t pageSize, size_t chipSize, uint8_t writeCycleTime_ms ): m_i2c{i2c}, m_i2cAddress(address), m_chipSize(chipSize), m_pageSize(pageSize), m_writeCycleTime_ms{writeCycleTime_ms} {} size_t I2CEeprom::read(size_t address, char &value) { if (checkSpace(address, 1)) { char values[2]; convert_address(address,values); if (m_i2c.write(m_i2cAddress, values, 2) == 0) { if (m_i2c.read(m_i2cAddress, &value, 1) == 0) { return 1; } } } return 0; } size_t I2CEeprom::read(size_t address, char *buffer, size_t size) { if (checkSpace(address, size)) { char values[2]; convert_address(address,values); if (m_i2c.write(m_i2cAddress, values, 2) == 0) { if (m_i2c.read(m_i2cAddress, buffer, size) == 0) { return size; } } } return 0; } size_t I2CEeprom::write(size_t address, char value) { if (checkSpace(address, 1)) { char values[3]; convert_address(address,values); values[2] = value; if (m_i2c.write(m_i2cAddress, values, 3) != 0) { return 0; } waitForWrite(); return 1; } else { return 0; } } size_t I2CEeprom::ll_write(size_t address, const char *buffer, size_t size) { if (m_i2c.write(m_i2cAddress, buffer, size) == 0) { waitForWrite(); return size; } else { std::cout << "EE i2c write failed\n"; return 0; } } size_t I2CEeprom::write(size_t address, const char *buffer, size_t size) { if (checkSpace(address, size)) { size_t const malloc_size = std::min(size,m_pageSize) + 2U; char * tempBuffer = new char [malloc_size]; if ( tempBuffer == nullptr) { std::cout << "EE i2c buf malloc failed\n"; return 0; } size_t bytesLeft = size; size_t numBytesToWrite = std::min( size, m_pageSize - (address % m_pageSize)); while( bytesLeft ) { convert_address(address,tempBuffer); memcpy(tempBuffer + 2,buffer, numBytesToWrite); if ( ll_write(address,tempBuffer, numBytesToWrite + 2U) == numBytesToWrite + 2U) { buffer += numBytesToWrite; address += numBytesToWrite; bytesLeft -= numBytesToWrite; numBytesToWrite = std::min(bytesLeft,m_pageSize); } else { std::cout << "EE i2c write failed\n"; break; } } delete [] tempBuffer; return size - bytesLeft; } else { return 0; } } void I2CEeprom::waitForWrite() { ThisThread::sleep_for(m_writeCycleTime_ms); // The chip doesn't ACK while writing to the actual EEPROM // so loop trying to do a zero byte write until it is ACKed // by the chip. while (m_i2c.write(m_i2cAddress, 0, 0) != 0) { ThisThread::sleep_for(1); } }