Skip to content

storage: Add the I2C EEPROM block device #3564

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions features/filesystem/i2cee/I2CEEBlockDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/* Simple access class for I2C EEPROM chips like Microchip 24LC
* Copyright (c) 2015 Robin Hourahane
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "I2CEEBlockDevice.h"

#define I2CEE_TIMEOUT 10000


I2CEEBlockDevice::I2CEEBlockDevice(
PinName sda, PinName scl, uint8_t addr,
bd_size_t size, bd_size_t block, int freq)
: _i2c(sda, scl), _i2c_addr(addr), _size(size), _block(block)
{
_i2c.frequency(freq);
}

bd_error_t I2CEEBlockDevice::init()
{
return _sync();
}

bd_error_t I2CEEBlockDevice::deinit()
{
return 0;
}

bd_error_t I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
{
// Check the address and size fit onto the chip.
if (!is_valid_read(addr, size)) {
return BD_ERROR_PARAMETER;
}

_i2c.start();
if (!_i2c.write(_i2c_addr | 0) ||
!_i2c.write((char)(addr >> 8)) ||
!_i2c.write((char)(addr & 0xff))) {
return BD_ERROR_DEVICE_ERROR;
}
_i2c.stop();

if (_i2c.read(_i2c_addr, static_cast<char*>(buffer), size) < 0) {
return BD_ERROR_DEVICE_ERROR;
}

return 0;
}

bd_error_t I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
{
// Check the addr and size fit onto the chip.
if (!is_valid_program(addr, size)) {
return BD_ERROR_PARAMETER;
}

// While we have some more data to write.
while (size > 0) {
_i2c.start();
if (!_i2c.write(_i2c_addr | 0) ||
!_i2c.write((char)(addr >> 8)) ||
!_i2c.write((char)(addr & 0xff))) {
return BD_ERROR_DEVICE_ERROR;
}

for (unsigned i = 0; i < _block; i++) {
_i2c.write(static_cast<const char*>(buffer)[i]);
}
_i2c.stop();

bd_error_t err = _sync();
if (err) {
return err;
}

addr += _block;
size -= _block;
buffer = static_cast<const char*>(buffer) + _block;
}

return 0;
}

bd_error_t I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size)
{
// No erase needed
return 0;
}

bd_error_t I2CEEBlockDevice::_sync()
{
// The chip doesn't ACK while writing to the actual EEPROM
// so loop trying to do a zero byte write until it is ACKed
// by the chip.
for (int i = 0; i < I2CEE_TIMEOUT; i++) {
if (_i2c.write(_i2c_addr | 0, 0, 0) < 1) {
return 0;
}

wait_ms(1);
}

return BD_ERROR_DEVICE_ERROR;
}

bd_size_t I2CEEBlockDevice::get_read_size()
{
return 1;
}

bd_size_t I2CEEBlockDevice::get_program_size()
{
return _block;
}

bd_size_t I2CEEBlockDevice::get_erase_size()
{
return _block;
}

bd_size_t I2CEEBlockDevice::size()
{
return _size;
}
149 changes: 149 additions & 0 deletions features/filesystem/i2cee/I2CEEBlockDevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* Simple access class for I2C EEPROM chips like Microchip 24LC
* Copyright (c) 2015 Robin Hourahane
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_I2CEEPROM_BLOCK_DEVICE_H
#define MBED_I2CEEPROM_BLOCK_DEVICE_H

/* If the target has no I2C support then I2CEEPROM is not supported */
#ifdef DEVICE_I2C

#include <mbed.h>
#include "BlockDevice.h"


/** BlockDevice for I2C based flash device such as
* Microchip's 24LC or ATMEL's AT24C ranges
*
* @code
* #include "mbed.h"
* #include "I2CEEBlockDevice.h"
*
* // Create 24LC device with 32Kbytes of memory
* I2CEEBlockDevice flash(D14, D15, 0xa0, 32*1024);
*
* int main() {
* printf("flash test\n");
* mx52r.init();
* printf("flash size: %llu\n", flash.size());
* printf("flash read size: %llu\n", flash.get_read_size());
* printf("flash program size: %llu\n", flash.get_program_size());
* printf("flash erase size: %llu\n", flash.get_erase_size());
*
* uint8_t *buffer = malloc(flash.get_erase_size());
* sprintf(buffer, "Hello World!\n");
* flash.erase(0, flash.get_erase_size());
* flash.write(buffer, 0, flash.get_erase_size());
* flash.read(buffer, 0, flash.get_erase_size());
* printf("%s", buffer);
*
* flash.deinit();
* }
*/
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 block size of the device in bytes, defaults to 32bytes
* @param freq The frequency of the I2C bus, defaults to 400K.
*/
I2CEEBlockDevice(
PinName sda, PinName scl, uint8_t address,
bd_size_t size, bd_size_t block=32,
int bus_speed=400000);

/** Initialize a block device
*
* @return 0 on success or a negative error code on failure
*/
virtual bd_error_t init();

/** Deinitialize a block device
*
* @return 0 on success or a negative error code on failure
*/
virtual bd_error_t deinit();

/** Read blocks from a block device
*
* @param buffer Buffer to write blocks to
* @param addr Address of block to begin reading from
* @param size Size to read in bytes, must be a multiple of read block size
* @return 0 on success, negative error code on failure
*/
virtual bd_error_t read(void *buffer, bd_addr_t addr, bd_size_t size);

/** Program blocks to a block device
*
* The blocks must have been erased prior to being programmed
*
* @param buffer Buffer of data to write to blocks
* @param addr Address of block to begin writing to
* @param size Size to write in bytes, must be a multiple of program block size
* @return 0 on success, negative error code on failure
*/
virtual bd_error_t program(const void *buffer, bd_addr_t addr, bd_size_t size);

/** Erase blocks on a block device
*
* The state of an erased block is undefined until it has been programmed
*
* @param addr Address of block to begin erasing
* @param size Size to erase in bytes, must be a multiple of erase block size
* @return 0 on success, negative error code on failure
*/
virtual bd_error_t erase(bd_addr_t addr, bd_size_t size);

/** Get the size of a readable block
*
* @return Size of a readable block in bytes
*/
virtual bd_size_t get_read_size();

/** Get the size of a programable block
*
* @return Size of a programable block in bytes
* @note Must be a multiple of the read size
*/
virtual bd_size_t get_program_size();

/** Get the size of a eraseable block
*
* @return Size of a eraseable block in bytes
* @note Must be a multiple of the program size
*/
virtual bd_size_t get_erase_size();

/** Get the total size of the underlying device
*
* @return Size of the underlying device in bytes
*/
virtual bd_size_t size();

private:
I2C _i2c;
uint8_t _i2c_addr;
uint32_t _size;
uint32_t _block;

bd_error_t _sync();
};

#endif /* DEVICE_SPI */

#endif /* MBED_SD_BLOCK_DEVICE_H */
Loading