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 18:03:39 2020 +0000
Revision:
12:d9a44fb3b9a6
Parent:
4:d8f51b136dbd
More documentation tidying and format wand

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 12:d9a44fb3b9a6 25 //return true if system is little-endian
skyscraper 2:f3188aaccf80 26 inline bool little_endian()
skyscraper 2:f3188aaccf80 27 {
skyscraper 2:f3188aaccf80 28 int n = 1;
skyscraper 2:f3188aaccf80 29 // little endian if true
skyscraper 2:f3188aaccf80 30 return(*(char *)&n == 1);
skyscraper 2:f3188aaccf80 31 }
skyscraper 2:f3188aaccf80 32
skyscraper 12:d9a44fb3b9a6 33 // Put EEprom address into 2 char array
skyscraper 12:d9a44fb3b9a6 34 // in corrcet endiannness
skyscraper 2:f3188aaccf80 35 void convert_address(size_t const & addressIn, char* arOut)
skyscraper 2:f3188aaccf80 36 {
skyscraper 2:f3188aaccf80 37 constexpr size_t int_size = sizeof(size_t);
skyscraper 2:f3188aaccf80 38 union uu {
skyscraper 2:f3188aaccf80 39 uu(size_t a) : address{a} {}
skyscraper 2:f3188aaccf80 40 size_t address;
skyscraper 2:f3188aaccf80 41 const char ar[int_size];
skyscraper 2:f3188aaccf80 42 } u{addressIn};
skyscraper 2:f3188aaccf80 43 static_assert(sizeof(u) == int_size,"");
skyscraper 2:f3188aaccf80 44 if( little_endian()) {
skyscraper 2:f3188aaccf80 45 arOut[1] = u.ar[0];
skyscraper 2:f3188aaccf80 46 arOut[0] = u.ar[1];
skyscraper 2:f3188aaccf80 47 } else {
skyscraper 2:f3188aaccf80 48 // std::cout << "big endian\n";
skyscraper 2:f3188aaccf80 49 arOut[1] = u.ar[int_size - 1]; // lsb
skyscraper 2:f3188aaccf80 50 arOut[0] = u.ar[int_size - 2]; //nextsb
skyscraper 2:f3188aaccf80 51 }
skyscraper 2:f3188aaccf80 52 }
skyscraper 2:f3188aaccf80 53 }
skyscraper 2:f3188aaccf80 54
skyscraper 2:f3188aaccf80 55 I2CEeprom::I2CEeprom(
skyscraper 12:d9a44fb3b9a6 56 I2C & i2c,
skyscraper 12:d9a44fb3b9a6 57 int address,
skyscraper 12:d9a44fb3b9a6 58 size_t pageSize,
skyscraper 12:d9a44fb3b9a6 59 size_t chipSize,
skyscraper 12:d9a44fb3b9a6 60 uint8_t writeCycleTime_ms
skyscraper 12:d9a44fb3b9a6 61 ):
skyscraper 2:f3188aaccf80 62 m_i2c{i2c},
rhourahane 1:b23f5561266c 63 m_i2cAddress(address),
rhourahane 1:b23f5561266c 64 m_chipSize(chipSize),
skyscraper 2:f3188aaccf80 65 m_pageSize(pageSize),
skyscraper 2:f3188aaccf80 66 m_writeCycleTime_ms{writeCycleTime_ms}
skyscraper 2:f3188aaccf80 67 {}
skyscraper 2:f3188aaccf80 68
skyscraper 2:f3188aaccf80 69 size_t I2CEeprom::read(size_t address, char &value)
rhourahane 1:b23f5561266c 70 {
skyscraper 12:d9a44fb3b9a6 71 if (checkSpace(address, 1)) {
skyscraper 12:d9a44fb3b9a6 72 char values[2];
skyscraper 12:d9a44fb3b9a6 73 convert_address(address,values);
skyscraper 12:d9a44fb3b9a6 74 if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
skyscraper 12:d9a44fb3b9a6 75 if (m_i2c.read(m_i2cAddress, &value, 1) == 0) {
skyscraper 12:d9a44fb3b9a6 76 return 1;
skyscraper 12:d9a44fb3b9a6 77 }
rhourahane 1:b23f5561266c 78 }
rhourahane 1:b23f5561266c 79 }
rhourahane 1:b23f5561266c 80 return 0;
rhourahane 1:b23f5561266c 81 }
rhourahane 1:b23f5561266c 82
skyscraper 2:f3188aaccf80 83 size_t I2CEeprom::read(size_t address, char *buffer, size_t size)
skyscraper 2:f3188aaccf80 84 {
skyscraper 12:d9a44fb3b9a6 85 if (checkSpace(address, size)) {
skyscraper 12:d9a44fb3b9a6 86 char values[2];
skyscraper 12:d9a44fb3b9a6 87 convert_address(address,values);
skyscraper 12:d9a44fb3b9a6 88 if (m_i2c.write(m_i2cAddress, values, 2) == 0) {
skyscraper 12:d9a44fb3b9a6 89 if (m_i2c.read(m_i2cAddress, buffer, size) == 0) {
skyscraper 12:d9a44fb3b9a6 90 return size;
skyscraper 12:d9a44fb3b9a6 91 }
rhourahane 1:b23f5561266c 92 }
rhourahane 1:b23f5561266c 93 }
rhourahane 1:b23f5561266c 94 return 0;
rhourahane 1:b23f5561266c 95 }
skyscraper 4:d8f51b136dbd 96
skyscraper 2:f3188aaccf80 97 size_t I2CEeprom::write(size_t address, char value)
skyscraper 2:f3188aaccf80 98 {
skyscraper 12:d9a44fb3b9a6 99 if (checkSpace(address, 1)) {
skyscraper 12:d9a44fb3b9a6 100 char values[3];
skyscraper 12:d9a44fb3b9a6 101 convert_address(address,values);
skyscraper 12:d9a44fb3b9a6 102 values[2] = value;
skyscraper 12:d9a44fb3b9a6 103 if (m_i2c.write(m_i2cAddress, values, 3) != 0) {
skyscraper 12:d9a44fb3b9a6 104 return 0;
skyscraper 12:d9a44fb3b9a6 105 }
skyscraper 12:d9a44fb3b9a6 106 waitForWrite();
skyscraper 12:d9a44fb3b9a6 107 return 1;
skyscraper 12:d9a44fb3b9a6 108 } else {
rhourahane 1:b23f5561266c 109 return 0;
rhourahane 1:b23f5561266c 110 }
rhourahane 1:b23f5561266c 111 }
rhourahane 1:b23f5561266c 112
skyscraper 2:f3188aaccf80 113 size_t I2CEeprom::ll_write(size_t address, const char *buffer, size_t size)
skyscraper 2:f3188aaccf80 114 {
skyscraper 12:d9a44fb3b9a6 115 if (m_i2c.write(m_i2cAddress, buffer, size) == 0) {
skyscraper 12:d9a44fb3b9a6 116 waitForWrite();
skyscraper 12:d9a44fb3b9a6 117 return size;
skyscraper 12:d9a44fb3b9a6 118 } else {
skyscraper 2:f3188aaccf80 119 std::cout << "EE i2c write failed\n";
skyscraper 2:f3188aaccf80 120 return 0;
rhourahane 1:b23f5561266c 121 }
rhourahane 1:b23f5561266c 122 }
rhourahane 1:b23f5561266c 123
skyscraper 2:f3188aaccf80 124 size_t I2CEeprom::write(size_t address, const char *buffer, size_t size)
skyscraper 2:f3188aaccf80 125 {
skyscraper 12:d9a44fb3b9a6 126 if (checkSpace(address, size)) {
skyscraper 12:d9a44fb3b9a6 127 size_t const malloc_size = std::min(size,m_pageSize) + 2U;
skyscraper 12:d9a44fb3b9a6 128 char * tempBuffer = new char [malloc_size];
skyscraper 12:d9a44fb3b9a6 129 if ( tempBuffer == nullptr) {
skyscraper 12:d9a44fb3b9a6 130 std::cout << "EE i2c buf malloc failed\n";
skyscraper 12:d9a44fb3b9a6 131 return 0;
skyscraper 12:d9a44fb3b9a6 132 }
skyscraper 12:d9a44fb3b9a6 133 size_t bytesLeft = size;
skyscraper 12:d9a44fb3b9a6 134 size_t numBytesToWrite
skyscraper 12:d9a44fb3b9a6 135 = std::min( size, m_pageSize - (address % m_pageSize));
skyscraper 12:d9a44fb3b9a6 136 while( bytesLeft ) {
skyscraper 12:d9a44fb3b9a6 137 convert_address(address,tempBuffer);
skyscraper 12:d9a44fb3b9a6 138 memcpy(tempBuffer + 2,buffer, numBytesToWrite);
skyscraper 12:d9a44fb3b9a6 139 if ( ll_write(address,tempBuffer, numBytesToWrite + 2U)
skyscraper 12:d9a44fb3b9a6 140 == numBytesToWrite + 2U) {
skyscraper 12:d9a44fb3b9a6 141 buffer += numBytesToWrite;
skyscraper 12:d9a44fb3b9a6 142 address += numBytesToWrite;
skyscraper 12:d9a44fb3b9a6 143 bytesLeft -= numBytesToWrite;
skyscraper 12:d9a44fb3b9a6 144 numBytesToWrite = std::min(bytesLeft,m_pageSize);
skyscraper 12:d9a44fb3b9a6 145 } else {
skyscraper 12:d9a44fb3b9a6 146 std::cout << "EE i2c write failed\n";
skyscraper 12:d9a44fb3b9a6 147 break;
skyscraper 12:d9a44fb3b9a6 148 }
skyscraper 12:d9a44fb3b9a6 149 }
skyscraper 12:d9a44fb3b9a6 150 delete [] tempBuffer;
skyscraper 12:d9a44fb3b9a6 151 return size - bytesLeft;
skyscraper 12:d9a44fb3b9a6 152 } else {
skyscraper 3:b2a132553b02 153 return 0;
skyscraper 3:b2a132553b02 154 }
rhourahane 1:b23f5561266c 155 }
rhourahane 1:b23f5561266c 156
skyscraper 2:f3188aaccf80 157 void I2CEeprom::waitForWrite()
skyscraper 2:f3188aaccf80 158 {
skyscraper 2:f3188aaccf80 159 ThisThread::sleep_for(m_writeCycleTime_ms);
rhourahane 1:b23f5561266c 160 // The chip doesn't ACK while writing to the actual EEPROM
rhourahane 1:b23f5561266c 161 // so loop trying to do a zero byte write until it is ACKed
rhourahane 1:b23f5561266c 162 // by the chip.
rhourahane 1:b23f5561266c 163 while (m_i2c.write(m_i2cAddress, 0, 0) != 0) {
skyscraper 2:f3188aaccf80 164 ThisThread::sleep_for(1);
rhourahane 1:b23f5561266c 165 }
rhourahane 1:b23f5561266c 166 }