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=0prima 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.
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.
⚠ 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 ; <<< 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.
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 "vivo" 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
- 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).
- 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.
- 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.
- 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