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.

Dependents:   Test_I2CEeprom Alternator2020_06

This class provides a simple read write interface for I2C EEPROMs like Microchip's 24LC range and AMTELS AT24C range. The class ensure that writes respect the page size of the chip to ensure larger blocks can be written in one call. The class uses the supplied buffer to directly write to the chip so no extra RAM is used.

Files at this revision

API Documentation at this revision

Comitter:
rhourahane
Date:
Sun Jul 19 09:34:04 2015 +0000
Parent:
0:f275a33797f1
Commit message:
Updates to make library more generic to allow support for more I2C EEPROM chips.

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
Mem24LCXX.cpp Show diff for this revision Revisions of this file
Mem24LCXX.h Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/I2CEeprom.cpp	Sun Jul 19 09:34:04 2015 +0000
@@ -0,0 +1,198 @@
+/* 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 "I2CEeprom.h"
+
+I2CEeprom::I2CEeprom(PinName sda, PinName scl, int address, size_t pageSize, size_t chipSize, int busSpeed):
+    m_i2c(sda, scl),
+    m_i2cAddress(address),
+    m_chipSize(chipSize),
+    m_pageSize(pageSize)
+{
+    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) };
+    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) {
+    // Check the address and size fit onto the chip.
+    if (!checkSpace(address, size))
+        return 0;
+        
+    char values[] = { (address >> 8), (address & 0xFF) };
+    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) {
+    // Check the address and size fit onto the chip.
+    if (!checkSpace(address, 1))
+        return 0;
+        
+    char values[] = { (address >> 8), (address & 0xFF), 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))
+        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;
+        }
+
+        // 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;
+    }
+    
+    return size;
+}
+
+size_t I2CEeprom::fill(size_t address, char value, size_t size) {
+    // Check the address and size fit onto the chip.
+    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;
+            }
+        } else {
+            if (left <= m_pageSize) {
+                toWrite = left;
+            } else {
+                toWrite = m_pageSize;
+            }
+        }
+        
+        //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;
+}
+
+void I2CEeprom::waitForWrite() {
+    // 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);
+    }
+}
+
+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.
+        if ((address >= m_chipSize) || ((address + size) >= m_chipSize))
+            return false;
+        else
+            return true;
+    }
+    
+    return true;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/I2CEeprom.h	Sun Jul 19 09:34:04 2015 +0000
@@ -0,0 +1,108 @@
+/* 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.
+ */
+#ifndef __I2CEEPROM_H__
+#define __I2CEEPROM_H__
+
+#include <mbed.h>
+
+/// 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.
+/// The class handles multiple page writes so any amount of data can be written in
+/// a single call to write. The writes are not buffered so additional memory use 
+/// is kept to a minimum.
+///
+/// Although not tested it should work with the MAC versions of Microchip range
+/// as well but the chipSize will need to be set to include the ROM area as well.
+class I2CEeprom {
+public:
+    /// Constructor to create a new instance of the class.
+    /// @param sda The pin name for the sda line of the I2C bus.
+    /// @param scl The pin name for the scl line of the I2C bus.
+    /// @param address The 8bit I2C address of the chip in the range 0xA0 - 0xAE.
+    /// @param pageSize The size of the page used in writing to the chip.
+    /// @param chipSize The size of the memory in the chip to allow range checkng. Set to
+    /// 0 to disable checks.
+    /// @param busSpeed The frequency of the I2C bus defaults to 400K.
+    I2CEeprom(PinName sda, PinName scl, int address, size_t pageSize, size_t chipSize, int busSpeed = 400000);
+    
+    /// Read a single byte from the address in memory.
+    /// @param address Memory address to read from.
+    /// @param value Variable to receive value read.
+    /// @returns Number of bytes read from memory.
+    size_t read(size_t address, char &value);
+    
+    /// Read multiple bytes starting from the address in memory.
+    /// @param address Memory address to start reading from.
+    /// @param buffer Pointer to buffer to hold bytes read from memory.
+    /// @param size Number of bytes to be read from memory.
+    /// @returns Number of bytes read from memory.
+    size_t read(size_t address, char *buffer, size_t size);
+    
+    /// Read either an instance or an array of a POD type from memory.
+    /// Note the value of the type can't contain pointers.
+    /// @param address Start address for reading memory.
+    /// @param value Object to be read from memory.
+    /// @returns Number of bytes read from memory.
+    template<typename T> size_t read(size_t address, T &value) {
+        return read(address, reinterpret_cast<char *>(&value), sizeof(T));
+    }
+    
+    /// Write a single byte to the address in memory.
+    /// @param address Memory address to write to.
+    /// @param value Value to be written to memory.
+    /// @returns Number of bytes written to memory.
+    size_t write(size_t address, char value);
+
+    /// Write multiple bytes starting from the address in memory.
+    /// @param address Memory address to start writting to.
+    /// @param buffer Pointer to buffer holding the bytes to write to memory.
+    /// @param size Number of bytes to be written to memory.
+    /// @returns Number of bytes written to memory.
+    size_t write(size_t address, const char *buffer, size_t size);
+    
+    /// Write either an instance or an array of a POD type to memory.
+    /// Note the value of the type can't contain pointers.
+    /// @param address Start address to write to memory.
+    /// @returns Number of bytes written to memory.
+    template<typename T> size_t write(size_t address, const T &value) {
+        return write(address, reinterpret_cast<const char *>(&value), sizeof(T));
+    }
+    
+    /// Fill a range of memory with a single value. No memory is allocated
+    /// so large areas can be filled with minimal memory usage.
+    /// @param address Starting address to write to.
+    /// @param value Value to be written to memory.
+    /// @Param size Number of bytes to be written.
+    /// @returns Number of bytes written to memory.
+    size_t fill(size_t address, char value, size_t size);
+    
+private:
+    // Wait for a write cycle to complete using polling and small waits.
+    void waitForWrite();
+    
+    // Validate that the proposed opperation will fit in the size of
+    // the chip.
+    bool checkSpace(size_t address, size_t size);
+    
+private:
+    I2C m_i2c;
+    int m_i2cAddress;
+    size_t m_chipSize;
+    size_t m_pageSize;
+};
+
+#endif
\ No newline at end of file
--- a/Mem24LCXX.cpp	Sat Jun 27 13:03:49 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/* Simple access class for Microchip 24LC EEPROM chips library
- * 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 "Mem24LCXX.h"
-
-const int PageSize = 64;
-
-Mem24LCXX::Mem24LCXX(PinName sda, PinName scl, int address, size_t chipSize, int busSpeed):
-    m_i2c(sda, scl),
-    m_i2cAddress(address),
-    m_chipSize(chipSize)
-{
-    m_i2c.frequency(busSpeed);
-}
-
-size_t Mem24LCXX::read(size_t address, char &value) {
-    if (!checkSpace(address, 1))
-        return 0;
-        
-    char values[] = { (address >> 8), (address & 0xFF) };
-    if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
-        if (m_i2c.read(m_i2cAddress, &value, 1) == 0) {
-            return 1;
-        }
-    }
-    
-    return 0;
-}
-
-size_t Mem24LCXX::read(size_t address, char *buffer, size_t size) {
-    if (!checkSpace(address, size))
-        return 0;
-        
-    char values[] = { (address >> 8), (address & 0xFF) };
-    if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
-        if (m_i2c.read(m_i2cAddress, buffer, size) == 0) {
-            return size;
-        }
-    }
-    
-    return 0;
-}
-
-size_t Mem24LCXX::write(size_t address, char value) {
-    if (!checkSpace(address, 1))
-        return 0;
-        
-    char values[] = { (address >> 8), (address & 0xFF), value };
-    if (m_i2c.write(m_i2cAddress, values, 3) != 0) {
-        return 0;
-    }
-    
-    waitForWrite();
-    
-    return 1;
-}
-
-size_t Mem24LCXX::write(size_t address, const char *buffer, size_t size) {
-    if (!checkSpace(address, size))
-        return 0;
-        
-    const char *page = buffer;
-    size_t left = size;
-    
-    while (left != 0) {
-        size_t toWrite;
-        if ((address % PageSize) != 0) {
-            toWrite = (((address / PageSize) + 1) * 64) - address;
-            if (toWrite > size) {
-                toWrite = size;
-            }
-        } else {
-            if (left <= PageSize) {
-                toWrite = left;
-            } else {
-                toWrite = PageSize;
-            }
-        }
-        
-        printf("Writing [%.*s] at %d size %d\n\r", toWrite, page, 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(*page) == 0) {
-                return size - left;
-            }
-            ++page;
-        }
-        
-        m_i2c.stop();
-        
-        waitForWrite();
-        
-        left -= toWrite;
-        address += toWrite;
-    }
-    return size;
-}
-
-size_t Mem24LCXX::fill(size_t address, char value, size_t size) {
-    if (!checkSpace(address, size))
-        return 0;
-        
-    size_t left = size;
-    
-    while (left != 0) {
-        size_t toWrite;
-        if ((address % PageSize) != 0) {
-            toWrite = (((address / PageSize) + 1) * 64) - address;
-            if (toWrite > size) {
-                toWrite = size;
-            }
-        } else {
-            if (left <= PageSize) {
-                toWrite = left;
-            } else {
-                toWrite = PageSize;
-            }
-        }
-        
-        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 true;
-}
-
-void Mem24LCXX::waitForWrite() {
-    while (m_i2c.write(m_i2cAddress, 0, 0) != 0) {
-        // Wait for ack.
-        wait_ms(1);
-    }
-}
-
-bool Mem24LCXX::checkSpace(size_t address, size_t size) {
-    if ((address >= m_chipSize) || ((address + size) >= m_chipSize))
-        return false;
-    else
-        return true;
-}
-
--- a/Mem24LCXX.h	Sat Jun 27 13:03:49 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/* Simple access class for Microchip 24LC EEPROM chips library
- * 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.
- */
-#ifndef __MEM24LCXX_H__
-#define __MEM24LCXX_H__
-
-#include <mbed.h>
-
-/// Class to provide simple access to Microchip's 24LC range of I2C EEPROM memory chips.
-/// Chips up to 64Kb in size are directly supported, the bank selected 128Kb chips
-/// can be handled by creating to instances of the class one for each bank.
-/// The class handles multiple page writes so any amount of data can be written in
-/// a single call to write. The writes are not buffered so additional memory use 
-/// is kept to a minimum.
-///
-/// Although not tested it should work with the MAC versions as well but the chipSize will
-/// need to be set to include the ROM area as well.
-class Mem24LCXX {
-public:
-    /// Constructor to create a new instance of the class.
-    /// @param sda The pin name for the sda line of the I2C bus.
-    /// @param scl The pin name for the scl line of the I2C bus.
-    /// @param address The 8bit I2C address of the chip in the range 0xA0 - 0xAE.
-    /// @param chipSize The size of the memory in the chip to allow range checkng.
-    /// @param busSpeed The frequency of the I2C bus defaults to 400K.
-    Mem24LCXX(PinName sda, PinName scl, int address, size_t chipSize, int busSpeed = 400000);
-    
-    /// Read a single byte from the address in memory.
-    /// @param address Memory address to read from.
-    /// @param value Variable to receive value read.
-    /// @returns Number of bytes read from memory.
-    size_t read(size_t address, char &value);
-    
-    /// Read multiple bytes starting from the address in memory.
-    /// @param address Memory address to start reading from.
-    /// @param buffer Pointer to buffer to hold bytes read from memory.
-    /// @param size Number of bytes to be read from memory.
-    /// @returns Number of bytes read from memory.
-    size_t read(size_t address, char *buffer, size_t size);
-    
-    /// Read either an instance or an array of a POD type from memory.
-    /// Note the value of the type can't contain pointers.
-    /// @param address Start address for reading memory.
-    /// @param value Object to be read from memory.
-    /// @returns Number of bytes read from memory.
-    template<typename T> size_t read(size_t address, T &value) {
-        return read(address, reinterpret_cast<char *>(&value), sizeof(T));
-    }
-    
-    /// Write a single byte to the address in memory.
-    /// @param address Memory address to write to.
-    /// @param value Value to be written to memory.
-    /// @returns Number of bytes written to memory.
-    size_t write(size_t address, char value);
-
-    /// Write multiple bytes starting from the address in memory.
-    /// @param address Memory address to start writting to.
-    /// @param buffer Pointer to buffer holding the bytes to write to memory.
-    /// @param size Number of bytes to be written to memory.
-    /// @returns Number of bytes written to memory.
-    size_t write(size_t address, const char *buffer, size_t size);
-    
-    /// Write either an instance or an array of a POD type to memory.
-    /// Note the value of the type can't contain pointers.
-    /// @param address Start address to write to memory.
-    /// @returns Number of bytes written to memory.
-    template<typename T> size_t write(size_t address, const T &value) {
-        return write(address, reinterpret_cast<const char *>(&value), sizeof(T));
-    }
-    
-    /// Fill a range of memory with a single value. No memory is allocated
-    /// so large areas can be filled with minimal memory usage.
-    /// @param address Starting address to write to.
-    /// @param value Value to be written to memory.
-    /// @Param size Number of bytes to be written.
-    /// @returns Number of bytes written to memory.
-    size_t fill(size_t address, char value, size_t size);
-    
-private:
-    // Wait for a write cycle to complete using polling and small waits.
-    void waitForWrite();
-    
-    // Validate that the proposed opperation will fit in the size of
-    // the chip.
-    bool checkSpace(size_t address, size_t size);
-    
-private:
-    I2C m_i2c;
-    int m_i2cAddress;
-    size_t m_chipSize;
-};
-
-#endif
\ No newline at end of file