EEPROM Access Routines For PIC18

While I was playing with CAN bus, I needed to write something to EEPROM memory - to keep it safe between reboots. Imagine my surprise when calls to eeprom_read and eeprom_write returned errors:

warning: unsupported: The Read_b_eep routine is no longer supported. Please use the MPLAB X MCC.
warning: unsupported: The Busy_eep routine is no longer supported. Please use the MPLAB X MCC.
warning: unsupported: The Write_b_eep routine is no longer supported. Please use the MPLAB X MCC.

It seems PIC18 series doesn't support old EEPROM routines that worked on PIC16 but, at the same time, my PIC is old enough not to be properly supported in the suggested MCC. I guess it was time to write PIC18 version of these functions myself.

Reading over documentation for PIC18F25K80, I was pleasantly surprised that EEPROM writing hasn't really changed. My assembly code I wrote back in PIC16F84 days was quite close to what's needed these days too. The only significant change was expanding EEADR to allow for more than 256 bytes. Yes, there are some shenanigans with setting EEPGD/CFGS bits but that's done only once.

My replacement for eeprom_read function was similarly named eeprom18_read and it's really a drop-in replacement. Not to be the one to dwell in past, I opted to change my ASM code to XC8 flavored C. It's essentially just setting up EEPROM address, followed by read request, a short pause to get the data, and then reading the data itself.

Code
unsigned char eeprom18_read(unsigned int offset) {
EECON1bits.EEPGD = 0; //accesses data EEPROM memory
EECON1bits.CFGS = 0; //accesses data EEPROM memory

EEADRH = offset >> 8;
EEADR = offset;

EECON1bits.RD = 1; //initiates an EEPROM read
Nop(); //it can be read after one NOP instruction

return EEDATA;
}

Writing was a bit more involved but not too much so. After setting EEPROM address and data, it was important to allow EEPROM write and disable interrupts as ancient EEPROM unlock sequence (0x55 0xAA) must not be interrupted. After write is initiated, code will wait for it to complete and then restore interrupts. For safety it will also disable further writes to EEPROM until it's needed again.

Code
void eeprom18_write(unsigned int offset, unsigned char value) {
EECON1bits.EEPGD = 0; //accesses data EEPROM memory
EECON1bits.CFGS = 0; //accesses data EEPROM memory

EEADRH = offset >> 8;
EEADR = offset;

EEDATA = value;

EECON1bits.WREN = 1; //allows write cycles
unsigned char oldGIE = GIE; //interrupts be disabled during this code segment

EECON2 = 0x55; //write sequence unlock
EECON2 = 0xAA; //write sequence unlock

EECON1bits.WR = 1; //initiates a data EEPROM erase/write cycle
while(EECON1bits.WR); //waits for write cycle to complete

GIE = oldGIE; //restore interrupts
EECON1bits.WREN = 0; //disable write
}

3 thoughts to “EEPROM Access Routines For PIC18”

  1. Hi Josip.
    I found this post helpful.
    So is there no need for a replacement for the old Busy_eep() routine with the nop() and while-loop code you have in your solution ?
    Also, in your eeprom18_write routine you define “value” twice, any reason for this ?
    Thanks,
    Hammerson

    1. If Busy_eep works with your compiler, you can continue to use it – this code is just a replacement for when compiler has no such methods.
      I fixed copy/paste error resulting in value defined twice.

      1. You need to set EEPGD back to 1 if you want to be able to read constants that you put in rom (table read will stop working because the compiler will assume this bit is still set). I just ran into this problem when porting code from SourceBoost to Microchip’s XC8.

        Here is my code for 18F2620 (OLD_DEV) and 18F26K40

        unsigned char EERead(unsigned char Addr)
        {    
            unsigned char RetVal;
                
        #ifdef OLD_F_DEV
            EEADR = Addr;
            EEADRH = 0;  // Just access low 256 bytes
            EECON1bits.EEPGD=0;
            EECON1bits.CFGS = 0;
            EECON1bits.RD = 1;
            RetVal = EEDATA;
            EECON1bits.EEPGD=1;   // Restore flash access for table read
            return(RetVal);
        #else    
            NVMADRL = Addr;
            NVMADRH = 0;  // Just access low 256 bytes
            NVMCON1bits.NVMREG0 = 0;
            NVMCON1bits.NVMREG1 = 0;
            NVMCON1bits.RD = 1;
            RetVal = NVMDAT;
            NVMCON1bits.NVMREG1 = 1;  // Restore flash access for table read
            return(RetVal);    
        #endif 
        }
        

Leave a Reply

Your email address will not be published. Required fields are marked *