Author Topic: PIC18F XC8 - EEPROM (Newbie Question)  (Read 6870 times)

0 Members and 1 Guest are viewing this topic.

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
PIC18F XC8 - EEPROM (Newbie Question)
« on: January 30, 2017, 10:38:36 pm »
Hi all,
  I'm very fresh to all of this, please go easy on me!

Code: [Select]
//Global Variables
volatile char  T0COUNT;  //Variable for Timer0 Counter
volatile char  LPFLG ;  //Flag for MEDIUM PUMP speed timer
volatile char MEDTIME = 5; //Loops for TIMER0 to run


//Set Timer Interrupt Procedure

void interrupt high_isr (void){
if(INTCONbits.TMR0IF == 1) {  //Check for status of TMR0 Overflow
INTCONbits.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 1
        T0COUNT = 0;
    }

   
}
if(INTCON3bits.INT3IF == 1){ //if programming pin is HIGH
    INTCON3bits.INT3IF = 0; //Clear interrupt flag
      TRISBbits.RB2 = 1; //enable RB2 PROGDIR pin
    if(PROGDIR == 1){ //check for HI PROGDIR pin
        __delay_ms (50);  //wait 50ms for bounce
        if(PROGDIR == 1){
            DIR= ~DIR;
             }
                 
       
    }
}
}
int main (){
    OSCCONbits.IRCF2 = 1; //Set clock speed to 1MHz
    OSCCONbits.IRCF1 = 0; //Set clock speed to 1MHz
    OSCCONbits.IRCF0 = 0; //Set clock speed to 1MHz
    RCONbits.IPEN = 1; //enable interrupt priority
    INTCONbits.GIE = 1; //enable all interrupts
    INTCONbits.PEIE =1; //enable internal interrupts
    T0CONbits.TMR0ON = 0; //switch timer OFF in initial state
    T0CONbits.T0PS0 = 1;  //set TIMER0 prescaler to 1:256 on TOP0-2
    T0CONbits.T0PS1 = 1;
    T0CONbits.T0PS2 = 1;
    T0CONbits.PSA = 1; //Enable pre-scaler
    T0CONbits.T0CS = 0; //TIMER0 uses Internal Osccilator
    T0CONbits.T016BIT = 0; //TIMER0 uses 16 bit counter
    ADCON1 = 0xFF; //switch OFF Port-A ADC
    CMCON = 0xFF; //switch OFF comparitors
    PWMEN1 = 0xFF; //switch OFF PWM module
    INTCONbits.TMR0IE = 1; //enable TIMER0 overflow bit
    INTCON3bits.INT3E = 1; //enable INT3 External Interrupt
    INTCON2bits.INTEDG3 = 1; //enable INT3 RIDING EDGE interrupt
   
    TRISAbits.RA0 = 0; //define A0 as OUTPUT
    TRISAbits.RA1 = 0; //define A1 as OUTPUT
    TRISBbits.RB0 = 1; //define B0 as INPUT
     
     
    while (1)  //Do this forever
       
           if(SPEED == 1 ) { //if SPPED pin HI
             
               if(LPFLG == 0){ //If Loop Counter FLAG LO
                    T0CONbits.TMR0ON = 1; //switch timer ON
        PULSE= ~PULSE; //Set PULSE toggle
        __delay_us(25); //Toggle pulse MEDIUM
       }
               
    if(LPFLG == 1 ) {  //if Loop Counter FLAG HI
        T0CONbits.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 counter
        PULSE= ~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 = [address] 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-time
  • When 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 EEPROM
[li]

[/li][/list]

I'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
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3233
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #1 on: January 30, 2017, 11:24:15 pm »
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.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12806
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #2 on: January 30, 2017, 11:58:41 pm »
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.
 
The following users thanked this post: ed_reardon

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #3 on: January 31, 2017, 12:08:02 am »
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.

Many 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
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1610
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #4 on: January 31, 2017, 12:21:07 am »
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.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12806
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #5 on: January 31, 2017, 12:26:39 am »
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: [Select]
// 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
 

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #6 on: January 31, 2017, 08:37:26 pm »
Okay I've had a go at this following the instructions in the datasheet, I may well have cocked this up!  :-//

void read_eeprom (unsigned int ADDRESS){   //Set up function for 'read_eeprom' and define address as unsigned_int.
    EEADR = ADDRESS;  // load ADDRESS in to EEADR register
    EECON1bits.EEPGD =0; //set access to EEPROM memory
    EECON1bits.RD = 1;   //  Initiate EEPROM read,  bit cleared by hardware after read.
           
}
as well as creating function:

void write_eeprom (unsigned int ADDRESS, unsigned int DATA){
    INTCONbits.GIE = 0; //switch off ALL interrupts during read
    EECON1bits.WREN = 1; //enable WRITE to EEPROM
    EEADR = ADDRESS;  //load ADDRESS in to EEADR register
    EEDATA = DATA;  //load DATA in to DATA register
    EECON2 = 0x55;  //write 55h to EECON2 to initiate write
    EECON2 = 0x0AA; //write 0AAh to EECON2 to initiate write
    EECON1bits.WR = 1; //initiate WRITE cycle
    INTCONbits.GIE = 1; // re-enable global interupts

***
as well as the following in the High ISR:

 if(EEIF =1){ //if EEPROM write successful
    EEIF = 0;  //reset EEPROM write flag
    EECON1bits.WREN = 0; //Inhibit further reads until read_eeprom called.

The whole shebang:
Code: [Select]
/*
 * 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 Variables
volatile int  T0COUNT;  //Variable for Timer0 Counter
volatile int  LPFLG ;  //Flag for MEDIUM PUMP speed timer
volatile int MEDTIME = 5; //Loops for TIMER0 to run

void read_eeprom (unsigned int ADDR){   //Set up function for 'read_eeprom' and define address as unsigned_int.
    EEADR = ADDR;  // load ADDRESS in to EEADR register
    EECON1bits.EEPGD =0; //set access to EEPROM memory
    EECON1bits.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 read
    EECON1bits.WREN = 1; //enable WRITE to EEPROM
    EEADR = ADDRESS;  //load ADDRESS in to EEADR register
    EEDATA = DATA;  //load DATA in to DATA register
    EECON2 = 0x55;  //write 55h to EECON2 to initiate write
    EECON2 = 0x0AA; //write 0AAh to EECON2 to initiate write
    EECON1bits.WR = 1; //initiate WRITE cycle
    INTCONbits.GIE = 1; // re-enable global interupts
   
   
   
   
}



//Set Timer Interrupt Procedure

void interrupt high_isr (void){
if(INTCONbits.TMR0IF == 1) {  //Check for status of TMR0 Overflow
INTCONbits.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 1
        T0COUNT = 0;
    }

   
}
if(INTCON3bits.INT3IF == 1){ //if programming pin is HIGH
    INTCON3bits.INT3IF = 0; //Clear interrupt flag
      TRISBbits.RB2 = 1; //enable RB2 PROGDIR pin
    if(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 successful
    EEIF = 0;  //reset EEPROM write flag
    EECON1bits.WREN = 0; //Inhibit further reads until read_eeprom called.
}


}

                 
                     
         
int main (){
    OSCCONbits.IRCF2 = 1; //Set clock speed to 1MHz
    OSCCONbits.IRCF1 = 0; //Set clock speed to 1MHz
    OSCCONbits.IRCF0 = 0; //Set clock speed to 1MHz
    RCONbits.IPEN = 1; //enable interrupt priority
    INTCONbits.GIE = 1; //enable all interrupts
    INTCONbits.PEIE =1; //enable internal interrupts
    T0CONbits.TMR0ON = 0; //switch timer OFF in initial state
    T0CONbits.T0PS0 = 1;  //set TIMER0 prescaler to 1:256 on TOP0-2
    T0CONbits.T0PS1 = 1;
    T0CONbits.T0PS2 = 1;
    T0CONbits.PSA = 1; //Enable pre-scaler
    T0CONbits.T0CS = 0; //TIMER0 uses Internal Osccilator
    T0CONbits.T016BIT = 0; //TIMER0 uses 16 bit counter
    ADCON1 = 0xFF; //switch OFF Port-A ADC
    CMCON = 0xFF; //switch OFF comparitors
    PWMEN1 = 0xFF; //switch OFF PWM module
    INTCONbits.TMR0IE = 1; //enable TIMER0 overflow bit
    INTCON3bits.INT3E = 1; //enable INT3 External Interrupt
    INTCON2bits.INTEDG3 = 1; //enable INT3 RIDING EDGE interrupt
   
    TRISAbits.RA0 = 0; //define A0 as OUTPUT
    TRISAbits.RA1 = 0; //define A1 as OUTPUT
    TRISBbits.RB0 = 1; //define B0 as INPUT
   
    DIR = (read_eeprom, 0x00);
   
    while (1)  //Do this forever
       
           if(SPEED == 1 ) { //if SPPED pin HI
             
               if(LPFLG == 0){ //If Loop Counter FLAG LO
                    T0CONbits.TMR0ON = 1; //switch timer ON
        PULSE= ~PULSE; //Set PULSE toggle
        __delay_us(25); //Toggle pulse MEDIUM
       }
               
    if(LPFLG == 1 ) {  //if Loop Counter FLAG HI
        T0CONbits.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 counter
        PULSE= ~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!'



« Last Edit: January 31, 2017, 09:06:45 pm by ed_reardon »
 

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #7 on: January 31, 2017, 08:40:04 pm »
Here's a package if you want to have a look  ^-^

 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12806
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #8 on: January 31, 2017, 09:24:02 pm »
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: [Select]
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 frequencies
Nop(); //Nop may be required for latency at high frequencies
return ( 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 sequence
EECON2 = 0xAA;
EECON1bits.WR = 1; // end critical sequence
while(EECON1bits.WR); //Wait till the write completion
INTCONbits.GIE = GIE_BIT_VAL;
EECON1bits.WREN = 0;
}

Rip out the EEPROM WREN stuff from the high ISR and use:
Code: [Select]
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, see http://www.microchip.com/forums/m797439.aspx
« Last Edit: January 31, 2017, 10:44:05 pm by Ian.M »
 
The following users thanked this post: ed_reardon

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #9 on: January 31, 2017, 10:34:01 pm »
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: [Select]
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 frequencies
Nop(); //Nop may be required for latency at high frequencies
return ( 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 sequence
EECON2 = 0xAA;
EECON1bits.WR = 1; // end critical sequence
while(EECON1bits.WR); //Wait till the write completion
INTCONbits.GIE = GIE_BIT_VAL;
EECON1bits.WREN = 0;
}

Rip out the EEPROM WREN stu__EEPROM_DATA()ff from the high ISR and use:
Code: [Select]
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, see http://www.microchip.com/forums/m797439.aspx

Many 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



 

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #10 on: January 31, 2017, 10:43:40 pm »
I think it might be because I'm recalling the GIE back in the write routine?  :-//

Let me have a fiddle :)
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12806
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #11 on: January 31, 2017, 10:52:36 pm »
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 either waiting 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.
 
The following users thanked this post: ed_reardon

Offline ed_reardonTopic starter

  • Regular Contributor
  • *
  • Posts: 131
  • Country: gb
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #12 on: February 01, 2017, 12:24:52 am »
Got there in the end,  damn blasted curly braces!

It's all a good learning curve though!

 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12806
Re: PIC18F XC8 - EEPROM (Newbie Question)
« Reply #13 on: February 01, 2017, 12:34:44 am »
There's another refinement that may be useful tf the write delay ever becomes a problem.

If you move the
Code: [Select]
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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf