Lezione 24 di 43

Lezione 23

🎓 PIC16 — From Zero to Hero · Lezione 23 · Modulo 4

EEPROM — Dati che sopravvivono allo spegnimento 💾

I 64 byte di EEPROM del PIC16F84A. Lettura immediata, scrittura con sequenza di sblocco obbligatoria.
Salvataggio di configurazioni, contatori e calibrazioni che resistono ai cicli di alimentazione.

🎯 Cosa impari in questa lezione

  • I 4 registri SFR in Bank 1 per accedere alla EEPROM: EEADR, EEDATA, EECON1, EECON2
  • Lettura EEPROM: 3 istruzioni — EEADR ← addr, RD=1, leggi EEDATA
  • La sequenza di sblocco scrittura: 10 passi obbligatori nell’ordine esatto
  • Perché GIE=0 prima di 0x55/0xAA è critico: un interrupt tra i due magic bytes annulla la scrittura
  • Differenza tra RAM (volatile), EEPROM (non volatile, 1M cicli) e Flash (solo da programmer, 10K cicli)
  • Pattern contatore non volatile: leggi → incrementa → riscrivi ad ogni accensione
  • Endurance e data retention: quando la EEPROM si “consuma”

📋 Scheda lezione

Lezione: 23 / 42
Modulo: 4 — Interrupt avanzati
Device: PIC16F84A
Prerequisiti: Lez. 10 (banchi)
Difficoltà: ★★★☆☆
Registri nuovi: EEADR, EEDATA, EECON1/2
Bit nuovi: RD, WR, WREN, EEIF


1 · I registri EEPROM

La EEPROM del PIC16F84A è accessibile tramite quattro registri SFR in Bank 1. A differenza della RAM che si legge e scrive con una sola istruzione, la EEPROM richiede di impostare prima l’indirizzo in EEADR (0x00–0x3F) e poi leggere o scrivere tramite EEDATA. I bit di controllo in EECON1 orchestrano il timing interno dell’operazione. EECON2 non è un vero registro — è un “cancello” hardware che si apre solo con la sequenza esatta 0x55/0xAA.

Registri EEPROM in Bank 1 — lettura e scrittura EEADR (0x09) Indirizzo 0x00–0x3F EEDATA (0x08) Dato 8 bit EECON1 (0x88) EEIF WR WREN RD EECON2 (0x89) 0x55 / 0xAA (unlock) EEPROM — 64 byte 0x00 0x01 0x02 0x03 ··· 0x3E 0x3F Endurance: ≥ 1.000.000 cicli R/W Data retention: ≥ 40 anni Scrittura: ~10ms per byte Lettura: istantanea (1 ciclo) Non volatile: sopravvive allo spegnimento

2 · Lettura EEPROM

La lettura è semplice: si carica l’indirizzo in EEADR, si imposta il bit RD di EECON1, e il dato è disponibile in EEDATA già nell’istruzione successiva. Il bit RD si azzera automaticamente — non serve BCF manuale. Attenzione: questi registri sono in Bank 1, quindi ogni accesso richiede il cambio banco con BSF/BCF STATUS,RP0.

Codice — funzione EEPROM_READ riutilizzabile

; ============================================================
; Lezione 23 — EEPROM: lettura
; EEADR ← indirizzo, RD=1, leggi EEDATA
; PIC16F84A — 64 byte EEPROM @ 0x00–0x3F
; ============================================================
    LIST    P=16F84A
    #INCLUDE <p16f84a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

    CBLOCK  0x0C
EE_VAL          ; byte letto da EEPROM
EE_ADDR         ; indirizzo da leggere
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0010
INIT
    BSF     STATUS,RP0
    CLRF    TRISA
    BCF     STATUS,RP0
    CLRF    PORTA

; ============================================================
; EEPROM_READ: legge 1 byte da indirizzo in EE_ADDR
; Risultato in EE_VAL e in W
; ============================================================
EEPROM_READ
    MOVF    EE_ADDR,W
    BSF     STATUS,RP0      ; Bank 1
    MOVWF   EEADR           ; carica indirizzo
    BSF     EECON1,RD       ; avvia lettura (RD si azzera da solo)
    MOVF    EEDATA,W        ; leggi dato (disponibile subito)
    BCF     STATUS,RP0      ; torna Bank 0
    MOVWF   EE_VAL
    RETURN

