diff --git a/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.cpp index e0c466a1f2f..aafc048e6fb 100644 --- a/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.cpp @@ -22,8 +22,10 @@ using namespace mbed; I2CEEBlockDevice::I2CEEBlockDevice( PinName sda, PinName scl, uint8_t addr, - bd_size_t size, bd_size_t block, int freq) - : _i2c_addr(addr), _size(size), _block(block) + bd_size_t size, bd_size_t block, int freq, + bool address_is_eight_bit) + : _i2c_addr(addr), _size(size), _block(block), + _address_is_eight_bit(address_is_eight_bit) { _i2c = new (_i2c_buffer) I2C(sda, scl); _i2c->frequency(freq); @@ -31,8 +33,10 @@ I2CEEBlockDevice::I2CEEBlockDevice( I2CEEBlockDevice::I2CEEBlockDevice( I2C *i2c_obj, uint8_t addr, - bd_size_t size, bd_size_t block) - : _i2c_addr(addr), _size(size), _block(block) + bd_size_t size, bd_size_t block, + bool address_is_eight_bit) + : _i2c_addr(addr), _size(size), _block(block), + _address_is_eight_bit(address_is_eight_bit) { _i2c = i2c_obj; } @@ -58,26 +62,29 @@ int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) // Check the address and size fit onto the chip. MBED_ASSERT(is_valid_read(addr, size)); + auto *pBuffer = static_cast(buffer); + _i2c->start(); - if (!_i2c->write(_i2c_addr | 0) || - !_i2c->write((char)(addr >> 8)) || - !_i2c->write((char)(addr & 0xff))) { + if (1 != _i2c->write(get_paged_device_address(addr))) { return BD_ERROR_DEVICE_ERROR; } - _i2c->stop(); + if (!_address_is_eight_bit && 1 != _i2c->write((char)(addr >> 8u))) { + return BD_ERROR_DEVICE_ERROR; + } - auto err = _sync(); - if (err) { - return err; + if (1 != _i2c->write((char)(addr & 0xffu))) { + return BD_ERROR_DEVICE_ERROR; } - if (0 != _i2c->read(_i2c_addr, static_cast(buffer), size)) { + _i2c->stop(); + + if (0 != _i2c->read(_i2c_addr, pBuffer, size)) { return BD_ERROR_DEVICE_ERROR; } - return 0; + return BD_ERROR_OK; } int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) @@ -85,6 +92,8 @@ int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size // Check the addr and size fit onto the chip. MBED_ASSERT(is_valid_program(addr, size)); + const auto *pBuffer = static_cast(buffer); + // While we have some more data to write. while (size > 0) { uint32_t off = addr % _block; @@ -92,14 +101,22 @@ int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size _i2c->start(); - if (!_i2c->write(_i2c_addr | 0) || - !_i2c->write((char)(addr >> 8)) || - !_i2c->write((char)(addr & 0xff))) { + if (1 != _i2c->write(get_paged_device_address(addr))) { + return BD_ERROR_DEVICE_ERROR; + } + + if (!_address_is_eight_bit && 1 != _i2c->write((char)(addr >> 8u))) { + return BD_ERROR_DEVICE_ERROR; + } + + if (1 != _i2c->write((char)(addr & 0xffu))) { return BD_ERROR_DEVICE_ERROR; } for (unsigned i = 0; i < chunk; i++) { - _i2c->write(static_cast(buffer)[i]); + if (1 != _i2c->write(pBuffer[i])) { + return BD_ERROR_DEVICE_ERROR; + } } _i2c->stop(); @@ -112,10 +129,10 @@ int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size addr += chunk; size -= chunk; - buffer = static_cast(buffer) + chunk; + pBuffer += chunk; } - return 0; + return BD_ERROR_OK; } int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size) @@ -164,3 +181,15 @@ const char *I2CEEBlockDevice::get_type() const { return "I2CEE"; } + +uint8_t I2CEEBlockDevice::get_paged_device_address(bd_addr_t address) +{ + if (!_address_is_eight_bit) { + return _i2c_addr; + } else { + // Use the three least significant bits of the 2nd byte as the page + // The page will be bits 2-4 of the user defined addresses. + return _i2c_addr | ((address & 0x0700u) >> 7u); + } +} + diff --git a/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.h b/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.h index 4ba286676fa..2cf767e8e62 100644 --- a/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.h @@ -59,29 +59,37 @@ class I2CEEBlockDevice : public BlockDevice { public: /** Constructor to create an I2CEEBlockDevice on I2C pins * - * @param sda The pin name for the sda line of the I2C bus. - * @param scl The pin name for the scl line of the I2C bus. - * @param addr The 8bit I2C address of the chip, common range 0xa0 - 0xae. - * @param size The size of the device in bytes - * @param block The page size of the device in bytes, defaults to 32bytes - * @param freq The frequency of the I2C bus, defaults to 400K. + * @param sda The pin name for the sda line of the I2C bus. + * @param scl The pin name for the scl line of the I2C bus. + * @param addr The 8bit I2C address of the chip, common range 0xa0 - 0xae. + * @param size The size of the device in bytes + * @param block The page size of the device in bytes, defaults to 32bytes + * @param freq The frequency of the I2C bus, defaults to 400K. + * @param address_is_eight_bit Specifies whether the EEPROM device is using eight bit + * addresses instead of 16 bit addresses. This is used for example + * in AT24C series chips. */ I2CEEBlockDevice( PinName sda, PinName scl, uint8_t address, bd_size_t size, bd_size_t block = 32, - int bus_speed = 400000); + int bus_speed = 400000, + bool address_is_eight_bit = false); /** Constructor to create an I2CEEBlockDevice on I2C pins - * - * @param i2c The I2C instance pointer - * @param addr The 8bit I2C address of the chip, common range 0xa0 - 0xae. - * @param size The size of the device in bytes - * @param block The page size of the device in bytes, defaults to 32bytes - * @param freq The frequency of the I2C bus, defaults to 400K. - */ + * + * @param i2c The I2C instance pointer + * @param addr The 8bit I2C address of the chip, common range 0xa0 - 0xae. + * @param size The size of the device in bytes + * @param block The page size of the device in bytes, defaults to 32bytes + * @param freq The frequency of the I2C bus, defaults to 400K. + * @param address_is_eight_bit Specifies whether the EEPROM device is using eight bit + * addresses instead of 16 bit addresses. This is used for example + * in AT24C series chips. + */ I2CEEBlockDevice( mbed::I2C *i2c_obj, uint8_t address, - bd_size_t size, bd_size_t block = 32); + bd_size_t size, bd_size_t block = 32, + bool address_is_eight_bit = false); /** Destructor of I2CEEBlockDevice */ @@ -166,10 +174,21 @@ class I2CEEBlockDevice : public BlockDevice { mbed::I2C *_i2c; uint32_t _i2c_buffer[sizeof(mbed::I2C) / sizeof(uint32_t)]; uint8_t _i2c_addr; + bool _address_is_eight_bit; uint32_t _size; uint32_t _block; int _sync(); + + /** + * Gets the device's I2C address with respect to the requested page. + * When eight-bit mode is disabled, this function is a noop. + * When eight-bit mode is enabled, it sets the bits required + * in the devices address. Other bits remain unchanged. + * @param address An address in the requested page. + * @return The device's I2C address for that page + */ + uint8_t get_paged_device_address(bd_addr_t address); };