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
Revision 12:d9a44fb3b9a6, committed 2020-03-28
- Comitter:
- skyscraper
- Date:
- Sat Mar 28 18:03:39 2020 +0000
- Parent:
- 11:c3609e691623
- Commit message:
- More documentation tidying and format wand
Changed in this revision
I2CEeprom.cpp | Show annotated file Show diff for this revision Revisions of this file |
I2CEeprom.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/I2CEeprom.cpp Sat Mar 28 17:01:58 2020 +0000 +++ b/I2CEeprom.cpp Sat Mar 28 18:03:39 2020 +0000 @@ -22,6 +22,7 @@ namespace { +//return true if system is little-endian inline bool little_endian() { int n = 1; @@ -29,6 +30,8 @@ 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); @@ -50,12 +53,12 @@ } I2CEeprom::I2CEeprom( - I2C & i2c, - int address, - size_t pageSize, - size_t chipSize, - uint8_t writeCycleTime_ms - ): + I2C & i2c, + int address, + size_t pageSize, + size_t chipSize, + uint8_t writeCycleTime_ms +): m_i2c{i2c}, m_i2cAddress(address), m_chipSize(chipSize), @@ -65,14 +68,13 @@ size_t I2CEeprom::read(size_t address, char &value) { - // Check the address and size fit onto the chip. - if (!checkSpace(address, 1)) - return 0; - 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; + 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; @@ -80,14 +82,13 @@ size_t I2CEeprom::read(size_t address, char *buffer, size_t size) { - // Check the address and size fit onto the chip. - if (!checkSpace(address, size)) - return 0; - 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; + 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; @@ -95,62 +96,62 @@ size_t I2CEeprom::write(size_t address, char value) { - // Check the address and size fit onto the chip. - if (!checkSpace(address, 1)) - return 0; - char values[3]; - convert_address(address,values); - values[2] = value; - if (m_i2c.write(m_i2cAddress, values, 3) != 0) { + 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; } - - waitForWrite(); - return 1; } size_t I2CEeprom::ll_write(size_t address, const char *buffer, size_t size) { - // TODO: change i2c address bits dependent on eeprom data address - // if size > 64k I - if (m_i2c.write(m_i2cAddress, buffer, size) != 0) { + if (m_i2c.write(m_i2cAddress, buffer, size) == 0) { + waitForWrite(); + return size; + } else { std::cout << "EE i2c write failed\n"; return 0; } - waitForWrite(); - return size; } size_t I2CEeprom::write(size_t address, const char *buffer, size_t size) { - if (!checkSpace(address, size)) { - return 0; - } - 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"; + 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; } - 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; } void I2CEeprom::waitForWrite() @@ -163,4 +164,3 @@ ThisThread::sleep_for(1); } } -
--- a/I2CEeprom.h Sat Mar 28 17:01:58 2020 +0000 +++ b/I2CEeprom.h Sat Mar 28 18:03:39 2020 +0000 @@ -1,25 +1,25 @@ -/*! \file I2CEeprom.h */ +/*! \file I2CEeprom.h */ /*! \class I2CEeprom */ /*! \brief Simple access class for I2C EEPROM chips like Microchip 24LC */ - - /* Copyright (c) 2015 Robin Hourahane - * Copyright (c) 2020 Andy Little - * - * 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. - */ - + +/* Copyright (c) 2015 Robin Hourahane +* Copyright (c) 2020 Andy Little +* +* 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. +*/ + #ifndef __I2CEEPROM_H__ #define __I2CEEPROM_H__ @@ -27,106 +27,109 @@ /// Based on the original Mbed I2CEeprom library by Robin Hourahane. /// -/// Class to provide simple access to I2C EEPROM chips like Microchip's 24LC +/// Class to provide simple access to I2C EEPROM chips like Microchip's 24LC /// or AMTELS AT24C series. Chips up to 64Kb in size are directly supported. -/// -/// The library was tested on a Microchip 24LC128. +/// +/// The library was tested on a Microchip 24LC128. /// -/// The I2CEeprom class handles multiple page writes so any amount of data can -/// be written in a single call to write. +/// The I2CEeprom class handles multiple page writes so any amount of data can +/// be written in a single call to write. /// /// The code is modified from the original to better support RTOS. /// At start of a write(addr,buffer,size), a buffer of up to pageSize + 2 /// is allocated on the heap, and the data and address are copied to it. -/// This allows the cuurent write chunk to proceed without unlocking, which +/// This allows the cuurent write chunk to proceed without unlocking, which /// prevents another device on the bus access and so aborting the write. -/// +/// /// This allocation per write fits my usecase, where eeeprom variables /// are written in a special menu mode at start up, but it may be better to /// preallocate a buffer once at startup, if writes are occurring at arbitrary /// times during execution, to prevent surprises with out of memory issues /// when trying to write during normal execution. /// -/// The constructor takes the I2C bus by non-const reference argument. This +/// The constructor takes the I2C bus by non-const reference argument. This /// enables a single I2C bus to be shared among many i2c devices. -/// Mbed OS enforces a lock on the bus during a bus read/write so reads -/// are atomic. Writes are split into separate atomic chunks that only write +/// Mbed OS enforces a lock on the bus during a bus read/write so reads +/// are atomic. Writes are split into separate atomic chunks that only write /// one eeprom page. After a chunk is sent, the thread is sent to sleep for -/// writeCycleTime_ms as in the constructor arg, and is then polled at 1 ms -/// intervals to discover whether the write has completed. +/// writeCycleTime_ms specified in the constructor arg, and is then polled +/// at 1 ms intervals to discover whether the write has completed. -class I2CEeprom { +class I2CEeprom +{ public: /// Constructor to create a new instance of the class. - /// @param i2c A reference to the i2c bus the chip is connected to. - /// @param address The 8bit I2C address of the chip. - /// @param pageSize The size of the page used in writing to the chip. - /// @param chipSize The size of the memory in the chip for range check. - /// @param writeCycleTime_ms The write cycle time in ms. + /// @param i2c A reference to the i2c bus that the chip is connected to. + /// @param address The 8bit device I2C address + /// @param pageSize The device page size. + /// @param chipSize The device size for range check. + /// @param writeCycleTime_ms The device write cycle time in ms. I2CEeprom( - I2C& i2c, - int address, - size_t pageSize, - size_t chipSize, - uint8_t writeCycleTime_ms); - + I2C& i2c, + int address, + size_t pageSize, + size_t chipSize, + uint8_t writeCycleTime_ms); + /// Read a single byte from the EEprom. - /// @param address EEprom address to read from. - /// @param value Reference of char to read into. + /// @param address EEprom read address. + /// @param value Memory reference of char to read into. /// @returns Number of bytes read . size_t read(size_t address, char &value); - - /// Read multiple bytes starting from EEprom memory. - /// @param address EEprom Memory start read address. - /// @param buffer Pointer to buffer to hold bytes read. + + /// Read multiple bytes from the EEprom. + /// @param address EEprom start read address. + /// @param buffer buffer to read into. /// @param size Number of bytes to read. /// @returns Number of bytes read. size_t read(size_t address, char *buffer, size_t size); - + /// Deserialise object of type T from Eeprom memory. - /// @param address Start address of Object in EEprom. - /// @param value Reference to Object to deserialise into. + /// @param address EEprom start address + /// @param value Memory Reference of object to deserialise into . /// @returns Number of bytes read. - template<typename T> size_t read(size_t address, T &value) { + template<typename T> size_t read(size_t address, T &value) + { return read(address, reinterpret_cast<char *>(&value), sizeof(T)); } - + /// Write a char to EEprom. - /// @param address Eeprom address to write to. - /// @param value Value of char to write. + /// @param address Eeprom write address. + /// @param value Char value to write. /// @returns Number of bytes written. size_t write(size_t address, char value); - /// Write multiple bytes to EEprom - /// Note that in this implementation, the write is split into chunks - /// of up to pageSize and for each chunk a buffer of up to pageSize + 2 - /// is temporarily allocated on the heap while the write is in progress. - /// @param address Start EEprom address. - /// @param buffer Buffer holding the bytes to write. + /// Write multiple bytes to EEprom. + /// In this implementation, the write is split into chunks of up to pageSize + /// and for each chunk a buffer of up to pageSize + 2 is temporarily + /// allocated on the heap while the write is in progress. + /// @param address EEprom start address. + /// @param buffer Buffer to write. /// @param size Number of bytes to write. /// @returns Number of bytes written. size_t write(size_t address, const char *buffer, size_t size); /// Serialise an object of type T. - /// @param address EEprom write start address. + /// @param address EEprom start address. /// @param value Object to be serialised. /// @returns Number of bytes written. - template<typename T> size_t write(size_t address, const T &value) { + template<typename T> size_t write(size_t address, const T &value) + { return write(address, reinterpret_cast<const char *>(&value), sizeof(T)); } - + private: /// Sleep thread while write completes. void waitForWrite(); - /// atomic chunk up to page aize write + /// atomic chunk up to page size write size_t ll_write(size_t address, const char *buffer, size_t size); - + /// Validate that proposed operation is in bounds. bool checkSpace(size_t address, size_t size) { - return (address + size) < m_chipSize ; + return (address + size) < m_chipSize ; } - + private: I2C & m_i2c; int const m_i2cAddress;