; ============================================================
; MAIN: legge indirizzo 0x00, 0x01, 0x02 e mostra su PORTA
; ============================================================
MAIN
    CLRF    EE_ADDR
    CALL    EEPROM_READ     ; legge 0x00
    MOVF    EE_VAL,W
    ANDLW   0x1F            ; PORTA ha solo 5 bit utili
    MOVWF   PORTA

    MOVLW   0x01
    MOVWF   EE_ADDR
    CALL    EEPROM_READ     ; legge 0x01
    ; ... usa EE_VAL

    GOTO    MAIN

    END

3 · Scrittura EEPROM — la sequenza di sblocco

La scrittura è deliberatamente complessa. Microchip ha progettato questa sequenza di sblocco proprio per rendere impossibile una scrittura accidentale — un glitch, un valore casuale in EECON2, un puntatore corrotto non possono attivare la scrittura. Tutti i 10 passi devono essere eseguiti nell’ordine esatto e senza interruzioni tra il primo 0x55 e il WR=1.

Sequenza scrittura EEPROM — tutti i passaggi sono obbligatori MOVWF EEADR indirizzo MOVWF EEDATA dato BSF EECON1 WREN=1 ⚠ CRITICO BCF INTCON GIE=0 disab. INT EECON2 ← 0x55 ← 0xAA BSF EECON1 WR=1 BSF INTCON GIE=1 riab. INT attendi WR=0 ~10ms Cosa succede nell’hardware dopo WR=1 ciclo di cancellazione + programmazione EEPROM (~10ms) — CPU continua, WR rimane 1 WR=0 scrittura OK ⚠ Se un interrupt interrompe la sequenza 0x55/0xAA la scrittura non avviene — GIE=0 è essenziale WREN=0 dopo ogni scrittura protegge da scritture accidentali in caso di glitch software

⚠ Perché GIE=0 è obbligatorio: Se un interrupt arriva tra MOVWF EECON2 (0x55) e il secondo MOVWF EECON2 (0xAA), il PIC entra nella ISR. La ISR esegue altre istruzioni che “inquinano” la sequenza EECON2. Al ritorno dalla ISR (RETFIE), il codice tenta di scrivere 0xAA ma la finestra di sblocco è già chiusa. Risultato: WREN rimane 1 ma WR non si attiva — la scrittura non avviene silenziosamente. Disabilitare GIE garantisce che la finestra 0x55/0xAA sia atomica.

Codice — funzione EEPROM_WRITE completa

; ============================================================
; Lezione 23 — EEPROM: scrittura
; La sequenza di scrittura è OBBLIGATORIA e precisa:
; 1. EEADR ← indirizzo
; 2. EEDATA ← valore
; 3. WREN=1 (abilita scrittura)
; 4. GIE=0 (disabilita interrupt — critico!)
; 5. EECON2 ← 0x55  (unlock 1)
; 6. EECON2 ← 0xAA  (unlock 2)
; 7. WR=1  (avvia scrittura)
; 8. GIE=1 (riabilita interrupt)
; 9. Attendi WR=0 (fine scrittura ~10ms)
; 10. WREN=0 (disabilita scrittura — sicurezza)
; ============================================================
    LIST    P=16F84A
    #INCLUDE <p16f84a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

    CBLOCK  0x0C
EE_VAL
EE_ADDR
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0010
INIT
    BSF     STATUS,RP0
    CLRF    TRISA
    BCF     STATUS,RP0
    CLRF    PORTA

; ============================================================
; EEPROM_WRITE: scrive EE_VAL all'indirizzo EE_ADDR
; Non interrompere questa sequenza!
; ============================================================
EEPROM_WRITE
    BSF     STATUS,RP0      ; Bank 1

    MOVF    EE_ADDR,W
    MOVWF   EEADR           ; imposta indirizzo
    MOVF    EE_VAL,W
    MOVWF   EEDATA          ; imposta dato

    BSF     EECON1,WREN     ; abilita scrittura EEPROM

    BCF     INTCON,GIE      ; &lt;&lt;&lt; disabilita interrupt OBBLIGATORIO
    ; Sequenza di sblocco hardware (magic bytes)
    MOVLW   0x55
    MOVWF   EECON2          ; unlock step 1
    MOVLW   0xAA
    MOVWF   EECON2          ; unlock step 2
    BSF     EECON1,WR       ; avvia ciclo di scrittura (~10ms)
    BSF     INTCON,GIE      ; riabilita interrupt

    ; Attendi fine scrittura (WR torna 0 quando completata)
