-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Changes from 9 commits
cd34860
8d1978e
52aed22
e850984
267d8cc
76a177f
7c589ae
532f378
3741440
4d0c346
440fa49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
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; | ||
} | ||
|
@@ -58,48 +62,76 @@ 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)); | ||
|
||
_i2c->start(); | ||
auto *pBuffer = reinterpret_cast<char *>(buffer); | ||
boomer41 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (!_i2c->write(_i2c_addr | 0) || | ||
!_i2c->write((char)(addr >> 8)) || | ||
!_i2c->write((char)(addr & 0xff))) { | ||
return BD_ERROR_DEVICE_ERROR; | ||
} | ||
while (size > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure that the read operation have to be paged for some devices? If tried with AT24MAC and read operation as simple as: 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 = reinterpret_cast<char *>(buffer);
_i2c->start();
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;
}
_i2c->stop();
if (0 != _i2c->read(_i2c_addr, pBuffer, size)) {
return BD_ERROR_DEVICE_ERROR;
}
return BD_ERROR_OK;
} And it works.. For me, it seems that only the write operation have to be single byte or full page, but no more. Read operation can be as large as the whole device. This seems to be true for at least AT24C and Microchip 24XX devices:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my experience, I've never seen paging limitation on reads. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately i can't check because I only have the 2k variants. |
||
uint32_t off = addr % _block; | ||
uint32_t chunk = (off + size < _block) ? size : (_block - off); | ||
|
||
_i2c->start(); | ||
|
||
_i2c->stop(); | ||
if (1 != _i2c->write(get_paged_device_address(addr))) { | ||
return BD_ERROR_DEVICE_ERROR; | ||
} | ||
|
||
auto err = _sync(); | ||
if (err) { | ||
return err; | ||
} | ||
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; | ||
} | ||
|
||
_i2c->stop(); | ||
|
||
auto err = _sync(); | ||
|
||
if (err) { | ||
return err; | ||
} | ||
|
||
if (0 != _i2c->read(_i2c_addr, pBuffer, chunk)) { | ||
return BD_ERROR_DEVICE_ERROR; | ||
} | ||
|
||
if (0 != _i2c->read(_i2c_addr, static_cast<char *>(buffer), size)) { | ||
return BD_ERROR_DEVICE_ERROR; | ||
addr += chunk; | ||
size -= chunk; | ||
pBuffer += chunk; | ||
} | ||
|
||
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)); | ||
|
||
auto const *pBuffer = reinterpret_cast<char const *>(buffer); | ||
boomer41 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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(); | ||
|
@@ -112,10 +144,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) | ||
|
@@ -164,3 +196,14 @@ const char *I2CEEBlockDevice::get_type() const | |
{ | ||
return "I2CEE"; | ||
} | ||
|
||
uint8_t I2CEEBlockDevice::get_paged_device_address(const bd_addr_t &address) | ||
{ | ||
if (!this->_address_is_eight_bit) { | ||
return this->_i2c_addr; | ||
boomer41 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} 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 this->_i2c_addr | ((address & 0x0700u) >> 7u); | ||
} | ||
} | ||
boomer41 marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
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 toI2C
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.
There was a problem hiding this comment.
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.