Class to provide simple access to I2C EEPROM chiles like Microchip's 24LC range or AMTELS AT24C range. Chips up to 64Kb in size are directly supported. Updated to Mbed OS v 5.1

Dependents:   storage_test

Committer:
skyscraper
Date:
Sat Mar 28 00:53:14 2020 +0000
Revision:
2:f3188aaccf80
Parent:
1:b23f5561266c
Child:
3:b2a132553b02
modify eeprom for working with threads

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rhourahane 1:b23f5561266c 1 /* Simple access class for I2C EEPROM chips like Microchip 24LC
rhourahane 1:b23f5561266c 2 * Copyright (c) 2015 Robin Hourahane
rhourahane 1:b23f5561266c 3 *
rhourahane 1:b23f5561266c 4 * Licensed under the Apache License, Version 2.0 (the "License");
rhourahane 1:b23f5561266c 5 * you may not use this file except in compliance with the License.
rhourahane 1:b23f5561266c 6 * You may obtain a copy of the License at
rhourahane 1:b23f5561266c 7 *
rhourahane 1:b23f5561266c 8 * http://www.apache.org/licenses/LICENSE-2.0
rhourahane 1:b23f5561266c 9 *
rhourahane 1:b23f5561266c 10 * Unless required by applicable law or agreed to in writing, software
rhourahane 1:b23f5561266c 11 * distributed under the License is distributed on an "AS IS" BASIS,
rhourahane 1:b23f5561266c 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rhourahane 1:b23f5561266c 13 * See the License for the specific language governing permissions and
rhourahane 1:b23f5561266c 14 * limitations under the License.
rhourahane 1:b23f5561266c 15 */
skyscraper 2:f3188aaccf80 16
skyscraper 2:f3188aaccf80 17 #include <iostream>
skyscraper 2:f3188aaccf80 18 #include <algorithm>
skyscraper 2:f3188aaccf80 19
rhourahane 1:b23f5561266c 20 #include "I2CEeprom.h"
rhourahane 1:b23f5561266c 21
skyscraper 2:f3188aaccf80 22 namespace
skyscraper 2:f3188aaccf80 23 {
skyscraper 2:f3188aaccf80 24
skyscraper 2:f3188aaccf80 25 inline bool little_endian()
skyscraper 2:f3188aaccf80 26 {
skyscraper 2:f3188aaccf80 27 int n = 1;
skyscraper 2:f3188aaccf80 28 // little endian if true
skyscraper 2:f3188aaccf80 29 return(*(char *)&n == 1);
skyscraper 2:f3188aaccf80 30 }
skyscraper 2:f3188aaccf80 31
skyscraper 2:f3188aaccf80 32 void convert_address(size_t const & addressIn, char* arOut)
skyscraper 2:f3188aaccf80 33 {
skyscraper 2:f3188aaccf80 34 constexpr size_t int_size = sizeof(size_t);
skyscraper 2:f3188aaccf80 35 union uu {
skyscraper 2:f3188aaccf80 36 uu(size_t a) : address{a} {}
skyscraper 2:f3188aaccf80 37 size_t address;
skyscraper 2:f3188aaccf80 38 const char ar[int_size];
skyscraper 2:f3188aaccf80 39 } u{addressIn};
skyscraper 2:f3188aaccf80 40 static_assert(sizeof(u) == int_size,"");
skyscraper 2:f3188aaccf80 41 if( little_endian()) {
skyscraper 2:f3188aaccf80 42 arOut[1] = u.ar[0];
skyscraper 2:f3188aaccf80 43 arOut[0] = u.ar[1];
skyscraper 2:f3188aaccf80 44 } else {
skyscraper 2:f3188aaccf80 45 // std::cout << "big endian\n";
skyscraper 2:f3188aaccf80 46 arOut[1] = u.ar[int_size - 1]; // lsb
skyscraper 2:f3188aaccf80 47 arOut[0] = u.ar[int_size - 2]; //nextsb
skyscraper 2:f3188aaccf80 48 }
skyscraper 2:f3188aaccf80 49 }
skyscraper 2:f3188aaccf80 50 }
skyscraper 2:f3188aaccf80 51
skyscraper 2:f3188aaccf80 52 I2CEeprom::I2CEeprom(
skyscraper 2:f3188aaccf80 53 I2C & i2c,
skyscraper 2:f3188aaccf80 54 int address,
skyscraper 2:f3188aaccf80 55 size_t pageSize,
skyscraper 2:f3188aaccf80 56 size_t chipSize,
skyscraper 2:f3188aaccf80 57 uint8_t writeCycleTime_ms
skyscraper 2:f3188aaccf80 58 ):
skyscraper 2:f3188aaccf80 59 m_i2c{i2c},
rhourahane 1:b23f5561266c 60 m_i2cAddress(address),
rhourahane 1:b23f5561266c 61 m_chipSize(chipSize),
skyscraper 2:f3188aaccf80 62 m_pageSize(pageSize),
skyscraper 2:f3188aaccf80 63 m_writeCycleTime_ms{writeCycleTime_ms}
skyscraper 2:f3188aaccf80 64 {}
skyscraper 2:f3188aaccf80 65
skyscraper 2:f3188aaccf80 66 size_t I2CEeprom::read(size_t address, char &value)
rhourahane 1:b23f5561266c 67 {
rhourahane 1:b23f5561266c 68 // Check the address and size fit onto the chip.
rhourahane 1:b23f5561266c 69 if (!checkSpace(address, 1))
rhourahane 1:b23f5561266c 70 return 0;
skyscraper 2:f3188aaccf80 71 char values[2];
skyscraper 2:f3188aaccf80 72 convert_address(address,values);
rhourahane 1:b23f5561266c 73 if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
rhourahane 1:b23f5561266c 74 if (m_i2c.read(m_i2cAddress, &value, 1) == 0) {
rhourahane 1:b23f5561266c 75 return 1;
rhourahane 1:b23f5561266c 76 }
rhourahane 1:b23f5561266c 77 }
rhourahane 1:b23f5561266c 78 return 0;
rhourahane 1:b23f5561266c 79 }
rhourahane 1:b23f5561266c 80
skyscraper 2:f3188aaccf80 81 size_t I2CEeprom::read(size_t address, char *buffer, size_t size)
skyscraper 2:f3188aaccf80 82 {
rhourahane 1:b23f5561266c 83 // Check the address and size fit onto the chip.
rhourahane 1:b23f5561266c 84 if (!checkSpace(address, size))
rhourahane 1:b23f5561266c 85 return 0;
skyscraper 2:f3188aaccf80 86 char values[2];
skyscraper 2:f3188aaccf80 87 convert_address(address,values);
rhourahane 1:b23f5561266c 88 if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
rhourahane 1:b23f5561266c 89 if (m_i2c.read(m_i2cAddress, buffer, size) == 0) {
rhourahane 1:b23f5561266c 90 return size;
rhourahane 1:b23f5561266c 91 }
rhourahane 1:b23f5561266c 92 }
rhourahane 1:b23f5561266c 93 return 0;
rhourahane 1:b23f5561266c 94 }
skyscraper 2:f3188aaccf80 95 // works ok
skyscraper 2:f3188aaccf80 96 size_t I2CEeprom::write(size_t address, char value)
skyscraper 2:f3188aaccf80 97 {
rhourahane 1:b23f5561266c 98 // Check the address and size fit onto the chip.
rhourahane 1:b23f5561266c 99 if (!checkSpace(address, 1))
rhourahane 1:b23f5561266c 100 return 0;
skyscraper 2:f3188aaccf80 101 char values[3];
skyscraper 2:f3188aaccf80 102 convert_address(address,values);
skyscraper 2:f3188aaccf80 103 values[2] = value;
rhourahane 1:b23f5561266c 104 if (m_i2c.write(m_i2cAddress, values, 3) != 0) {
rhourahane 1:b23f5561266c 105 return 0;
rhourahane 1:b23f5561266c 106 }
skyscraper 2:f3188aaccf80 107
rhourahane 1:b23f5561266c 108 waitForWrite();
rhourahane 1:b23f5561266c 109 return 1;
rhourahane 1:b23f5561266c 110 }
rhourahane 1:b23f5561266c 111
skyscraper 2:f3188aaccf80 112
skyscraper 2:f3188aaccf80 113 size_t I2CEeprom::ll_write(size_t address, const char *buffer, size_t size)
skyscraper 2:f3188aaccf80 114 {
skyscraper 2:f3188aaccf80 115 // TODO: change i2c address bits dependent on eeprom data address
skyscraper 2:f3188aaccf80 116 // if size > 64k
skyscraper 2:f3188aaccf80 117 // write 2 bytes ee address + data size
skyscraper 2:f3188aaccf80 118 char* tempBuffer = new char [size + 2];
skyscraper 2:f3188aaccf80 119 if ( tempBuffer == nullptr) {
skyscraper 2:f3188aaccf80 120 delete [] tempBuffer;
skyscraper 2:f3188aaccf80 121 std::cout << "EE i2c write malloc buf failed \n";
rhourahane 1:b23f5561266c 122 return 0;
skyscraper 2:f3188aaccf80 123 }
skyscraper 2:f3188aaccf80 124 convert_address(address,tempBuffer);
skyscraper 2:f3188aaccf80 125 memcpy(tempBuffer+2,buffer,size);
rhourahane 1:b23f5561266c 126
skyscraper 2:f3188aaccf80 127 if (m_i2c.write(m_i2cAddress, tempBuffer, size+2) != 0) {
skyscraper 2:f3188aaccf80 128 delete [] tempBuffer;
skyscraper 2:f3188aaccf80 129 std::cout << "EE i2c write failed\n";
skyscraper 2:f3188aaccf80 130 return 0;
rhourahane 1:b23f5561266c 131 }
skyscraper 2:f3188aaccf80 132 delete [] tempBuffer;
skyscraper 2:f3188aaccf80 133 waitForWrite();
rhourahane 1:b23f5561266c 134 return size;
rhourahane 1:b23f5561266c 135 }
rhourahane 1:b23f5561266c 136
skyscraper 2:f3188aaccf80 137 // ret num written
skyscraper 2:f3188aaccf80 138 size_t I2CEeprom::write(size_t address, const char *buffer, size_t size)
skyscraper 2:f3188aaccf80 139 {
rhourahane 1:b23f5561266c 140 // Check the address and size fit onto the chip.
skyscraper 2:f3188aaccf80 141 if (!checkSpace(address, size)) {
rhourahane 1:b23f5561266c 142 return 0;
skyscraper 2:f3188aaccf80 143 }
skyscraper 2:f3188aaccf80 144 size_t bytesLeft = size;
skyscraper 2:f3188aaccf80 145 size_t numBytesToWrite
skyscraper 2:f3188aaccf80 146 = std::min( size, m_pageSize - (address % m_pageSize));
skyscraper 2:f3188aaccf80 147 while(bytesLeft) {
skyscraper 2:f3188aaccf80 148 if ( ll_write(address,buffer, numBytesToWrite)
skyscraper 2:f3188aaccf80 149 == numBytesToWrite) {
skyscraper 2:f3188aaccf80 150 buffer += numBytesToWrite;
skyscraper 2:f3188aaccf80 151 address += numBytesToWrite;
skyscraper 2:f3188aaccf80 152 bytesLeft -= numBytesToWrite;
skyscraper 2:f3188aaccf80 153 numBytesToWrite = std::min(bytesLeft,m_pageSize);
rhourahane 1:b23f5561266c 154 } else {
skyscraper 2:f3188aaccf80 155 break;
rhourahane 1:b23f5561266c 156 }
rhourahane 1:b23f5561266c 157 }
skyscraper 2:f3188aaccf80 158 return size - bytesLeft;
rhourahane 1:b23f5561266c 159 }
rhourahane 1:b23f5561266c 160
skyscraper 2:f3188aaccf80 161 void I2CEeprom::waitForWrite()
skyscraper 2:f3188aaccf80 162 {
skyscraper 2:f3188aaccf80 163 ThisThread::sleep_for(m_writeCycleTime_ms);
rhourahane 1:b23f5561266c 164 // The chip doesn't ACK while writing to the actual EEPROM
rhourahane 1:b23f5561266c 165 // so loop trying to do a zero byte write until it is ACKed
rhourahane 1:b23f5561266c 166 // by the chip.
rhourahane 1:b23f5561266c 167 while (m_i2c.write(m_i2cAddress, 0, 0) != 0) {
rhourahane 1:b23f5561266c 168 // Wait for ack.
skyscraper 2:f3188aaccf80 169 ThisThread::sleep_for(1);
rhourahane 1:b23f5561266c 170 }
rhourahane 1:b23f5561266c 171 }
rhourahane 1:b23f5561266c 172
skyscraper 2:f3188aaccf80 173 bool I2CEeprom::checkSpace(size_t address, size_t size)
skyscraper 2:f3188aaccf80 174 {
skyscraper 2:f3188aaccf80 175 // Only check, if chip size is non-zero.
rhourahane 1:b23f5561266c 176 if (m_chipSize != 0) {
rhourahane 1:b23f5561266c 177 // Check that the address start in the chip and doesn't
rhourahane 1:b23f5561266c 178 // extend past.
rhourahane 1:b23f5561266c 179 if ((address >= m_chipSize) || ((address + size) >= m_chipSize))
rhourahane 1:b23f5561266c 180 return false;
rhourahane 1:b23f5561266c 181 else
rhourahane 1:b23f5561266c 182 return true;
rhourahane 1:b23f5561266c 183 }
skyscraper 2:f3188aaccf80 184
rhourahane 1:b23f5561266c 185 return true;
rhourahane 1:b23f5561266c 186 }