-
Notifications
You must be signed in to change notification settings - Fork 3k
Add Eight-Bit-Addressing mode to I2CEEBlockDevice. #12446
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
Changes from 7 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,64 +62,99 @@ 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 *charBuffer = reinterpret_cast<char *>(buffer); | ||
|
|
||
| if (!_i2c->write(_i2c_addr | 0) || | ||
| !_i2c->write((char)(addr >> 8)) || | ||
| !_i2c->write((char)(addr & 0xff))) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
| auto const handler = [&](const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &pagedDeviceAddress) -> int { | ||
| _i2c->start(); | ||
|
|
||
| _i2c->stop(); | ||
| if (1 != _i2c->write(pagedDeviceAddress)) | ||
| { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| auto err = _sync(); | ||
| if (err) { | ||
| return err; | ||
| } | ||
| if (!_address_is_eight_bit && 1 != _i2c->write((char)(pagedStart >> 8u))) | ||
| { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| if (0 != _i2c->read(_i2c_addr, static_cast<char *>(buffer), size)) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
| if (1 != _i2c->write((char)(pagedStart & 0xffu))) | ||
| { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| return 0; | ||
| _i2c->stop(); | ||
|
|
||
| auto err = _sync(); | ||
| if (err) | ||
| { | ||
| return err; | ||
| } | ||
|
|
||
| if (0 != _i2c->read(_i2c_addr, charBuffer, pagedLength)) | ||
| { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| charBuffer += size; | ||
|
|
||
| return BD_ERROR_OK; | ||
| }; | ||
|
|
||
| return do_paged(addr, size, handler); | ||
| } | ||
|
|
||
| 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)); | ||
|
|
||
| // 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); | ||
| auto const *charBuffer = reinterpret_cast<char const *>(buffer); | ||
|
|
||
| _i2c->start(); | ||
| auto const handler = [&](const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &pagedDeviceAddress) -> int { | ||
| // 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); | ||
|
|
||
| if (!_i2c->write(_i2c_addr | 0) || | ||
| !_i2c->write((char)(addr >> 8)) || | ||
| !_i2c->write((char)(addr & 0xff))) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
| _i2c->start(); | ||
|
|
||
| for (unsigned i = 0; i < chunk; i++) { | ||
| _i2c->write(static_cast<const char *>(buffer)[i]); | ||
| } | ||
| if (1 != _i2c->write(pagedDeviceAddress)) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| _i2c->stop(); | ||
| if (!_address_is_eight_bit && 1 != _i2c->write((char)(pagedStart >> 8u))) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| int err = _sync(); | ||
| if (1 != _i2c->write((char)(addr & 0xffu))) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
|
|
||
| if (err) { | ||
| return err; | ||
| for (unsigned i = 0; i < chunk; i++) { | ||
| if (1 != _i2c->write(charBuffer[i])) { | ||
| return BD_ERROR_DEVICE_ERROR; | ||
| } | ||
| } | ||
|
|
||
| _i2c->stop(); | ||
|
|
||
| int err = _sync(); | ||
|
|
||
| if (err) { | ||
| return err; | ||
| } | ||
|
|
||
| addr += chunk; | ||
| size -= chunk; | ||
| charBuffer += chunk; | ||
| } | ||
|
|
||
| addr += chunk; | ||
| size -= chunk; | ||
| buffer = static_cast<const char *>(buffer) + chunk; | ||
| } | ||
| return BD_ERROR_OK; | ||
| }; | ||
|
|
||
| return 0; | ||
| auto const originalSize = size; | ||
| return do_paged(addr, originalSize, handler); | ||
| } | ||
|
|
||
| int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size) | ||
|
|
@@ -164,3 +203,69 @@ const char *I2CEEBlockDevice::get_type() const | |
| { | ||
| return "I2CEE"; | ||
| } | ||
|
|
||
| int I2CEEBlockDevice::do_paged(const bd_addr_t &startAddress, | ||
| const bd_size_t &length, | ||
| const paged_handler &handler) | ||
| { | ||
| // This helper is only used for eight bit mode. | ||
| if (!this->_address_is_eight_bit) { | ||
| return handler(startAddress, length, get_paged_device_address(0)); | ||
| } | ||
|
|
||
| auto currentStartAddress = startAddress; | ||
|
|
||
| auto const pageSize = 256; | ||
| bd_size_t lengthDone = 0; | ||
| while (lengthDone != length) { | ||
| /* Integer division => Round down */ | ||
| uint8_t const currentPage = currentStartAddress / pageSize; | ||
| bd_addr_t const nextPageBegin = (currentPage + 1) * pageSize; | ||
| bd_addr_t const currentReadEndAddressExclusive = std::min(nextPageBegin, startAddress + length); | ||
| bd_size_t const currentLength = currentReadEndAddressExclusive - currentStartAddress; | ||
| bd_addr_t const pagedBegin = currentStartAddress - (currentPage * pageSize); | ||
| uint8_t const pagedDeviceAddress = get_paged_device_address(currentPage); | ||
|
|
||
| auto const handlerReturn = handler(pagedBegin, currentLength, pagedDeviceAddress); | ||
| if (handlerReturn != BD_ERROR_OK) { | ||
| return handlerReturn; | ||
| } | ||
|
|
||
| currentStartAddress = currentReadEndAddressExclusive; | ||
| lengthDone += currentLength; | ||
| } | ||
|
|
||
| return BD_ERROR_OK; | ||
| } | ||
|
|
||
|
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. Think you've gone overboard and now have an excess blank line :) (Any Git diff should show both cases in red normally). 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. Oops, I didn't know where you wanted the NL. 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. It's possible I'm being misled by GitHub's view, but I think originally you had an unterminated final |
||
| uint8_t I2CEEBlockDevice::get_paged_device_address(const uint8_t &page) | ||
| { | ||
| if (!this->_address_is_eight_bit) { | ||
| return this->_i2c_addr; | ||
boomer41 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } else { | ||
| // This method uses a dynamically created bit mask for the page given. | ||
| // This ensures compatibility with all sizes of ICs. | ||
| // E. g. the 512K variants have two user address bits and one page bit. | ||
| // We don't want to forcefully override the two user address bits. | ||
|
|
||
| // Create a mask to cover all bits required to set page | ||
| // i starts at one because the LSB is used for R/W in I2C | ||
| uint8_t i = 1; | ||
| uint8_t addressMask = 0; | ||
| auto p = page; | ||
| while (p != 0u) { | ||
| addressMask |= (1u << i); | ||
| p >>= 1u; | ||
| i++; | ||
| } | ||
|
|
||
| uint8_t pagedDeviceAddress = this->_i2c_addr & static_cast<uint8_t>(~addressMask); | ||
| // Assert page < 0b111, because we don't have | ||
| // more bits for page encoding | ||
| // Don't actually write 0b111, this is a nonstandard extension. | ||
| MBED_ASSERT(page < 0x7); | ||
| pagedDeviceAddress |= static_cast<uint8_t>(page << 1u); | ||
|
|
||
| return pagedDeviceAddress; | ||
| } | ||
| } | ||
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 toI2Cto 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.