Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Eight-Bit-Addressing mode to I2CEEBlockDevice. #12446

Merged
merged 11 commits into from
Feb 21, 2020
67 changes: 48 additions & 19 deletions components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,21 @@ 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't directly relate to this PR, but this driver should now really have a constructor that takes const i2c_pinmap_t & and passes to I2C to offer the possibility to avoid pulling in pinmap tables.

It would be nice to add this, but I guess as a follow-up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this should be a separate PR as it actually has nothing to do with the 8bit-mode.

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);
}

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;
}
Expand All @@ -58,48 +62,61 @@ 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<char *>(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<char *>(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)
{
// Check the addr and size fit onto the chip.
MBED_ASSERT(is_valid_program(addr, size));

const auto *pBuffer = static_cast<const char *>(buffer);

// While we have some more data to write.
while (size > 0) {
uint32_t off = addr % _block;
uint32_t chunk = (off + size < _block) ? size : (_block - off);

_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<const char *>(buffer)[i]);
if (1 != _i2c->write(pBuffer[i])) {
return BD_ERROR_DEVICE_ERROR;
}
}

_i2c->stop();
Expand All @@ -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<const char *>(buffer) + chunk;
pBuffer += chunk;
}

return 0;
return BD_ERROR_OK;
}

int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size)
Expand Down Expand Up @@ -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);
}
}

Copy link
Contributor

@kjbracey kjbracey Feb 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you've gone overboard and now have an excess blank line :)

(Any Git diff should show both cases in red normally).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I didn't know where you wanted the NL.
Where exactly did you want the NL?
Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible I'm being misled by GitHub's view, but I think originally you had an unterminated final } line. I think you went to the bottom and pressed "Enter" twice rather than once, so you now have an excess blank line.

49 changes: 34 additions & 15 deletions components/storage/blockdevice/COMPONENT_I2CEE/I2CEEBlockDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
};


Expand Down