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

Dependents:   storage_test

Revision:
2:f3188aaccf80
Parent:
1:b23f5561266c
Child:
3:b2a132553b02
--- a/I2CEeprom.cpp	Sun Jul 19 09:34:04 2015 +0000
+++ b/I2CEeprom.cpp	Sat Mar 28 00:53:14 2020 +0000
@@ -13,177 +13,166 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <iostream>
+#include <algorithm>
+
 #include "I2CEeprom.h"
 
-I2CEeprom::I2CEeprom(PinName sda, PinName scl, int address, size_t pageSize, size_t chipSize, int busSpeed):
-    m_i2c(sda, scl),
+namespace
+{
+
+inline bool little_endian()
+{
+    int n = 1;
+    // little endian if true
+    return(*(char *)&n == 1);
+}
+
+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_pageSize(pageSize),
+    m_writeCycleTime_ms{writeCycleTime_ms}
+{}
+
+size_t I2CEeprom::read(size_t address, char &value)
 {
-    m_i2c.frequency(busSpeed);
-}
-
-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[] = { (address >> 8), (address & 0xFF) };
+    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) {
+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[] = { (address >> 8), (address & 0xFF) };
+    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) {
+// works ok
+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[] = { (address >> 8), (address & 0xFF), value };
+    char values[3];
+    convert_address(address,values);
+    values[2] = value;
     if (m_i2c.write(m_i2cAddress, values, 3) != 0) {
         return 0;
     }
-    
+
     waitForWrite();
-    
     return 1;
 }
 
-size_t I2CEeprom::write(size_t address, const char *buffer, size_t size) {
-    // Check the address and size fit onto the chip.
-    if (!checkSpace(address, size))
+
+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
+    // write 2 bytes ee address + data size
+    char* tempBuffer = new char [size + 2];
+    if ( tempBuffer == nullptr) {
+        delete [] tempBuffer;
+        std::cout << "EE i2c write malloc buf failed \n";
         return 0;
-        
-    const char *page = buffer;
-    size_t left = size;
-    
-    // Whle we have some more data to write.
-    while (left != 0) {
-        // Calculate the number of bytes we can write in the current page.
-        // If the address is not page aligned then write enough to page 
-        // align it.
-        size_t toWrite;
-        if ((address % m_pageSize) != 0) {
-            toWrite = (((address / m_pageSize) + 1) * m_pageSize) - address;
-            if (toWrite > size) {
-                toWrite = size;
-            }
-        } else {
-            if (left <= m_pageSize) {
-                toWrite = left;
-            } else {
-                toWrite = m_pageSize;
-            }
-        }
-        
-        //printf("Writing [%.*s] at %d size %d\n\r", toWrite, page, address, toWrite);
-        // Start the page write with the addres ine one write call.
-        char values[] = { (address >> 8), (address & 0xFF) };
-        if (m_i2c.write(m_i2cAddress, values, 2, true) != 0) {
-            // Write failed to return bytes written so far.
-            return size - left;
-        }
+    }
+    convert_address(address,tempBuffer);
+    memcpy(tempBuffer+2,buffer,size);
 
-        // Write the bytes out one at a time to avoid having to copy them to
-        // another buffer.        
-        for (int count = 0; count != toWrite; ++count) {
-            if (m_i2c.write(*page) == 0) {
-            // Write failed to return bytes written so far.
-                return size - left;
-            }
-            ++page;
-        }
-        
-        // Stop the transaction now we've completed the page
-        // write.
-        m_i2c.stop();
-        
-        waitForWrite();
-        
-        // Update the counters with the amount we've just written
-        left -= toWrite;
-        address += toWrite;
+    if (m_i2c.write(m_i2cAddress, tempBuffer, size+2) != 0) {
+        delete [] tempBuffer;
+        std::cout << "EE i2c write failed\n";
+        return 0;
     }
-    
+    delete [] tempBuffer;
+    waitForWrite();
     return size;
 }
 
-size_t I2CEeprom::fill(size_t address, char value, size_t size) {
+// ret num written
+size_t I2CEeprom::write(size_t address, const char *buffer, size_t size)
+{
     // Check the address and size fit onto the chip.
-    if (!checkSpace(address, size))
+    if (!checkSpace(address, size)) {
         return 0;
-        
-    size_t left = size;
-    
-    while (left != 0) {
-        size_t toWrite;
-        if ((address % m_pageSize) != 0) {
-            toWrite = (((address / m_pageSize) + 1) * 64) - address;
-            if (toWrite > size) {
-                toWrite = size;
-            }
+    }
+    size_t bytesLeft = size;
+    size_t numBytesToWrite
+        = std::min( size, m_pageSize - (address % m_pageSize));
+    while(bytesLeft) {
+        if ( ll_write(address,buffer, numBytesToWrite)
+           == numBytesToWrite) {
+            buffer += numBytesToWrite;
+            address += numBytesToWrite;
+            bytesLeft -= numBytesToWrite;
+            numBytesToWrite = std::min(bytesLeft,m_pageSize);
         } else {
-            if (left <= m_pageSize) {
-                toWrite = left;
-            } else {
-                toWrite = m_pageSize;
-            }
+            break;
         }
-        
-        //printf("Writing %d at %d size %d\n\r", value, address, toWrite);
-        char values[] = { (address >> 8), (address & 0xFF) };
-        if (m_i2c.write(m_i2cAddress, values, 2, true) != 0) {
-            return size - left;
-        }
-        
-        for (int count = 0; count != toWrite; ++count) {
-            if (m_i2c.write(value) == 0)
-                return size - left;
-        }
-        
-        m_i2c.stop();
-        
-        waitForWrite();
-        
-        left -= toWrite;
-        address += toWrite;
     }
-    
-    return size;
+    return size - bytesLeft;
 }
 
-void I2CEeprom::waitForWrite() {
+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) {
         // Wait for ack.
-        wait_ms(1);
+        ThisThread::sleep_for(1);
     }
 }
 
-bool I2CEeprom::checkSpace(size_t address, size_t size) {
-    // Only check if chip size is non-zero.
+bool I2CEeprom::checkSpace(size_t address, size_t size)
+{
+    // Only check, if chip size is non-zero.
     if (m_chipSize != 0) {
         // Check that the address start in the chip and doesn't
         // extend past.
@@ -192,7 +181,6 @@
         else
             return true;
     }
-    
+
     return true;
 }
-