mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
456 lines
10 KiB
C++
456 lines
10 KiB
C++
//
|
|
// FILE: I2C_24LC1025.cpp
|
|
// AUTHOR: Rob Tillaart
|
|
// VERSION: 0.3.0
|
|
// PURPOSE: I2C_24LC1025 library for Arduino with EEPROM I2C_24LC1025 et al.
|
|
// URL: https://github.com/RobTillaart/I2C_24LC1025
|
|
|
|
|
|
#include "I2C_24LC1025.h"
|
|
|
|
|
|
// I2C buffer needs max 2 bytes for EEPROM address
|
|
// 1 byte for EEPROM register address is available in transmit buffer
|
|
#if defined(ESP32) || defined(ESP8266) || defined(PICO_RP2040)
|
|
#define I2C_BUFFERSIZE 128
|
|
#else
|
|
#define I2C_BUFFERSIZE 30 // AVR, STM
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PUBLIC FUNCTIONS
|
|
//
|
|
I2C_24LC1025::I2C_24LC1025(uint8_t deviceAddress, TwoWire * wire)
|
|
{
|
|
_deviceAddress = deviceAddress;
|
|
_deviceSize = I2C_DEVICESIZE_24LC1025;
|
|
_pageSize = I2C_PAGESIZE_24LC1025;
|
|
_wire = wire;
|
|
}
|
|
|
|
|
|
bool I2C_24LC1025::begin(int8_t writeProtectPin)
|
|
{
|
|
_lastWrite = 0;
|
|
_writeProtectPin = writeProtectPin;
|
|
if (_writeProtectPin >= 0)
|
|
{
|
|
pinMode(_writeProtectPin, OUTPUT);
|
|
preventWrite();
|
|
}
|
|
return isConnected();
|
|
}
|
|
|
|
|
|
bool I2C_24LC1025::isConnected()
|
|
{
|
|
_wire->beginTransmission(_deviceAddress);
|
|
return (_wire->endTransmission() == 0);
|
|
}
|
|
|
|
|
|
uint8_t I2C_24LC1025::getAddress()
|
|
{
|
|
return _deviceAddress;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// WRITE SECTION
|
|
//
|
|
|
|
// returns I2C status, 0 = OK
|
|
int I2C_24LC1025::writeByte(const uint32_t memoryAddress, const uint8_t data)
|
|
{
|
|
int rv = _WriteBlock(memoryAddress, &data, 1);
|
|
return rv;
|
|
}
|
|
|
|
|
|
// returns I2C status, 0 = OK
|
|
int I2C_24LC1025::setBlock(const uint32_t memoryAddress, const uint8_t data, const uint32_t length)
|
|
{
|
|
uint8_t buffer[I2C_BUFFERSIZE];
|
|
for (uint8_t i = 0; i < I2C_BUFFERSIZE; i++)
|
|
{
|
|
buffer[i] = data;
|
|
}
|
|
int rv = _pageBlock(memoryAddress, buffer, length, false);
|
|
return rv;
|
|
}
|
|
|
|
|
|
// returns I2C status, 0 = OK
|
|
int I2C_24LC1025::writeBlock(const uint32_t memoryAddress, const uint8_t * buffer, const uint32_t length)
|
|
{
|
|
int rv = _pageBlock(memoryAddress, buffer, length, true);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// READ SECTION
|
|
//
|
|
|
|
// returns the value stored in memoryAddress
|
|
uint8_t I2C_24LC1025::readByte(const uint32_t memoryAddress)
|
|
{
|
|
uint8_t rdata;
|
|
_ReadBlock(memoryAddress, &rdata, 1);
|
|
return rdata;
|
|
}
|
|
|
|
|
|
// returns bytes read.
|
|
uint32_t I2C_24LC1025::readBlock(const uint32_t memoryAddress, uint8_t * buffer, const uint32_t length)
|
|
{
|
|
uint32_t addr = memoryAddress;
|
|
uint32_t len = length;
|
|
uint32_t rv = 0;
|
|
|
|
if ((addr < 0x10000) && ((addr + len) > 0x10000))
|
|
{
|
|
uint32_t sublen = 0x10000 - addr;
|
|
rv = readBlock(addr, (uint8_t *) buffer, sublen);
|
|
rv += readBlock(0x10000, (uint8_t *) &buffer[sublen], len - sublen);
|
|
return rv;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
uint8_t cnt = I2C_BUFFERSIZE;
|
|
if (cnt > len) cnt = len;
|
|
rv += _ReadBlock(addr, buffer, cnt);
|
|
addr += cnt;
|
|
buffer += cnt;
|
|
len -= cnt;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// UPDATE SECTION
|
|
//
|
|
|
|
// returns 0 == OK
|
|
int I2C_24LC1025::updateByte(const uint32_t memoryAddress, const uint8_t data)
|
|
{
|
|
if (data == readByte(memoryAddress)) return 0;
|
|
return writeByte(memoryAddress, data);
|
|
}
|
|
|
|
|
|
// returns bytes written.
|
|
uint32_t I2C_24LC1025::updateBlock(const uint32_t memoryAddress, const uint8_t * buffer, const uint32_t length)
|
|
{
|
|
uint32_t addr = memoryAddress;
|
|
uint32_t len = length;
|
|
uint32_t rv = 0;
|
|
while (len > 0)
|
|
{
|
|
uint8_t buf[I2C_BUFFERSIZE];
|
|
uint8_t cnt = I2C_BUFFERSIZE;
|
|
|
|
if (cnt > len) cnt = len;
|
|
rv += _ReadBlock(addr, buf, cnt);
|
|
if (memcmp(buffer, buf, cnt) != 0)
|
|
{
|
|
_pageBlock(addr, buffer, cnt, true);
|
|
}
|
|
addr += cnt;
|
|
buffer += cnt;
|
|
len -= cnt;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// VERIFY SECTION
|
|
//
|
|
|
|
// return false if write or verify failed.
|
|
bool I2C_24LC1025::writeByteVerify(const uint32_t memoryAddress, const uint8_t value)
|
|
{
|
|
if (writeByte(memoryAddress, value) != 0 ) return false;
|
|
uint8_t data = readByte(memoryAddress);
|
|
return (data == value);
|
|
}
|
|
|
|
|
|
// return false if write or verify failed.
|
|
bool I2C_24LC1025::writeBlockVerify(const uint32_t memoryAddress, const uint8_t * buffer, const uint32_t length)
|
|
{
|
|
if (writeBlock(memoryAddress, buffer, length) != 0) return false;
|
|
uint8_t data[length];
|
|
if (readBlock(memoryAddress, data, length) != length) return false;
|
|
return memcmp(data, buffer, length) == 0;
|
|
}
|
|
|
|
|
|
// return false if write or verify failed.
|
|
bool I2C_24LC1025::setBlockVerify(const uint32_t memoryAddress, const uint8_t value, const uint32_t length)
|
|
{
|
|
if (setBlock(memoryAddress, value, length) != 0) return false;
|
|
uint8_t data[length];
|
|
if (readBlock(memoryAddress, data, length) != length) return false;
|
|
for (uint32_t i = 0; i < length; i++)
|
|
{
|
|
if (data[i] != value) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// return false if write or verify failed.
|
|
bool I2C_24LC1025::updateByteVerify(const uint32_t memoryAddress, const uint8_t value)
|
|
{
|
|
if (updateByte(memoryAddress, value) != 0 ) return false;
|
|
uint8_t data = readByte(memoryAddress);
|
|
return (data == value);
|
|
}
|
|
|
|
|
|
// return false if write or verify failed.
|
|
bool I2C_24LC1025::updateBlockVerify(const uint32_t memoryAddress, const uint8_t * buffer, const uint32_t length)
|
|
{
|
|
if (updateBlock(memoryAddress, buffer, length) != length) return false;
|
|
uint8_t data[length];
|
|
if (readBlock(memoryAddress, data, length) != length) return false;
|
|
return memcmp(data, buffer, length) == 0;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// METADATA SECTION
|
|
//
|
|
uint32_t I2C_24LC1025::getDeviceSize()
|
|
{
|
|
return _deviceSize;
|
|
}
|
|
|
|
|
|
uint8_t I2C_24LC1025::getPageSize()
|
|
{
|
|
return _pageSize;
|
|
}
|
|
|
|
|
|
uint32_t I2C_24LC1025::getLastWrite()
|
|
{
|
|
return _lastWrite;
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::setExtraWriteCycleTime(uint8_t ms)
|
|
{
|
|
_extraTWR = ms;
|
|
}
|
|
|
|
|
|
uint8_t I2C_24LC1025::getExtraWriteCycleTime()
|
|
{
|
|
return _extraTWR;
|
|
}
|
|
|
|
|
|
//
|
|
// WRITEPROTECT
|
|
//
|
|
bool I2C_24LC1025::hasWriteProtectPin()
|
|
{
|
|
return (_writeProtectPin >= 0);
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::allowWrite()
|
|
{
|
|
if (hasWriteProtectPin())
|
|
{
|
|
digitalWrite(_writeProtectPin, LOW);
|
|
}
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::preventWrite()
|
|
{
|
|
if (hasWriteProtectPin())
|
|
{
|
|
digitalWrite(_writeProtectPin, HIGH);
|
|
}
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::setAutoWriteProtect(bool b)
|
|
{
|
|
if (hasWriteProtectPin())
|
|
{
|
|
_autoWriteProtect = b;
|
|
}
|
|
}
|
|
|
|
|
|
bool I2C_24LC1025::getAutoWriteProtect()
|
|
{
|
|
return _autoWriteProtect;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PRIVATE
|
|
//
|
|
|
|
// _pageBlock aligns buffer to page boundaries for writing.
|
|
// and to I2C buffer size
|
|
// returns 0 = OK otherwise error
|
|
int I2C_24LC1025::_pageBlock(uint32_t memoryAddress, const uint8_t * buffer, const uint16_t length, const bool incrBuffer)
|
|
{
|
|
uint32_t addr = memoryAddress;
|
|
uint16_t len = length;
|
|
while (len > 0)
|
|
{
|
|
uint8_t bytesUntilPageBoundary = this->_pageSize - addr % this->_pageSize;
|
|
|
|
uint8_t cnt = I2C_BUFFERSIZE;
|
|
if (cnt > len) cnt = len;
|
|
if (cnt > bytesUntilPageBoundary) cnt = bytesUntilPageBoundary;
|
|
|
|
int rv = _WriteBlock(addr, buffer, cnt);
|
|
if (rv != 0) return rv;
|
|
|
|
addr += cnt;
|
|
if (incrBuffer) buffer += cnt;
|
|
len -= cnt;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::_beginTransmission(uint32_t memoryAddress)
|
|
{
|
|
// chapter 5+6 - datasheet - need three bytes for address
|
|
_actualAddress = _deviceAddress;
|
|
if (memoryAddress >= 0x10000) _actualAddress |= 0x04; // addresbit 16
|
|
|
|
|
|
// Wait until EEPROM gives ACK again.
|
|
// this is a bit faster than the hardcoded 5 milliSeconds // chapter 7
|
|
// TWR = WriteCycleTime
|
|
uint32_t waitTime = I2C_WRITEDELAY + _extraTWR * 1000UL; // do the math once.
|
|
while ((micros() - _lastWrite) <= waitTime)
|
|
{
|
|
_wire->beginTransmission(_actualAddress);
|
|
if (_wire->endTransmission() == 0) break;
|
|
yield();
|
|
delayMicroseconds(50);
|
|
}
|
|
|
|
uint16_t memAddr = (memoryAddress & 0xFFFF);
|
|
_wire->beginTransmission(_actualAddress); // device address + bit 16
|
|
_wire->write((memAddr >> 8) & 0xFF); // highByte
|
|
_wire->write(memAddr & 0xFF); // lowByte
|
|
}
|
|
|
|
|
|
// pre: length <= this->_pageSize && length <= I2C_BUFFERSIZE;
|
|
// returns 0 = OK otherwise error
|
|
int I2C_24LC1025::_WriteBlock(uint32_t memoryAddress, const uint8_t * buffer, const uint8_t length)
|
|
{
|
|
_waitEEReady();
|
|
if (_autoWriteProtect)
|
|
{
|
|
digitalWrite(_writeProtectPin, LOW);
|
|
}
|
|
|
|
this->_beginTransmission(memoryAddress);
|
|
_wire->write(buffer, length);
|
|
int rv = _wire->endTransmission();
|
|
|
|
if (_autoWriteProtect)
|
|
{
|
|
digitalWrite(_writeProtectPin, HIGH);
|
|
}
|
|
|
|
_lastWrite = micros();
|
|
|
|
yield(); // For OS scheduling
|
|
|
|
// if (rv != 0)
|
|
// {
|
|
// if (_debug)
|
|
// {
|
|
// Serial.print("mem addr w: ");
|
|
// Serial.print(memoryAddress, HEX);
|
|
// Serial.print("\t");
|
|
// Serial.println(rv);
|
|
// }
|
|
// return -(abs(rv)); // error
|
|
// }
|
|
return rv;
|
|
}
|
|
|
|
|
|
// pre: buffer is large enough to hold length bytes
|
|
// returns bytes read
|
|
uint8_t I2C_24LC1025::_ReadBlock(uint32_t memoryAddress, uint8_t * buffer, const uint8_t length)
|
|
{
|
|
_waitEEReady();
|
|
|
|
// _beginTransmissinon sets actual address !!!
|
|
this->_beginTransmission(memoryAddress);
|
|
int rv = _wire->endTransmission();
|
|
if (rv != 0)
|
|
{
|
|
// if (_debug)
|
|
// {
|
|
// Serial.print("mem addr r: ");
|
|
// Serial.print(memoryAddress, HEX);
|
|
// Serial.print("\t");
|
|
// Serial.println(rv);
|
|
// }
|
|
return 0; // error
|
|
}
|
|
|
|
// readBytes will always be equal or smaller to length
|
|
uint8_t readBytes = _wire->requestFrom(_actualAddress, length);
|
|
yield(); // For OS scheduling
|
|
uint8_t cnt = 0;
|
|
while (cnt < readBytes)
|
|
{
|
|
buffer[cnt++] = _wire->read();
|
|
}
|
|
return readBytes;
|
|
}
|
|
|
|
|
|
void I2C_24LC1025::_waitEEReady()
|
|
{
|
|
// Wait until EEPROM gives ACK again.
|
|
// this is a bit faster than the hardcoded 5 milliSeconds
|
|
// TWR = WriteCycleTime
|
|
uint32_t waitTime = I2C_WRITEDELAY + _extraTWR * 1000UL;
|
|
while ((micros() - _lastWrite) <= waitTime)
|
|
{
|
|
if (isConnected()) return;
|
|
// TODO remove previous code
|
|
// _wire->beginTransmission(_deviceAddress);
|
|
// int x = _wire->endTransmission();
|
|
// if (x == 0) return;
|
|
yield(); // For OS scheduling
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// -- END OF FILE --
|
|
|