ArduinoLibs
|
00001 /* 00002 * Copyright (C) 2012 Southern Storm Software, Pty Ltd. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00020 * DEALINGS IN THE SOFTWARE. 00021 */ 00022 00023 #include "EEPROM24.h" 00024 #include "I2CMaster.h" 00025 00095 EEPROM24::EEPROM24(I2CMaster &bus, unsigned long type, uint8_t bank) 00096 : _bus(&bus) 00097 , _size((type & 0xFFFF) * ((type >> 16) & 0x0FFF)) 00098 , _pageSize((type >> 16) & 0x0FFF) 00099 , _mode((uint8_t)((type >> 28) & 0x0F)) 00100 , i2cAddress(0x50) 00101 { 00102 // Adjust the I2C address for the memory bank of the chip. 00103 switch (_mode) { 00104 case EE_BSEL_NONE: 00105 i2cAddress += (bank & 0x07); 00106 break; 00107 case EE_BSEL_8BIT_ADDR: { 00108 uint8_t addrBits = 8; 00109 unsigned long size = 0x0100; 00110 while (size < _size) { 00111 ++addrBits; 00112 size <<= 1; 00113 } 00114 if (addrBits < 11) 00115 i2cAddress += ((bank << (addrBits - 8)) & 0x07); 00116 break; } 00117 case EE_BSEL_17BIT_ADDR: 00118 i2cAddress += ((bank << 1) & 0x06); 00119 break; 00120 case EE_BSEL_17BIT_ADDR_ALT: 00121 i2cAddress += bank & 0x03; 00122 break; 00123 } 00124 } 00125 00152 bool EEPROM24::available() 00153 { 00154 // Perform a "Current Address Read" on the EEPROM. We don't care about 00155 // the returned byte. We only care if the read request was ACK'ed or not. 00156 if (!_bus->startRead(i2cAddress, 1)) 00157 return false; 00158 _bus->read(); 00159 return true; 00160 } 00161 00167 uint8_t EEPROM24::read(unsigned long address) 00168 { 00169 if (address >= _size) 00170 return 0; 00171 writeAddress(address); 00172 if (!_bus->startRead(i2cAddress, 1)) 00173 return 0; 00174 return _bus->read(); 00175 } 00176 00187 size_t EEPROM24::read(unsigned long address, void *data, size_t length) 00188 { 00189 if (address >= _size || !length) 00190 return 0; 00191 if ((address + length) > _size) 00192 length = (size_t)(_size - address); 00193 writeAddress(address); 00194 if (!_bus->startRead(i2cAddress, length)) 00195 return 0; 00196 uint8_t *d = (uint8_t *)data; 00197 unsigned int count = 0; 00198 while (_bus->available()) { 00199 *d++ = _bus->read(); 00200 ++count; 00201 } 00202 return count; 00203 } 00204 00213 bool EEPROM24::write(unsigned long address, uint8_t value) 00214 { 00215 if (address >= _size) 00216 return false; 00217 writeAddress(address); 00218 _bus->write(value); 00219 return waitForWrite(); 00220 } 00221 00235 size_t EEPROM24::write(unsigned long address, const void *data, size_t length) 00236 { 00237 if (address >= _size) 00238 return 0; 00239 if ((address + length) > _size) 00240 length = (size_t)(_size - address); 00241 bool needAddress = true; 00242 size_t result = 0; 00243 size_t page = 0; 00244 const uint8_t *d = (const uint8_t *)data; 00245 while (length > 0) { 00246 if (needAddress) { 00247 writeAddress(address); 00248 needAddress = false; 00249 } 00250 _bus->write(*d++); 00251 ++address; 00252 ++page; 00253 if ((address & (_pageSize - 1)) == 0) { 00254 // At the end of a page, so perform a flush. 00255 if (!waitForWrite()) 00256 return result; // Could not write this page. 00257 needAddress = true; 00258 result += page; 00259 page = 0; 00260 } 00261 --length; 00262 } 00263 if (!needAddress) { 00264 if (!waitForWrite()) 00265 return result; // Could not write the final page. 00266 } 00267 return result + page; 00268 } 00269 00270 void EEPROM24::writeAddress(unsigned long address) 00271 { 00272 switch (_mode) { 00273 case EE_BSEL_NONE: 00274 _bus->startWrite(i2cAddress); 00275 _bus->write((uint8_t)(address >> 8)); 00276 _bus->write((uint8_t)address); 00277 break; 00278 case EE_BSEL_8BIT_ADDR: 00279 _bus->startWrite(i2cAddress | (((uint8_t)(address >> 8)) & 0x07)); 00280 _bus->write((uint8_t)address); 00281 break; 00282 case EE_BSEL_17BIT_ADDR: 00283 _bus->startWrite(i2cAddress | (((uint8_t)(address >> 16)) & 0x01)); 00284 _bus->write((uint8_t)(address >> 8)); 00285 _bus->write((uint8_t)address); 00286 break; 00287 case EE_BSEL_17BIT_ADDR_ALT: 00288 _bus->startWrite(i2cAddress | (((uint8_t)(address >> 14)) & 0x04)); 00289 _bus->write((uint8_t)(address >> 8)); 00290 _bus->write((uint8_t)address); 00291 break; 00292 } 00293 } 00294 00295 bool EEPROM24::waitForWrite() 00296 { 00297 // 1000 iterations is going to be approximately 100ms when the I2C 00298 // clock is 100 kHz. If there has been no response in that time 00299 // then we assume that the write has failed and timeout. 00300 if (!_bus->endWrite()) 00301 return false; 00302 unsigned count = 1000; 00303 while (count > 0) { 00304 _bus->startWrite(i2cAddress); 00305 if (_bus->endWrite()) 00306 return true; 00307 --count; 00308 } 00309 return false; 00310 }