├── .gitattributes ├── .github └── workflows │ └── add_issue_to_project.yml ├── ISSUE_TEMPLATE.md ├── keywords.txt ├── library.properties ├── examples ├── Example2_AdvancedI2C │ └── Example2_AdvancedI2C.ino ├── Example4_DetectSettings │ └── Example4_DetectSettings.ino ├── Example5_CustomSettings │ └── Example5_CustomSettings.ino ├── Example1_BasicReadWrite │ └── Example1_BasicReadWrite.ino ├── Example7_AutoDetectionTest │ └── Example7_AutoDetectionTest.ino ├── Example3_SettingsStruc │ └── Example3_SettingsStruc.ino ├── Example9_SaveChangedSettings │ └── Example9_SaveChangedSettings.ino ├── Example6_UniversalProgrammer │ └── Example6_UniversalProgrammer.ino └── Example8_InterfaceTest │ └── Example8_InterfaceTest.ino ├── LICENSE.md ├── README.md └── src ├── SparkFun_External_EEPROM.h └── SparkFun_External_EEPROM.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/add_issue_to_project.yml: -------------------------------------------------------------------------------- 1 | name: Add new issue to our main project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@main 14 | with: 15 | # You can target a project in a different organization 16 | # to the issue 17 | project-url: https://github.com/orgs/sparkfun/projects/19 18 | github-token: ${{ secrets.DEFECT_ADD_TO_PROJECT }} 19 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Subject of the issue 2 | Describe your issue here. If you reference a datasheet please specify which one and in which section (ie, the protocol manual, section 5.1.2). Additionally, screenshots are easy to paste into github. 3 | 4 | ### Your workbench 5 | * What development board or microcontroller are you using? 6 | * What version of hardware or breakout board are you using? 7 | * How is the breakout board wired to your microcontroller? 8 | * How is everything being powered? 9 | * Are there any additional details that may help us help you? 10 | 11 | ### Steps to reproduce 12 | Tell us how to reproduce this issue. Please post stripped down example code demonstrating your issue. 13 | 14 | ### Expected behavior 15 | Tell us what should happen 16 | 17 | ### Actual behavior 18 | Tell us what happens instead 19 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ExternalEEPROM KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | isConnected KEYWORD2 17 | isBusy KEYWORD2 18 | read KEYWORD2 19 | write KEYWORD2 20 | erase KEYWORD2 21 | setMemorySize KEYWORD2 22 | getMemorySize KEYWORD2 23 | setMemoryType KEYWORD2 24 | length KEYWORD2 25 | setPageSize KEYWORD2 26 | getPageSize KEYWORD2 27 | setPageWriteTime KEYWORD2 28 | getPageWriteTime KEYWORD2 29 | enablePollForWriteComplete KEYWORD2 30 | disablePollForWriteComplete KEYWORD2 31 | get KEYWORD2 32 | put KEYWORD2 33 | setI2CBufferSize KEYWORD2 34 | getI2CBufferSize KEYWORD2 35 | putString KEYWORD2 36 | getString KEYWORD2 37 | 38 | ####################################### 39 | # Constants (LITERAL1) 40 | ####################################### 41 | 42 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SparkFun External EEPROM Arduino Library 2 | version=3.2.12 3 | author=SparkFun Electronics 4 | maintainer=SparkFun Electronics 5 | sentence=Library for I2C Communication with external EEPROMs 6 | paragraph=A library for the advanced control of any I2C based EEPROM. This library writes extremely fast and automatically handles the writing of pages to make the entire EEPROM act as one large writable block. Automatically reads and writes ints, floats, arrays, and structs. Works with all EEPROMs including very large >512kbit EEPROMs and any Wire port (Wire1, etc). Use with Qwiic EEPROM to make it easy to read and write cal data and other user settings. Configurable I2C buffer size increases throughput up to 30kB/s. Includes examples demonstrating wear-level writing, SD file to EEPROM write, and auto writes/verifies to EEPROM. Tested with 24LC00, 24LC01B, 24AA02, 24LC04B, 24LC16BH, 24LC32A, 24LC256, CAT24C512, 24LC1025, and 24LC1026. 7 | category=Data Storage 8 | url=https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library 9 | architectures=* 10 | -------------------------------------------------------------------------------- /examples/Example2_AdvancedI2C/Example2_AdvancedI2C.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Read and write settings and calibration data to an external I2C EEPROM 3 | By: Nathan Seidle 4 | SparkFun Electronics 5 | Date: December 11th, 2019 6 | License: This code is public domain but you buy me a beer if you use this 7 | and we meet someday (Beerware license). 8 | Feel like supporting our work? Buy a board from SparkFun! 9 | https://www.sparkfun.com/products/18355 10 | 11 | This example demonstrates how to pass a custom EEPROM address and Wire. 12 | 13 | This library supports EEPROMs with any I2C address and 14 | any Wire hardware (Wire1, Wire2, etc) by passing them into begin. 15 | 16 | For this example, the I2C EEPROM should have all its ADR0 to VCC, 17 | ADR1 to GND, and ADR2 to GND. 18 | 19 | Hardware Connections: 20 | I used an Artemis for this example. Make sure to connect the PTH of Qwiic EEPROM to the pins of the seconday I2C bus. 21 | 22 | pin 0 on Artemis RedBoard = SDA on Qwiic EEPROM 23 | pin 6 = SCL 24 | GND to GND 25 | 3.3V to 3.3V 26 | 27 | Load this sketch 28 | Open output window at 115200bps 29 | */ 30 | 31 | #include 32 | 33 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 34 | ExternalEEPROM myMem; 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | //delay(250); //Often needed for ESP based platforms 40 | Serial.println("Qwiic EEPROM example"); 41 | 42 | // Wire1.setClock(400000); //set I2C communication to 400kHz 43 | Wire1.begin(); 44 | 45 | // Default to the Qwiic 24xx512 EEPROM: https://www.sparkfun.com/products/18355 46 | myMem.setMemoryType(512); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 47 | 48 | #define EEPROM_ADDRESS 0b1010001 //0b1010(A2 A1 A0): A standard I2C EEPROM with the ADR0 bit set to VCC 49 | 50 | //Connect to a EEPROM with ADR0 set to VCC and use the Wire1 hardware to talk to the EEPROM 51 | if (myMem.begin(EEPROM_ADDRESS, Wire1) == false) //And Uno will fail to compile here 52 | { 53 | Serial.println("No memory detected. Freezing."); 54 | while (true) 55 | ; 56 | } 57 | Serial.println("Memory detected!"); 58 | 59 | float myValue3 = -7.35; 60 | myMem.put(20, myValue3); //(location, data) 61 | float myRead3; 62 | myMem.get(20, myRead3); //location to read, thing to put data into 63 | Serial.print("I read: "); 64 | Serial.println(myRead3); 65 | } 66 | 67 | void loop() 68 | { 69 | 70 | } 71 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | SparkFun License Information 2 | ============================ 3 | 4 | SparkFun uses two different licenses for our files — one for hardware and one for code. 5 | 6 | Hardware 7 | --------- 8 | 9 | **SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).** 10 | 11 | Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode). 12 | 13 | You are free to: 14 | 15 | Share — copy and redistribute the material in any medium or format 16 | Adapt — remix, transform, and build upon the material 17 | for any purpose, even commercially. 18 | The licensor cannot revoke these freedoms as long as you follow the license terms. 19 | Under the following terms: 20 | 21 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 22 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 23 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 24 | Notices: 25 | 26 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 27 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 28 | 29 | 30 | Code 31 | -------- 32 | 33 | **SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).** 34 | 35 | The MIT License (MIT) 36 | 37 | Copyright (c) 2016 SparkFun Electronics 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy 40 | of this software and associated documentation files (the "Software"), to deal 41 | in the Software without restriction, including without limitation the rights 42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | copies of the Software, and to permit persons to whom the Software is 44 | furnished to do so, subject to the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | -------------------------------------------------------------------------------- /examples/Example4_DetectSettings/Example4_DetectSettings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Automatic size detection of EEPROM 3 | Author: Nathan Seidle 4 | Created: June 23, 2023 5 | License: Lemonadeware. Buy me a lemonade (or other) someday. 6 | 7 | This sketch demonstrates how to detect memory size, address bytes, 8 | page size, and write time. 9 | 10 | Known/compatible memory types (basically all I2C EEPROMs): 11 | 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page size 12 | 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page size 13 | 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page size 14 | 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page size 15 | 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page size 16 | 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page size 17 | 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page size 18 | 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page size 19 | 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page size 20 | 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page size 21 | 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page size 22 | 24xx1025 - 1024000 bit / 128000 byte - 2 address bytes, 128 byte page size 23 | 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page size 24 | */ 25 | 26 | #include 27 | 28 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 29 | ExternalEEPROM myMem; 30 | 31 | void setup() 32 | { 33 | Serial.begin(115200); 34 | Serial.println("Qwiic EEPROM example"); 35 | 36 | Wire.begin(); 37 | 38 | //We don't need to specify the memory specs before begin(). We're just looking 39 | //for a I2C device to ACK. 40 | 41 | if (myMem.begin() == false) 42 | { 43 | Serial.println("No memory detected. Freezing."); 44 | while (1) 45 | ; 46 | } 47 | Serial.println("Memory detected!"); 48 | 49 | Serial.print("Detected number of address bytes: "); 50 | Serial.println(myMem.detectAddressBytes()); 51 | 52 | //Page size detection is limited by the platform. For example, the Uno has an I2C buffer 53 | //that is 32 bytes. Therefore, page sizes above 16 bytes cannot be detected or used. For maximum 54 | //write speeds to an EEPROM, use a platform with a large I2C buffer (ie ESP32 is 128 bytes) 55 | //and an EEPROM with a large page size (24XX512 is 128 bytes). 56 | Serial.print("Detected pageSizeBytes: "); 57 | Serial.println(myMem.detectPageSizeBytes()); 58 | 59 | //The EEPROM write time is 5 ms for all EEPROMs currently manufactured. 60 | //Automatically detecting the write time is therefore not generally needed, 61 | //but it's here if needed. 62 | Serial.print("Detected page write time (ms): "); 63 | Serial.println(myMem.detectWriteTimeMs()); 64 | 65 | uint32_t eepromSizeBytes = myMem.detectMemorySizeBytes(); 66 | Serial.print("Detected EEPROM size (bytes): "); 67 | Serial.print(eepromSizeBytes); 68 | Serial.print(" bytes / "); 69 | if (eepromSizeBytes < 128) 70 | { 71 | Serial.print(eepromSizeBytes * 8); 72 | Serial.print(" Bits"); 73 | } 74 | else 75 | { 76 | Serial.print(eepromSizeBytes * 8 / 1024); 77 | Serial.print(" kBits"); 78 | } 79 | Serial.print(" - 24XX"); 80 | if (eepromSizeBytes == 16) 81 | Serial.print("00"); 82 | else 83 | { 84 | if ((eepromSizeBytes * 8 / 1024) < 10) Serial.print("0"); 85 | Serial.print(eepromSizeBytes * 8 / 1024); 86 | } 87 | Serial.println(); 88 | } 89 | 90 | void loop() 91 | { 92 | } -------------------------------------------------------------------------------- /examples/Example5_CustomSettings/Example5_CustomSettings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Explicitly tell the library the size of the EEPROM, page size, and address bytes 3 | Author: Nathan Seidle 4 | Created: June 23, 2023 5 | License: Lemonadeware. Buy me a lemonade (or other) someday. 6 | 7 | This sketch demonstrates how to detect memory size, address bytes, 8 | page size, and write time. You can also pre-assign these specs to avoid 9 | the start up delay caused by the detection routines. 10 | 11 | Known/compatible memory types (basically all I2C EEPROMs): 12 | 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page size 13 | 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page size 14 | 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page size 15 | 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page size 16 | 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page size 17 | 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page size 18 | 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page size 19 | 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page size 20 | 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page size 21 | 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page size 22 | 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page size 23 | 24xx1025 - 1024000 bit / 128000 byte - 2 address bytes, 128 byte page size 24 | 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page size 25 | */ 26 | 27 | #include 28 | 29 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 30 | ExternalEEPROM myMem; 31 | 32 | void setup() 33 | { 34 | Serial.begin(115200); 35 | Serial.println("Qwiic EEPROM example"); 36 | 37 | Wire.begin(); 38 | 39 | //Explicitly set the address bytes, page size, and memory size for this EEPROM 40 | // 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page 41 | myMem.setMemorySizeBytes(65536); //This function will set the AddressBytes and PageSize. 42 | myMem.setAddressBytes(2); //Call these functions after MemorySizeBytes. Only needed if you have a very unique EEPROM with odd Address Bytes and Page Sizes. 43 | myMem.setPageSizeBytes(128); 44 | 45 | if (myMem.begin() == false) 46 | { 47 | Serial.println("No memory detected. Freezing."); 48 | while (1) 49 | ; 50 | } 51 | Serial.println("Memory detected!"); 52 | 53 | Serial.print("Mem size in bytes: "); 54 | Serial.println(myMem.length()); 55 | 56 | //Yes you can read and write bytes, but you shouldn't! 57 | byte myValue1 = 200; 58 | myMem.write(0, myValue1); //(location, data) 59 | 60 | byte myRead1 = myMem.read(0); 61 | Serial.print("I read (should be 200): "); 62 | Serial.println(myRead1); 63 | 64 | //You should use gets and puts. This will automatically and correctly arrange 65 | //the bytes for larger variable types. 66 | int myValue2 = -366; 67 | myMem.put(10, myValue2); //(location, data) 68 | int myRead2; 69 | myMem.get(10, myRead2); //location to read, thing to put data into 70 | Serial.print("I read (should be -366): "); 71 | Serial.println(myRead2); 72 | 73 | float myValue3 = -7.35; 74 | myMem.put(20, myValue3); //(location, data) 75 | float myRead3; 76 | myMem.get(20, myRead3); //location to read, thing to put data into 77 | Serial.print("I read (should be -7.35): "); 78 | Serial.println(myRead3); 79 | 80 | String myString = "Hi, I am just a simple test string"; 81 | unsigned long nextEEPROMLocation = myMem.putString(30, myString); 82 | String myRead4 = ""; 83 | myMem.getString(30, myRead4); 84 | Serial.print("I read: "); 85 | Serial.println(myRead4); 86 | Serial.print("Next available EEPROM location: "); 87 | Serial.println(nextEEPROMLocation); 88 | } 89 | 90 | void loop() 91 | { 92 | } -------------------------------------------------------------------------------- /examples/Example1_BasicReadWrite/Example1_BasicReadWrite.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Read and write settings and calibration data to an external I2C EEPROM 3 | By: Nathan Seidle 4 | SparkFun Electronics 5 | Date: December 11th, 2019 6 | License: This code is public domain but you buy me a beer if you use this 7 | and we meet someday (Beerware license). 8 | Feel like supporting our work? Buy a board from SparkFun! 9 | https://www.sparkfun.com/products/18355 10 | 11 | This example demonstrates how to read and write various variables to memory. 12 | 13 | The I2C EEPROM should have all its ADR pins set to GND (0). This is default 14 | on the Qwiic board. 15 | 16 | Hardware Connections: 17 | Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped board 18 | Load this sketch 19 | Open output window at 115200bps 20 | */ 21 | 22 | #include 23 | 24 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 25 | ExternalEEPROM myMem; 26 | 27 | void setup() 28 | { 29 | Serial.begin(115200); 30 | //delay(250); //Often needed for ESP based platforms 31 | Serial.println("Qwiic EEPROM example"); 32 | 33 | Wire.begin(); 34 | 35 | //We must set the memory specs. Pick your EEPROM From the list: 36 | 37 | // 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page 38 | // 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page 39 | // 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page 40 | // 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page 41 | // 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page 42 | // 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page 43 | // 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page 44 | // 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page 45 | // 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page 46 | // 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page 47 | // 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page 48 | // 24xx1025 - 1024000 bit / 128000 byte - 2 address byte, 128 byte page 49 | // 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page 50 | 51 | // Setting the memory type configures the memory size in bytes, the number of address bytes, and the page size in bytes. 52 | 53 | // Default to the Qwiic 24xx512 EEPROM: https://www.sparkfun.com/products/18355 54 | myMem.setMemoryType(512); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 55 | 56 | if (myMem.begin() == false) 57 | { 58 | Serial.println("No memory detected. Freezing."); 59 | while (true) 60 | ; 61 | } 62 | Serial.println("Memory detected!"); 63 | 64 | Serial.print("Mem size in bytes: "); 65 | Serial.println(myMem.length()); 66 | 67 | //Yes you can read and write bytes, but you shouldn't! 68 | byte myValue1 = 200; 69 | myMem.write(0, myValue1); //(location, data) 70 | 71 | byte myRead1 = myMem.read(0); 72 | Serial.print("I read (should be 200): "); 73 | Serial.println(myRead1); 74 | 75 | //You should use gets and puts. This will automatically and correctly arrange 76 | //the bytes for larger variable types. 77 | int myValue2 = -366; 78 | myMem.put(10, myValue2); //(location, data) 79 | int myRead2; 80 | myMem.get(10, myRead2); //location to read, thing to put data into 81 | Serial.print("I read (should be -366): "); 82 | Serial.println(myRead2); 83 | 84 | float myValue3 = -7.35; 85 | myMem.put(20, myValue3); //(location, data) 86 | float myRead3; 87 | myMem.get(20, myRead3); //location to read, thing to put data into 88 | Serial.print("I read (should be -7.35): "); 89 | Serial.println(myRead3); 90 | 91 | String myString = "Hi, I am just a simple test string"; 92 | unsigned long nextEEPROMLocation = myMem.putString(30, myString); 93 | String myRead4 = ""; 94 | myMem.getString(30, myRead4); 95 | Serial.print("I read: "); 96 | Serial.println(myRead4); 97 | Serial.print("Next available EEPROM location: "); 98 | Serial.println(nextEEPROMLocation); 99 | } 100 | 101 | void loop() 102 | { 103 | } -------------------------------------------------------------------------------- /examples/Example7_AutoDetectionTest/Example7_AutoDetectionTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Automatic size detection of EEPROM 3 | Author: Nathan Seidle 4 | Created: June 23, 2023 5 | License: Lemonadeware. Buy me a lemonade (or other) someday. 6 | 7 | This sketch demonstrates how to detect memory size, address bytes, 8 | page size, and write time. You can also pre-assign these specs to avoid 9 | the start up delay caused by the detection routines. 10 | 11 | Known/compatible memory types (basically all I2C EEPROMs): 12 | 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page size 13 | 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page size 14 | 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page size 15 | 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page size 16 | 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page size 17 | 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page size 18 | 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page size 19 | 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page size 20 | 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page size 21 | 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page size 22 | 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page size 23 | 24xx1025 - 1024000 bit / 128000 byte - 2 address bytes, 128 byte page size 24 | 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page size 25 | */ 26 | 27 | #include 28 | 29 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 30 | ExternalEEPROM myMem; 31 | 32 | void setup() 33 | { 34 | Serial.begin(115200); 35 | Serial.println("Qwiic EEPROM example"); 36 | 37 | Wire.begin(); 38 | 39 | //randomSeed(analogRead(A0) * analogRead(A1)); 40 | 41 | if (myMem.begin() == false) 42 | { 43 | Serial.println("No memory detected. Freezing."); 44 | while (1) 45 | ; 46 | } 47 | Serial.println("Memory detected!"); 48 | 49 | // myMem.write(127, 25); //location, thing 50 | // myMem.write(129, 36); //location, thing 51 | // 52 | // uint8_t myVal1 = myMem.read(127); 53 | // Serial.print("myVal1: "); 54 | // Serial.println(myVal1); 55 | // 56 | // uint8_t myVal2 = myMem.read(129); 57 | // Serial.print("myVal2: "); 58 | // Serial.println(myVal2); 59 | // 60 | // while (1); 61 | 62 | //Write a series of bytes then test to see if auto-detect changes them 63 | //The detection functions *should not* modify the data on the EEPROM 64 | 65 | int maxBytes = 300; //Ideally we write this many bytes 66 | if (maxBytes > myMem.getMemorySizeBytes()) 67 | maxBytes = myMem.getMemorySizeBytes(); 68 | 69 | for (int x = 0 ; x < maxBytes ; x++) 70 | myMem.write(x, (uint8_t)('0' + x)); 71 | 72 | // Serial.print("Detected number of address bytes: "); 73 | //Serial.println(myMem.detectAddressBytes()); 74 | 75 | Serial.print("Detected pageSizeBytes: "); 76 | Serial.println(myMem.detectPageSizeBytes()); 77 | 78 | // Serial.print("Detected page write time (ms): "); 79 | // Serial.println(myMem.detectWriteTimeMs()); 80 | 81 | //uint32_t eepromSizeBytes = myMem.detectMemorySizeBytes(); 82 | uint32_t eepromSizeBytes = myMem.getMemorySizeBytes(); 83 | Serial.print("Detected EEPROM size (bytes): "); 84 | Serial.print(eepromSizeBytes); 85 | Serial.print(" bytes / "); 86 | if (eepromSizeBytes < 128) 87 | { 88 | Serial.print(eepromSizeBytes * 8); 89 | Serial.print(" Bits"); 90 | } 91 | else 92 | { 93 | Serial.print(eepromSizeBytes * 8 / 1024); 94 | Serial.print(" kBits"); 95 | } 96 | Serial.print(" - 24XX"); 97 | if (eepromSizeBytes == 16) 98 | Serial.print("00"); 99 | else 100 | { 101 | if ((eepromSizeBytes * 8 / 1024) < 10) Serial.print("0"); 102 | Serial.print(eepromSizeBytes * 8 / 1024); 103 | } 104 | Serial.println(); 105 | 106 | Serial.print("Checking memory: "); 107 | bool allTestsPassed = true; 108 | for (int x = 0 ; x < maxBytes ; x++) 109 | { 110 | byte readValue = myMem.read(x); 111 | if (readValue != (uint8_t)('0' + x)) 112 | { 113 | Serial.print("Error detected in location: "); 114 | Serial.print(x); 115 | allTestsPassed = false; 116 | break; 117 | } 118 | } 119 | 120 | if (allTestsPassed == true) 121 | Serial.print("Good"); 122 | Serial.println(); 123 | 124 | //Do basic read/write tests 125 | byte myValue1 = 200; 126 | myMem.write(0, myValue1); //(location, data) 127 | 128 | byte myRead1 = myMem.read(0); 129 | Serial.print("I read (should be 200): "); 130 | Serial.println(myRead1); 131 | 132 | int myValue2 = -366; 133 | myMem.put(10, myValue2); //(location, data) 134 | int myRead2; 135 | myMem.get(10, myRead2); //location to read, thing to put data into 136 | Serial.print("I read (should be -366): "); 137 | Serial.println(myRead2); 138 | 139 | if (myMem.getMemorySizeBytes() > 16) 140 | { 141 | float myValue3 = -7.35; 142 | myMem.put(20, myValue3); //(location, data) 143 | float myRead3; 144 | myMem.get(20, myRead3); //location to read, thing to put data into 145 | Serial.print("I read (should be -7.35): "); 146 | Serial.println(myRead3); 147 | 148 | String myString = "Hi, I am just a simple test string"; //34 characters 149 | unsigned long nextEEPROMLocation = myMem.putString(70, myString); 150 | 151 | String myRead4 = ""; 152 | myMem.getString(70, myRead4); 153 | Serial.print("I read: "); 154 | Serial.println(myRead4); 155 | Serial.print("Next available EEPROM location: "); 156 | Serial.println(nextEEPROMLocation); 157 | } 158 | } 159 | 160 | void loop() 161 | { 162 | } -------------------------------------------------------------------------------- /examples/Example3_SettingsStruc/Example3_SettingsStruc.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Read and write settings and calibration data to an external I2C EEPROM 3 | By: Nathan Seidle 4 | SparkFun Electronics 5 | Date: December 11th, 2019 6 | License: This code is public domain but you buy me a beer if you use this 7 | and we meet someday (Beerware license). 8 | Feel like supporting our work? Buy a board from SparkFun! 9 | https://www.sparkfun.com/products/18355 10 | 11 | This example demonstrates how to record various user settings easily to EEPROM. 12 | 13 | The I2C EEPROM should have all its ADR pins set to GND (0). This is default 14 | on the Qwiic board. 15 | 16 | Hardware Connections: 17 | Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped board 18 | Load this sketch 19 | Open output window at 115200bps 20 | */ 21 | 22 | #include 23 | 24 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 25 | ExternalEEPROM myMem; 26 | 27 | #define LOCATION_SETTINGS 0 //Position in EEPROM to store the user setting structure 28 | 29 | //This is the struct that contains all the user settings. Add as many vars as you want. 30 | struct struct_userSettings { 31 | unsigned long baudRate; 32 | bool logDate; 33 | bool enableIMU; 34 | float calValue; 35 | }; 36 | 37 | //These are the default settings for each variable. They will be written if the EEPROM is blank. 38 | struct_userSettings settings = { 39 | .baudRate = 115200, 40 | .logDate = false, 41 | .enableIMU = true, 42 | .calValue = -5.17, 43 | }; 44 | 45 | void setup() 46 | { 47 | Serial.begin(115200); 48 | //delay(250); //Often needed for ESP based platforms 49 | Serial.println(F("Qwiic EEPROM example")); 50 | 51 | Wire.begin(); 52 | 53 | // Default to the Qwiic 24xx512 EEPROM: https://www.sparkfun.com/products/18355 54 | myMem.setMemoryType(512); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 55 | 56 | if (myMem.begin() == false) 57 | { 58 | Serial.println(F("No memory detected. Freezing.")); 59 | while (true); 60 | } 61 | Serial.println(F("Memory detected!")); 62 | 63 | Serial.print("Size of user settings (bytes): "); 64 | Serial.println(sizeof(settings)); 65 | 66 | loadUserSettings(); 67 | 68 | Serial.print("Baud rate: "); 69 | Serial.println(settings.baudRate); 70 | 71 | Serial.print("logDate: "); 72 | if (settings.logDate == true) Serial.println("True"); 73 | else Serial.println("False"); 74 | 75 | Serial.print("calValue: "); 76 | Serial.println(settings.calValue, 2); 77 | 78 | //Now we can change something 79 | settings.baudRate = 57600; 80 | 81 | //Now we can save it 82 | recordUserSettings(); 83 | 84 | //And we never have to worry about byte alignment or EEPROM locations! 85 | 86 | Serial.println("Press any key to get menu"); 87 | } 88 | 89 | void loop() 90 | { 91 | if (Serial.available()) mainMenu(); //Present user menu 92 | } 93 | 94 | void mainMenu() 95 | { 96 | while (1) 97 | { 98 | Serial.println(); 99 | Serial.println("Menu: Main Menu"); 100 | 101 | Serial.println("1) Set Baud Rate"); 102 | Serial.println("x) Exit"); 103 | 104 | byte incoming = getByteChoice(); //Timeout after 10 seconds 105 | 106 | if (incoming == '1') 107 | { 108 | Serial.print("Enter baud rate (1200 to 115200): "); 109 | int newBaud = getNumber(); 110 | if (newBaud < 1200 || newBaud > 115200) 111 | { 112 | Serial.println("Error: baud rate out of range"); 113 | } 114 | else 115 | { 116 | settings.baudRate = newBaud; 117 | recordUserSettings(); 118 | } 119 | } 120 | else if (incoming == 'x') 121 | break; 122 | else 123 | { 124 | Serial.print("Unknown choice: "); 125 | Serial.write(incoming); 126 | Serial.println(); 127 | } 128 | } 129 | } 130 | 131 | //Blocking wait for user input 132 | void waitForInput() 133 | { 134 | delay(10); //Wait for any incoming chars to hit buffer 135 | while (Serial.available() > 0) Serial.read(); //Clear buffer 136 | while (Serial.available() == 0); 137 | } 138 | 139 | //Get single byte from user 140 | //Waits for and returns the character that the user provides 141 | byte getByteChoice() 142 | { 143 | waitForInput(); //Wait for user to send a value 144 | return (Serial.read()); 145 | } 146 | 147 | //Get a string/value from user, remove all non-numeric values 148 | int getNumber() 149 | { 150 | waitForInput(); //Wait for user to send a value 151 | 152 | //Get input from user 153 | char cleansed[20]; 154 | 155 | int spot = 0; 156 | while (spot < 20 - 1) //Leave room for terminating \0 157 | { 158 | while (Serial.available() == 0) ; //Wait for user input 159 | 160 | byte incoming = Serial.read(); 161 | if (incoming == '\n' || incoming == '\r') 162 | { 163 | Serial.println(); 164 | break; 165 | } 166 | 167 | if (isDigit(incoming) == true) 168 | { 169 | Serial.write(incoming); //Echo user's typing 170 | cleansed[spot++] = (char)incoming; 171 | } 172 | } 173 | 174 | cleansed[spot] = '\0'; 175 | 176 | String tempValue = cleansed; 177 | return (tempValue.toInt()); 178 | } 179 | 180 | 181 | //Load the current settings from EEPROM into the settings struct 182 | void loadUserSettings() 183 | { 184 | //Uncomment these lines to forcibly erase the EEPROM and see how the defaults are set 185 | //Serial.println("Erasing EEPROM"); 186 | //myMem.erase(); 187 | 188 | //Check to see if EEPROM is blank. If the first four spots are zeros then we can assume the EEPROM is blank. 189 | uint32_t testRead = 0; 190 | if (myMem.get(LOCATION_SETTINGS, testRead) == 0) //EEPROM address to read, thing to read into 191 | { 192 | //At power on, settings are set to defaults within the struct. 193 | //So go record the struct as it currently exists so that defaults are set. 194 | recordUserSettings(); 195 | 196 | Serial.println("Default settings applied"); 197 | } 198 | else 199 | { 200 | //Read current settings 201 | myMem.get(LOCATION_SETTINGS, settings); 202 | } 203 | } 204 | 205 | //Record the current settings into EEPROM 206 | void recordUserSettings() 207 | { 208 | myMem.put(LOCATION_SETTINGS, settings); //That' 209 | } 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SparkFun External EEPROM Arduino Library 2 | =========================================================== 3 | 4 | ![SparkFun Qwiic EEPROM](https://cdn.sparkfun.com//assets/parts/1/7/7/0/1/18355-SparkFun_Qwiic_EEPROM_Breakout_-_512Kbit-01.jpg) 5 | 6 | [*SparkFun Qwiic EEPROM (COM-18355)*](https://www.sparkfun.com/products/18355) 7 | 8 | A simple-to-use I2C library for talking to any EEPROM. It uses the same template system found in the Arduino EEPROM library so you can use the same get() and put() functions. 9 | 10 | Various external EEPROMs have various interface specs (overall size, page size, write times, etc). This library works with all types and allows the various settings to be set at runtime. All read and write restrictions associated with pages are taken care of. You can access the external memory as if it was contiguous. 11 | 12 | Once the library has been started, the memory type needs to be set, the following is an example for the [Qwiic 24xx512 EEPROM](https://www.sparkfun.com/products/18355): 13 | 14 | myMem.setMemoryType(512); 15 | 16 | Where *512* is the model (ie, 24LC**512**). Setting the memory type configures the memory size in bytes, the number of address bytes, and the page size in bytes. The following memory types are valid: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 17 | 18 | * **0** - 24xx00 / ie [24LC00](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC00%20-%20128.pdf) 19 | * **1** - 24xx01 / ie [24LC01B](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC01%20-%201k.pdf) 20 | * **2** - 24xx02 / ie [24LC02B](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC02%20-%202k.pdf) 21 | * **4** - 24xx04 / ie [CAT24C04](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC04%20-%204k%20Onsemi.PDF) 22 | * **8** - 24xx08 / ie [BR24G08](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC08%20-%208k%20Rohm.pdf) 23 | * **16** - 24xx16 / ie [24AA16](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC16%20-%2016k.pdf) 24 | * **32** - 24xx32 / ie [24LC32A](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC32%20-%2032k.pdf) 25 | * **64** - 24xx64 / ie [24FC64](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC64%20-%2064k.pdf) 26 | * **128** - 24xx128 / ie [24LC128](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC128-%20128k.pdf) 27 | * **256** - 24xx256 / ie [24AA256](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC256%20-%20256k.pdf) 28 | * **512** - 24xx512 / ie [24C512C](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC512%20-%20512k.pdf) 29 | * **1025** - 24xx1025 / ie [24LC1025](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC1024%20-%201Mbit.pdf) 30 | * **2048** - 24xx2048 / ie [AT24CM02](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs/blob/main/24LC2048%20-%202Mbit.pdf) 31 | 32 | For a list of all the EEPROM datasheets, please see [this repo](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library_Docs). We don't want to store the PDFs in the library repo, otherwise, every user will have to download all the PDFs just to install the library. 33 | 34 | Alternatively, the individual settings can be set. If setMemorySizeBytes/setAddressBytes/setPageSizeBytes() are called, they will overwrite any previous settings set by `setMemoryType()`. 35 | 36 | myMem.setMemorySizeBytes(65536); 37 | myMem.setAddressBytes(2); // Set address bytes and page size after MemorySizeBytes() 38 | myMem.setPageSizeBytes(128); 39 | 40 | Set the memory type, or set the memory settings, but not both. 41 | 42 | This library is best used with the [Qwiic EEPROM](https://www.sparkfun.com/products/18355). 43 | 44 | This library can be installed via the Arduino Library manager. Search for **SparkFun External EEPROM**. 45 | 46 | Want to help? Please do! We are always looking for ways to improve and build out features of this library. 47 | 48 | Thanks to: 49 | 50 | * [Hutch67](https://github.com/Hutch67) for correcting the [test for pollForWriteComplete setting](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/4/files) 51 | * [TylerBird](https://github.com/TylerBird) for correcting [ambiguous int declaration](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/7) and adding STM32 support 52 | * [quarcko](https://github.com/quarcko) for [small EEPROM support](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/11) (24LC02). 53 | * [robsonos](https://github.com/robsonos) adding for [nRF52 support](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/12). 54 | * [RayNieport](https://github.com/RayNieport) for adding [RP2040 support](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/14). 55 | * [wolfbert](https://github.com/wolfbert) for adding [ESP8266 support](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/15). 56 | * [romain145](https://github.com/romain145) for fixing a [multi-page write](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/18) bug. 57 | * [wollewald](https://github.com/wollewald) for [read/write strings](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/21). 58 | * [giminotron5](https://github.com/giminotron5) for adding [write protect control](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/30). 59 | * [merlinz01](https://github.com/merlinz01) for adding [putChanged()](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/38) method 60 | * [clavisound](https://github.com/clavisound) and [marshfolx](https://github.com/marshfolx) for adding [SAMD E](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/43) and [STM32Duino](https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/pull/46) buffer definitions 61 | 62 | Repository Contents 63 | ------------------- 64 | 65 | * **/examples** - Example sketches for the library (.ino). Run these from the Arduino IDE. 66 | * **/src** - Source files for the library (.cpp, .h). 67 | * **keywords.txt** - Keywords from this library that will be highlighted in the Arduino IDE. 68 | * **library.properties** - General library properties for the Arduino package manager. 69 | 70 | Documentation 71 | -------------- 72 | 73 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library. 74 | 75 | License Information 76 | ------------------- 77 | 78 | This product is _**open source**_! 79 | 80 | Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! 81 | 82 | Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license. 83 | 84 | Distributed as-is; no warranty is given. 85 | 86 | - Your friends at SparkFun. 87 | -------------------------------------------------------------------------------- /examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Read and write data to an external I2C EEPROM 3 | By: Nathan Seidle, Merlin Z 4 | SparkFun Electronics 5 | Date: July 11, 2024 6 | License: This code is public domain but you buy me a coffee if you use this 7 | and we meet someday (Beerware license). 8 | Feel like supporting our work? Buy a board from SparkFun! 9 | https://www.sparkfun.com/products/18355 10 | 11 | This example demonstrates how to record various user settings easily to 12 | EEPROM, only writing settings that changed in order to preserve the life of 13 | the memory. Use the ExternalEEPROM::putChanged() method instead of 14 | External_EEPROM::put() to do this. 15 | 16 | The I2C EEPROM should have all its ADR pins set to GND (0). This is default 17 | on the Qwiic board. 18 | 19 | Hardware Connections: 20 | Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped 21 | board. 22 | Load this sketch. 23 | Open serial monitor window with baud rate at 115200 and line ending set to 24 | Line Feed. 25 | */ 26 | 27 | // Include the necessary libraries 28 | #include 29 | 30 | // Click here to get the library: 31 | // http://librarymanager/All#SparkFun_External_EEPROM 32 | // or download the zip here: 33 | // https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/archive/master.zip 34 | #include "SparkFun_External_EEPROM.h" 35 | 36 | // Declare the EEPROM object 37 | ExternalEEPROM myMem; 38 | 39 | // This is the position in EEPROM to store the settings struct 40 | #define LOCATION_SETTINGS 0 41 | 42 | // This is the data that we use to validate the EEPROM data. 43 | // The __TIME__ macro is the time the sketch was compiled, in HH:MM:SS format. 44 | // This means that if the sketch is recompiled, the EEPROM data will be 45 | // considered invalid, 46 | #define VALID_DATA __TIME__ 47 | 48 | // This is the struct that contains all the settings. 49 | // Add as many vars as you want. 50 | typedef struct { 51 | // This first buffer is used to determine if the EEPROM has 52 | // been written to with valid data. If the bytes match the VALID_DATA 53 | // constant, then we can assume the data is valid. 54 | char memoryIsValid[sizeof(VALID_DATA)]; 55 | // A baud rate setting (doesn't actually do anything in this example) 56 | unsigned long baudRate; 57 | // A setting to enable or disable the built-in LED 58 | bool enableLED; 59 | } Settings; 60 | 61 | // This is the settings object. 62 | Settings settings; 63 | 64 | void setup() { 65 | // Set up serial communication 66 | Serial.begin(115200); 67 | // delay(250); // Often needed for ESP based platforms 68 | 69 | while (!Serial.available()) { 70 | Serial.println(F("Press any key to begin.")); 71 | delay(1000); 72 | } 73 | Serial.println(F("EEPROM load/save example")); 74 | 75 | // Initialize I2C 76 | Wire.begin(); 77 | 78 | // Configure the EEPROM 79 | // Default to the Qwiic 24xx512 EEPROM: 80 | // https://www.sparkfun.com/products/18355 81 | // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 82 | // For example, to use a 24C02 EEPROM, set the memory type to 2 83 | // Make sure to set this correctly or this example won't work! 84 | myMem.setMemoryType(2); 85 | 86 | // Initialize the EEPROM 87 | if (myMem.begin() == false) { 88 | Serial.println(F("No memory detected. Freezing.")); 89 | while (true); 90 | } 91 | Serial.println(F("Memory detected!")); 92 | 93 | // Print out the size of the user settings struct 94 | Serial.print("Size of user settings in bytes: "); 95 | Serial.println(sizeof(settings)); 96 | 97 | Serial.println("Settings are not initialized or loaded from EEPROM."); 98 | 99 | // Present user menu 100 | mainMenu(); 101 | } 102 | 103 | void loop() { 104 | if (Serial.available()) mainMenu(); // Present user menu 105 | } 106 | 107 | void mainMenu() { 108 | int number; 109 | while (1) { 110 | Serial.println(); 111 | Serial.println("Main menu:"); 112 | Serial.println(); 113 | Serial.println("P: Print Settings"); 114 | Serial.println("L: Load Settings"); 115 | Serial.println("S: Save Settings"); 116 | Serial.println("R: Reset Settings to Default"); 117 | Serial.println("B: Set Baud Rate"); 118 | Serial.println("T: Toggle LED Setting"); 119 | Serial.println(); 120 | Serial.print("Enter choice: "); 121 | 122 | byte selection = readByte(); 123 | 124 | switch (selection) { 125 | case 'p': 126 | case 'P': 127 | printSettings(); 128 | break; 129 | case 'l': 130 | case 'L': 131 | loadSettings(); 132 | break; 133 | case 's': 134 | case 'S': 135 | saveSettings(); 136 | break; 137 | case 'r': 138 | case 'R': 139 | resetSettings(); 140 | break; 141 | case 'b': 142 | case 'B': 143 | Serial.print("Enter baud rate (1200 to 115200): "); 144 | number = readNumber(); 145 | if (number < 1200 || number > 115200) { 146 | Serial.println("Error: baud rate out of range"); 147 | } else { 148 | settings.baudRate = number; 149 | } 150 | break; 151 | case 't': 152 | case 'T': 153 | settings.enableLED = !settings.enableLED; 154 | syncLED(); 155 | Serial.print("LED is now "); 156 | Serial.println(settings.enableLED ? "on" : "off"); 157 | break; 158 | default: 159 | Serial.print("Unknown choice: "); 160 | Serial.write(selection); 161 | Serial.println(); 162 | } 163 | } 164 | } 165 | 166 | // Blocking wait for user input 167 | void waitForInput() { 168 | delay(10); // Wait for any incoming chars to hit buffer 169 | while (Serial.available() > 0) Serial.read(); // Clear buffer 170 | while (Serial.available() == 0); 171 | } 172 | 173 | // Get single byte from user. 174 | // Waits for and returns the character that the user provides 175 | byte readByte() { 176 | waitForInput(); 177 | return (Serial.read()); 178 | } 179 | 180 | // Get a string/value from user, remove all non-numeric values 181 | int readNumber() { 182 | waitForInput(); 183 | 184 | char numberString[20 + 1]; // Room for 20 digits plus terminating \0 185 | 186 | int index = 0; 187 | while (index < 20) { 188 | // Wait for user input 189 | while (Serial.available() == 0); 190 | 191 | // Read the incoming byte 192 | byte incoming = Serial.read(); 193 | 194 | // If the user hits enter, we're done 195 | if (incoming == '\n' || incoming == '\r') { 196 | Serial.println(); 197 | break; 198 | } 199 | 200 | // Check for a digit 201 | if (isDigit(incoming) == true) { 202 | // Echo the digit 203 | Serial.write(incoming); 204 | // Add the digit to the string 205 | numberString[index] = (char)incoming; 206 | index++; 207 | } 208 | } 209 | 210 | // Null terminate the string 211 | numberString[index] = '\0'; 212 | 213 | // Convert the string to an integer and return it 214 | String tempValue = numberString; 215 | return (tempValue.toInt()); 216 | } 217 | 218 | // This function is called to sync the LED state with the settings 219 | // (On my board at least), the LED is active low, so we invert the setting 220 | void syncLED() { digitalWrite(PIN_LED, !settings.enableLED); } 221 | 222 | // Reset the settings struct to default values (without writing to EEPROM) 223 | void resetSettings() { 224 | // Set the first 8 bytes of the settings struct to the VALID_DATA constant 225 | memcpy(settings.memoryIsValid, VALID_DATA, sizeof(settings.memoryIsValid)); 226 | // Set the baud rate to 9600 227 | settings.baudRate = 9600; 228 | // Set the LED to off 229 | settings.enableLED = false; 230 | syncLED(); 231 | 232 | Serial.println("Settings reset to default."); 233 | } 234 | 235 | // Load the current settings from EEPROM into the settings struct 236 | void loadSettings() { 237 | // Call the get() method with the location and the struct to load into 238 | myMem.get(LOCATION_SETTINGS, settings); 239 | 240 | // Sync the LED with the settings 241 | syncLED(); 242 | 243 | Serial.println("Settings loaded from EEPROM."); 244 | 245 | if (memcmp(settings.memoryIsValid, VALID_DATA, 246 | sizeof(settings.memoryIsValid)) != 0) { 247 | Serial.println( 248 | "Memory is not valid. You should reset the settings and save them."); 249 | // In a real application, you would do this here: 250 | // resetSettings(); 251 | // saveSettings(); 252 | } else { 253 | Serial.println("Memory is valid!"); 254 | } 255 | } 256 | 257 | // Record the current settings into EEPROM 258 | void saveSettings() { 259 | // The putChanged() method writes the data to the EEPROM 260 | // (the same as the put() method except it only writes data that changed). 261 | myMem.put(LOCATION_SETTINGS, settings); 262 | 263 | Serial.println("Settings saved to EEPROM."); 264 | } 265 | 266 | // Print the current settings to the serial monitor 267 | void printSettings() { 268 | Serial.println("Current settings:"); 269 | Serial.println(); 270 | 271 | Serial.print("Memory valid: "); 272 | for (int i = 0; i < sizeof(settings.memoryIsValid); i++) { 273 | Serial.print(settings.memoryIsValid[i], 16); 274 | Serial.print(' '); 275 | } 276 | Serial.println(); 277 | Serial.print(" VALID_DATA: "); 278 | for (int i = 0; i < sizeof(settings.memoryIsValid); i++) { 279 | Serial.print(VALID_DATA[i], 16); 280 | Serial.print(' '); 281 | } 282 | Serial.println(); 283 | 284 | Serial.print("Baud rate: "); 285 | Serial.println(settings.baudRate); 286 | 287 | Serial.print("LED: "); 288 | Serial.println(settings.enableLED ? "on" : "off"); 289 | } 290 | -------------------------------------------------------------------------------- /src/SparkFun_External_EEPROM.h: -------------------------------------------------------------------------------- 1 | /* 2 | This is a library to read/write to external I2C EEPROMs. 3 | It uses the same template system found in the Arduino 4 | EEPROM library so you can use the same get() and put() functions. 5 | 6 | https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library 7 | Best used with the Qwiic EEPROM: https://www.sparkfun.com/products/18355 8 | 9 | Various external EEPROMs have various interface specs 10 | (overall size, page size, write times, etc). This library works with 11 | all types and allows the various settings to be set at runtime. 12 | 13 | All read and write restrictions associated with pages are taken care of. 14 | You can access the external memory as if it was contiguous. 15 | 16 | Development environment specifics: 17 | Arduino IDE 1.8.x 18 | 19 | This library is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | 24 | */ 25 | 26 | #ifndef _SPARKFUN_EXTERNAL_EEPROM_H 27 | #define _SPARKFUN_EXTERNAL_EEPROM_H 28 | 29 | #include "Arduino.h" 30 | #include "Wire.h" 31 | 32 | #if defined(ARDUINO_ARCH_APOLLO3) 33 | 34 | #define I2C_BUFFER_LENGTH_RX \ 35 | 256 // Hardcoding until issue is resolved: https://github.com/sparkfun/Arduino_Apollo3/issues/351 36 | #define I2C_BUFFER_LENGTH_TX 256 37 | 38 | #elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) 39 | 40 | #define I2C_BUFFER_LENGTH_RX BUFFER_LENGTH // I2C_BUFFER_LENGTH is defined in Wire.H 41 | #define I2C_BUFFER_LENGTH_TX BUFFER_LENGTH 42 | 43 | #elif defined(__SAMD21G18A__) || defined(__SAMD21E18A__) 44 | 45 | #define I2C_BUFFER_LENGTH_RX SERIAL_BUFFER_SIZE // SAMD21 uses RingBuffer.h 46 | #define I2C_BUFFER_LENGTH_TX SERIAL_BUFFER_SIZE 47 | 48 | #elif defined(__SAMD51__) || defined(ADAFRUIT_METRO_M4_EXPRESS) 49 | 50 | // SAMD51 doesn't have a definition, but it does have this as a template size 51 | // in Wire.h: https://github.com/adafruit/ArduinoCore-samd/blob/master/libraries/Wire/Wire.h#L74 52 | #define I2C_BUFFER_LENGTH_RX 256 53 | #define I2C_BUFFER_LENGTH_TX 256 54 | 55 | #elif (defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || \ 56 | defined(__MK66FX1M0__) || defined(__IMXRT1062__)) // 3.0/3.1-3.2/LC/3.5/3.6/4.0 57 | 58 | #define I2C_BUFFER_LENGTH_RX BUFFER_LENGTH // Teensy 59 | #define I2C_BUFFER_LENGTH_TX BUFFER_LENGTH 60 | 61 | #elif defined(ESP32) 62 | 63 | #define I2C_BUFFER_LENGTH_RX I2C_BUFFER_LENGTH 64 | #define I2C_BUFFER_LENGTH_TX I2C_BUFFER_LENGTH 65 | 66 | #elif defined(ESP8266) 67 | 68 | #define I2C_BUFFER_LENGTH_RX BUFFER_LENGTH // BUFFER_LENGTH is defined in Wire.h for ESP8266 69 | #define I2C_BUFFER_LENGTH_TX BUFFER_LENGTH 70 | 71 | #elif defined(STM32) 72 | 73 | #define I2C_BUFFER_LENGTH_RX BUFFER_LENGTH // BUFFER_LENGTH is defined in Wire.h for STM32 74 | #define I2C_BUFFER_LENGTH_TX BUFFER_LENGTH 75 | 76 | #elif defined(STM32_CORE_VERSION) 77 | 78 | #define I2C_BUFFER_LENGTH_RX BUFFER_LENGTH // BUFFER_LENGTH is defined in Wire.h for STM32 79 | #define I2C_BUFFER_LENGTH_TX BUFFER_LENGTH 80 | 81 | #elif defined(NRF52_SERIES) 82 | 83 | #define I2C_BUFFER_LENGTH_RX SERIAL_BUFFER_SIZE // Adafruit Bluefruit nRF52 Boards uses RingBuffer.h 84 | #define I2C_BUFFER_LENGTH_TX SERIAL_BUFFER_SIZE 85 | 86 | #elif defined(ARDUINO_ARCH_RP2040) 87 | 88 | #ifdef WIRE_BUFFER_SIZE 89 | #define I2C_BUFFER_LENGTH_RX WIRE_BUFFER_SIZE // 128 - defined in Wire.h (provided by pico-arduino-compat) 90 | #define I2C_BUFFER_LENGTH_TX WIRE_BUFFER_SIZE 91 | #elif defined(ARDUINO_RASPBERRY_PI_PICO) 92 | 93 | #define I2C_BUFFER_LENGTH_RX \ 94 | 256 // Not properly defined but set at 256: 95 | // https://github.com/arduino/ArduinoCore-mbed/blob/master/libraries/Wire/Wire.h 96 | #define I2C_BUFFER_LENGTH_TX 256 97 | #else 98 | #pragma GCC warning \ 99 | "This RP2040 platform doesn't have a wire buffer size defined. Defaulting to 32 bytes. Please contribute to this library!" 100 | 101 | // Default to safe 32 bytes 102 | #define I2C_BUFFER_LENGTH_RX 32 103 | #define I2C_BUFFER_LENGTH_TX 32 104 | #endif 105 | 106 | #else 107 | 108 | #pragma GCC warning \ 109 | "This platform doesn't have a wire buffer size defined. Defaulting to 32 bytes. Please contribute to this library!" 110 | 111 | // Default to safe 32 bytes 112 | #define I2C_BUFFER_LENGTH_RX 32 113 | #define I2C_BUFFER_LENGTH_TX 32 114 | 115 | #endif 116 | 117 | struct struct_memorySettings 118 | { 119 | TwoWire *i2cPort; 120 | uint8_t deviceAddress; 121 | uint32_t memorySize_bytes; 122 | uint16_t pageSize_bytes; 123 | uint8_t writeTime_ms; 124 | bool pollForWriteComplete; 125 | uint8_t addressSize_bytes; 126 | uint8_t wpPin; 127 | }; 128 | 129 | class ExternalEEPROM 130 | { 131 | public: 132 | uint8_t read(uint32_t eepromLocation); 133 | int read(uint32_t eepromLocation, uint8_t *buff, uint16_t bufferSize); 134 | int write(uint32_t eepromLocation, uint8_t dataToWrite); 135 | int write(uint32_t eepromLocation, const uint8_t *dataToWrite, uint16_t blockSize); 136 | 137 | bool begin(uint8_t deviceAddress = 0b1010000, TwoWire &wirePort = Wire, uint8_t WP = 255); // By default use the Wire port 138 | 139 | bool isConnected(uint8_t i2cAddress = 255); 140 | bool isBusy(uint8_t i2cAddress = 255); 141 | void erase(uint8_t toWrite = 0x00); // Erase the entire memory. Optional: write a given byte to each spot. 142 | 143 | // void settings(struct_memorySettings newSettings); //Set all the settings using the settings struct 144 | 145 | uint32_t detectMemorySizeBytes(); // Attempts to detect the size of the EEPROM 146 | void setMemorySizeBytes(uint32_t memSize); // Set the size of memory in bytes 147 | uint32_t getMemorySizeBytes(); // Return size of EEPROM 148 | void setMemorySize(uint32_t memSize); // Depricated 149 | uint32_t getMemorySize(); // Depricated 150 | uint32_t length(); // Return size of EEPROM in bytes 151 | 152 | void setMemoryType(uint16_t typeNumber); // Valid types: 00, 01, 02, 04, 08, 16, 32, 64, 128, 256, 512, 1025, 2048 153 | 154 | uint8_t detectAddressBytes(); // Determine the number of address bytes, 1 or 2 155 | void setAddressBytes(uint8_t addressBytes); 156 | uint8_t getAddressBytes(); 157 | 158 | uint16_t detectPageSizeBytes(); // Returns the number of bytes successfully written in one page 159 | void setPageSizeBytes(uint16_t pageSize); // Set the size of the page we can write at a time 160 | uint16_t getPageSizeBytes(); 161 | void setPageSize(uint16_t pageSize); // Depricated 162 | uint16_t getPageSize(); // Depricated 163 | 164 | uint8_t detectWriteTimeMs(uint8_t numberOfTests = 8); 165 | void setWriteTimeMs(uint8_t writeTimeMS); // Set the number of ms required per page write 166 | uint8_t getWriteTimeMs(); 167 | void setPageWriteTime(uint8_t writeTimeMS); // Depricated 168 | uint8_t getPageWriteTime(); // Depricated 169 | 170 | void enablePollForWriteComplete(); // Most EEPROMs all I2C polling of when a write has completed 171 | void disablePollForWriteComplete(); 172 | constexpr uint16_t getI2CBufferSize(); // Return the size of the TX buffer 173 | 174 | // Functionality to 'get' and 'put' objects to and from EEPROM. 175 | template T &get(uint32_t idx, T &t) 176 | { 177 | uint8_t *ptr = (uint8_t *)&t; 178 | read(idx, ptr, sizeof(T)); // Address, data, sizeOfData 179 | return t; 180 | } 181 | 182 | template const T &put(uint32_t idx, const T &t) // Address, data 183 | { 184 | const uint8_t *ptr = (const uint8_t *)&t; 185 | write(idx, ptr, sizeof(T)); // Address, data, sizeOfData 186 | return t; 187 | } 188 | 189 | template const T &putChanged(uint32_t idx, const T &t) // Address, data 190 | { 191 | const uint8_t *newData = (const uint8_t *)&t; 192 | uint8_t oldData[sizeof(T)]; 193 | read(idx, oldData, sizeof(T)); // Address, data, sizeOfData 194 | for (uint16_t i = 0; i < sizeof(T); i++) { 195 | if (oldData[i] != newData[i]) { 196 | write(idx + i, newData[i]); 197 | } 198 | } 199 | return t; 200 | } 201 | 202 | uint32_t putString(uint32_t eepromLocation, String &strToWrite); 203 | void getString(uint32_t eepromLocation, String &strToRead); 204 | 205 | private: 206 | // Default settings are for onsemi CAT24C51 512Kbit I2C EEPROM used on SparkFun Qwiic EEPROM Breakout 207 | struct_memorySettings settings = { 208 | .i2cPort = &Wire, 209 | .deviceAddress = 210 | 0b1010000, // 0x50; format is 0b1010 + (A2 A1 A0) or 0b1010 + (B0 A1 A0) for larger (>512kbit) EEPROMs 211 | .memorySize_bytes = 4096, // Default to 4096, to support 24xx32 / 4096 byte EEPROMs and larger 212 | .pageSize_bytes = 32, // Default to 32 bytes, to support 24xx32 / 4096 byte EEPROMs and larger 213 | .writeTime_ms = 5, //All EEPROMs seem to have a max write time of 5ms 214 | .pollForWriteComplete = true, 215 | .addressSize_bytes = 2, // Default to two address bytes, to support 24xx32 / 4096 byte EEPROMs and larger 216 | .wpPin = 255, // By default, the write protection pin is not set 217 | }; 218 | }; 219 | 220 | #endif //_SPARKFUN_EXTERNAL_EEPROM_H 221 | -------------------------------------------------------------------------------- /examples/Example6_UniversalProgrammer/Example6_UniversalProgrammer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | "Universal" EEPROM programmer for Arduino 3 | Author: Nathan Seidle 4 | Created: March 25, 2020 5 | License: Lemonadeware. Buy me a lemonade (or other) someday. 6 | 7 | This sketch demonstrates how to create a I2C programmer that reads 8 | any binary data file from the SD card and writes it as quickly as possible to the EEPROM. 9 | User can verify a file against an EEPROM. Autoprogram mode allows a user to batch program 10 | a large number of EEPROMs. 11 | 12 | This sketch was created because the software currently available for the CH341 is terrible and often 13 | lacks newly released EEPROMs such as the 24LC1025. This library and sketch demonstrate 14 | how to throw out the windows program and use hardware instead. 15 | 16 | Uno is supported but is *very* slow. Artemis is ~10x faster (https://www.sparkfun.com/products/15444). 17 | 18 | Wire a microSD breakout to the SPI pins: 19 | 20 | SCK on Artemis RedBoard = SCK on microSD breakout (https://www.sparkfun.com/products/544) 21 | MISO = DO 22 | MOSI = DI 23 | 10 = CS 24 | GND = GND 25 | 3.3V = VCC 26 | 27 | Connect Qwiic EEPROM with Qwiic cable 28 | 29 | To really test the full functionality of this example, make sure to load an SD card with a file 30 | named data.bin and some made up binary data. 31 | 32 | Note: On larger EEPROMs, pin 3 must be tied to 3.3V. RTFM. 33 | 34 | Current benchmarking: 35 | 112586 bytes takes 3.801s to write or 29.6kB/s 36 | 37 | TODO: 38 | Modify all library features config from menu 39 | View files and select a file 40 | */ 41 | 42 | #include 43 | 44 | const byte PIN_STAT_LED = 13; 45 | 46 | //microSD Interface 47 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 48 | #include 49 | #include 50 | const byte PIN_MICROSD_CHIP_SELECT = 10; 51 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 52 | 53 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 54 | ExternalEEPROM myMem; 55 | 56 | const byte EEPROM_ADDRESS = 0b1010000; //The 7-bit unshifted address for the 24LC1025 57 | String binFileName = "data.bin"; //File to write to the EEPROM 58 | 59 | void setup() 60 | { 61 | Serial.begin(115200); 62 | Serial.println(F("SparkFun I2C EEPROM Writer")); 63 | 64 | pinMode(PIN_STAT_LED, OUTPUT); 65 | 66 | Wire.begin(); 67 | Wire.setClock(400000); 68 | 69 | SPI.begin(); 70 | 71 | beginSD(); 72 | 73 | //Set settings for a 24LC1025 74 | myMem.setMemoryType(1025); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 75 | } 76 | 77 | void loop() 78 | { 79 | while (myMem.begin(EEPROM_ADDRESS) == false) 80 | { 81 | Serial.println(F("No memory detected.")); 82 | delay(250); 83 | } 84 | 85 | Serial.println(); 86 | Serial.println(F("Memory detected!")); 87 | Serial.print(F("Current file to record: ")); 88 | Serial.println(binFileName); 89 | Serial.println(F("w)rite file to EEPROM")); 90 | Serial.println(F("v)erify EEPROM against file")); 91 | Serial.println(F("s)et file name to read from")); 92 | Serial.println(F("e)rase EEPROM")); 93 | Serial.println(F("a)uto program")); 94 | Serial.println(); 95 | 96 | while (Serial.available()) 97 | Serial.read(); 98 | while (Serial.available() == 0) 99 | ; 100 | byte choice = Serial.read(); 101 | 102 | if (choice == 'w') 103 | { 104 | Serial.println(F("Writing file to EEPROM")); 105 | writeFileToEEPROM((char *)"data.bin"); 106 | } 107 | else if (choice == 'v') 108 | { 109 | Serial.println(F("Verifying")); 110 | verifyFileOnEEPROM((char *)"data.bin"); 111 | } 112 | else if (choice == 's') 113 | { 114 | Serial.print(F("Enter file to open: ")); 115 | binFileName = Serial.readString(); 116 | } 117 | else if (choice == 'e') 118 | { 119 | Serial.println(F("This will take a moment. Be patient.")); 120 | myMem.erase(); 121 | Serial.println(F("Erase complete")); 122 | } 123 | else if (choice == 'a') 124 | { 125 | autoProgram(); 126 | } 127 | else 128 | { 129 | Serial.print(F("Unknown: ")); 130 | Serial.write(choice); 131 | Serial.println(); 132 | } 133 | } 134 | 135 | void autoProgram() 136 | { 137 | int programCount = 0; 138 | 139 | delay(50); //Wait for any incoming characters to be received 140 | 141 | //Clear the serial buffer 142 | while (Serial.available()) 143 | Serial.read(); 144 | 145 | Serial.println(F("Auto Programming!")); 146 | Serial.println(F("Press any key to exit")); 147 | 148 | while (1) 149 | { 150 | if (Serial.available()) 151 | return; 152 | 153 | if (myMem.begin(EEPROM_ADDRESS) == true) 154 | { 155 | Serial.println(F("New device detected")); 156 | 157 | delay(500); //Wait for user to get IC inserted into breadboard 158 | 159 | if (writeFileToEEPROM((char *)"data.bin") == false) 160 | { 161 | Serial.println(F("Write failed")); 162 | continue; 163 | } 164 | 165 | if (verifyFileOnEEPROM((char *)"data.bin") == false) 166 | { 167 | Serial.println(F("Verify failed")); 168 | continue; 169 | } 170 | 171 | programCount++; 172 | 173 | Serial.print(F("Program count: ")); 174 | Serial.println(programCount); 175 | 176 | //Wait for user to remove this device 177 | Serial.println(F("Remove IC and insert new one")); 178 | while (myMem.begin(EEPROM_ADDRESS) == true) 179 | delay(1000); 180 | } 181 | else 182 | { 183 | Serial.print(F("No IC detected")); 184 | while (myMem.begin(EEPROM_ADDRESS) == false) 185 | { 186 | delay(1000); 187 | Serial.print(F(".")); 188 | } 189 | Serial.println(); 190 | } 191 | } 192 | } 193 | 194 | //Given a file name, verify the contents of the EEPROM match the file contents 195 | bool verifyFileOnEEPROM(char *fileName) 196 | { 197 | File binFile = SD.open(fileName); 198 | if (binFile == false) 199 | { 200 | Serial.print(F("Programming file '")); 201 | Serial.print((String)fileName); 202 | Serial.println(F("' not found")); 203 | return (false); 204 | } 205 | Serial.println(F("Verification file opened")); 206 | 207 | int error = 0; 208 | uint32_t EEPROMLocation = 0; 209 | uint8_t *onEEPROM = (uint8_t *)malloc(myMem.getPageSizeBytes()); //Create a buffer to hold a page 210 | 211 | while (binFile.available()) 212 | { 213 | uint8_t bytesToRead = myMem.getPageSizeBytes(); 214 | 215 | if (EEPROMLocation + bytesToRead > myMem.getMemorySizeBytes()) 216 | bytesToRead = myMem.getMemorySizeBytes() - EEPROMLocation; 217 | 218 | if (bytesToRead > binFile.available()) 219 | bytesToRead = binFile.available(); 220 | 221 | //myMem.get(EEPROMLocation, onEEPROM); //Location, data 222 | myMem.read(EEPROMLocation, onEEPROM, myMem.getPageSizeBytes()); //Location, data 223 | 224 | //Verify what was read from the EEPROM matches the file 225 | for (int x = 0; x < bytesToRead; x++) 226 | { 227 | uint8_t onFile = binFile.read(); 228 | 229 | if (onEEPROM[x] != onFile) 230 | { 231 | Serial.print(F("Verify failed at location 0x")); 232 | Serial.print(EEPROMLocation + x, HEX); 233 | Serial.print(F(". Read 0x")); 234 | Serial.print(onEEPROM[x], HEX); 235 | Serial.print(F(", expected 0x")); 236 | Serial.print(onFile, HEX); 237 | Serial.println(F(".")); 238 | if (error++ > 4) 239 | { 240 | binFile.close(); 241 | return (false); 242 | } 243 | } 244 | } 245 | 246 | EEPROMLocation += bytesToRead; 247 | 248 | if (EEPROMLocation % (myMem.getPageSizeBytes() * 128) == 0) 249 | Serial.print(F(".")); 250 | 251 | if (digitalRead(PIN_STAT_LED) == LOW) 252 | digitalWrite(PIN_STAT_LED, HIGH); 253 | else 254 | digitalWrite(PIN_STAT_LED, LOW); 255 | } 256 | 257 | Serial.println(F("Verification PASSED!")); 258 | 259 | binFile.close(); 260 | free(onEEPROM); 261 | return (true); 262 | } 263 | 264 | bool writeFileToEEPROM(char *fileName) 265 | { 266 | File binFile = SD.open(fileName); 267 | if (binFile == false) 268 | { 269 | Serial.print(F("Programming file '")); 270 | Serial.print((String)fileName); 271 | Serial.println(F("' not found")); 272 | return (false); 273 | } 274 | Serial.print(F("Programming file opened: ")); 275 | Serial.print(binFile.size()); 276 | Serial.println(F(" bytes")); 277 | 278 | //The estimated write time is composed of many things: 279 | //Number of bytes we need to write 280 | //The amount of time it takes to transfer a byteLimit: 30 bytes on Uno, overwrite with setI2CBufferSize (Wire clock speed / 10 / number of bytes) 281 | //The amount of time it takes to record a page (setPageWrite time = 5ms) 282 | //The number of writes we have to do (totalBytes / bytelimit) 283 | float timePerDataTransfer = 1.0 / (400000.0 / 10.0) * myMem.getI2CBufferSize(); 284 | float timePerPageWrite = 0.005; 285 | int writesRequired = binFile.size() / myMem.getI2CBufferSize(); 286 | 287 | //Serial.print(F("timePerByteLimit: ")); 288 | //Serial.println(timePerByteLimit, 6); 289 | 290 | //Serial.print(F("writesRequired: ")); 291 | //Serial.println(writesRequired); 292 | 293 | Serial.print(F("Estimated write time: ")); 294 | Serial.print((timePerDataTransfer + timePerPageWrite) * writesRequired, 3); 295 | Serial.println(F("s")); 296 | 297 | uint8_t *buffer = (uint8_t *)malloc(myMem.getPageSizeBytes()); //Create a buffer to hold a page 298 | uint8_t bufferLocation = 0; 299 | 300 | uint32_t EEPROMLocation = 0; 301 | 302 | long startTime = millis(); 303 | while (binFile.available()) 304 | { 305 | buffer[bufferLocation++] = binFile.read(); 306 | 307 | //If the buffer if full, record to EEPROM 308 | if (bufferLocation == myMem.getPageSizeBytes()) 309 | { 310 | bufferLocation = 0; 311 | 312 | //Record buffer to EEPROM 313 | myMem.write(EEPROMLocation, buffer, myMem.getPageSizeBytes()); //Location, data 314 | 315 | EEPROMLocation += myMem.getPageSizeBytes(); 316 | 317 | if (EEPROMLocation % (myMem.getPageSizeBytes() * 128) == 0) 318 | Serial.print(F(".")); 319 | 320 | if (digitalRead(PIN_STAT_LED) == LOW) 321 | digitalWrite(PIN_STAT_LED, HIGH); 322 | else 323 | digitalWrite(PIN_STAT_LED, LOW); 324 | } 325 | } 326 | 327 | //Write anything remaining in the buffer 328 | if (bufferLocation > 0) 329 | myMem.write(EEPROMLocation, buffer, bufferLocation); //Location, data, length 330 | 331 | long endTime = millis(); 332 | 333 | Serial.println(); 334 | Serial.print(F("Write complete. Elapsed time: ")); 335 | Serial.print((endTime - startTime) / 1000.0, 3); 336 | Serial.println(F("s")); 337 | 338 | binFile.close(); 339 | free(buffer); 340 | return (true); 341 | } 342 | 343 | void beginSD() 344 | { 345 | pinMode(PIN_MICROSD_CHIP_SELECT, OUTPUT); 346 | digitalWrite(PIN_MICROSD_CHIP_SELECT, HIGH); //Be sure SD is deselected 347 | 348 | //Max power up time is 250ms: https://www.kingston.com/datasheets/SDCIT-specsheet-64gb_en.pdf 349 | delay(10); 350 | 351 | if (SD.begin(PIN_MICROSD_CHIP_SELECT) == false) 352 | { 353 | delay(250); //Give SD more time to power up, then try again 354 | if (SD.begin(PIN_MICROSD_CHIP_SELECT) == false) 355 | { 356 | Serial.println("SD init failed. Do you have the correct board selected in Arduino? Is card present? Formatted?"); 357 | digitalWrite(PIN_MICROSD_CHIP_SELECT, HIGH); //Be sure SD is deselected 358 | return; 359 | } 360 | } 361 | 362 | Serial.println("SD card online"); 363 | } 364 | -------------------------------------------------------------------------------- /examples/Example8_InterfaceTest/Example8_InterfaceTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Read and write settings and calibration data to an external I2C EEPROM 3 | By: Nathan Seidle 4 | SparkFun Electronics 5 | Date: December 11th, 2019 6 | License: This code is public domain but you buy me a beer if you use this 7 | and we meet someday (Beerware license). 8 | Feel like supporting our work? Buy a board from SparkFun! 9 | https://www.sparkfun.com/products/18355 10 | 11 | This example demonstrates how to read and write various variables to memory. 12 | 13 | The I2C EEPROM should have all its ADR pins set to GND (0). This is default 14 | on the Qwiic board. 15 | 16 | Known/compatible memory types (basically all I2C EEPROMs): 17 | 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page size 18 | 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page size 19 | 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page size 20 | 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page size 21 | 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page size 22 | 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page size 23 | 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page size 24 | 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page size 25 | 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page size 26 | 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page size 27 | 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page size 28 | 24xx1024 - 1024000 bit / 128000 byte - 2 address bytes, 128 byte page size 29 | 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page size 30 | 31 | Hardware Connections: 32 | Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped board 33 | Load this sketch 34 | Open output window at 115200bps 35 | */ 36 | 37 | #include 38 | 39 | #include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM 40 | ExternalEEPROM myMem; 41 | 42 | void setup() 43 | { 44 | Serial.begin(115200); 45 | delay(10); 46 | Serial.println("I2C EEPROM example"); 47 | 48 | // Pick any unconnected analog pin 49 | randomSeed(analogRead(A0)); 50 | // randomSeed(analogRead(A15)); 51 | 52 | unsigned int randomLocation; 53 | 54 | Wire.begin(); 55 | Wire.setClock(400000); 56 | // Wire.setClock(1000000); 57 | 58 | // Set the memory specs 59 | // 24xx00 - 128 bit / 16 bytes - 1 address byte, 1 byte page 60 | // 24xx01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page 61 | // 24xx02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page 62 | // 24xx04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page 63 | // 24xx08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page 64 | // 24xx16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page 65 | // 24xx32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page 66 | // 24xx64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page 67 | // 24xx128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page 68 | // 24xx256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page 69 | // 24xx512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page 70 | // 24xx1024 - 1024000 bit / 128000 byte - 2 address byte, 128 byte page 71 | // 24xxM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page 72 | 73 | // Before running the interface test, the memory type needs to be set, OR the memory 74 | // size/address bytes/page size needs to be set. But not both. 75 | 76 | // setMemoryType() sets the memorySizeBytes, addressBytes, and pageSizeBytes variables. 77 | // myMem.setMemoryType(512); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 78 | 79 | // If setMemorySizeBytes/setAddressBytes/setPageSizeBytes() are called, they will overwrite 80 | // any previous settings set by setMemoryType(). 81 | myMem.setMemorySizeBytes(65536); 82 | myMem.setAddressBytes(2); // Set address bytes and page size after MemorySizeBytes() 83 | myMem.setPageSizeBytes(128); 84 | 85 | if (myMem.begin() == false) 86 | { 87 | Serial.println("No memory detected. Freezing."); 88 | while (1) 89 | ; 90 | } 91 | Serial.println("Memory detected!"); 92 | 93 | uint32_t eepromSizeBytes = myMem.getMemorySizeBytes(); 94 | Serial.print("EEPROM type: 24xx"); 95 | if (eepromSizeBytes == 16) 96 | Serial.print("00"); 97 | else 98 | { 99 | if ((eepromSizeBytes * 8 / 1024) < 10) 100 | Serial.print("0"); 101 | Serial.print(eepromSizeBytes * 8 / 1024); 102 | } 103 | Serial.print(" - bytes: "); 104 | Serial.print(eepromSizeBytes); 105 | Serial.println(); 106 | 107 | Serial.print("Number of address bytes: "); 108 | Serial.println(myMem.getAddressBytes()); 109 | 110 | Serial.print("Page size in bytes: "); 111 | Serial.println(myMem.getPageSizeBytes()); 112 | 113 | bool allTestsPassed = true; 114 | 115 | // Erase test 116 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 117 | Serial.print("Time to erase all EEPROM: "); 118 | long startTime = millis(); 119 | myMem.erase(); 120 | long endTime = millis(); 121 | Serial.print(endTime - startTime); 122 | Serial.println("ms"); 123 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 124 | 125 | // Byte sequential test 126 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 127 | Serial.println(); 128 | Serial.println("8 bit tests"); 129 | byte myValue1 = 200; 130 | byte myValue2 = 23; 131 | randomLocation = random(0, myMem.length() - (sizeof(byte) * 2)); 132 | 133 | startTime = micros(); 134 | myMem.write(randomLocation, myValue1); //(location, data) 135 | while (myMem.isConnected() == false) 136 | ; // Wait for write to complete 137 | endTime = micros(); 138 | Serial.print("Time to record byte: "); 139 | Serial.print(endTime - startTime); 140 | Serial.println(" us"); 141 | 142 | myMem.put(randomLocation + sizeof(byte), myValue2); 143 | while (myMem.isConnected() == false) 144 | ; // Wait for write to complete 145 | 146 | startTime = micros(); 147 | myMem.write(randomLocation, myValue1); //(location, data) 148 | while (myMem.isConnected() == false) 149 | ; // Wait for write to complete 150 | endTime = micros(); 151 | Serial.print("Time to write identical byte to same location (should be ~0ms): "); 152 | Serial.print(endTime - startTime); 153 | Serial.println(" us"); 154 | 155 | startTime = micros(); 156 | byte response1 = myMem.read(randomLocation); 157 | endTime = micros(); 158 | Serial.print("Time to read byte: "); 159 | Serial.print(endTime - startTime); 160 | Serial.println(" us"); 161 | 162 | byte response2 = myMem.read(randomLocation + sizeof(byte)); 163 | Serial.print("Location "); 164 | Serial.print(randomLocation); 165 | Serial.print(" should be "); 166 | Serial.print(myValue1); 167 | Serial.print(": "); 168 | Serial.print(response1); 169 | if (myValue1 == response1 ? Serial.print(" - Success") : Serial.print(" - Fail")) 170 | ; 171 | Serial.println(); 172 | 173 | Serial.print("Location "); 174 | Serial.print(randomLocation + sizeof(byte)); 175 | Serial.print(" should be "); 176 | Serial.print(myValue2); 177 | Serial.print(": "); 178 | Serial.print(response2); 179 | if (myValue2 == response2 ? Serial.print(" - Success") : Serial.print(" - Fail")) 180 | ; 181 | Serial.println(); 182 | 183 | if (myValue1 != response1) 184 | allTestsPassed = false; 185 | if (myValue2 != response2) 186 | allTestsPassed = false; 187 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 188 | 189 | Serial.println(""); 190 | Serial.println("16 bit tests"); 191 | 192 | // int16_t and uint16_t tests 193 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 194 | uint16_t myValue3 = 3411; 195 | int16_t myValue4 = -366; 196 | randomLocation = random(0, myMem.length() - (sizeof(int16_t) * 2)); 197 | 198 | startTime = micros(); 199 | myMem.put(randomLocation, myValue3); 200 | endTime = micros(); 201 | Serial.print("Time to record int16: "); 202 | Serial.print(endTime - startTime); 203 | Serial.println(" us"); 204 | 205 | myMem.put(randomLocation + sizeof(int16_t), myValue4); 206 | 207 | uint16_t response3; 208 | int16_t response4; 209 | 210 | startTime = micros(); 211 | myMem.get(randomLocation, response3); 212 | endTime = micros(); 213 | Serial.print("Time to read int16: "); 214 | Serial.print(endTime - startTime); 215 | Serial.println(" us"); 216 | 217 | myMem.get(randomLocation + sizeof(int16_t), response4); 218 | Serial.print("Location "); 219 | Serial.print(randomLocation); 220 | Serial.print(" should be "); 221 | Serial.print(myValue3); 222 | Serial.print(": "); 223 | Serial.print(response3); 224 | if (myValue3 == response3 ? Serial.print(" - Success") : Serial.print(" - Fail")) 225 | ; 226 | Serial.println(); 227 | 228 | Serial.print("Location "); 229 | Serial.print(randomLocation + sizeof(int16_t)); 230 | Serial.print(" should be "); 231 | Serial.print(myValue4); 232 | Serial.print(": "); 233 | Serial.print(response4); 234 | if (myValue4 == response4 ? Serial.print(" - Success") : Serial.print(" - Fail")) 235 | ; 236 | Serial.println(); 237 | 238 | if (myValue3 != response3) 239 | allTestsPassed = false; 240 | if (myValue4 != response4) 241 | allTestsPassed = false; 242 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 243 | 244 | Serial.println(""); 245 | Serial.println("int tests"); 246 | 247 | // int and unsigned int tests 248 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 249 | Serial.print("Size of int: "); 250 | Serial.println(sizeof(int)); // Uno reports this as 2 251 | int myValue5 = -2450; 252 | unsigned int myValue6 = 4001; 253 | randomLocation = random(0, myMem.length() - (sizeof(int) * 2)); 254 | 255 | startTime = micros(); 256 | myMem.put(randomLocation, myValue5); 257 | endTime = micros(); 258 | Serial.print("Time to record int: "); 259 | Serial.print(endTime - startTime); 260 | Serial.println(" us"); 261 | myMem.put(randomLocation + sizeof(int), myValue6); 262 | 263 | int response5; 264 | unsigned int response6; 265 | 266 | startTime = micros(); 267 | myMem.get(randomLocation, response5); 268 | endTime = micros(); 269 | Serial.print("Time to read int: "); 270 | Serial.print(endTime - startTime); 271 | Serial.println(" us"); 272 | 273 | myMem.get(randomLocation + sizeof(int), response6); 274 | Serial.print("Location "); 275 | Serial.print(randomLocation); 276 | Serial.print(" should be "); 277 | Serial.print(myValue5); 278 | Serial.print(": "); 279 | Serial.print(response5); 280 | if (myValue5 == response5 ? Serial.print(" - Success") : Serial.print(" - Fail")) 281 | ; 282 | Serial.println(); 283 | 284 | Serial.print("Location "); 285 | Serial.print(randomLocation + sizeof(int)); 286 | Serial.print(" should be "); 287 | Serial.print(myValue6); 288 | Serial.print(": "); 289 | Serial.print(response6); 290 | if (myValue6 == response6 ? Serial.print(" - Success") : Serial.print(" - Fail")) 291 | ; 292 | Serial.println(); 293 | 294 | if (myValue5 != response5) 295 | allTestsPassed = false; 296 | if (myValue6 != response6) 297 | allTestsPassed = false; 298 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 299 | 300 | Serial.println(""); 301 | Serial.println("32 bit tests"); 302 | 303 | // int32_t and uint32_t sequential test 304 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 305 | int32_t myValue7 = -341002; 306 | uint32_t myValue8 = 241544; 307 | randomLocation = random(0, myMem.length() - (sizeof(int32_t) * 2)); 308 | 309 | myMem.put(randomLocation, myValue7); 310 | myMem.put(randomLocation + sizeof(int32_t), myValue8); 311 | 312 | int32_t response7; 313 | uint32_t response8; 314 | 315 | startTime = micros(); 316 | myMem.get(randomLocation, response7); 317 | endTime = micros(); 318 | Serial.print("Time to read int32: "); 319 | Serial.print(endTime - startTime); 320 | Serial.println(" us"); 321 | 322 | myMem.get(randomLocation + sizeof(int32_t), response8); 323 | Serial.print("Location "); 324 | Serial.print(randomLocation); 325 | Serial.print(" should be "); 326 | Serial.print(myValue7); 327 | Serial.print(": "); 328 | Serial.print(response7); 329 | if (myValue7 == response7 ? Serial.print(" - Success") : Serial.print(" - Fail")) 330 | ; 331 | Serial.println(); 332 | 333 | Serial.print("Location "); 334 | Serial.print(randomLocation + sizeof(int32_t)); 335 | Serial.print(" should be "); 336 | Serial.print(myValue8); 337 | Serial.print(": "); 338 | Serial.print(response8); 339 | if (myValue8 == response8 ? Serial.print(" - Success") : Serial.print(" - Fail")) 340 | ; 341 | Serial.println(); 342 | 343 | if (myValue7 != response7) 344 | allTestsPassed = false; 345 | if (myValue8 != response8) 346 | allTestsPassed = false; 347 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 348 | 349 | // float (32) sequential test 350 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 351 | Serial.print("Size of float: "); 352 | Serial.println(sizeof(float)); 353 | float myValue9 = -7.35; 354 | float myValue10 = 5.22; 355 | randomLocation = random(0, myMem.length() - (sizeof(float) * 2)); 356 | 357 | myMem.put(randomLocation, myValue9); 358 | myMem.put(randomLocation + sizeof(float), myValue10); 359 | 360 | float response9; 361 | float response10; 362 | 363 | startTime = micros(); 364 | myMem.get(randomLocation, response9); 365 | endTime = micros(); 366 | Serial.print("Time to read float: "); 367 | Serial.print(endTime - startTime); 368 | Serial.println(" us"); 369 | 370 | myMem.get(randomLocation + sizeof(float), response10); 371 | Serial.print("Location "); 372 | Serial.print(randomLocation); 373 | Serial.print(" should be "); 374 | Serial.print(myValue9); 375 | Serial.print(": "); 376 | Serial.print(response9); 377 | if (myValue9 == response9 ? Serial.print(" - Success") : Serial.print(" - Fail")) 378 | ; 379 | Serial.println(); 380 | 381 | Serial.print("Location "); 382 | Serial.print(randomLocation + sizeof(float)); 383 | Serial.print(" should be "); 384 | Serial.print(myValue10); 385 | Serial.print(": "); 386 | Serial.print(response10); 387 | if (myValue10 == response10 ? Serial.print(" - Success") : Serial.print(" - Fail")) 388 | ; 389 | Serial.println(); 390 | 391 | if (myValue9 != response9) 392 | allTestsPassed = false; 393 | if (myValue10 != response10) 394 | allTestsPassed = false; 395 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 396 | 397 | Serial.println(""); 398 | Serial.println("64 bit tests"); 399 | 400 | // double (64) sequential test 401 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 402 | Serial.print("Size of double: "); 403 | Serial.println(sizeof(double)); 404 | double myValue11 = -290.3485723409857; 405 | double myValue12 = 384.957; // 34987; 406 | double myValue13 = 917.14159; 407 | double myValue14 = 254.8877; 408 | randomLocation = random(0, myMem.length() - (sizeof(double) * 2)); 409 | 410 | startTime = micros(); 411 | myMem.put(randomLocation, myValue11); 412 | endTime = micros(); 413 | Serial.print("Time to record 64-bits: "); 414 | Serial.print(endTime - startTime); 415 | Serial.println(" us"); 416 | 417 | myMem.put(randomLocation + sizeof(double), myValue12); 418 | myMem.put(myMem.length() - sizeof(double), myValue13); // Test end of EEPROM space 419 | 420 | double response11; 421 | double response12; 422 | double response13; 423 | 424 | startTime = micros(); 425 | myMem.get(randomLocation, response11); 426 | endTime = micros(); 427 | Serial.print("Time to read 64-bits: "); 428 | Serial.print(endTime - startTime); 429 | Serial.println(" us"); 430 | 431 | myMem.get(randomLocation + sizeof(double), response12); 432 | myMem.get(myMem.length() - sizeof(double), response13); 433 | Serial.print("Location "); 434 | Serial.print(randomLocation); 435 | Serial.print(" should be "); 436 | Serial.print(myValue11); 437 | Serial.print(": "); 438 | Serial.print(response11); 439 | if (myValue11 == response11 ? Serial.print(" - Success") : Serial.print(" - Fail")) 440 | ; 441 | Serial.println(); 442 | 443 | Serial.print("Location "); 444 | Serial.print(randomLocation + sizeof(double)); 445 | Serial.print(" should be "); 446 | Serial.print(myValue12); 447 | Serial.print(": "); 448 | Serial.print(response12); 449 | if (myValue12 == response12 ? Serial.print(" - Success") : Serial.print(" - Fail")) 450 | ; 451 | Serial.println(); 452 | 453 | Serial.print("Edge of EEPROM "); 454 | Serial.print(myMem.length() - sizeof(double)); 455 | Serial.print(" should be "); 456 | Serial.print(myValue13); 457 | Serial.print(": "); 458 | Serial.print(response13); 459 | if (myValue13 == response13 ? Serial.print(" - Success") : Serial.print(" - Fail")) 460 | ; 461 | Serial.println(); 462 | 463 | double response14; 464 | myMem.put(myMem.length() - sizeof(double), myValue14); // Test the re-write of a spot 465 | myMem.get(myMem.length() - sizeof(double), response14); 466 | Serial.print("Rewrite of "); 467 | Serial.print(myMem.length() - sizeof(double)); 468 | Serial.print(" should be "); 469 | Serial.print(myValue14); 470 | Serial.print(": "); 471 | Serial.print(response14); 472 | if (myValue14 == response14 ? Serial.print(" - Success") : Serial.print(" - Fail")) 473 | ; 474 | Serial.println(); 475 | 476 | if (myValue11 != response11) 477 | allTestsPassed = false; 478 | if (myValue12 != response12) 479 | allTestsPassed = false; 480 | if (myValue13 != response13) 481 | allTestsPassed = false; 482 | if (myValue14 != response14) 483 | allTestsPassed = false; 484 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 485 | 486 | Serial.println(""); 487 | Serial.println("Buffer Write Test"); 488 | 489 | // Buffer write test 490 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 491 | // char myChars[242] = "Lorem ipsum dolor sit amet, has in verterem accusamus. Nulla viderer inciderint eum at. Quo 492 | // elit nullam malorum te, agam fuisset detracto an sea, eam ut liber aperiri. Id qui velit facilisi. Mel probatus 493 | // definitionem id, eu amet vidisse eum."; 494 | char myChars[88] = "Lorem ipsum dolor sit amet, has in verterem accusamus. Nulla viderer inciderint eum at."; 495 | randomLocation = random(0, myMem.length() - sizeof(myChars)); 496 | 497 | Serial.print("Calculated time to record array of "); 498 | Serial.print(sizeof(myChars)); 499 | Serial.print(" characters: ~"); 500 | Serial.print((uint32_t)sizeof(myChars) / myMem.getPageSizeBytes() * myMem.getWriteTimeMs()); 501 | Serial.println("ms"); 502 | 503 | startTime = micros(); 504 | myMem.put(randomLocation, myChars); 505 | endTime = micros(); 506 | Serial.print("Time to record array: "); 507 | Serial.print(endTime - startTime); 508 | Serial.println(" us"); 509 | 510 | char readMy[sizeof(myChars)]; 511 | 512 | startTime = micros(); 513 | myMem.get(randomLocation, readMy); 514 | endTime = micros(); 515 | Serial.print("Time to read array: "); 516 | Serial.print(endTime - startTime); 517 | Serial.println(" us"); 518 | 519 | // Serial.print("Location "); 520 | // Serial.print(randomLocation); 521 | // Serial.print(" string should read:"); 522 | // Serial.println(myChars); 523 | // Serial.println(readMy); 524 | if (strcmp(myChars, readMy) != 0) 525 | { 526 | Serial.println("String compare failed"); 527 | allTestsPassed = false; 528 | } 529 | 530 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 531 | 532 | Serial.println(); 533 | Serial.print("Memory Contents:"); 534 | for (uint16_t x = 0; x < 32 * 4; x++) 535 | { 536 | if (x % 16 == 0) 537 | Serial.println(); 538 | Serial.print(" 0x"); 539 | if (myMem.read(x) < 0x10) 540 | Serial.print("0"); 541 | Serial.print(myMem.read(x), HEX); 542 | } 543 | Serial.println(); 544 | 545 | if (allTestsPassed == true) 546 | Serial.println("All tests PASSED!"); 547 | else 548 | Serial.println("Something went wrong. See output."); 549 | } 550 | 551 | void loop() 552 | { 553 | } -------------------------------------------------------------------------------- /src/SparkFun_External_EEPROM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is a library to read/write to external I2C EEPROMs. 3 | It uses the same template system found in the Arduino 4 | EEPROM library so you can use the same get() and put() functions. 5 | 6 | https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library 7 | Best used with the Qwiic EEPROM: https://www.sparkfun.com/products/18355 8 | 9 | Various external EEPROMs have various interface specs 10 | (overall size, page size, write times, etc). This library works with 11 | all types and allows the various settings to be set at runtime. 12 | 13 | All read and write restrictions associated with pages are taken care of. 14 | You can access the external memory as if it was contiguous. 15 | 16 | Development environment specifics: 17 | Arduino IDE 1.8.x 18 | 19 | This library is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | */ 24 | 25 | #include "SparkFun_External_EEPROM.h" 26 | #include "Arduino.h" 27 | #include "Wire.h" 28 | 29 | bool ExternalEEPROM::begin(uint8_t deviceAddress, TwoWire &wirePort, uint8_t WP) 30 | { 31 | if(WP != 255) 32 | { 33 | pinMode(WP, OUTPUT); 34 | digitalWrite(WP, HIGH); 35 | settings.wpPin = WP; 36 | } 37 | settings.i2cPort = &wirePort; // Grab which port the user wants us to use 38 | settings.deviceAddress = deviceAddress; 39 | 40 | if (isConnected() == false) 41 | { 42 | return false; 43 | } 44 | 45 | // if (settings.addressSize_bytes == 0) 46 | // { 47 | // detectAddressBytes(); 48 | // // Serial.print("Detected number of address bytes: "); 49 | // // Serial.println(getAddressBytes()); 50 | // } 51 | 52 | // if (settings.pageSize_bytes == 0) 53 | // { 54 | // detectPageSizeBytes(); 55 | // // Serial.print("Detected page size: "); 56 | // // Serial.println(getPageSizeBytes()); 57 | // } 58 | 59 | // if (settings.writeTime_ms == 0) 60 | // { 61 | // detectWriteTimeMs(); 62 | // Serial.print("Detected write time (ms): "); 63 | // Serial.println(getWriteTimeMs()); 64 | // } 65 | 66 | // if (settings.memorySize_bytes == 0) 67 | // { 68 | // detectMemorySizeBytes(); 69 | // // Serial.print("Detected memory size: "); 70 | // // Serial.println(getMemorySizeBytes()); 71 | // } 72 | 73 | return true; 74 | } 75 | 76 | // Erase entire EEPROM 77 | void ExternalEEPROM::erase(uint8_t toWrite) 78 | { 79 | uint8_t tempBuffer[settings.pageSize_bytes]; 80 | for (uint32_t x = 0; x < settings.pageSize_bytes; x++) 81 | tempBuffer[x] = toWrite; 82 | 83 | for (uint32_t addr = 0; addr < length(); addr += settings.pageSize_bytes) 84 | write(addr, tempBuffer, settings.pageSize_bytes); 85 | } 86 | 87 | uint32_t ExternalEEPROM::length() 88 | { 89 | return settings.memorySize_bytes; 90 | } 91 | 92 | // Returns true if device is detected 93 | bool ExternalEEPROM::isConnected(uint8_t i2cAddress) 94 | { 95 | if (i2cAddress == 255) 96 | i2cAddress = settings.deviceAddress; // We can't set the default to settings.deviceAddress so we use 255 instead 97 | 98 | settings.i2cPort->beginTransmission((uint8_t)i2cAddress); 99 | if (settings.i2cPort->endTransmission() == 0) 100 | return (true); 101 | return (false); 102 | } 103 | 104 | // Returns true if device is not answering (currently writing) 105 | // Caller can pass in an I2C address. This is helpful for larger EEPROMs that have two addresses (see block bit 2). 106 | bool ExternalEEPROM::isBusy(uint8_t i2cAddress) 107 | { 108 | if (i2cAddress == 255) 109 | i2cAddress = settings.deviceAddress; // We can't set the default to settings.deviceAddress so we use 255 instead 110 | 111 | if (isConnected(i2cAddress)) 112 | return (false); 113 | return (true); 114 | } 115 | 116 | // void ExternalEEPROM::settings(struct_memorySettings newSettings) 117 | //{ 118 | // settings.deviceAddress = newSettings.deviceAddress; 119 | // } 120 | 121 | // Old get/setMemorySize. Use get/setMemorySizeBytes 122 | void ExternalEEPROM::setMemorySize(uint32_t memSize) 123 | { 124 | setMemorySizeBytes(memSize); 125 | } 126 | uint32_t ExternalEEPROM::getMemorySize() 127 | { 128 | return getMemorySizeBytes(); 129 | } 130 | void ExternalEEPROM::setMemorySizeBytes(uint32_t memSize) 131 | { 132 | settings.memorySize_bytes = memSize; 133 | 134 | //Try to identify this memory size settings 135 | switch (memSize) 136 | { 137 | default: 138 | // Unknown memory size 139 | break; 140 | case (16): 141 | setAddressBytes(1); 142 | setPageSizeBytes(1); 143 | break; 144 | case (128): 145 | setAddressBytes(1); 146 | setPageSizeBytes(8); 147 | break; 148 | case (256): 149 | setAddressBytes(1); 150 | setPageSizeBytes(8); 151 | break; 152 | case (512): 153 | setAddressBytes(1); 154 | setPageSizeBytes(16); 155 | break; 156 | case (1024): 157 | setAddressBytes(1); 158 | setPageSizeBytes(16); 159 | break; 160 | case (2048): 161 | setAddressBytes(1); 162 | setPageSizeBytes(16); 163 | break; 164 | case (4096): 165 | setAddressBytes(2); 166 | setPageSizeBytes(32); 167 | break; 168 | case (8192): 169 | setAddressBytes(2); 170 | setPageSizeBytes(32); 171 | break; 172 | case (16384): 173 | setAddressBytes(2); 174 | setPageSizeBytes(64); 175 | break; 176 | case (32768): 177 | setAddressBytes(2); 178 | setPageSizeBytes(64); 179 | break; 180 | case (65536): 181 | setAddressBytes(2); 182 | setPageSizeBytes(128); 183 | break; 184 | case (128000): 185 | setAddressBytes(2); 186 | setPageSizeBytes(128); 187 | break; 188 | case (262144): 189 | setAddressBytes(2); 190 | setPageSizeBytes(256); 191 | break; 192 | } 193 | } 194 | uint32_t ExternalEEPROM::getMemorySizeBytes() 195 | { 196 | return settings.memorySize_bytes; 197 | } 198 | 199 | void ExternalEEPROM::setMemoryType(uint16_t typeNumber) 200 | { 201 | //Set settings based on known memory types 202 | switch (typeNumber) 203 | { 204 | default: 205 | // Unknown type number 206 | break; 207 | case (0): 208 | setMemorySizeBytes(16); 209 | break; 210 | case (1): 211 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //128 212 | break; 213 | case (2): 214 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //256 215 | break; 216 | case (4): 217 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //512 218 | break; 219 | case (8): 220 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //1024 221 | break; 222 | case (16): 223 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //2048 224 | break; 225 | case (32): 226 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //4096 227 | break; 228 | case (64): 229 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //8192 230 | break; 231 | case (128): 232 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //16384 233 | break; 234 | case (256): 235 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //32768 236 | break; 237 | case (512): 238 | setMemorySizeBytes(128 * (uint32_t)typeNumber); //65536 239 | break; 240 | case (1025): 241 | setMemorySizeBytes(128000); //128000 242 | break; 243 | case (2048): 244 | setMemorySizeBytes(262144); //262144 245 | break; 246 | } 247 | } 248 | 249 | // Old get/setPageSize. Use get/setPageSizeBytes 250 | void ExternalEEPROM::setPageSize(uint16_t pageSize) 251 | { 252 | setPageSizeBytes(pageSize); 253 | } 254 | uint16_t ExternalEEPROM::getPageSize() 255 | { 256 | return getPageSizeBytes(); 257 | } 258 | void ExternalEEPROM::setPageSizeBytes(uint16_t pageSize) 259 | { 260 | settings.pageSize_bytes = pageSize; 261 | } 262 | uint16_t ExternalEEPROM::getPageSizeBytes() 263 | { 264 | return settings.pageSize_bytes; 265 | } 266 | 267 | // Old get/setPageWriteTime. Use get/setWriteTimeMs 268 | void ExternalEEPROM::setPageWriteTime(uint8_t writeTimeMS) 269 | { 270 | setWriteTimeMs(writeTimeMS); 271 | } 272 | uint8_t ExternalEEPROM::getPageWriteTime() 273 | { 274 | return getWriteTimeMs(); 275 | } 276 | void ExternalEEPROM::setWriteTimeMs(uint8_t writeTimeMS) 277 | { 278 | settings.writeTime_ms = writeTimeMS; 279 | } 280 | uint8_t ExternalEEPROM::getWriteTimeMs() 281 | { 282 | return settings.writeTime_ms; 283 | } 284 | 285 | // Write a value to EEPROM, takes an average of time taken 286 | uint8_t ExternalEEPROM::detectWriteTimeMs(uint8_t numberOfTests) 287 | { 288 | uint8_t testLocation = 5; // Location in memory to do our tests 289 | 290 | uint8_t originalValue = read(testLocation); // Preserve data before we start writing 291 | 292 | uint32_t totalTime = 0; 293 | const uint8_t percentOverage = 10; 294 | 295 | // Create copy of internal settings before test 296 | uint32_t originalMemorySize = settings.memorySize_bytes; 297 | bool originalpollForWriteComplete = settings.pollForWriteComplete; 298 | 299 | setMemorySizeBytes(128); // Assume the smallest memory size during test 300 | settings.pollForWriteComplete = true; 301 | 302 | // We can't run this test if we don't know the number of address bytes 303 | if (settings.addressSize_bytes == 0) 304 | detectAddressBytes(); 305 | 306 | for (int x = 0; x < numberOfTests; x++) 307 | { 308 | // Avoid the default state of 0xFF = 255 and 0. Assumes user has randomSeed()ed something. 309 | // Do not use the original value 310 | // Do not use the value found in the next location either 311 | uint8_t magicValue = 0; 312 | do 313 | { 314 | magicValue = random(1, 255); // (Inclusive, exclusive) 315 | } while (magicValue == originalValue); 316 | 317 | unsigned long startTime = micros(); 318 | write(testLocation, magicValue); // Does a read before write. Uses polling. 319 | 320 | // Wait until write completes 321 | while (isBusy(settings.deviceAddress) == true) // Poll device's original address, not the modified one 322 | delayMicroseconds(100); // This shortens the amount of time waiting between writes but hammers the I2C bus 323 | 324 | unsigned long stopTime = micros(); 325 | totalTime += (stopTime - startTime); 326 | 327 | // Serial.print("delta: "); 328 | // Serial.println((stopTime - startTime)); 329 | } 330 | 331 | // Return spot to its original value 332 | write(testLocation, originalValue); 333 | 334 | // Return original settings 335 | settings.memorySize_bytes = originalMemorySize; 336 | settings.pollForWriteComplete = originalpollForWriteComplete; 337 | 338 | uint16_t avgTimeUs = totalTime / numberOfTests; 339 | 340 | // Serial.print("avgTimeUs: "); 341 | // Serial.println(avgTimeUs); 342 | 343 | settings.writeTime_ms = 344 | ceil(avgTimeUs * ((1.0 + percentOverage / 100.0) / 1000.0)); // Apply overage, convert to ms, round up 345 | 346 | return (settings.writeTime_ms); 347 | } 348 | 349 | void ExternalEEPROM::enablePollForWriteComplete() 350 | { 351 | settings.pollForWriteComplete = true; 352 | } 353 | void ExternalEEPROM::disablePollForWriteComplete() 354 | { 355 | settings.pollForWriteComplete = false; 356 | } 357 | 358 | constexpr uint16_t ExternalEEPROM::getI2CBufferSize() 359 | { 360 | return I2C_BUFFER_LENGTH_TX; 361 | } 362 | 363 | uint32_t ExternalEEPROM::putString(uint32_t eepromLocation, String &strToWrite) 364 | { 365 | uint16_t strLen = strToWrite.length() + 1; 366 | write(eepromLocation, (uint8_t *)strToWrite.c_str(), strLen); 367 | return (eepromLocation + strLen); 368 | } 369 | void ExternalEEPROM::getString(uint32_t eepromLocation, String &strToRead) 370 | { 371 | if (strToRead.length()) 372 | { 373 | strToRead.remove(0, strToRead.length()); 374 | } 375 | uint8_t tmp = 65; // dummy 376 | while (tmp != 0) 377 | { 378 | tmp = read(eepromLocation); 379 | if (tmp != 0) 380 | { 381 | strToRead += static_cast(tmp); 382 | } 383 | eepromLocation++; 384 | } 385 | } 386 | 387 | // Assuming there are discrete EEPROM sizes, return the number of bytes in the next model 388 | uint32_t getNextSizeBytes(uint32_t currentSizeBytes) 389 | { 390 | if (currentSizeBytes == 16) 391 | return 128; // 24LC00 - Lowest value 392 | return (currentSizeBytes * 2); // 24LC01, 02, 04, 08, etc 393 | } 394 | 395 | void ExternalEEPROM::setAddressBytes(uint8_t addressBytes) 396 | { 397 | settings.addressSize_bytes = addressBytes; 398 | } 399 | uint8_t ExternalEEPROM::getAddressBytes() 400 | { 401 | return settings.addressSize_bytes; 402 | } 403 | 404 | // Determines the number of address bytes to complete a successful write 405 | // Returns 1 or 2 406 | // Sets the internal setting 407 | uint8_t ExternalEEPROM::detectAddressBytes() 408 | { 409 | uint8_t testLocation = 1; 410 | 411 | // Create copy of internal settings before test 412 | uint32_t originalMemorySize = settings.memorySize_bytes; 413 | uint16_t originalPageSize = settings.pageSize_bytes; 414 | 415 | // Read and store before we start (potentially) writing 416 | // This will fail on two byte address EEPROMs when the memory size is below 4096 bytes 417 | uint8_t locationValueOriginal = read(testLocation); 418 | 419 | // Serial.print("locationValueOriginal: 0x"); 420 | // Serial.print(locationValueOriginal, HEX); 421 | 422 | setMemorySizeBytes(128); // Assume the smallest memory size during test 423 | setPageSizeBytes(1); // Assume a page size 424 | 425 | uint8_t addressBytes = 1; 426 | for (; addressBytes < 3; addressBytes++) 427 | { 428 | setAddressBytes(addressBytes); // Start test at one byte 429 | 430 | // Avoid the default state of 0xFF = 255 and 0. Assumes user has randomSeed()ed something. 431 | // Do not use the original value 432 | // Do not use the value found in the next location either 433 | uint8_t magicValue = 0; 434 | do 435 | { 436 | magicValue = random(1, 255); // (Inclusive, exclusive) 437 | } while (magicValue == locationValueOriginal); 438 | 439 | // Serial.print(" writing: 0x"); 440 | // Serial.print(magicValue, HEX); 441 | 442 | // Write this new value 443 | write(testLocation, magicValue); // Will use a 1 or 2 byte address depending on setMemorySizeBytes() 444 | 445 | // Read data back 446 | uint8_t locationValue = read(testLocation); 447 | // Serial.print(" read: 0x"); 448 | // Serial.print(locationValue, HEX); 449 | // Serial.print(" - "); 450 | 451 | if (locationValue == magicValue) 452 | { 453 | // Successful write. We've determined the number of address bytes 454 | break; 455 | } 456 | } 457 | 458 | // Serial.println("\r\nWrite success. One byte addressing."); 459 | // Return spot to its original value 460 | write(testLocation, locationValueOriginal); 461 | 462 | // Return original settings 463 | settings.memorySize_bytes = originalMemorySize; 464 | settings.pageSize_bytes = originalPageSize; 465 | 466 | // Error check 467 | if (addressBytes >= 3) 468 | addressBytes = 1; // If we failed, 1 guarantees we won't corrupt data with two byte address writes 469 | 470 | settings.addressSize_bytes = addressBytes; 471 | return (settings.addressSize_bytes); 472 | } 473 | 474 | // Determine the number of bytes we can write in a single go 475 | // Valid amounts are 1, 8, 16, 32, 128, and 256 bytes 476 | // Sets the internal setting 477 | uint16_t ExternalEEPROM::detectPageSizeBytes() 478 | { 479 | #define maxPageSize 256 // Used in very large 2M bit EEPROMs 480 | 481 | uint8_t originalValuesArray[maxPageSize]; // Preserves data in EEPROM before we start writing 482 | uint8_t tempArray[maxPageSize]; 483 | uint8_t testLocation = 0; // Location in memory to do our tests 484 | 485 | // We can't run this test if we don't know the number of address bytes 486 | if (settings.addressSize_bytes == 0) 487 | detectAddressBytes(); 488 | 489 | // We can't run this test unless we know how to properly read/write to the device 490 | if (settings.memorySize_bytes == 0) 491 | detectMemorySizeBytes(); 492 | 493 | // Read chunk from EEPROM and store 494 | for (uint16_t x = 0; x < maxPageSize; x++) 495 | originalValuesArray[x] = read(testLocation + x); // Read byte wise to avoid page size limitations 496 | 497 | uint16_t pageSizeBytes = 8; 498 | bool detectedPageSize = false; 499 | while (1) 500 | { 501 | // Create new array based on the original values 502 | for (uint16_t x = 0; x < pageSizeBytes; x++) 503 | tempArray[x] = (uint8_t)(originalValuesArray[x] + x); // Avoid original values 504 | 505 | setPageSizeBytes(pageSizeBytes); // Tell library to write this number of bytes at a time 506 | 507 | // Write new magic values to EEPROM 508 | write(testLocation, tempArray, pageSizeBytes); 509 | 510 | // Read magic values from EEPROM 511 | read(testLocation, tempArray, pageSizeBytes); 512 | 513 | // Check values 514 | for (uint16_t x = 0; x < pageSizeBytes; x++) 515 | { 516 | // Serial.print("temp: "); 517 | // Serial.print(tempArray[x]); 518 | // Serial.print(" original: "); 519 | // Serial.print((uint8_t)(originalValuesArray[x] + x)); 520 | // Serial.println(); 521 | 522 | if (tempArray[x] != (uint8_t)(originalValuesArray[x] + x)) 523 | { 524 | // We've detected the limit 525 | 526 | // Serial.print("pageSize: "); 527 | // Serial.print(pageSizeBytes); 528 | // Serial.print(" write failed. Previous page size is the answer "); 529 | 530 | // Reduce the page size to the last value 531 | if (pageSizeBytes == 32 || pageSizeBytes == 16 || pageSizeBytes == 256) 532 | pageSizeBytes /= 2; 533 | else if (pageSizeBytes == 128) 534 | pageSizeBytes = 32; 535 | else if (pageSizeBytes == 8) 536 | pageSizeBytes = 1; 537 | 538 | detectedPageSize = true; 539 | 540 | break; 541 | } 542 | } 543 | 544 | if (detectedPageSize) 545 | break; 546 | 547 | // Serial.print("pageSizeBytes: "); 548 | // Serial.print(pageSizeBytes); 549 | // Serial.println(" passed"); 550 | 551 | // Go to next page size 552 | if (pageSizeBytes == 8 || pageSizeBytes == 16 || pageSizeBytes == 128) 553 | pageSizeBytes *= 2; 554 | else if (pageSizeBytes == 32) 555 | pageSizeBytes = 128; 556 | else if (pageSizeBytes == maxPageSize) 557 | break; // EEPROMs with larger than 256 byte page writes are not known at this time. 558 | 559 | // We can't write more than I2C_BUFFER_LENGTH_TX at a time so that is the limit of our pageSize testing. 560 | if (pageSizeBytes > I2C_BUFFER_LENGTH_TX) 561 | { 562 | // Serial.print("Page size test limited by platform I2C buffer of: "); 563 | // Serial.println(I2C_BUFFER_LENGTH_TX); 564 | break; 565 | } 566 | } 567 | 568 | settings.pageSize_bytes = pageSizeBytes; 569 | 570 | // Write original chunk to EEPROM 571 | write(testLocation, originalValuesArray, pageSizeBytes); 572 | 573 | return (settings.pageSize_bytes); 574 | } 575 | 576 | // Attempts write-then-reads until failure to determine memory bounds 577 | // Identifies the following EEPROM types and their variants: 578 | // 24LC00 - 128 bit / 16 bytes - 1 address byte, 1 byte page size 579 | // 24LC01 - 1024 bit / 128 bytes - 1 address byte, 8 byte page size 580 | // 24LC02 - 2048 bit / 256 bytes - 1 address byte, 8 byte page size 581 | // 24LC04 - 4096 bit / 512 bytes - 1 address byte, 16 byte page size 582 | // 24LC08 - 8192 bit / 1024 bytes - 1 address byte, 16 byte page size 583 | // 24LC16 - 16384 bit / 2048 bytes - 1 address byte, 16 byte page size 584 | // 24LC32 - 32768 bit / 4096 bytes - 2 address bytes, 32 byte page size 585 | // 24LC64 - 65536 bit / 8192 bytes - 2 address bytes, 32 byte page size 586 | // 24LC128 - 131072 bit / 16384 bytes - 2 address bytes, 64 byte page size 587 | // 24LC256 - 262144 bit / 32768 bytes - 2 address bytes, 64 byte page size 588 | // 24LC512 - 524288 bit / 65536 bytes - 2 address bytes, 128 byte page size 589 | // 24LC1024 - 1024000 bit / 128000 byte - 2 address bytes, 128 byte page size 590 | // 24CM02 - 2097152 bit / 262144 byte - 2 address bytes, 256 byte page size 591 | // For EEPROMs of 4k, 8k, and 16k bit, there are three bits called 592 | // 'block select bits' inside the address byte that are used 593 | // For 32k, 64k, 128k, 256k, and 512k bit we need two address bytes 594 | // At 1Mbit (128,000 byte) and above there are two address bytes and a block select bit 595 | // is used but at the upper end of the address bits (so instead of A2/A1/A0 it's B0/A1/A0). 596 | uint32_t ExternalEEPROM::detectMemorySizeBytes() 597 | { 598 | // We do a read-write-read-write to test. 599 | uint32_t testLocation = (128 / 8) - 1; // Start at the last spot of the smallest EEPROM 600 | uint32_t lastGoodLocation = 0; 601 | 602 | // We can't run this test if we don't know the number of address bytes 603 | if (settings.addressSize_bytes == 0) 604 | detectAddressBytes(); 605 | 606 | // detectPageSizeBytes() calls this function so we cannot call it (endless loop) 607 | // Because detectMemorySizeBytes() is only doing single byte writes, a page size of 1 is fine for now 608 | if (settings.pageSize_bytes == 0) 609 | settings.pageSize_bytes = 1; 610 | 611 | // Determine if we are dealing with one or two byte addressing 612 | uint8_t addressBytes = getAddressBytes(); 613 | if (addressBytes == 2) 614 | testLocation = 4096 - 1; // Start test with 2-byte addresses 615 | else 616 | testLocation = 16 - 1; // Start test with 1-byte addresses 617 | 618 | // Smaller EEPROMs ignore and set the address bits A7 through A10 to zero. This causes writes to wrap around. 619 | // So to detect the edge of the usable space, we write to a location beyond the known edge of memory. 620 | // If that same value is at the last known good edge of memory then we know the address is ignoring the address 621 | // bit, it has wrapped the address, and we can infer the edge of memory. 622 | 623 | // 1 Read current value to preserve it 624 | // 2 Read current value at next threshold 625 | // 3 Write value to current spot 626 | // 4 Check value at next threshold. If it was changed then we know the address we wrote was wrapped. 627 | // 5 Return memory spot to its original value 628 | 629 | while (1) 630 | { 631 | // We must set our memory size to the next size up so that write sets the correct bits 632 | uint32_t nextLocation = getNextSizeBytes(testLocation + 1) - 1; 633 | 634 | // Serial.print("nextLocation: "); 635 | // Serial.print(nextLocation); 636 | 637 | setMemorySizeBytes(nextLocation + 1); 638 | 639 | // Serial.print(" Test: "); 640 | // Serial.print(nextLocation + 1); 641 | // Serial.print(" bytes/"); 642 | // Serial.print((nextLocation + 1) * 8); 643 | // Serial.print(" bits"); 644 | // Serial.print(", "); 645 | 646 | // Read and store before we start (potentially) writing 647 | uint8_t originalValue = read(testLocation); 648 | 649 | // Serial.print("originalValue: 0x"); 650 | // Serial.print(originalValue, HEX); 651 | 652 | uint8_t nextLocationOriginalValue = read(nextLocation); 653 | 654 | // Serial.print(" nextLocationOriginalValue: 0x"); 655 | // Serial.print(nextLocationOriginalValue, HEX); 656 | // Serial.print(" - "); 657 | 658 | // Avoid the default state of 0xFF = 255 and 0. Assumes user has randomSeed()ed something. 659 | // Do not use the original value 660 | // Do not use the value found in the next location either 661 | uint8_t magicValue = random(1, 255); // (Inclusive, exclusive) 662 | while (magicValue == originalValue && magicValue == nextLocationOriginalValue) 663 | magicValue = random(1, 255); // (Inclusive, exclusive) 664 | 665 | // Serial.print("writing: 0x"); 666 | // Serial.print(magicValue, HEX); 667 | 668 | // Write this new value to the edge of this block 669 | write(testLocation, magicValue); // Will use a 1 or 2 byte address depending on setMemorySizeBytes() 670 | 671 | // Read back data 672 | uint8_t newValue = read(testLocation); 673 | 674 | // Serial.print(" read: 0x"); 675 | // Serial.print(newValue, HEX); 676 | // Serial.print(" - "); 677 | 678 | // Read data from the next spot before we return the original spot value 679 | uint8_t nextNewValue = read(nextLocation); 680 | 681 | // Serial.print("nextNewValue: 0x"); 682 | // Serial.print(nextNewValue, HEX); 683 | // Serial.print(" - "); 684 | 685 | // Serial.println(); 686 | 687 | // Return spot to its original value 688 | write(testLocation, originalValue); 689 | 690 | lastGoodLocation = testLocation; 691 | 692 | // We should be able to write to all locations. But bail if we fail. 693 | if (newValue != magicValue) 694 | { 695 | // Serial.println("\r\nWrite failed"); 696 | break; 697 | } 698 | 699 | // We assume the address at the end of this memory space has changed, 700 | // but if the address at the next level *also* changes, then we know the 701 | // memory address has wrapped because the IC zeroes out the extra bits 702 | else if (nextNewValue == magicValue) 703 | { 704 | // Serial.println("The write affected the next level - we're done!"); 705 | break; 706 | } 707 | 708 | if (testLocation >= (65536 * 2) - 1) // Limit to 512kBit 709 | break; 710 | 711 | // Move to next location 712 | testLocation = nextLocation; 713 | } 714 | 715 | settings.memorySize_bytes = lastGoodLocation + 1; 716 | 717 | // Serial.print("Memory size in bytes: "); 718 | // Serial.println(settings.memorySize_bytes); 719 | 720 | return (settings.memorySize_bytes); 721 | } 722 | 723 | // Read a byte from a given location 724 | uint8_t ExternalEEPROM::read(uint32_t eepromLocation) 725 | { 726 | uint8_t tempByte; 727 | read(eepromLocation, &tempByte, 1); 728 | return tempByte; 729 | } 730 | 731 | // Bulk read from EEPROM 732 | // Handles breaking up read amt into 32 byte chunks (can be overriden with setI2CBufferSize) 733 | // Handles a read that straddles the 512kbit barrier 734 | int ExternalEEPROM::read(uint32_t eepromLocation, uint8_t *buff, uint16_t bufferSize) 735 | { 736 | int result = 0; 737 | 738 | uint16_t received = 0; 739 | while (received < bufferSize) 740 | { 741 | // Limit the amount to write to a page size 742 | uint16_t amtToRead = bufferSize - received; 743 | if (amtToRead > I2C_BUFFER_LENGTH_RX) // Arduino I2C buffer size limit 744 | amtToRead = I2C_BUFFER_LENGTH_RX; 745 | 746 | // Check if we are dealing with large (>512kbit) EEPROMs 747 | uint8_t i2cAddress = settings.deviceAddress; 748 | if (settings.memorySize_bytes > 0xFFFF) 749 | { 750 | // Figure out if we are going to cross the barrier with this read 751 | if (eepromLocation + received < 0xFFFF) 752 | { 753 | if (0xFFFF - (eepromLocation + received) < amtToRead) // 0xFFFF - 0xFFFA < 32 754 | amtToRead = 755 | 0xFFFF - (eepromLocation + received); // Limit the read amt to go right up to edge of barrier 756 | } 757 | 758 | // Figure out if we are accessing the lower half or the upper half 759 | if (eepromLocation + received > 0xFFFF) 760 | i2cAddress |= 0b100; // Set the block bit to 1 761 | } 762 | // Check if we are dealing with 24LC04/08/16 (512, 1024, and 2048 bytes) 763 | // These use a single address byte but change the I2C address 764 | else if (settings.memorySize_bytes >= 512 && settings.memorySize_bytes <= 2048) 765 | { 766 | // Set I2C Address bits (A2/A1/A0) accordingly 767 | i2cAddress |= ((eepromLocation + received) >> 8); 768 | } 769 | 770 | if (settings.pollForWriteComplete == false) 771 | { 772 | delay(settings.writeTime_ms); 773 | } 774 | 775 | // See if EEPROM is available or still writing a previous request 776 | while (isBusy(settings.deviceAddress) == true) // Poll device's original address, not the modified one 777 | delayMicroseconds(100); // This shortens the amount of time waiting between writes but hammers the I2C bus 778 | 779 | settings.i2cPort->beginTransmission(i2cAddress); 780 | if (settings.addressSize_bytes > 1) 781 | settings.i2cPort->write((uint8_t)((eepromLocation + received) >> 8)); // MSB 782 | settings.i2cPort->write((uint8_t)((eepromLocation + received) & 0xFF)); // LSB 783 | 784 | result = settings.i2cPort->endTransmission(); 785 | 786 | settings.i2cPort->requestFrom((uint8_t)i2cAddress, (size_t)amtToRead); 787 | 788 | for (uint16_t x = 0; x < amtToRead; x++) 789 | buff[received + x] = settings.i2cPort->read(); 790 | 791 | received += amtToRead; 792 | } 793 | 794 | return (result); 795 | } 796 | 797 | // Write a byte to a given location 798 | int ExternalEEPROM::write(uint32_t eepromLocation, uint8_t dataToWrite) 799 | { 800 | if (read(eepromLocation) != dataToWrite) // Update only if data is new 801 | return (write(eepromLocation, &dataToWrite, 1)); 802 | return (0); 803 | } 804 | 805 | // Write large bulk amounts 806 | // Limits writes to the I2C buffer size (default is 32 bytes) 807 | // Returns the result of the I2C endTransmission 808 | int ExternalEEPROM::write(uint32_t eepromLocation, const uint8_t *dataToWrite, uint16_t bufferSize) 809 | { 810 | int result = 0; 811 | 812 | // Error check 813 | if (eepromLocation + bufferSize >= settings.memorySize_bytes) 814 | bufferSize = settings.memorySize_bytes - eepromLocation; 815 | 816 | // Serial.print("bufferSize: "); 817 | // Serial.println(bufferSize); 818 | 819 | int16_t maxWriteSize = settings.pageSize_bytes; 820 | if (maxWriteSize > I2C_BUFFER_LENGTH_TX - settings.addressSize_bytes) 821 | maxWriteSize = 822 | I2C_BUFFER_LENGTH_TX - 823 | settings.addressSize_bytes; // Arduino has 32 byte limit. We loose 1 or 2 bytes to the EEPROM address 824 | 825 | // Serial.print("maxWriteSize: "); 826 | // Serial.println(maxWriteSize); 827 | 828 | // Break the buffer into page sized chunks 829 | uint16_t recorded = 0; 830 | while (recorded < bufferSize) 831 | { 832 | // Limit the amount to write to either the page size or the Arduino limit of 30 833 | int amtToWrite = bufferSize - recorded; 834 | 835 | // Serial.print("amtToWrite: "); 836 | // Serial.println(amtToWrite); 837 | 838 | if (amtToWrite > maxWriteSize) 839 | amtToWrite = maxWriteSize; 840 | 841 | // Serial.print("amtToWrite: "); 842 | // Serial.println(amtToWrite); 843 | 844 | if (amtToWrite > 1) 845 | { 846 | // Check for crossing of a page line. Writes cannot cross a page line. 847 | uint16_t pageNumber1 = (eepromLocation + recorded) / settings.pageSize_bytes; 848 | uint16_t pageNumber2 = (eepromLocation + recorded + amtToWrite - 1) / settings.pageSize_bytes; 849 | if (pageNumber2 > pageNumber1) 850 | amtToWrite = ((pageNumber1 + 1) * settings.pageSize_bytes) - 851 | (eepromLocation + recorded); // Limit the write amt to go right up to edge of page barrier 852 | } 853 | 854 | uint8_t i2cAddress = settings.deviceAddress; 855 | // Check if we are dealing with large (>512kbit) EEPROMs 856 | // These use two address bytes and a B0 'block' bit 857 | if (settings.memorySize_bytes > 0xFFFF) 858 | { 859 | // Figure out if we are accessing the lower half or the upper half 860 | if (eepromLocation + recorded > 0xFFFF) 861 | i2cAddress |= 0b100; // Set the block bit to 1 862 | } 863 | // Check if we are dealing with 24LC04/08/16 (512, 1024, and 2048 bytes) 864 | // These use a single address byte but change the I2C address 865 | else if (settings.memorySize_bytes >= 512 && settings.memorySize_bytes <= 2048) 866 | { 867 | // Set I2C Address bits (A2/A1/A0) accordingly 868 | i2cAddress |= ((eepromLocation + recorded) >> 8); 869 | } 870 | 871 | if (settings.pollForWriteComplete == false) 872 | { 873 | delay(settings.writeTime_ms); 874 | } 875 | 876 | // See if EEPROM is available or still writing a previous request 877 | while (isBusy(settings.deviceAddress) == true) // Poll device's original address, not the modified one 878 | delayMicroseconds(100); // This shortens the amount of time waiting between writes but hammers the I2C bus 879 | 880 | // Check if we are using Write Protection then disable WP for write access 881 | if(settings.wpPin != 255 ) digitalWrite(settings.wpPin, LOW); 882 | 883 | settings.i2cPort->beginTransmission(i2cAddress); 884 | if (settings.addressSize_bytes > 1) // Device larger than 16,384 bits have two byte addresses 885 | settings.i2cPort->write((uint8_t)((eepromLocation + recorded) >> 8)); // MSB 886 | settings.i2cPort->write((uint8_t)((eepromLocation + recorded) & 0xFF)); // LSB 887 | 888 | for (uint16_t x = 0; x < amtToWrite; x++) 889 | settings.i2cPort->write(dataToWrite[recorded + x]); 890 | 891 | result = settings.i2cPort->endTransmission(); // Send stop condition 892 | 893 | recorded += amtToWrite; 894 | 895 | // Serial.print("recorded: "); 896 | // Serial.println(recorded); 897 | 898 | if (settings.pollForWriteComplete == false) 899 | delay(settings.writeTime_ms); // Delay the amount of time to record a page 900 | 901 | // Enable Write Protection if we are using WP 902 | if(settings.wpPin != 255) digitalWrite(settings.wpPin, HIGH); 903 | } 904 | 905 | return (result); 906 | } 907 | --------------------------------------------------------------------------------