Writing to NOR flash memory

I agree completely with that, and please keep it in mind while reading the rest of this post.

Here is my code to read and write from a Microchip 24LC512 EEPROM. This was written to run on a 32 bit PIC, the register names reflect that, so would need to be modifed for another chip.

Note:

  • The function EEPROM_readWrite() to be called every time around loop() or (in the case of standard C) around main().
  • This code drives I2C directly, there is no I2C library or other code to handle the I2C hardware.
  • The 2 variables EEPROM_startAddress and EEPROM_endAddress are badly named as they are not addresses in EEPROM, they are are the start and end of the data to be written to or read back from EEPROM, in this case an array.
  • readNWrite controls whether the data is being written to or read from EEPROM.
  • The code assumes the data will be stored from address 0x00 in the EEPROM, obviously there is room for improvement there.
  • At the end of the main code below I have also included 2 short functions that start the reading or start the writing sequence.

Code offered 'as is' for you use, abuse, change or ignore as you see fit.

Main code

int16_t EEPROM_startAddress;
int16_t EEPROM_endAddress;
bool readNWrite;
const uint16_t RnW_pin = 0x0010;                                            // Read not Write pin RA4
void EEPROM_readWrite() {
    static uint32_t lastMillis;
    uint32_t currentMillis = millis();
    uint32_t wait = 10;                                                         // Wait for write operation, required maximum from data sheet is 5ms
    static uint8_t state = 0;
            
    if (EEPROM_startAddress != EEPROM_endAddress) {
        if (!(I2C2CON & 0x001f)) { 
            switch (state) {
                case 0:
                    if (EEPROM_startAddress > EEPROM_endAddress || EEPROM_startAddress > sizeof(HMI_SliderData) ||  EEPROM_endAddress > sizeof(HMI_SliderData)) {
                        EEPROM_startAddress = EEPROM_endAddress = 0;
                    }
                    LATACLR = RnW_pin;                                          // Write enable
                    I2C2CON = 0x8201;                                           // Send START
                    ++state;
                    break;
                case 1:
                    I2C2TRN = 0xa0;                                             // Write (Both read and write need the address sending as write first)
                    ++state;
                    break;
                case 2:
                    if (((~I2C2STAT) & 0x8000) && ((~I2C2STAT) & 0x4000)) {     // Check for ACKNOWLEDGE
                        I2C2TRN = (uint8_t)(EEPROM_startAddress >> 8);       // High byte of address                             
                        ++state;
                    }
                    break;
                case 3:
                    if (((~I2C2STAT) & 0x8000) && ((~I2C2STAT) & 0x4000)) {     // Check for ACKNOWLEDGE
                        I2C2TRN = (uint8_t)(EEPROM_startAddress);               // Low byte of address                     
                        ++state;
                    }
                case 4:
                    if (((~I2C2STAT) & 0x8000) && ((~I2C2STAT) & 0x4000)) {     // Check for ACKNOWLEDGE
                        if (readNWrite) {                                       // High means read, low means write
                            LATASET = RnW_pin;                                  // Disable EEPROM write
                            I2C2CONSET = 0x0002;                                // Send START for read
                            ++state;
                        } else {
                            state = 9;                                          // Start of write code
                        }
                    }
                    break;
                case 5:
                    I2C2TRN = 0xa1;                                             // Read
                    ++state;
                    break;
                case 6:
                    if (((~I2C2STAT) & 0x8000) && ((~I2C2STAT) & 0x4000)) {     // Check for ACKNOWLEDGE
                        I2C2CON = 0x8208;                                       // Enable receive
                        ++state;
                    }
               case 7:
                    if (I2C2STAT & 0x02) {                                      // Data in read buffer
                        *(HMI_SliderData[0][0] + EEPROM_startAddress) = I2C2RCV;
                        ++EEPROM_startAddress;
                        if (EEPROM_startAddress == EEPROM_endAddress) {
                            I2C2CON = 0x8204;                                   //Send STOP
                            readNWrite = 1;
                            state = 0;
                        } else {
                            if (EEPROM_startAddress == 128  || EEPROM_startAddress == 256 || EEPROM_startAddress == 384 || EEPROM_startAddress == 512) {
                               I2C2CONSET = 0x04;                               // Send STOP 
                               state = 0;                                       // Start again for new page
                            } else {
                                I2C2CONSET = 0x8210;                            //Send ACKNOWLEDGE
                                state = 8;
                            }
                        }
                    }
                    break;
                case 8:
                    I2C2CON = 0x8208;                                           // Enable receive
                    state = 7;
                    break;
                case 9:
                    I2C2TRN = *(HMI_SliderData[0][0] + EEPROM_startAddress);
                    ++state;
                    break;
                case 10:
                    if (((~I2C2STAT) & 0x8000) && ((~I2C2STAT) & 0x4000)) {     // Check for ACKNOWLEDGE
                        ++EEPROM_startAddress;
                        if (EEPROM_startAddress == EEPROM_endAddress) {         // Finished
                            I2C2CONSET = 0x04;                                  // Send STOP
                            lastMillis = millis();
                            state = 12;
                        } else {
                            if (EEPROM_startAddress == 128  || EEPROM_startAddress == 256 || EEPROM_startAddress == 384 || EEPROM_startAddress == 512) {
                               I2C2CONSET = 0x04;                               // Send STOP 
                               lastMillis = millis();                           // Wait for EEPROM write
                               state = 11;
                            } else {
                                state = 9;  
                            }
                        }
                    }
                    break;
                case 11:
                    if (currentMillis - lastMillis > wait) {
                        state = 0;                                              // Start again for new page
                    }
                    break;
                case 12:
                    if (currentMillis - lastMillis > wait) {
                        LATASET = RnW_pin;                                      // Disable EEPROM write
                        readNWrite = 1;
                        state = 0;
                    }
                    break;
                default:
                    I2C2CON = 0x8204;                                           //Send STOP
                    LATASET = RnW_pin;                                          // Disable EEPROM write
                    state = 0;
                    break;
            }
        }
    } else if (state) {
        if (currentMillis - lastMillis > wait) {
            LATASET = RnW_pin;                                  // Disable EEPROM write
            readNWrite = 1;
            state = 0;
        }
    }
}

Start the write sequence

// Write values in settings to EEPROM; only intended to be used once to initialise EEPROM on first use or if default settings are changed
void initialiseEEPROMwrite() {
    EEPROM_startAddress = 0x00;
    EEPROM_endAddress = sizeof(HMI_SliderData);
    readNWrite = 0;
}

Starts the read sequence

// Read data from EEPROM to initialise HMI_SliderData; to be used once EEPROM has been pre-configured with correct start values
void initialiseEEPROMread() {
    EEPROM_startAddress = 0x00;
    EEPROM_endAddress = sizeof(HMI_SliderData);
    readNWrite = 1;
}