Store Data in PIC16F877A EEPROM with PICSimLab
RAM forgets everything at reset; the PIC16F877A’s internal data EEPROM does not. This tutorial keeps a running counter in EEPROM: every button press increments it, the value appears on LEDs, and after a reset the counter continues where it left off — the classic test that non-volatile storage works.
What you need
- PICSimLab 0.9.3 — see the download page.
- MPLAB X IDE and the XC8 compiler.
- Board: McLab2 or PICGenios, microcontroller PIC16F877A.
- One Push Buttons spare part and one LEDs spare part.
Wiring used in this tutorial (example)
- Push Buttons output 1 → RD0 (count button, active high in this example)
- LEDs spare part → PORTB, showing the stored counter value
Steps
1. Generate the project and add the parts
Create the MPLAB X project for the PIC16F877A. Open Modules → Spare parts, add Inputs → Push Buttons and Outputs → LEDs, and connect them in Properties as above.
2. Understand the write sequence
EEPROM writes on the PIC16F877A are deliberately awkward so code cannot trigger them by accident: load address and data (EEADR/EEDATA), clear EEPGD to target data memory, set WREN, then write the exact unlock sequence 0x55 followed by 0xAA to EECON2 and set WR. The hardware clears WR when the write finishes. Reads are simple by comparison: set the address, clear EEPGD, set RD and take the byte from EEDATA.
3. Structure the program
At start-up, read the counter from EEPROM address 0 and show it. In the loop, wait for a button press (with a release wait as simple debounce), increment, store and display. To extend writable life in real projects, write only when the value actually changes — the example already follows that rule.
4. Build, load, run and reset
Build in MPLAB X, load the .hex and run. Press the button a few times, note the LED value, then reset the simulation: the counter must come back at the stored value, not zero. PICSimLab also offers a MEM 24CXXX spare part (an external I2C EEPROM) for larger storage — it needs I2C code like the PCF8574 LCD tutorial uses.
Example code (XC8)
// Example: persistent counter in data EEPROM - PIC16F877A, XC8
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
#include <xc.h>
#define _XTAL_FREQ 20000000 // Example: match the board clock
unsigned char eeprom_read_byte(unsigned char addr) {
EEADR = addr;
EECON1bits.EEPGD = 0; // data EEPROM, not flash
EECON1bits.RD = 1; // start read
return EEDATA;
}
void eeprom_write_byte(unsigned char addr, unsigned char data) {
EEADR = addr;
EEDATA = data;
EECON1bits.EEPGD = 0;
EECON1bits.WREN = 1; // allow writes
unsigned char gie_state = INTCONbits.GIE;
INTCONbits.GIE = 0; // unlock sequence must not be interrupted
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // start write
INTCONbits.GIE = gie_state;
while (EECON1bits.WR) { } // wait for completion
EECON1bits.WREN = 0;
}
void main(void) {
TRISB = 0x00; // PORTB outputs (LEDs)
TRISDbits.TRISD0 = 1; // RD0 input (button)
unsigned char count = eeprom_read_byte(0x00);
PORTB = count;
while (1) {
if (PORTDbits.RD0) { // button pressed
__delay_ms(30); // debounce
if (PORTDbits.RD0) {
count++;
eeprom_write_byte(0x00, count); // save it
PORTB = count;
while (PORTDbits.RD0) { } // wait for release
}
}
}
}
Try next
- LCD over I2C (PCF8574) — the I2C skills you need for the external 24CXXX EEPROM spare part.
- LED RB0 RC0 push button — more spare-parts button patterns.
- UART serial — log stored values to a terminal.
Troubleshooting
The value resets to a fixed number after every reset. A first run on a blank EEPROM often reads 0xFF, which is expected. If writes never stick, check the unlock sequence order (0x55 then 0xAA, then WR) and that EEPGD is cleared — with EEPGD set you would be writing to flash program memory instead.
One press counts many times. Button bounce: keep the debounce delay and the wait-for-release loop, and check the button’s active level in the part’s Properties matches the code.