WAIT_WR
    BTFSC   EECON1,WR
    GOTO    WAIT_WR

    BCF     EECON1,WREN     ; disabilita scrittura (sicurezza)
    BCF     STATUS,RP0      ; torna Bank 0
    RETURN

; ============================================================
; MAIN: scrive 0x42 in EEPROM[0x00], poi la rilegge e verifica
; ============================================================
MAIN
    MOVLW   0x00
    MOVWF   EE_ADDR
    MOVLW   0x42
    MOVWF   EE_VAL
    CALL    EEPROM_WRITE    ; scrive 0x42 @ 0x00

    ; Rileggi per verifica
    MOVF    EE_ADDR,W
    BSF     STATUS,RP0
    MOVWF   EEADR
    BSF     EECON1,RD
    MOVF    EEDATA,W
    BCF     STATUS,RP0
    MOVWF   PORTA           ; mostra il byte letto

    GOTO    MAIN

    END

4 · Contatore non volatile — sopravvive allo spegnimento

Il pattern classico per la EEPROM: all’accensione leggi il valore salvato, elabora, riscrivi. Il valore in RAM è quello “vivo” durante l’esecuzione; la EEPROM è il “backup permanente”. Si usa per contare accensioni, salvare l’ultima configurazione utente, memorizzare un valore di calibrazione misurato in fabbrica o tenere traccia di errori accumulati.

Tipi di memoria nel PIC16F84A — quando usare cosa RAM (GPR) Dimensione: 68 byte Accesso: 1 ciclo R/W Volatile: sì (persa a spegnimento) Cicli illimitati Variabili, stack software, flag EEPROM dati Dimensione: 64 byte Lettura: 1 ciclo / Scrittura: ~10ms Non volatile: sopravvive Endurance: 1.000.000 cicli Configurazioni, contatori, calibrazione Flash (programma) Dimensione: 1024 × 14 bit Lettura: esecuzione istruzioni Scrittura: solo da programmer Endurance: 10.000 cicli Codice + tabelle RETLW

Codice — contatore di accensioni

; ============================================================
; Lezione 23 — Contatore non volatile
; Il valore sopravvive allo spegnimento.
; Ad ogni accensione: legge EEPROM[0x00], incrementa, riscrive.
; Mostra il conteggio su PORTA (LED).
; PIC16F84A @ 4 MHz
; ============================================================
    LIST    P=16F84A
    #INCLUDE <p16f84a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

#DEFINE EE_CNT_ADDR 0x00    ; indirizzo EEPROM del contatore

    CBLOCK  0x0C
COUNTER         ; valore corrente del contatore
    ENDC

    ORG     0x0000

; ============================================================
; All'avvio: leggi contatore da EEPROM, incrementa, riscrivi
; ============================================================
INIT
    BSF     STATUS,RP0
    CLRF    TRISA
    MOVLW   0xFF
    MOVWF   TRISB           ; PORTB input (non usato qui)
    BCF     STATUS,RP0
    CLRF    PORTA

    ; Leggi contatore da EEPROM
    MOVLW   EE_CNT_ADDR
    BSF     STATUS,RP0
    MOVWF   EEADR
    BSF     EECON1,RD
    MOVF    EEDATA,W
    BCF     STATUS,RP0
    MOVWF   COUNTER

    ; Incrementa
    INCF    COUNTER,F

    ; Mostra subito su PORTA
    MOVF    COUNTER,W
    ANDLW   0x1F
    MOVWF   PORTA

    ; Riscrivi in EEPROM
    BSF     STATUS,RP0
    MOVLW   EE_CNT_ADDR
    MOVWF   EEADR
    MOVF    COUNTER,W
    MOVWF   EEDATA
    BSF     EECON1,WREN
    BCF     INTCON,GIE
    MOVLW   0x55
    MOVWF   EECON2
    MOVLW   0xAA
    MOVWF   EECON2
    BSF     EECON1,WR
    BSF     INTCON,GIE

WAIT_WRITE
    BTFSC   EECON1,WR
    GOTO    WAIT_WRITE

    BCF     EECON1,WREN
    BCF     STATUS,RP0

; ============================================================
; Loop principale: il contatore rimane stabile, lampeggia LED
; ============================================================
MAIN
    BSF     PORTA,4         ; segnale &quot;vivo&quot;
    CALL    DELAY
    BCF     PORTA,4
    CALL    DELAY
    GOTO    MAIN

DELAY
    MOVLW   D'200'
    MOVWF   0x70
