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
I2CEeprom.cpp@3:b2a132553b02, 2020-03-28 (annotated)
- Committer:
- skyscraper
- Date:
- Sat Mar 28 01:38:00 2020 +0000
- Revision:
- 3:b2a132553b02
- Parent:
- 2:f3188aaccf80
- Child:
- 4:d8f51b136dbd
minor mods and clean up
Who changed what in which revision?
User | Revision | Line number | New 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 | 3:b2a132553b02 | 116 | // if size > 64k I |
skyscraper | 3:b2a132553b02 | 117 | if (m_i2c.write(m_i2cAddress, buffer, size) != 0) { |
skyscraper | 2:f3188aaccf80 | 118 | std::cout << "EE i2c write failed\n"; |
skyscraper | 2:f3188aaccf80 | 119 | return 0; |
rhourahane | 1:b23f5561266c | 120 | } |
skyscraper | 2:f3188aaccf80 | 121 | waitForWrite(); |
rhourahane | 1:b23f5561266c | 122 | return size; |
rhourahane | 1:b23f5561266c | 123 | } |
rhourahane | 1:b23f5561266c | 124 | |
skyscraper | 2:f3188aaccf80 | 125 | // ret num written |
skyscraper | 2:f3188aaccf80 | 126 | size_t I2CEeprom::write(size_t address, const char *buffer, size_t size) |
skyscraper | 2:f3188aaccf80 | 127 | { |
skyscraper | 2:f3188aaccf80 | 128 | if (!checkSpace(address, size)) { |
rhourahane | 1:b23f5561266c | 129 | return 0; |
skyscraper | 2:f3188aaccf80 | 130 | } |
skyscraper | 3:b2a132553b02 | 131 | size_t const malloc_size = std::min(size,m_pageSize) + 2U; |
skyscraper | 3:b2a132553b02 | 132 | char * tempBuffer = new char [malloc_size]; |
skyscraper | 3:b2a132553b02 | 133 | if ( tempBuffer == nullptr){ |
skyscraper | 3:b2a132553b02 | 134 | std::cout << "EE i2c buf malloc failed\n"; |
skyscraper | 3:b2a132553b02 | 135 | return 0; |
skyscraper | 3:b2a132553b02 | 136 | } |
skyscraper | 2:f3188aaccf80 | 137 | size_t bytesLeft = size; |
skyscraper | 2:f3188aaccf80 | 138 | size_t numBytesToWrite |
skyscraper | 3:b2a132553b02 | 139 | = std::min( size, m_pageSize - (address % m_pageSize)); |
skyscraper | 2:f3188aaccf80 | 140 | while(bytesLeft) { |
skyscraper | 3:b2a132553b02 | 141 | convert_address(address,tempBuffer); |
skyscraper | 3:b2a132553b02 | 142 | memcpy(tempBuffer + 2,buffer, numBytesToWrite); |
skyscraper | 3:b2a132553b02 | 143 | if ( ll_write(address,tempBuffer, numBytesToWrite + 2U) |
skyscraper | 3:b2a132553b02 | 144 | == numBytesToWrite + 2U) { |
skyscraper | 2:f3188aaccf80 | 145 | buffer += numBytesToWrite; |
skyscraper | 2:f3188aaccf80 | 146 | address += numBytesToWrite; |
skyscraper | 2:f3188aaccf80 | 147 | bytesLeft -= numBytesToWrite; |
skyscraper | 2:f3188aaccf80 | 148 | numBytesToWrite = std::min(bytesLeft,m_pageSize); |
rhourahane | 1:b23f5561266c | 149 | } else { |
skyscraper | 3:b2a132553b02 | 150 | std::cout << "EE i2c write failed\n"; |
skyscraper | 2:f3188aaccf80 | 151 | break; |
rhourahane | 1:b23f5561266c | 152 | } |
rhourahane | 1:b23f5561266c | 153 | } |
skyscraper | 3:b2a132553b02 | 154 | delete [] tempBuffer; |
skyscraper | 2:f3188aaccf80 | 155 | return size - bytesLeft; |
rhourahane | 1:b23f5561266c | 156 | } |
rhourahane | 1:b23f5561266c | 157 | |
skyscraper | 2:f3188aaccf80 | 158 | void I2CEeprom::waitForWrite() |
skyscraper | 2:f3188aaccf80 | 159 | { |
skyscraper | 3:b2a132553b02 | 160 | |
skyscraper | 2:f3188aaccf80 | 161 | ThisThread::sleep_for(m_writeCycleTime_ms); |
rhourahane | 1:b23f5561266c | 162 | // The chip doesn't ACK while writing to the actual EEPROM |
rhourahane | 1:b23f5561266c | 163 | // so loop trying to do a zero byte write until it is ACKed |
rhourahane | 1:b23f5561266c | 164 | // by the chip. |
rhourahane | 1:b23f5561266c | 165 | while (m_i2c.write(m_i2cAddress, 0, 0) != 0) { |
rhourahane | 1:b23f5561266c | 166 | // Wait for ack. |
skyscraper | 2:f3188aaccf80 | 167 | ThisThread::sleep_for(1); |
rhourahane | 1:b23f5561266c | 168 | } |
rhourahane | 1:b23f5561266c | 169 | } |
rhourahane | 1:b23f5561266c | 170 | |
skyscraper | 2:f3188aaccf80 | 171 | bool I2CEeprom::checkSpace(size_t address, size_t size) |
skyscraper | 2:f3188aaccf80 | 172 | { |
skyscraper | 2:f3188aaccf80 | 173 | // Only check, if chip size is non-zero. |
rhourahane | 1:b23f5561266c | 174 | if (m_chipSize != 0) { |
rhourahane | 1:b23f5561266c | 175 | // Check that the address start in the chip and doesn't |
rhourahane | 1:b23f5561266c | 176 | // extend past. |
rhourahane | 1:b23f5561266c | 177 | if ((address >= m_chipSize) || ((address + size) >= m_chipSize)) |
rhourahane | 1:b23f5561266c | 178 | return false; |
rhourahane | 1:b23f5561266c | 179 | else |
rhourahane | 1:b23f5561266c | 180 | return true; |
rhourahane | 1:b23f5561266c | 181 | } |
skyscraper | 2:f3188aaccf80 | 182 | |
rhourahane | 1:b23f5561266c | 183 | return true; |
rhourahane | 1:b23f5561266c | 184 | } |