PIC C - Use of the PIC16F84 EEPROM

copyright, Peter H. Anderson, Baltimore, MD, Feb, '99


Introduction.

The PIC16F84 includes 64 bytes of on-board EEPROM. (The PIC12CE51X also includes on-board EEPROM. However, the programming and reading is much different).

Uses of this EEPROM include the storage of calibration constants, site dependent data, a unique identity number and quite possibly a very limited data logger. We have also used the EEPROM on the 16F84 to store the address of the next location in an external 24LCXX EEPROM to avoid overwriting old data in a data logging application if the PIC is reset.

Initializing EEPROM.

When using Microchip's MPLAB, EEPROM may be initialized when the PIC is programmed;

   ORG 2100H
   DE 1, 2, 3, 4

However, I had difficulty with this when using the High Tech Compiler as the compiler does not support the DE directive. I tried the following with a version of PIC C dated December, '97. It compiled. However, in examining the resulting .hex file I could not see anything close to that produced by MPLAB.

#asm
     psect eedata, delta=2, abs, ovrld
     ORG 2100H   	; starting point of EEPROM on 16F84
     DB 1, 2, 3, 4      ; EEPROM locations initialized using DB
#endasm

I then tried this with a version dated Dec, '98 and had better luck.

However, in looking at the hex file, it was of the form;

     01 00 02 00 03 00 04 00

It appears that the DB directive is actually referencing a 16-bit quantity and the high byte is set to zero. Note that the data is stored as low byte, followed by the high byte. Thus, every other data location in EEPROM is zero.

I finally struck on the following;

#asm
     psect eedata, delta=2, abs, ovrld
     ORG 2100H  	 ; starting point of EEPROM on 16F84
     DW 0201H, 0403H     ; EEPROM locations initialized using DW
#endasm

Note that the DW directive is used. However, as the data is stored as least significant byte followed by most significant, it is necessary to transpose the bytes. That is, if you desire 01 02, then use DW 0201H.

Program EEPROM_1.C.

Program EEPROM_1.C illustrates this concept. It is presumed that the intent is to initialize four locations to 1, 2, 3 and 4. However, when the DB directive is used, the data will be stored as 1, 0, 2, 0. When the data is defined using the DW directive, with the bytes transposed, the data is stored correctly.

// EEPROM_1.C
//
// Illustrates how to initialize EEPROM.  Illustrates the difference
// between using DB and DW in initializing.
//
// Program reads four locations at adr 00 in EEPROM initialized 
// using DB directive and displays on LCD.  Note that this will
// display as 01, 00, 02, 00.
//
// Then reads first four locations at adr 10H in EEPROM which was 
// initialized using the DW directive.  This will display as 01, 
// 02, 03, 04.
//
// Build project with EEPROM_1.C and LCD_F84.C
//
// copyright, Peter H. Anderson, Baltimore, MD, Feb, '99
//

#include <pic.h>
#include "lcd_f84.h"

unsigned char read_16f84_eeprom(unsigned char adr);

void main(void)
{
   unsigned char adr, d;

   lcd_init();

   for (adr=0x00; adr<4; adr ++) // fetch and display locations
				 // 00 - 03
   {
      d = read_16f84_eeprom(adr); // fetch from EEPROM
      lcd_hex_byte(d);   // and display
      lcd_char(' ');
   }

   lcd_new_line();

   for (adr=0x10; adr<(0x10)+4; adr++) // fetch and display locations
					// 10 - 13
   {
      d = read_16f84_eeprom(adr); // fetch from EEPROM
      lcd_hex_byte(d);   // and display
      lcd_char(' ');
   }
   
#asm
DONE:
   clrwdt
   GOTO DONE
#endasm
}

unsigned char read_16f84_eeprom(unsigned char adr)
{
   EEADR=adr;
   RD=1; // set the read bit
   return(EEDATA);
}

#asm
     psect eedata, delta=2, abs, ovrld

     ORG 2100H   ; starting point of EEPROM on 16F84
     DB 1, 2, 3, 4      ; EEPROM locations initialized using DB

     ORG 2110H
     DW 0201H, 0403H ; initialized using DW directive
#endasm
     

Program EEPROM_2.C.

This program illustrates how to write to and read from EEPROM. The nature of the program is to permit the user to perform n events and the device is no longer usable. In this case, 100 events are authorized by initializing EEPROM to 100. Each time an LED is flashed, the EEPROM is decremented and when at zero, the program goes into an endless loop.

This general concept might be used in a "charge for use" type system.

// EEPROM_2.C
//
// Illustrates how to initialize EEPROM, how to read from EEPROM and 
// write to EEPROM.  Note that this EEPROM is the EEPROM on the
// 16F84.
//
// EEPROM location 00 is initialized to 100 decimal in the assembler
// using the DW directive.  Each time function dec_count is called,
// the program decrements this value and checks to see if it is at
// zero.  Such an arrangement might be used to limit the number of
// accesses and might be used with a debit card type of application.
//
// Program continually flashes LED on RB4 at about 250 ms on and 250
// msec off.  Loops indefinitely.  However, counter in EEPROM is
// decremented on each pass.  Program locks when EEPROM counter is
// decremented to zero. 
//
// Note that even if power is turned off prior to the completion of
// 100 flashes, the latest EEPROM value will be retained for the
// subsequent run of the program.
//
// Build project with EEPROM_2.C and LCD_F84.C
//
// copyright, Peter H. Anderson, Baltimore, MD, Feb, '99
//

#include <pic.h>
#include "lcd_f84.h"

#define LED_DIR TRISB4 // LED on RB4
#define IN 1
#define OUT 0

#define LED_PIN RB4

void flash_led(void);
void write_16f84_eeprom(unsigned char adr, unsigned char d);
unsigned char read_16f84_eeprom(unsigned char adr);

void main(void)
{
   unsigned char n;
   LED_DIR=OUT;
   LED_PIN = 0;
   while(1)
   {
      n = read_16f84_eeprom(0x00); // fetch from EEPROM
      if (n==0)   break;  // if at zero, lock up
      --n;
      write_16f84_eeprom(0x00, n); // decrement and save
      flash_led();
   }

#asm
LOCK:
   clrwdt
   GOTO LOCK
#endasm
}

void flash_led(void)
{
   LED_PIN = 1;
   lcd_delay_ms(250);
   LED_PIN = 0;
   lcd_delay_ms(250);
}        
     

unsigned char read_16f84_eeprom(unsigned char adr)
{
   EEADR=adr;
   RD=1; // set the read bit
   return(EEDATA);
}

void write_16f84_eeprom(unsigned char adr, unsigned char d)
{
   EEADR = adr;
   EEDATA = d;

   WREN = 1;  // write enable
   EECON2 = 0x55; // protection sequence
   EECON2 = 0xaa;

   WR = 1;  // begin programming sequnce
   lcd_delay_ms(20);
   WREN = 0;  // disable write enable
}


#asm
     psect eedata, delta=2, abs, ovrld
     ORG 2100H   ; starting point of EEPROM on 16F84
     DW 0064H    ; EEPROM location 00 initialized to 100 decimal
#endasm