DLY DECFSZ  0x70,F
    GOTO    DLY
    RETURN

    END

5 · Endurance: quando la EEPROM si consuma

Ogni ciclo di scrittura degrada leggermente la cella EEPROM. Il datasheet garantisce almeno 1.000.000 di scritture per cella. Se scrivi ogni secondo, esaurisci l’endurance in ~11 giorni. Se scrivi ogni ora, dura ~114 anni. La strategia per applicazioni ad alta frequenza di scrittura è usare tecniche di wear leveling: distribuire le scritture su più celle, ruotando l’indirizzo usato. Per la maggior parte degli usi didattici e industriali a bassa frequenza l’endurance non è un problema pratico.

Regola pratica: non usare la EEPROM come RAM veloce. Non scrivere ad ogni ciclo del main loop. Scrivi solo quando il valore cambia davvero (parametro modificato dall’utente, contatore che ha raggiunto una soglia, calibrazione aggiornata). La lettura invece è completamente libera — 1 ciclo, nessuna usura.

🧪 Prova tu

  1. Carica il programma di lettura. Nel pannello EEPROM del simulatore, inserisci il valore 0x2A in indirizzo 0x00. Esegui in Step e verifica che PORTA mostri 0x0A (0x2A AND 0x1F = 0x0A, perché PORTA ha 5 bit).
  2. Carica il programma di scrittura. Esegui fino a dopo WAIT_WR. Controlla il pannello EEPROM: l’indirizzo 0x00 deve contenere 0x42. Verifica anche PORTA = 0x42.
  3. Carica il contatore non volatile. Esegui, poi resetta il simulatore (simula uno spegnimento). Ricarica il programma senza resettare la EEPROM. COUNTER deve essere incrementato rispetto all’esecuzione precedente.
  4. Sfida: modifica il contatore per salvare anche un secondo byte in EEPROM[0x01] con il valore inverso (COMF COUNTER,W). Alla prossima accensione leggi entrambi i byte e verifica che COUNTER + NOT(COUNTER) = 0xFF — una semplice verifica di integrità dei dati.

❓ Quiz rapido

1. Dopo BSF EECON1,RD perché il dato è disponibile nell’istruzione immediatamente successiva senza attendere?

Mostra risposta

La EEPROM è di tipo “byte leggibile in modo asincrono” sul PIC16F84A. Quando si imposta RD=1, il controller EEPROM legge la cella e carica EEDATA entro un ciclo di clock. Poiché il PIC16 impiega 4 cicli di clock per eseguire ogni istruzione, quando BSF termina EEDATA è già stato aggiornato. RD si azzera da solo prima che l’istruzione successiva (MOVF EEDATA,W) venga eseguita. Non è così su tutti i PIC: i modelli più recenti con EEPROM più grande richiedono un’attesa.


2. Cosa succede se si scrive in EEPROM[0x00] 1000 volte al secondo per 24 ore?

Mostra risposta

In 24 ore: 1000 × 3600 × 24 = 86.400.000 scritture — ben oltre il milione garantito. In pratica la cella inizierebbe a degradarsi intorno al giorno 11 (1.000.000 / 1000 / 3600 ≈ 0,28 ore = 17 minuti… quindi molto prima). La cella degradata inizia a perdere la carica più velocemente: i dati si corrompono in lettura, anche se non si scrive più. Soluzione: distribuire le scritture su 64 celle (wear leveling) porta l’endurance a ~11 giorni × 64 ≈ 2 anni.


3. Il programma fa BCF EECON1,WREN dopo ogni scrittura. Sarebbe sicuro tenerlo sempre a 1?

Mostra risposta

Tecnicamente funziona, ma è rischioso. Con WREN=1 permanente, un bug software che porta accidentalmente la sequenza 0x55/0xAA in EECON2 (magari per un puntatore corrotto o un salto errato) potrebbe scatenare una scrittura non intenzionale in EEPROM. Nei sistemi reali WREN=0 è una protezione difensiva importante: riduce la finestra temporale in cui una scrittura accidentale è possibile al solo periodo compreso tra BSF EECON1,WREN e BCF EECON1,WREN — pochi microsecondi invece di sempre.

Prossima lezione →

Lezione 24 — Lookup table in Flash con RETLW
Tabelle di costanti in memoria programma. RETLW come array read-only. Tabelle per display a 7 segmenti, seno in virgola fissa, codici ASCII. Limite di pagina e uso di PCLATH.

© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 23