PIC18F XC8 - EEPROM (Newbie Question) - Page 1
Forum section: electronics / beginners

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 30, 2017, 10:38:36 pm
content: Hi all,I'm very fresh to all of this, please go easy on me!Code://Global Variablesvolatile char T0COUNT; //Variable for Timer0 Countervolatile char LPFLG ; //Flag for MEDIUM PUMP speed timervolatile char MEDTIME = 5; //Loops for TIMER0 to run//Set Timer Interrupt Procedurevoid interrupt high_isr (void){if(INTCONbits.TMR0IF == 1) { //Check for status of TMR0 OverflowINTCONbits.TMR0IF = 0; //if OVERFLOW occurs reset TMR0 overflow flag.T0COUNT++; //Increment T0COUNT by 1 for every time TMR0 overflows.if(T0COUNT==MEDTIME) {LPFLG = 1; //When counter total matches time required, set flag to 1T0COUNT = 0;}}if(INTCON3bits.INT3IF == 1){ //if programming pin is HIGHINTCON3bits.INT3IF = 0; //Clear interrupt flagTRISBbits.RB2 = 1; //enable RB2 PROGDIR pinif(PROGDIR == 1){ //check for HI PROGDIR pin__delay_ms (50); //wait 50ms for bounceif(PROGDIR == 1){DIR= ~DIR;}}}}int main (){OSCCONbits.IRCF2 = 1; //Set clock speed to 1MHzOSCCONbits.IRCF1 = 0; //Set clock speed to 1MHzOSCCONbits.IRCF0 = 0; //Set clock speed to 1MHzRCONbits.IPEN = 1; //enable interrupt priorityINTCONbits.GIE = 1; //enable all interruptsINTCONbits.PEIE =1; //enable internal interruptsT0CONbits.TMR0ON = 0; //switch timer OFF in initial stateT0CONbits.T0PS0 = 1; //set TIMER0 prescaler to 1:256 on TOP0-2T0CONbits.T0PS1 = 1;T0CONbits.T0PS2 = 1;T0CONbits.PSA = 1; //Enable pre-scalerT0CONbits.T0CS = 0; //TIMER0 uses Internal OsccilatorT0CONbits.T016BIT = 0; //TIMER0 uses 16 bit counterADCON1 = 0xFF; //switch OFF Port-A ADCCMCON = 0xFF; //switch OFF comparitorsPWMEN1 = 0xFF; //switch OFF PWM moduleINTCONbits.TMR0IE = 1; //enable TIMER0 overflow bitINTCON3bits.INT3E = 1; //enable INT3 External InterruptINTCON2bits.INTEDG3 = 1; //enable INT3 RIDING EDGE interruptTRISAbits.RA0 = 0; //define A0 as OUTPUTTRISAbits.RA1 = 0; //define A1 as OUTPUTTRISBbits.RB0 = 1; //define B0 as INPUTwhile (1) //Do this foreverif(SPEED == 1 ) { //if SPPED pin HIif(LPFLG == 0){ //If Loop Counter FLAG LOT0CONbits.TMR0ON = 1; //switch timer ONPULSE= ~PULSE; //Set PULSE toggle__delay_us(25); //Toggle pulse MEDIUM}if(LPFLG == 1 ) { //if Loop Counter FLAG HIT0CONbits.TMR0ON = 0; //Switch TIMER0 OFF!PULSE= ~PULSE; //Set PULSE toggle__delay_us(50); //Toggle pulse SLOW}}else {T0CONbits.TMR0ON = 0; //Switch TIMER0 OFF!LPFLG = 0; //Reset loop counterPULSE= ~PULSE;__delay_us(5); //Toggle pulse FAST}return 0;}Now idiot here didn't realise of course that on power-interruption that the 'DIR' with restart at 0, which is no good, it should be field programmable (this is for controlling stepper motors - the wiring on them wasn't consistent so 50% will spin 'backwards' and re-wiring them is a faff, the driver direction pin is RB1).'Ah', thinks Ed, I'll put the value of DIR in the EEPROM so it can sit there forever and a day and it'll be initiated when the device restarts. I'm using PIC18F1330 which has 128b of EEPROM available. This field-programmable device will only need to be programmed perhaps maximum a few times a year, the datasheet for the chip warns about requiring refreshing of seldom used data, then in a box underneath directly contradicts this? (pg. 84 'If [EEPROM DATA] changes rarely, an array fresh is likely not required.)And now I'm stuck, I've tried to understand this but I'm very confused. I've read plenty of conflicting things about EEPROM implementation in XC8, can I just check the following concepts:EEPROM macros in XC8 are either not-implemented/badly implemented/easy and wonderful depending on where you look!Is it worthwhile for me to grasp writing my own macro with the datasheet or use the XC8 macros?When the variable is called in this code, can I simply point the variable at an address, ie. char DIR =  or is it more complicated than that?As above, will this be called constantly from EEPROM or is it loaded in to RAM, I've seen dire warnings online about poor logic 'burning out' addresses through constant read-write in run-timeWhen the unit is programmed in the field, will the unit need to be re-powered for the value to be loaded in, or on leaving the ISR will the new value be automatically loaded if it's in EEPROMI'm really sorry if these sound like idiot questions, I'm very new to this and it's an awful lot to take in, especially where internets sez conflicting things. I'm not after 'code' per-se I'm just trying to grasp the concept so I can move forward.Thanks,Ed

author: mikerj
author_rank: Super Contributor
timestamp: January 30, 2017, 11:24:15 pm
content: IIRC the problem is that data in EEPROM addresses that are never or rarely written can eventually get corrupted by frequent writes to other addresses, so it's recommended that these areas get refreshed occasionally. If you aren't doing frequent writes to any part of the EEPROM then you don't have to worry about this.

author: Ian.M
author_rank: Super Contributor
timestamp: January 30, 2017, 11:58:41 pm
content: XC8 only has built-in EEPROM support for older PIC12/16. All PIC18 EEPROM support was depreciated in favour of PLIB and now that's depreciated in favour of MCC which doesn't support older devices!Your best bet is to install PLIB (Legacy Peripheral Libraries for PIC18) (separate download from XC compilers page). I don't recommend linking with PLIB directly, its becoming increasingly incompatible with new XC8 releases, so copy the files from the XC8 subfolder sources\pic18\plib\EEP into your project then modify them to suit. The functionality is quite basic - read or write a byte at a specified address.Your code should copy the parameter(s) from EEPROM to ordinary RAM variable(s) at startup then use the RAM copies. If you require user intervention to write the parameters back to the EEPROM, and only write on a debounced button push or similar so there is virtually no possibility of the write being triggered by a hardware fault you don't really have to worry about endurance. You *WILL* run into endurance problems if you do stuff like automatically write back parameters when impending power failure is detected, and have poor power quality - one application in an Eastern European factory was actually writing the same location several times a minute and wore out the EEPROM in under a year!.To avoid data corruption (+ a very small risk of FLASH corruption) its essential to either enable BOR (or provide an external reset controller) if you are using the EEPROM.The byte endurance is high enough that the user can mash that 'store' button 9 times a day for 30 years before you have any risk of trouble and you've got even odds of it surviving then times as much use. However if you have say 100 bytes of frequently changed parameters and the rest stuff that's only set once like a serial number, you are more likely to run into trouble as parameter D124 tell you that after 1 million individual byte writes you should refresh (read back and rewrite) the rest of the EEPROM. If you write the whole 100 byte parameter block each time that's only 10K writes or fractionally under once a day for 30 years.

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 31, 2017, 12:08:02 am
content: Quote from: Ian.M on January 30, 2017, 11:58:41 pmMany thanks Ian,That might explain why with only the xc.h include although MPLab will compile 'eeprom_write' when debugging they're just stepped over.That seems a bit odd to me, as it seems to think it's an allowable statement but they're simply stepped over at runtime?I'll get hold of PLIB accordingly!Cheers,Ed

author: Buriedcode
author_rank: Super Contributor
timestamp: January 31, 2017, 12:21:07 am
content: I'm not sure you really need PLIB as you could just write your own routine to read (simply) and if needs be, write (just as easy) the EEPROM. If you've found it, and it works, then fine but its far from essential.There is a 'write error' flag for detecting if the write was interrupted in some way which is generally a good check.The datasheet provides sample code necessary for both operations, although it is in assembly - should be easy enough to follow given the notes. I, or I'm sure someone, else can provide C routines if needs be.

author: Ian.M
author_rank: Super Contributor
timestamp: January 31, 2017, 12:26:39 am
content: You don't *NEED* PLIB, but rather than reinventing the wheel, you might as well start from the PLIB sources.Regarding the supposedly built-in EEPROM macros: Some F---ing Idiot decided they should be 'dummied' out if you aren't linking with PLIB instead of raising an error due to being depreciated.See include\pic18.h:Code:// MACROS for EEPROM Access// EEPROM related functions no longer supported. Use the peripheral library implementation if available// or the MPLAB X MCC.#if _EEPROMSIZE > 0 && defined(_PLIB)_NO_PLIB_SUPPORT(Read_b_eep) unsigned char Read_b_eep(unsigned int badd);_NO_PLIB_SUPPORT(Busy_eep) void Busy_eep(void);_NO_PLIB_SUPPORT(Write_b_eep) void Write_b_eep(unsigned int badd, unsigned char bdat);#define EEPROM_READ(addr)Read_b_eep(addr)#define eeprom_read(addr)Read_b_eep(addr)#define EEPROM_WRITE(addr, value)(Busy_eep(), Write_b_eep(addr,value))#define eeprom_write(addr, value)(Busy_eep(), Write_b_eep(addr,value))#else#define EEPROM_READ(addr)0// Added only for code portability#define eeprom_read(addr)0#define EEPROM_WRITE(addr, value)// Added only for code portability#define eeprom_write(addr, value)#endif

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 31, 2017, 08:37:26 pm
content: Okay I've had a go at this following the instructions in the datasheet, I may well have cocked this up!voidread_eeprom(unsigned int ADDRESS){ //Set up function for 'read_eeprom' and define address as unsigned_int.EEADR = ADDRESS; // load ADDRESS in to EEADR registerEECON1bits.EEPGD =0; //set access to EEPROM memoryEECON1bits.RD = 1; // Initiate EEPROM read, bit cleared by hardware after read.}as well as creating function:voidwrite_eeprom(unsigned int ADDRESS, unsigned int DATA){INTCONbits.GIE = 0; //switch off ALL interrupts during readEECON1bits.WREN = 1; //enable WRITE to EEPROMEEADR = ADDRESS; //load ADDRESS in to EEADR registerEEDATA = DATA; //load DATA in to DATA registerEECON2 = 0x55; //write 55h to EECON2 to initiate writeEECON2 = 0x0AA; //write 0AAh to EECON2 to initiate writeEECON1bits.WR = 1; //initiate WRITE cycleINTCONbits.GIE = 1; // re-enable global interupts***as well as the following in the High ISR:if(EEIF =1){ //if EEPROM write successfulEEIF = 0; //reset EEPROM write flagEECON1bits.WREN = 0; //Inhibit further reads until read_eeprom called.The whole shebang:Code:/** File: timer_setup.c* Author: ANDY** Created on January 22, 2017, 10:42 AM*/#include <pic18f1330.h>#include <xc.h>/*Pumo Driver*/// P// Written by ED REARDON// Rev 1.0 October 2016// Written for XC8 C compiler v1.38// Please do not modify any code without instruction.// PIC18F1330 Configuration Bit Settings// 'C' source line config statements// CONFIG1H#pragma config OSC = INTIO1 // Oscillator (Internal oscillator, CLKO function on RA6, port function on RA7)#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)#pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)// CONFIG2L#pragma config PWRT = ON // Power-up Timer Enable bit (PWRT enabled)#pragma config BOR = BOHW // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))#pragma config BORV = 1 // Brown-out Reset Voltage bits ()// CONFIG2H#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))#pragma config WDTPS = 128 // Watchdog Timer Postscale Select bits (1:128)// CONFIG3L#pragma config PWMPIN = OFF // PWM Output Pins Reset State Control bit (PWM outputs disabled upon Reset)#pragma config LPOL = HIGH // Low-Side Transistors Polarity bit (Even PWM Output Polarity Control bit) (PWM0, PWM2 and PWM4 are active-high (default))#pragma config HPOL = HIGH // High Side Transistors Polarity bit (Odd PWM Output Polarity Control bit) (PWM1, PWM3 and PWM5 are active-high (default))// CONFIG3H#pragma config FLTAMX = RA7 // FLTA Mux bit (FLTA input is muxed onto RA7)#pragma config T1OSCMX = HIGH // T1OSO/T1CKI MUX bit (T1OSO/T1CKI pin resides on RA6)#pragma config MCLRE = OFF // Master Clear Enable bit (RA5 input pin enabled, MCLR pin disabled)// CONFIG4L#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable bit (Reset on stack overflow/underflow enabled)#pragma config BBSIZ = BB1K // Boot Block Size Select bits (1K Words (2048 Bytes) Boot Block size)#pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled)// CONFIG5L#pragma config CP0 = OFF // Code Protection bit Block 0 (000800-000FFF) (Block 0 is not code-protected)#pragma config CP1 = OFF // Code Protection bit Block 1 (001000-001FFF) (Block 1 is not code-protected)// CONFIG5H#pragma config CPB = OFF // Code Protection bit (Boot Block Memory Area) (Boot Block is not code-protected)#pragma config CPD = OFF // Code Protection bit (Data EEPROM) (Data EEPROM is not code-protected)// CONFIG6L#pragma config WRT0 = OFF // Write Protection bit Block 0 (000800-000FFF) (Block 0 is not write-protected)#pragma config WRT1 = OFF // Write Protection bit Block 1 (001000-001FFF) (Block 1 is not write-protected)// CONFIG6H#pragma config WRTC = OFF // Write Protection bit (Configuration Registers) (Configuration registers are not write-protected)#pragma config WRTB = OFF // Write Protection bit (Boot Block Memory Area) (Boot Block is not write-protected)#pragma config WRTD = OFF // Write Protection bit (Data EEPROM) (Data EEPROM is not write-protected)// CONFIG7L#pragma config EBTR0 = OFF // Table Read Protection bit Block 0 (000800-000FFF) (Block 0 is not protected from table reads executed in other blocks)#pragma config EBTR1 = OFF // Table Read Protection bit Block 1 (001000-001FFF) (Block 1 is not protected from table reads executed in other blocks)// CONFIG7H#pragma config EBTRB = OFF // Table Read Protection bit (Boot Block Memory Area) (Boot Block is not protected from table reads executed in other blocks)// #pragma config statements should precede project file includes.// Use project enums instead of #define for ON and OFF.#define _XTAL_FREQ 1000000#define PULSE LATAbits.LATA0 //define pin RA0 as PULSE pin#define DIR LATAbits.LATA1 //define pin RA1 as DIRECTION pin#define SPEED PORTBbits.RB0 //Define SPEED H/L Pin#define PROGDIR PORTBbits.RB2 //Define Direction Programming Pin//Global Variablesvolatile int T0COUNT; //Variable for Timer0 Countervolatile int LPFLG ; //Flag for MEDIUM PUMP speed timervolatile int MEDTIME = 5; //Loops for TIMER0 to runvoid read_eeprom (unsigned int ADDR){ //Set up function for 'read_eeprom' and define address as unsigned_int.EEADR = ADDR; // load ADDRESS in to EEADR registerEECON1bits.EEPGD =0; //set access to EEPROM memoryEECON1bits.RD = 1; // Initiate EEPROM read, bit cleared by hardware after read.}void write_eeprom (unsigned int ADDRESS, unsigned int DATA){INTCONbits.GIE = 0; //switch off ALL interrupts during readEECON1bits.WREN = 1; //enable WRITE to EEPROMEEADR = ADDRESS; //load ADDRESS in to EEADR registerEEDATA = DATA; //load DATA in to DATA registerEECON2 = 0x55; //write 55h to EECON2 to initiate writeEECON2 = 0x0AA; //write 0AAh to EECON2 to initiate writeEECON1bits.WR = 1; //initiate WRITE cycleINTCONbits.GIE = 1; // re-enable global interupts}//Set Timer Interrupt Procedurevoid interrupt high_isr (void){if(INTCONbits.TMR0IF == 1) { //Check for status of TMR0 OverflowINTCONbits.TMR0IF = 0; //if OVERFLOW occurs reset TMR0 overflow flag.T0COUNT++; //Increment T0COUNT by 1 for every time TMR0 overflows.if(T0COUNT==MEDTIME) {LPFLG = 1; //When counter total matches time required, set flag to 1T0COUNT = 0;}}if(INTCON3bits.INT3IF == 1){ //if programming pin is HIGHINTCON3bits.INT3IF = 0; //Clear interrupt flagTRISBbits.RB2 = 1; //enable RB2 PROGDIR pinif(PROGDIR == 1){ //check for HI PROGDIR pin{if(DIR == 0);write_eeprom(0xFF, 1);}{if (DIR == !0);write_eeprom(0xFF, 0);}}if(EEIF =1){ //if EEPROM write successfulEEIF = 0; //reset EEPROM write flagEECON1bits.WREN = 0; //Inhibit further reads until read_eeprom called.}}int main (){OSCCONbits.IRCF2 = 1; //Set clock speed to 1MHzOSCCONbits.IRCF1 = 0; //Set clock speed to 1MHzOSCCONbits.IRCF0 = 0; //Set clock speed to 1MHzRCONbits.IPEN = 1; //enable interrupt priorityINTCONbits.GIE = 1; //enable all interruptsINTCONbits.PEIE =1; //enable internal interruptsT0CONbits.TMR0ON = 0; //switch timer OFF in initial stateT0CONbits.T0PS0 = 1; //set TIMER0 prescaler to 1:256 on TOP0-2T0CONbits.T0PS1 = 1;T0CONbits.T0PS2 = 1;T0CONbits.PSA = 1; //Enable pre-scalerT0CONbits.T0CS = 0; //TIMER0 uses Internal OsccilatorT0CONbits.T016BIT = 0; //TIMER0 uses 16 bit counterADCON1 = 0xFF; //switch OFF Port-A ADCCMCON = 0xFF; //switch OFF comparitorsPWMEN1 = 0xFF; //switch OFF PWM moduleINTCONbits.TMR0IE = 1; //enable TIMER0 overflow bitINTCON3bits.INT3E = 1; //enable INT3 External InterruptINTCON2bits.INTEDG3 = 1; //enable INT3 RIDING EDGE interruptTRISAbits.RA0 = 0; //define A0 as OUTPUTTRISAbits.RA1 = 0; //define A1 as OUTPUTTRISBbits.RB0 = 1; //define B0 as INPUTDIR = (read_eeprom, 0x00);while (1) //Do this foreverif(SPEED == 1 ) { //if SPPED pin HIif(LPFLG == 0){ //If Loop Counter FLAG LOT0CONbits.TMR0ON = 1; //switch timer ONPULSE= ~PULSE; //Set PULSE toggle__delay_us(25); //Toggle pulse MEDIUM}if(LPFLG == 1 ) { //if Loop Counter FLAG HIT0CONbits.TMR0ON = 0; //Switch TIMER0 OFF!PULSE= ~PULSE; //Set PULSE toggle__delay_us(50); //Toggle pulse SLOW}}else {T0CONbits.TMR0ON = 0; //Switch TIMER0 OFF!LPFLG = 0; //Reset loop counterPULSE= ~PULSE;__delay_us(5); //Toggle pulse FAST}return 0;}Now I've got two issues, assuming I've set up my functions correctly:Firstly the really confusing one* When compiling the compiler complains that 'read_eeprom' function isn't called, although it clearly is as 'DIR == (read_eeprom 0xFF)Stepping through in MPLAB with the software it just ignores this, it doesn't go round the read_eeprom function.* When write_eeprom is called in the interrupt it steps through 'WRITE*_eeprom' on both 0/!0 on '0!' it does raise the complete interrupt on!0 but not on 0.I don't know if this is normal behaviour or not but it seems odd that it still calls the function even if the argument isn't the case.This is going to be numpty here having done something silly, I'm fraustrated now as once this is done I'm ready to 'rock and roll!'Any suggestions ladies and gents, I can smell this finish line now!I've only been writing 'C' a few days, so any helpful pointers really are welcomed!*Edited, initially said 'read!'

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 31, 2017, 08:40:04 pm
content: Here's a package if you want to have a look

author: Ian.M
author_rank: Super Contributor
timestamp: January 31, 2017, 09:24:02 pm
content: Your read function is borked - it doesn't return anything and you aren't even calling it correctly:DIR =(read_eeprom, 0x00);Unless you cant tolerate the typically 4ms delay for an EEPROM byte write to complete, it is usual to simply poll the WR bit rather than using the EEPROM write complete interrupt. Also you can clear WREN immediately after setting WR as it doesn't abort the current write.Here's a slightly simplified version of the PLIB code:Code:unsigned char EEread_b(unsigned char badd ){EEADR = badd;EECON1bits.CFGS = 0;EECON1bits.EEPGD = 0;EECON1bits.RD = 1;Nop(); //Nop may be required for latency at high frequenciesNop(); //Nop may be required for latency at high frequenciesreturn ( EEDATA ); // return with read byte}void EEwrite_b( unsigned char badd, unsigned char bdat ){bit GIE_BIT_VAL;EEADR = badd;EEDATA = bdat;EECON1bits.EEPGD = 0;EECON1bits.CFGS = 0;EECON1bits.WREN = 1;GIE_BIT_VAL = INTCONbits.GIE;INTCONbits.GIE = 0;EECON2 = 0x55; // critical unlock sequenceEECON2 = 0xAA;EECON1bits.WR = 1; // end critical sequencewhile(EECON1bits.WR); //Wait till the write completionINTCONbits.GIE = GIE_BIT_VAL;EECON1bits.WREN = 0;}Rip out the EEPROM WREN stuff from the high ISR and use:Code:DIR = EEread_b(0x00);to read DIR from the byte at address 0x00 of the EEPROM.Getting data preloaded into byte 0 before your program runs is another matter - there is a macro __EEPROM_DATA() for it but it loads eight bytes at a time starting from 0 and incrementing the address each time. If you need to leave gaps or not start from 0, seehttp://www.microchip.com/forums/m797439.aspx

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 31, 2017, 10:34:01 pm
content: Quote from: Ian.M on January 31, 2017, 09:24:02 pmMany thanks Ian. M, as always. I should have added the return, I am a newbie, forgive me, of course it doesn't make any sense for it not to do anything! That was the first function I've ever written though. I can stand a delay of well.. .minutes frankly so I've taken the ISR routine out and gone to the modifed PLIB as you describe.Can I preload the EEPROM through the IPE rather than in software? Is that a possibility rather than doing it in chip? Essentially I just want a 1 to start with and for the user to change this between 1 and 0, that's all I demand of this EEPROM. Or can I set another 'flag' in memory to show that the variable has been changed then before the while loop have something like:(in concept! not code)- See if flag has been set to change variables- If they have been changed, ignore- If they haven't been changed before, write one to DIR memory.If I pre-load '1 or 0' in to the address in the simulator window I'm getting the DIR pin to behave as I expect, however in my 'if' statements to write DIR pin are both called, this is something in my logic here, essentially it always comes out of the interrupt at 0 and the write function is called on both branches. That's something weird I must have set up!Am I making sense or talking nonsense!Cheers,Ed

author: ed_reardon
author_rank: Regular Contributor
timestamp: January 31, 2017, 10:43:40 pm
content: I think it might be because I'm recalling the GIE back in the write routine?Let me have a fiddle

author: Ian.M
author_rank: Super Contributor
timestamp: January 31, 2017, 10:52:36 pm
content: Yes, the IPE can be used to directly edit the EEPROM before programming, and also to read it back and view its contents after running. However there is a fly in the ointment - the programming algorithm can allow the PIC to run for a short time in between phases of the programming process, so if your code writes to the EEPROM immediately after startup without eitherwaiting for an external pin change, or a delay (approx 1 second is good), its likely to start writing to the EEPROM before programming or program/data readback is finished. The PIC resets as it enters each phase of programming, so any EEPROM write in progress gets aborted, often leaving 0xFF (the erased value), but depending on the exact moment the programming cycle was aborted, other values are also possible.

author: ed_reardon
author_rank: Regular Contributor
timestamp: February 01, 2017, 12:24:52 am
content: Got there in the end, damn blasted curly braces!It's all a good learning curve though!

author: Ian.M
author_rank: Super Contributor
timestamp: February 01, 2017, 12:34:44 am
content: There's another refinement that may be useful tf the write delay ever becomes a problem.If you move theCode:while(EECON1bits.WR); //Wait till the write completionto the beginning of the write routine and also insert the same line at the beginning of the read routine, then the write function can return immediately while the write happens in the background. It only blocks if you write another byte before its finished or attempt to read while a write is still in progress.That lets your program carry on doing other stuff that cant tolerate a 4ms delay caused by waiting for the EEPROM write.