Driver library for Microchip I2C EERAM (47x04 and 47x16) 4 kbit or 16 kbit EEPROM backed SRAM.
EERAM.h
- Committer:
- vargham
- Date:
- 2017-10-29
- Revision:
- 7:038c52e2268b
- Parent:
- 6:b3f9b29b07ba
File content as of revision 7:038c52e2268b:
/** * @file EERAM.h * @brief mbed driver for Microchip I2C EERAM devices (47x04 and 47x16) * @author Mark Peter Vargha, vmp@varghamarkpeter.hu * @version 1.4.0 * * Copyright (c) 2017 * * 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 EERAM_h #define EERAM_h #include "mbed.h" //#define DEBUG_EERAM #ifdef DEBUG_EERAM extern Serial serial; #endif enum ProtectedMemoryArea { NONE = 0, U64, U32, U16, U8, U4, U2, ALL }; /** An I2C EERAM interface to communicate with Microchip 47x04 and 47x16 devices * 4 kbit (512 byte) or 16 kbit (2048 byte) EEPROM backed I2C SRAM * The device could detect power down and stores SRAM contents in EEPROM. The SRAM is recalled from EEPROM on power up. * * <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/20005371C.pdf">47x04 and 47x16 datasheet</a> * <a href="http://ww1.microchip.com/downloads/cn/AppNotes/cn588417.pdf">Recommended Usage of Microchip I2C EERAM Devices</a> * <a href="http://ww1.microchip.com/downloads/en/AppNotes/00002257A.pdf">Choosing the Right EERAM VCAP Capacitor</a> * * Example: * @code #include "mbed.h" #include "EERAM.h" EERAM eeram(PC_9, PA_8, 2048); //SDA, SCL int main() { if (!eeram.isReady(100)) //Checks device with 100 ms timeout { printf("Device is not present."); while (1); } eeram.readStatus(); //Reads status register eeram.setAutoStoreEnabled(true, true); //Set auto store on power down to true and stores if not stored before while (1) { char dataStore[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; eeram.writeBytes(0x100, dataStore, 16); //We can not wear EEPROM out, so it is ok to write data to the device frequently. wait(2.0); char dataRead[16]; eeram.readBytes(0x100, dataRead, 16); wait(2.0); float floatToWrite = -1.976f; const uint16_t address = 0x200; eeram.write(address, &floatToWrite); wait(2.0); float floatToRead = 0; eeram.read(address, &floatToWrite); wait(2.0); } } * @endcode */ class EERAM { public: /** Create an I2C EERAM interface, connected to the specified pins, with specified size and with the specified address pins * @param SDA I2C data line pin * @param SCL I2C clock line pin * @param memorySize Size of EERAM, 512 for 47x04 and 2048 for 47x16 * @param A1 EERAM A1 pin state (true = high, false = low) * @param A2 EERAM A2 pin state (true = high, false = low) */ // EERAM(PinName SDA, PinName SCL, uint16_t memorySize, bool A1 = false, bool A2 = false) : // _i2c(SDA, SCL), // _memorySize(memorySize) // { // initialize(A1, A2); // }; /** Create an I2C EERAM interface, connected to the I2C, with specified size and with the specified address pins. * @param 12c I2C * @param memorySize Size of EERAM, 512 for 47x04 and 2048 for 47x16 * @param A1 EERAM A1 pin state (true = high, false = low) * @param A2 EERAM A2 pin state (true = high, false = low) */ EERAM(I2C &i2c, uint16_t memorySize, bool A1 = false, bool A2 = false) : _i2c(i2c), _memorySize(memorySize) { initialize(A1, A2); }; /** Puts the given 16 bit SRAM address into the first two bytes of the given buffer. The buffer size shall be minimum two bytes. No length check. */ static void putAddressIntoBuffer(uint16_t address, char *data); /** Copies the bytes from the given memory area to the given buffer. Uses the size of T to determine write length. * @param buffer Buffer to put bytes into * @param source Pointer to the memory location * * @return Number of written bytes. */ template <typename T> static int putIntoBuffer(char *buffer, const int bufferSize, T *source) { const int length = sizeof(T); if (length <= bufferSize) { char *bytes = static_cast<char*>(static_cast<void*>(source)); memcpy(buffer, bytes, length); return length; } else { return 0; } } /** Copies the bytes from the given buffer to the given memory area. Uses the size of T to determine read length. * @param buffer Buffer to get bytes from * @param destination Pointer to the memory location * * @return Number of read bytes. */ template <typename T> static int getFromBuffer(char *buffer, const int bufferSize, T *destination) { const int length = sizeof(T); if (length <= bufferSize) { char *bytes = static_cast<char*>(static_cast<void*>(destination)); memcpy(bytes, buffer, length); return length; } else { return 0; } } /** Copies the bytes from the given memory area to the given EERAM address. Uses the size of T and the length to determine write length. Allowed types: Primitives, fixed arrays and structs without pointers. * @param source Pointer to memory where copy from. * @param length Length of the array. Pass 1 here if it is not an array. * * @return Number of written bytes. */ template <typename T> int write(uint16_t address, T *source, const int length = 1) { bool success = false; const int typeLength = sizeof(T); const int writeLength = typeLength * length; const int addressLength = sizeof(uint16_t); char *sourceAsBytes = static_cast<char*>(static_cast<void*>(source)); success = checkAddressRange(address, writeLength); if (success) //Address range check { uint16_t addressPtr = address; int sourcePtr = 0; for (int i = 0; i < length; i++) { const int bufferSize = typeLength + addressLength; char buffer[bufferSize]; putAddressIntoBuffer(addressPtr, buffer); memcpy(buffer + addressLength, sourceAsBytes + sourcePtr, typeLength); success = writeBytes(buffer, bufferSize); if (success) //Write to I2C { addressPtr += typeLength; sourcePtr += typeLength; } else { break; } } } return success ? writeLength : 0; } /** Copies the bytes from the given EERAM address to the given memory area. Uses the size of T and the length to determine read length. Allowed types: Primitives, fixed arrays and structs without pointers. Destination shall be allocated. * @param destination Pointer to memory where copy to. * @param length Length of the array. Pass 1 here if it is not an array. * @param direct Applicable only when length > 1. If true reads all items of the array in one I2C read, otherwise read them in blocks. * * @return Number of read bytes. */ template <typename T> int read(uint16_t address, T *destination, const int length = 1, bool direct = false) { bool success = false; const int typeLength = sizeof(T); const int readLength = typeLength * length; char *destinationAsBytes = static_cast<char*>(static_cast<void*>(destination)); success = checkAddressRange(address, readLength); if (success) success = setMemoryPointer(address, true); if (success) { if (direct) { success = _i2c.read(_sramAddressRead, destinationAsBytes, readLength) == 0; } else { uint16_t addressPtr = address; int destinationPtr = 0; for (int i = 0; i < length; i++) { const int bufferSize = typeLength; char buffer[bufferSize]; success = readBytes(addressPtr, buffer, bufferSize); if (success) { memcpy(destinationAsBytes + destinationPtr, buffer, bufferSize); addressPtr += typeLength; destinationPtr += typeLength; } else { break; } } } } return success ? readLength : 0; } /** Writes bytes to the specified address. * @param address The 16 bit destination address * @param data Pointer to the data buffer to read from * @param length Data length * * @return * true on success * false on fail */ bool writeBytes(uint16_t address, char *data, int length); /** Writes data to the SRAM. The first two bytes shall be the address. * @param data Pointer to the data buffer to read from * @param length Data length * * @return * true on success * false on fail */ bool writeBytes(char *data, int length); /** Reads data from the specified address. * @param address The 16 bit source address * @param data Pointer to the data buffer to read to * @param length Data length * * @return * true on success * false on fail */ bool readBytes(uint16_t address, char *data, int length); /** Reads data to the specified buffer. The first two bytes shall be the address. These bytes will be unchanged. * @param data Pointer to the data buffer to read to * @param length Data length * * @return * true on success * false on fail */ bool readBytes(char *data, int length); /** Fills memory with the specified byte from the specified address * @param address The 16 bit destination address * @param data The byte to write * @param length Memory are to fill with the data byte * * @return * true on success * false on fail */ bool fillMemory(uint16_t address, char data, int length); /** Fills the whole memory with the specified byte * @param data The byte to write * * @return * true on success * false on fail */ bool fillMemory(char data); /** Continuously checks I2C EERAM device till ready or reaches timeout * @param timeout_ms Timeout for busy-wait I2C device polling * * @return * true on ready * false on timeout */ bool isReady(int timeout_ms); /** Checks I2C EERAM device one time * * @return * true on ready * false on not ready */ bool isReady(); /** Store SRAM in EEPROM * @param block If true, busy waits for operation end. * * @return * true on success * false on fail */ bool store(bool block); /** Recall SRAM from EEPROM * @param block If true, busy waits for operation end. * * @return * true on success * false on fail */ bool recall(bool block); /** Reads status register * * @return * true on success * false on fail */ bool readStatus(); /** Gets status register * * @return The content of the 8 bit status register */ inline uint8_t getStatus() const { return _status; } /** Writes the 8 bit status register to the I2C device * @param block If true, busy waits for operation end. * * @return * true on success * false on fail */ bool writeStatus(bool block); /** Writes the 8 bit status register to the I2C device if changed * @param block If true, busy waits for operation end. */ void writeStatusIfChanged(bool block); /** Sets protected memory area. The selected area will be write protected. * @param protectedMemoryArea The enum represents the area from NONE through upper 64, 32, 16, 8, 4, 2 to ALL * @param store If true, calls writeStatusIfChanged() */ void setProtectedMemoryArea(ProtectedMemoryArea protectedMemoryArea, bool store = false); /** Gets protected memory area * Have to call readStatus() to read register's fresh value. */ ProtectedMemoryArea getProtectedMemoryArea(); /** Gets Memory Modified * Have to call readStatus() to read register's fresh value. * @return * true The SRAM memory have been modified since the last store or recall operation * false The SRAM memory have not been modified since the last store or recall operation */ bool isMemoryModified(); /** Sets Auto Store Enabled * @param enabled Auto store SRAM to EEPROM on power down enabled * @param store If true, calls writeStatusIfChanged() */ void setAutoStoreEnabled(bool enabled, bool store = false); /** Gets Auto Store Enabled * Have to call readStatus() to read register's fresh value. * @return * true Auto Store is Enabled * false Auto Store is not Enabled */ bool isAutoStoreEnabled(); /** Sets event detected * @param detected The value of the detected bit * @param store If true, calls writeStatusIfChanged() */ void setEventDetected(bool detected, bool store); /** Gets event detected * Have to call readStatus() to read register's fresh value. * @return * true External store event detected (The HS pin pulled up) * false External event not detected */ bool isEventDetected(); /** Prints the SRAM content from the given address to the given serial port in human readable format in 1-32 byte long lines * @param serial The port to print to */ void dump(Serial &serial, uint16_t start, uint16_t length, int lineSize = 16); /** Prints the whole SRAM content to the given serial port in human readable format in 16 byte long lines * @param serial The port to print to */ void dump(Serial &serial); /** Prints the 8 bit status register's contents to the given serial port in human readable format * @param serial The port to print to */ void dumpRegisters(Serial &serial); private: static const uint8_t RW_BIT = 0; static const uint8_t A1_BIT = 2; static const uint8_t A2_BIT = 3; static const uint8_t STATUS_AM_BIT = 7; static const uint8_t STATUS_ASE_BIT = 1; static const uint8_t STATUS_EVENT_BIT = 0; static const uint8_t OPCODE_SRAM = 0b10100000; static const uint8_t OPCODE_CONTROL = 0b00110000; static const uint8_t REGISTER_STATUS = 0x0; static const uint8_t REGISTER_COMMAND = 0x55; static const uint8_t COMMAND_STORE = 0b00110011; static const uint8_t COMMAND_RECALL = 0b11011101; static const int TIME_RECALL_16_MS = 5; static const int TIME_RECALL_04_MS = 2; static const int TIME_STORE_16_MS = 25; static const int TIME_STORE_04_MS = 8; static const int TIME_STORE_STATUS_MS = 1; I2C &_i2c; uint16_t _memorySize; uint8_t _sramAddressWrite; uint8_t _sramAddressRead; uint8_t _controlAddressWrite; uint8_t _controlAddressRead; uint8_t _status; uint8_t _statusToWrite; void initialize(bool A1 = false, bool A2 = false); bool checkAddressRange(uint16_t start, uint16_t length); bool writeRegister(uint8_t registerAddress, uint8_t data); bool setMemoryPointer(uint16_t address, bool stop = true); bool setMemoryPointer(uint8_t address_0, uint8_t address_1, bool stop = true); }; #endif //EERAM_h