/**
* @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.
*
* 47x04 and 47x16 datasheet
* Recommended Usage of Microchip I2C EERAM Devices
* Choosing the Right EERAM VCAP Capacitor
*
* 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
static int putIntoBuffer(char *buffer, const int bufferSize, T *source)
{
const int length = sizeof(T);
if (length <= bufferSize)
{
char *bytes = static_cast(static_cast(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
static int getFromBuffer(char *buffer, const int bufferSize, T *destination)
{
const int length = sizeof(T);
if (length <= bufferSize)
{
char *bytes = static_cast(static_cast(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
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(static_cast(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
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(static_cast(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