x

Dependents:   20180621_FT813

Files at this revision

API Documentation at this revision

Comitter:
JackB
Date:
Mon Jul 23 12:23:08 2018 +0000
Commit message:
I2CEE

Changed in this revision

I2CEEBlockDevice.cpp Show annotated file Show diff for this revision Revisions of this file
I2CEEBlockDevice.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 45d514e74be4 I2CEEBlockDevice.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/I2CEEBlockDevice.cpp	Mon Jul 23 12:23:08 2018 +0000
@@ -0,0 +1,134 @@
+/* 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 "I2CEEBlockDevice.h"
+
+#define I2CEE_TIMEOUT 10000
+ 
+
+I2CEEBlockDevice::I2CEEBlockDevice(
+        PinName sda, PinName scl, uint8_t addr,
+        bd_size_t size, bd_size_t block, int freq)
+    : _i2c(sda, scl), _i2c_addr(addr), _size(size), _block(block)
+{
+    _i2c.frequency(freq);
+}
+
+int I2CEEBlockDevice::init()
+{
+    return _sync();
+}
+
+int I2CEEBlockDevice::deinit()
+{
+    return 0;
+}
+
+int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
+{
+    // Check the address and size fit onto the chip.
+    MBED_ASSERT(is_valid_read(addr, size));
+
+    _i2c.start();
+    if (!_i2c.write(_i2c_addr | 0) ||
+        !_i2c.write((char)(addr >> 8)) ||
+        !_i2c.write((char)(addr & 0xff))) {
+        return BD_ERROR_DEVICE_ERROR;
+    }
+    _i2c.stop();
+
+    if (_i2c.read(_i2c_addr, static_cast<char*>(buffer), size) < 0) {
+        return BD_ERROR_DEVICE_ERROR;
+    }
+
+    return 0;
+}
+ 
+int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
+{
+    // Check the addr and size fit onto the chip.
+    MBED_ASSERT(is_valid_program(addr, size));
+        
+    // While we have some more data to write.
+    while (size > 0) {
+        uint32_t off = addr % _block;
+        uint32_t chunk = (off + size < _block) ? size : (_block - off);
+
+        _i2c.start();
+        if (!_i2c.write(_i2c_addr | 0) ||
+            !_i2c.write((char)(addr >> 8)) ||
+            !_i2c.write((char)(addr & 0xff))) {
+            return BD_ERROR_DEVICE_ERROR;
+        }
+
+        for (unsigned i = 0; i < chunk; i++) {
+            _i2c.write(static_cast<const char*>(buffer)[i]);
+        }
+        _i2c.stop();
+
+        int err = _sync();
+        if (err) {
+            return err;
+        }
+
+        addr += chunk;
+        size -= chunk;
+        buffer = static_cast<const char*>(buffer) + chunk;
+    }
+
+    return 0;
+}
+
+int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size)
+{
+    // No erase needed
+    return 0;
+}
+
+int I2CEEBlockDevice::_sync()
+{
+    // 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.
+    for (int i = 0; i < I2CEE_TIMEOUT; i++) {
+        if (_i2c.write(_i2c_addr | 0, 0, 0) < 1) {
+            return 0;
+        }
+
+        wait_ms(1);
+    }
+
+    return BD_ERROR_DEVICE_ERROR;
+}
+ 
+bd_size_t I2CEEBlockDevice::get_read_size() const
+{
+    return 1;
+}
+
+bd_size_t I2CEEBlockDevice::get_program_size() const
+{
+    return 1;
+}
+
+bd_size_t I2CEEBlockDevice::get_erase_size() const
+{
+    return 1;
+}
+
+bd_size_t I2CEEBlockDevice::size() const
+{
+    return _size;
+}
diff -r 000000000000 -r 45d514e74be4 I2CEEBlockDevice.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/I2CEEBlockDevice.h	Mon Jul 23 12:23:08 2018 +0000
@@ -0,0 +1,153 @@
+/* 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 MBED_I2CEEPROM_BLOCK_DEVICE_H
+#define MBED_I2CEEPROM_BLOCK_DEVICE_H
+ 
+#include <mbed.h>
+#include "BlockDevice.h"
+
+ 
+/** BlockDevice for I2C based flash device such as
+ *  Microchip's 24LC or ATMEL's AT24C ranges
+ *
+ *  @code
+ *  // Here's an example using a 24LC256 on a GR PEACH
+ *  #include "mbed.h"
+ *  #include "I2CEEBlockDevice.h"
+ *  
+ *  // Create EEPROM device on I2C bus with 32kbytes of memory
+ *  I2CEEBlockDevice i2cee(D14, D15, 0xa0, 32*1024);
+ *  
+ *  int main() {
+ *      printf("i2cee test\n");
+ *  
+ *      // Initialize the device and print the memory layout
+ *      i2cee.init();
+ *      printf("i2cee size: %llu\n",         i2cee.size());
+ *      printf("i2cee read size: %llu\n",    i2cee.get_read_size());
+ *      printf("i2cee program size: %llu\n", i2cee.get_program_size());
+ *      printf("i2cee erase size: %llu\n",   i2cee.get_erase_size());
+ *  
+ *      // Write "Hello World!" to the first block
+ *      char *buffer = (char*)malloc(i2cee.get_erase_size());
+ *      sprintf(buffer, "Hello World!\n");
+ *      i2cee.erase(0, i2cee.get_erase_size());
+ *      i2cee.program(buffer, 0, i2cee.get_erase_size());
+ *  
+ *      // Read back what was stored
+ *      i2cee.read(buffer, 0, i2cee.get_erase_size());
+ *      printf("%s", buffer);
+ *  
+ *      // Deinitialize the device
+ *      i2cee.deinit();
+ *  }
+ *  @endcode
+ */
+class I2CEEBlockDevice : public BlockDevice {
+public:
+    /** Constructor to create an I2CEEBlockDevice on I2C pins
+     *
+     *  @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 addr     The 8bit I2C address of the chip, common range 0xa0 - 0xae.
+     *  @param size     The size of the device in bytes
+     *  @param block    The page size of the device in bytes, defaults to 32bytes
+     *  @param freq     The frequency of the I2C bus, defaults to 400K.
+     */
+    I2CEEBlockDevice(
+            PinName sda, PinName scl, uint8_t address,
+            bd_size_t size, bd_size_t block=32,
+            int bus_speed=400000);
+
+    /** Initialize a block device
+     *
+     *  @return         0 on success or a negative error code on failure
+     */
+    virtual int init();
+
+    /** Deinitialize a block device
+     *
+     *  @return         0 on success or a negative error code on failure
+     */
+    virtual int deinit();
+
+    /** Read blocks from a block device
+     *
+     *  @param buffer   Buffer to write blocks to
+     *  @param addr     Address of block to begin reading from
+     *  @param size     Size to read in bytes, must be a multiple of read block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
+
+    /** Program blocks to a block device
+     *
+     *  The blocks must have been erased prior to being programmed
+     *
+     *  @param buffer   Buffer of data to write to blocks
+     *  @param addr     Address of block to begin writing to
+     *  @param size     Size to write in bytes, must be a multiple of program block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
+
+    /** Erase blocks on a block device
+     *
+     *  The state of an erased block is undefined until it has been programmed
+     *
+     *  @param addr     Address of block to begin erasing
+     *  @param size     Size to erase in bytes, must be a multiple of erase block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int erase(bd_addr_t addr, bd_size_t size);
+
+    /** Get the size of a readable block
+     *
+     *  @return         Size of a readable block in bytes
+     */
+    virtual bd_size_t get_read_size() const;
+
+    /** Get the size of a programable block
+     *
+     *  @return         Size of a programable block in bytes
+     *  @note Must be a multiple of the read size
+     */
+    virtual bd_size_t get_program_size() const;
+
+    /** Get the size of a eraseable block
+     *
+     *  @return         Size of a eraseable block in bytes
+     *  @note Must be a multiple of the program size
+     */
+    virtual bd_size_t get_erase_size() const;
+
+    /** Get the total size of the underlying device
+     *
+     *  @return         Size of the underlying device in bytes
+     */
+    virtual bd_size_t size() const;
+    
+private:
+    I2C _i2c;
+    uint8_t _i2c_addr;
+    uint32_t _size;
+    uint32_t _block;
+
+    int _sync();
+};
+ 
+
+#endif  /* MBED_I2CEEPROM_BLOCK_DEVICE_H */