From 41b9b3da392bba8fa5d08106a9420970d4251f64 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sun, 10 Jun 2012 14:02:36 +1000 Subject: [PATCH] Support for 24LCXX EEPROM's via I2C --- doc/Doxyfile | 2 +- doc/mainpage.dox | 1 + libraries/I2C/EEPROM24.cpp | 310 +++++++++++++++++++++++++++++++ libraries/I2C/EEPROM24.h | 87 +++++++++ libraries/I2C/eeprom_circuit.fig | 100 ++++++++++ libraries/I2C/eeprom_circuit.png | Bin 0 -> 4996 bytes 6 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 libraries/I2C/EEPROM24.cpp create mode 100644 libraries/I2C/EEPROM24.h create mode 100644 libraries/I2C/eeprom_circuit.fig create mode 100644 libraries/I2C/eeprom_circuit.png diff --git a/doc/Doxyfile b/doc/Doxyfile index 4e3dd91d..3c9d3fa0 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -688,7 +688,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = ../libraries/BlinkLED/examples/Cylon ../libraries/BlinkLED/examples/Cylon4 ../libraries/BlinkLED/examples/StarTrek ../libraries/BlinkLED/examples/Charlieplex ../libraries/LCD/examples/HelloWorld ../libraries/LCD/examples/Form ../libraries/RTC/examples/AlarmClock ../libraries/DMD ../libraries/IR +IMAGE_PATH = ../libraries/BlinkLED/examples/Cylon ../libraries/BlinkLED/examples/Cylon4 ../libraries/BlinkLED/examples/StarTrek ../libraries/BlinkLED/examples/Charlieplex ../libraries/LCD/examples/HelloWorld ../libraries/LCD/examples/Form ../libraries/RTC/examples/AlarmClock ../libraries/DMD ../libraries/IR ../libraries/I2C # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 14f48b4a..78c8b95c 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -75,6 +75,7 @@ I2C master. \li SoftI2C class that implements the master side of the I2C protocol in software on any arbitrary pair of pins for DATA and CLOCK. This class supports both 7-bit and 10-bit I2C addresses. +\li EEPROM24 class for reading and writing 24LCXX family EEPROM's. \section main_RTC Realtime Clock Library diff --git a/libraries/I2C/EEPROM24.cpp b/libraries/I2C/EEPROM24.cpp new file mode 100644 index 00000000..dcd093a3 --- /dev/null +++ b/libraries/I2C/EEPROM24.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "EEPROM24.h" +#include "I2CMaster.h" + +/** + * \class EEPROM24 EEPROM24.h + * \brief Reading and writing EEPROM's from the 24LCXX family. + * + * The 24LCXX family of EEPROM's provide a variety of memory sizes from + * 16 bytes up to 128 kBytes that can be accessed via the I2C protocol. + * These chips can be used to augment the 1 kByte or so of builtin EEPROM + * memory that is typical on Arduino boards. The EEPROM should be wired + * to an Arduino Uno as follows: + * + * \image html eeprom_circuit.png + * + * Access to a 24LCXX chip is initialized as follows: + * + * \code + * SoftI2C i2c(A4, A5); + * EEPROM24 eeprom(i2c, EEPROM_24LC256); + * \endcode + * + * Once initialized, read() and write() can be used to manipulate the + * contents of the EEPROM's memory. + * + * The following EEPROM types are supported by this class: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ChipTypeSize
24lc00\c EEPROM_24LC0016 bytes
24lc01\c EEPROM_24LC01128 bytes
24lc014\c EEPROM_24LC014128 bytes
24lc02\c EEPROM_24LC02256 bytes
24lc024\c EEPROM_24LC024256 bytes
24lc025\c EEPROM_24LC025256 bytes
24lc04\c EEPROM_24LC04512 bytes
24lc08\c EEPROM_24LC081 kByte
24lc16\c EEPROM_24LC162 kBytes
24lc32\c EEPROM_24LC324 kBytes
24lc64\c EEPROM_24LC648 kBytes
24lc128\c EEPROM_24LC12816 kBytes
24lc256\c EEPROM_24LC25632 kBytes
24lc512\c EEPROM_24LC51264 kBytes
24lc1025\c EEPROM_24LC1025128 kBytes
24lc1026\c EEPROM_24LC1026128 kBytes
+ * + * There can be multiple 24LCXX chips on the same I2C bus, as long as their + * A0, A1, and A2 address pins are set to different values. For example, + * two 24LC256 chips can be used to provide the same memory capacity as a + * single 24LC512 chip. The optional bank parameter to the constructor + * is used to assign different bank addresses to each chip: + * + * \code + * SoftI2C i2c(A4, A5); + * EEPROM24 eeprom0(i2c, EEPROM_24LC256, 0); + * EEPROM24 eeprom1(i2c, EEPROM_24LC256, 1); + * \endcode + * + * \sa I2CMaster + */ + +/** + * \brief Constructs a new EEPROM access object on \a bus for an EEPROM + * of the specified \a type. + * + * The \a bank can be used to choose between multiple EEPROM's on + * \a bus of the specified \a type. The \a bank corresponds to the value + * that is set on the EEPROM's A0, A1, and A2 address pins. Note that + * some EEPROM's have less than 3 address pins; consult the datasheet + * for more information. + */ +EEPROM24::EEPROM24(I2CMaster &bus, unsigned long type, uint8_t bank) + : _bus(&bus) + , _size((type & 0xFFFF) * ((type >> 16) & 0x0FFF)) + , _pageSize((type >> 16) & 0x0FFF) + , _mode((uint8_t)((type >> 28) & 0x0F)) + , i2cAddress(0x50) +{ + // Adjust the I2C address for the memory bank of the chip. + switch (_mode) { + case EE_BSEL_NONE: + i2cAddress += (bank & 0x07); + break; + case EE_BSEL_8BIT_ADDR: { + uint8_t addrBits = 8; + unsigned long size = 0x0100; + while (size < _size) { + ++addrBits; + size <<= 1; + } + if (addrBits < 11) + i2cAddress += ((bank << (addrBits - 8)) & 0x07); + break; } + case EE_BSEL_17BIT_ADDR: + i2cAddress += ((bank << 1) & 0x06); + break; + case EE_BSEL_17BIT_ADDR_ALT: + i2cAddress += bank & 0x03; + break; + } +} + +/** + * \fn unsigned long EEPROM24::size() const + * \brief Returns the size of the EEPROM in bytes. + * + * \sa pageSize() + */ + +/** + * \fn unsigned long EEPROM24::pageSize() const + * \brief Returns the size of a single EEPROM page in bytes. + * + * Writes that are a multiple of the page size and aligned on a page + * boundary will typically be more efficient than non-aligned writes. + * + * \sa size() + */ + +/** + * \brief Returns true if the EEPROM is available on the I2C bus; + * false otherwise. + * + * This function can be used to probe the I2C bus to determine if the + * EEPROM is present or not. + * + * \sa read(), write() + */ +bool EEPROM24::available() +{ + // Perform a "Current Address Read" on the EEPROM. We don't care about + // the returned byte. We only care if the read request was ACK'ed or not. + if (!_bus->startRead(i2cAddress, 1)) + return false; + _bus->read(); + return true; +} + +/** + * \brief Reads a single byte from the EEPROM at \a address. + * + * \sa write() + */ +uint8_t EEPROM24::read(unsigned long address) +{ + if (address >= _size) + return 0; + writeAddress(address); + if (!_bus->startRead(i2cAddress, 1)) + return 0; + return _bus->read(); +} + +/** + * \brief Reads a block of \a length bytes from the EEPROM at \a address + * into the specified \a data buffer. + * + * Returns the number of bytes that were read, which may be short if + * \a address + \a length is greater than size() or the EEPROM is + * not available on the I2C bus. + * + * \sa write(), available() + */ +size_t EEPROM24::read(unsigned long address, void *data, size_t length) +{ + if (address >= _size || !length) + return 0; + if ((address + length) > _size) + length = (size_t)(_size - address); + writeAddress(address); + if (!_bus->startRead(i2cAddress, length)) + return 0; + uint8_t *d = (uint8_t *)data; + unsigned int count = 0; + while (_bus->available()) { + *d++ = _bus->read(); + ++count; + } + return count; +} + +/** + * \brief Writes a byte \a value to \a address in the EEPROM. + * + * Returns true if the byte was written successfully, or false if + * \a address is out of range or the EEPROM is not available on the I2C bus. + * + * \sa read(), available() + */ +bool EEPROM24::write(unsigned long address, uint8_t value) +{ + if (address >= _size) + return false; + writeAddress(address); + _bus->write(value); + return waitForWrite(); +} + +/** + * \brief Writes \a length bytes from a \a data buffer to \a address + * in the EEPROM. + * + * Returns the number of bytes that were written, which may be short if + * \a address + \a length is greater than size() or the EEPROM is not + * available on the I2C bus. + * + * Best performance will be achieved if \a address and \a length are a + * multiple of pageSize(). + * + * \sa read(), available(), pageSize() + */ +size_t EEPROM24::write(unsigned long address, const void *data, size_t length) +{ + if (address >= _size) + return 0; + if ((address + length) > _size) + length = (size_t)(_size - address); + bool needAddress = true; + size_t result = 0; + size_t page = 0; + const uint8_t *d = (const uint8_t *)data; + while (length > 0) { + if (needAddress) { + writeAddress(address); + needAddress = false; + } + _bus->write(*d++); + ++address; + ++page; + if ((address & (_pageSize - 1)) == 0) { + // At the end of a page, so perform a flush. + if (!waitForWrite()) + return result; // Could not write this page. + needAddress = true; + result += page; + page = 0; + } + --length; + } + if (!needAddress) { + if (!waitForWrite()) + return result; // Could not write the final page. + } + return result + page; +} + +void EEPROM24::writeAddress(unsigned long address) +{ + switch (_mode) { + case EE_BSEL_NONE: + _bus->startWrite(i2cAddress); + _bus->write((uint8_t)(address >> 8)); + _bus->write((uint8_t)address); + break; + case EE_BSEL_8BIT_ADDR: + _bus->startWrite(i2cAddress | (((uint8_t)(address >> 8)) & 0x07)); + _bus->write((uint8_t)address); + break; + case EE_BSEL_17BIT_ADDR: + _bus->startWrite(i2cAddress | (((uint8_t)(address >> 16)) & 0x01)); + _bus->write((uint8_t)(address >> 8)); + _bus->write((uint8_t)address); + break; + case EE_BSEL_17BIT_ADDR_ALT: + _bus->startWrite(i2cAddress | (((uint8_t)(address >> 14)) & 0x04)); + _bus->write((uint8_t)(address >> 8)); + _bus->write((uint8_t)address); + break; + } +} + +bool EEPROM24::waitForWrite() +{ + // 1000 iterations is going to be approximately 100ms when the I2C + // clock is 100 kHz. If there has been no response in that time + // then we assume that the write has failed and timeout. + if (!_bus->endWrite()) + return false; + unsigned count = 1000; + while (count > 0) { + _bus->startWrite(i2cAddress); + if (_bus->endWrite()) + return true; + --count; + } + return false; +} diff --git a/libraries/I2C/EEPROM24.h b/libraries/I2C/EEPROM24.h new file mode 100644 index 00000000..5f0a7d68 --- /dev/null +++ b/libraries/I2C/EEPROM24.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef EEPROM24_h +#define EEPROM24_h + +#include +#include + +class I2CMaster; + +// Block select modes. +#define EE_BSEL_NONE 0 +#define EE_BSEL_8BIT_ADDR 1 +#define EE_BSEL_17BIT_ADDR 2 +#define EE_BSEL_17BIT_ADDR_ALT 3 + +// Create an EEPROM descriptor from byte size, page size, and block select mode. +#define _EE24(byteSize, pageSize, mode) \ + (((byteSize) / (pageSize)) | (((unsigned long)(pageSize)) << 16) | \ + (((unsigned long)(mode)) << 28)) + +// Type descriptors for the 24LCXX range of EEPROM's. +#define EEPROM_24LC00 _EE24(16UL, 1, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC01 _EE24(128UL, 8, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC014 _EE24(128UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC02 _EE24(256UL, 8, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC024 _EE24(256UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC025 _EE24(256UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC04 _EE24(512UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC08 _EE24(1024UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC16 _EE24(2048UL, 16, EE_BSEL_8BIT_ADDR) +#define EEPROM_24LC32 _EE24(4096UL, 32, EE_BSEL_NONE) +#define EEPROM_24LC64 _EE24(8192UL, 32, EE_BSEL_NONE) +#define EEPROM_24LC128 _EE24(16384UL, 32, EE_BSEL_NONE) +#define EEPROM_24LC256 _EE24(32768UL, 64, EE_BSEL_NONE) +#define EEPROM_24LC512 _EE24(65536UL, 128, EE_BSEL_NONE) +#define EEPROM_24LC1025 _EE24(131072UL, 128, EE_BSEL_17BIT_ADDR_ALT) +#define EEPROM_24LC1026 _EE24(131072UL, 128, EE_BSEL_17BIT_ADDR) + +class EEPROM24 +{ +public: + EEPROM24(I2CMaster &bus, unsigned long type, uint8_t bank = 0); + + unsigned long size() const { return _size; } + unsigned long pageSize() const { return _pageSize; } + + bool available(); + + uint8_t read(unsigned long address); + size_t read(unsigned long address, void *data, size_t length); + + bool write(unsigned long address, uint8_t value); + size_t write(unsigned long address, const void *data, size_t length); + +private: + I2CMaster *_bus; + unsigned long _size; + unsigned long _pageSize; + uint8_t _mode; + uint8_t i2cAddress; + + void writeAddress(unsigned long address); + bool waitForWrite(); +}; + +#endif diff --git a/libraries/I2C/eeprom_circuit.fig b/libraries/I2C/eeprom_circuit.fig new file mode 100644 index 00000000..9a721b1b --- /dev/null +++ b/libraries/I2C/eeprom_circuit.fig @@ -0,0 +1,100 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +6 3105 3555 3195 3645 +1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 3150 3600 30 30 3150 3600 3150 3630 +-6 +6 3105 3780 3195 3870 +1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 3150 3825 30 30 3150 3825 3150 3855 +-6 +6 3105 4005 3195 4095 +1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 3150 4050 30 30 3150 4050 3150 4080 +-6 +6 3060 4095 3240 4365 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 3135 4365 3165 4365 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 3100 4320 3200 4320 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 3060 4275 3240 4275 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 3150 4095 3150 4275 +-6 +6 5400 3285 5715 3465 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5580 3375 5715 3375 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5535 3375 5400 3375 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5580 3285 5580 3465 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5535 3285 5535 3465 +-6 +6 5760 3600 5940 3870 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5835 3870 5865 3870 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5800 3825 5900 3825 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5760 3780 5940 3780 +2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2 + 5850 3600 5850 3780 +-6 +6 5130 3330 5220 3420 +1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5175 3375 30 30 5175 3375 5175 3405 +-6 +6 5805 3555 5895 3645 +1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5850 3600 30 30 5850 3600 5850 3630 +-6 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3150 3600 3825 3600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3150 3825 3825 3825 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3150 4050 3825 4050 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3825 3150 4725 3150 4725 4275 3825 4275 3825 3150 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 3825 3375 3150 3375 3150 4140 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3 + 1 1 1.00 60.00 120.00 + 4725 3375 5175 3375 5175 2700 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5400 3375 5085 3375 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5850 3375 5670 3375 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 4725 3600 5850 3600 5850 3375 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3 + 1 1 1.00 60.00 120.00 + 4725 4050 5175 4050 5175 4500 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3 + 1 1 1.00 60.00 120.00 + 4725 3825 5625 3825 5625 4500 +4 0 0 50 -1 0 12 0.0000 4 135 105 3690 4005 4\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 4770 4005 5\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 4770 3780 6\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 4770 3555 7\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 4770 3330 8\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 3690 3780 3\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 3690 3555 2\001 +4 0 0 50 -1 0 12 0.0000 4 135 105 3690 3330 1\001 +4 0 0 50 -1 0 8 0.0000 4 90 180 3870 3420 A0\001 +4 0 0 50 -1 0 8 0.0000 4 90 180 3870 3645 A1\001 +4 0 0 50 -1 0 8 0.0000 4 90 180 3870 3870 A2\001 +4 0 0 50 -1 0 8 0.0000 4 90 315 3870 4095 GND\001 +4 0 0 50 -1 0 8 0.0000 4 90 210 4500 3645 WP\001 +4 0 0 50 -1 0 8 0.0000 4 90 285 4410 3420 VCC\001 +4 0 0 50 -1 0 8 0.0000 4 90 255 4455 3870 SCL\001 +4 0 0 50 -1 0 8 0.0000 4 90 285 4455 4095 SDA\001 +4 0 0 50 -1 0 12 0.0000 4 135 735 3915 3060 24LCXX\001 +4 0 0 50 -1 0 12 0.0000 4 135 240 5040 2610 5V\001 +4 0 0 50 -1 0 12 0.0000 4 135 525 5310 3195 100nF\001 +4 0 0 50 -1 0 12 0.0000 4 135 240 5040 4680 A4\001 +4 0 0 50 -1 0 12 0.0000 4 135 240 5490 4680 A5\001 diff --git a/libraries/I2C/eeprom_circuit.png b/libraries/I2C/eeprom_circuit.png new file mode 100644 index 0000000000000000000000000000000000000000..af6013eaf80180a13466b5cb35dcdf2d79f3b2fc GIT binary patch literal 4996 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 literal 0 HcmV?d00001