Driver library for Microchip I2C EERAM (47x04 and 47x16) 4 kbit or 16 kbit EEPROM backed SRAM.

Dependents:   EERAM_example

EERAM.h

Committer:
vargham
Date:
2017-04-27
Revision:
3:a869096d7a5d
Parent:
2:bdbf9de0e985
Child:
4:fd84e2f0d1de

File content as of revision 3:a869096d7a5d:

/**
* @file    EERAM.h
* @brief   mbed driver for Microchip I2C EERAM devices (47x04 and 47x16)
* @author  Mark Peter Vargha, vmp@varghamarkpeter.hu
* @version 1.2.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

union MemoryAddress
{
    uint16_t address;
    char bytes[2];
};

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.write(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.read(0x100, dataRead, 16);
        wait(2.0);
        float floatToWrite = -1.976f;
        const uint16_t address = 0x200;
        eeram.writeValue(address, &floatToWrite);
        wait(2.0);
        float floatToRead = 0;
        eeram.readValue(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 param Pointer to the memory location
    *
    * @return Number of written bytes.
    */
    template <typename T>
    static int putValueIntoBuffer(char *buffer, const int bufferSize, T *param)
    {
        const int length = sizeof(T);
        if (length <= bufferSize)
        {
            char *bytes = static_cast<char*>(static_cast<void*>(param));
            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 param Pointer to the memory location
    *
    * @return Number of read bytes.
    */
    template <typename T>
    static int getValueFromBuffer(char *buffer, const int bufferSize, T *param)
    {
        const int length = sizeof(T);
        if (length <= bufferSize)
        {
            char *bytes = static_cast<char*>(static_cast<void*>(param));
            memcpy(bytes, buffer, length);
            return length;
        }
        else
        {
            return 0;
        }
    }

    /** Copies the bytes from the given memory area to the given address of the EERAM. Uses the size of T to determine write length.
    * @param source Pointer to the memory location where copy from
    *
    * @return Number of written bytes.
    */
    template <typename T>
    int writeValue(uint16_t address, T *source)
    {
        bool success = false;
        const int bufferSize = sizeof(T) + 2;
        char buffer[bufferSize];
        putAddressIntoBuffer(address, buffer);
        putValueIntoBuffer(buffer + 2, bufferSize - 2, source);
        success = write(buffer, bufferSize);
        return success ? bufferSize - 2 : 0;
    }

    /** Copies the bytes from the given memory area to the given address of the EERAM. Uses the size of T and the array length to determine write length.
    * @param sourceArray Pointer to the array where copy from
    * @param length Length of the array
    *
    * @return Number of written bytes.
    */
    template <typename T>
    int writeArray(uint16_t address, T *sourceArray, const int length)
    {
        uint16_t addressPtr = address;
        for (int i = 0; i < length; i++)
        {
            int writtenBytes = writeValue(addressPtr, &sourceArray[i]);
            if (writtenBytes == 0) break;
            addressPtr += writtenBytes;
        }
        return addressPtr - address;
    }

    /** Copies the bytes from the given EERAM address to the given memory area. Uses the size of T to determine read length.
    * @param destination Pointer to the memory location where copy to
    *
    * @return Number of read bytes.
    */
    template <typename T>
    int readValue(uint16_t address, T *destination)
    {
        bool success = false;
        const int bufferSize = sizeof(T);
        char buffer[bufferSize];
        success = read(address, buffer, bufferSize);
        if (success)
        {
            getValueFromBuffer(buffer, bufferSize, destination);
        }
        return success ? bufferSize : 0;
    }

    /** Copies the bytes from the given EERAM address to the given memory area. Uses the size of T and the array length to determine read length.
    * @param destinationArray Pointer to the array where copy to
    * @param length Length of the array
    *
    * @return Number of read bytes.
    */
    template <typename T>
    int readArray(uint16_t address, T *destinationArray, const int length)
    {
        uint16_t addressPtr = address;
        for (int i = 0; i < length; i++)
        {
            int readBytes = readValue(addressPtr, &destinationArray[i]);
            if (readBytes == 0) break;
            addressPtr += readBytes;
        }
        return addressPtr - address;
    }

    /** Writes data 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 write(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 write(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 read(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 read(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