Lezione 14 di 43

Lezione 13

🎓 PIC16 — From Zero to Hero · Lezione 13 · Modulo 2

Display a 7 segmenti 🔢

Come tradurre una cifra in pattern di segmenti con una lookup table RETLW.
Multiplexing di due display con una sola PORTB e alternanza a 100 Hz.

🎯 Cosa impari in questa lezione

  • Come è fatto un display 7 segmenti (Common Cathode vs Common Anode)
  • Come costruire una lookup table con RETLW e come funziona l’indirizzamento con PCL
  • Il pericolo del boundary di pagina con ADDWF PCL,F
  • Programma completo: contatore 0–9 su singola cifra con reset da pulsante
  • Tecnica del multiplexing: due display con una sola PORTB
  • Subroutine SPLIT_DIGITS: divisione senza istruzione DIV
  • Anti-ghosting: il blanking tra le cifre

📋 Scheda lezione

Lezione: 13 / 42
Modulo: 2 — I/O Digitale
Device: PIC16F84A
Prerequisiti: Lez. 09 (RETLW, PCL)
Difficoltà: ★★★☆☆
Pattern chiave: lookup table, multiplexing


1 · La struttura del display 7 segmenti

a f b g e c d dp cifra “8” — tutti ON

Un display 7 segmenti è composto da 7 LED etichettati a–g (più il punto decimale dp). In configurazione Common Cathode tutti i catodi sono collegati a GND: portare un anodo a +5V accende il segmento. Ogni bit di PORTB controlla un segmento: RB0=a, RB1=b, … RB6=g, RB7=dp.

Per visualizzare il numero 5 servono i segmenti a, c, d, f, g → bit 0,2,3,5,6 = 0b01101101 = 0x6D.

⚠ Common Anode: la logica si inverte — 0 accende, 1 spegne. Usare COMF sui pattern prima di scrivere su PORTB. Nel simulatore si usa Common Cathode.


2 · La lookup table con RETLW

Invece di calcolare il pattern a runtime, usiamo una tabella in Program Memory: ogni RETLW k ritorna con W=k. L’indice della cifra viene sommato a PCL per saltare direttamente alla RETLW corretta.

W = 0x05 indice cifra “5” CALL SEG_TABLE SEG_TABLE ADDWF PCL,F RETLW 0x3F ; 0 RETLW 0x06 ; 1 RETLW 0x5B ; 2 RETLW 0x4F ; 3 RETLW 0x66 ; 4 RETLW 0x6D ; 5 ← selezionata W = 0x6D dopo RETURN → MOVWF PORTB 5 PORTB=0x6D
; Tabella codifica 0–9, Common Cathode
; La tabella DEVE stare interamente in una pagina da 256 word.
; Piazzarla dopo GOTO INIT (ORG 0x0001) lo garantisce.
SEG_TABLE
    ADDWF   PCL,F
    RETLW   0x3F           ; 0: a b c d e f
    RETLW   0x06           ; 1: b c
    RETLW   0x5B           ; 2: a b d e g
    RETLW   0x4F           ; 3: a b c d g
    RETLW   0x66           ; 4: b c f g
    RETLW   0x6D           ; 5: a c d f g
    RETLW   0x7D           ; 6: a c d e f g
    RETLW   0x07           ; 7: a b c
    RETLW   0x7F           ; 8: tutti i segmenti
    RETLW   0x6F           ; 9: a b c d f g

⚠ Boundary di pagina: ADDWF PCL,F somma solo gli 8 bit bassi del PC. Se la tabella attraversa il confine 0x0FF/0x100, il salto finisce nella pagina sbagliata. Soluzione: piazzare SEG_TABLE subito dopo ORG 0x0000 / GOTO INIT, oppure usare PCLATH per tabelle più grandi.


3 · Programma — Contatore 0–9 su singolo display

; ============================================================
; Lezione 13 — Display 7 segmenti, singola cifra
; Contatore 0-9 con lookup table. Reset pulsante RA0.
; PIC16F84A @ 4 MHz   PORTB: RB0=a ... RB6=g, RB7=dp
; ============================================================
    LIST    P=16F84A
    #INCLUDE <p16f84a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

    CBLOCK  0x0C
DIGIT                   ; cifra corrente (0-9)
D_HI                    ; delay loop esterno
D_LO                    ; delay loop interno
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0005
INIT
    BSF     STATUS,RP0         ; Bank 1
    CLRF    TRISB              ; PORTB tutto output (segmenti)
    MOVLW   0x01
    MOVWF   TRISA              ; RA0 = input (pulsante)
    BCF     STATUS,RP0         ; Bank 0
    CLRF    PORTB              ; display spento
    CLRF    DIGIT

LOOP
    ; 1. Converti DIGIT → pattern segmenti
    MOVF    DIGIT,W
    CALL    SEG_TABLE
    MOVWF   PORTB

    ; 2. Aspetta ~500ms (2 × 250ms)
    CALL    DELAY_250MS
    CALL    DELAY_250MS

    ; 3. Pulsante reset (RA0 = 0 se premuto)
    BTFSS   PORTA,0
    CLRF    DIGIT

    ; 4. Incrementa, azzera dopo 9
    INCF    DIGIT,F
    MOVF    DIGIT,W
    SUBLW   D'10'              ; W = 10 - DIGIT
    BTFSS   STATUS,Z
    GOTO    LOOP
    CLRF    DIGIT
    GOTO    LOOP

; ============================================================
; SEG_TABLE — deve stare tutta in una pagina da 256 word
; W in: cifra (0-9)   W out: pattern PORTB (Common Cathode)
; ============================================================
SEG_TABLE
    ADDWF   PCL,F
    RETLW   0x3F               ; 0: a b c d e f
    RETLW   0x06               ; 1: b c
    RETLW   0x5B               ; 2: a b d e g
    RETLW   0x4F               ; 3: a b c d g
    RETLW   0x66               ; 4: b c f g
    RETLW   0x6D               ; 5: a c d f g
    RETLW   0x7D               ; 6: a c d e f g
    RETLW   0x07               ; 7: a b c
    RETLW   0x7F               ; 8: a b c d e f g
    RETLW   0x6F               ; 9: a b c d f g

; ============================================================
; DELAY_250MS ≈ 250ms @ 4 MHz
; D_HI=250, D_LO=0 (→256): 250×256×3 cicli ≈ 192000 µs
; + overhead → ~250ms (abbastanza per uso didattico)
; ============================================================
DELAY_250MS
    MOVLW   D'250'
    MOVWF   D_HI
DLY_OUTER
    CLRF    D_LO               ; 256 iterazioni
DLY_INNER
    DECFSZ  D_LO,F             ; 1 ciclo (2 se skip)
    GOTO    DLY_INNER          ; 2 cicli
    DECFSZ  D_HI,F
    GOTO    DLY_OUTER
    RETURN

    END

4 · Multiplexing — Due cifre con una sola PORTB

Con due display collegati in parallelo su PORTB, si usa un pin di abilitazione per ciascuno (RA0 → DIG0, RA1 → DIG1). Si accende un solo display alla volta per 5 ms, poi si commuta: l’occhio percepisce entrambe le cifre accese contemporaneamente (effetto persistenza, frequenza = 100 Hz).

PIC16F84A PORTB RB0-RB6 segmenti RA0 → DIG0 RA1 → DIG1 Bus segmenti in parallelo DIG0 (unità) 3 DIG1 (decine) 7 Istante attuale: RA0=1 → DIG0 ON RA1=0 → DIG1 OFF alterna ogni 5ms Calcola SPLIT_DIGITS unità / decine DIG0 (unità) RA0=1, RA1=0 PORTB=pattern, 5ms DIG1 (decine) RA0=0, RA1=1 PORTB=pattern, 5ms Conta refresh DECFSZ REFRESH,F 50×10ms = 500ms Incrementa COUNTER++ azzera dopo 99 loop infinito

Programma completo — Contatore 00–99 con multiplexing

; ============================================================
; Lezione 13 — Multiplexing 2 display 7 segmenti
; Contatore 00-99. PORTB=segmenti, RA0=DIG0, RA1=DIG1
; ============================================================
    LIST    P=16F84A
    #INCLUDE <p16f84a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

    CBLOCK  0x0C
COUNTER                 ; valore corrente 0-99
DIGIT0                  ; unità  (0-9)
DIGIT1                  ; decine (0-9)
REFRESH                 ; contatore cicli refresh
D_LO                    ; delay interno
TEMP                    ; variabile temporanea
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0005
INIT
    BSF     STATUS,RP0
    CLRF    TRISB              ; PORTB tutto output (segmenti)
    CLRF    TRISA              ; PORTA tutto output (abilitazioni)
    BCF     STATUS,RP0
    CLRF    PORTA              ; display spenti
    CLRF    PORTB
    CLRF    COUNTER
    MOVLW   D'50'
    MOVWF   REFRESH            ; 50 × 10ms = 500ms

; --- Loop principale: ogni iterazione = 1 ciclo refresh (10ms) ---
LOOP
    CALL    SPLIT_DIGITS       ; calcola DIGIT0, DIGIT1

    ; DIG0 (unità) per 5ms
    MOVF    DIGIT0,W
    CALL    SEG_TABLE
    MOVWF   PORTB
    BSF     PORTA,0             ; RA0=1: DIG0 ON
    BCF     PORTA,1             ; RA1=0: DIG1 OFF
    CALL    DELAY_5MS

    ; Blanking: evita ghosting durante la commutazione
    CLRF    PORTA
    CLRF    PORTB

    ; DIG1 (decine) per 5ms
    MOVF    DIGIT1,W
    CALL    SEG_TABLE
    MOVWF   PORTB
    BCF     PORTA,0             ; RA0=0: DIG0 OFF
    BSF     PORTA,1             ; RA1=1: DIG1 ON
    CALL    DELAY_5MS

    CLRF    PORTA
    CLRF    PORTB

    ; Ogni 50 cicli (500ms) incrementa COUNTER
    DECFSZ  REFRESH,F
    GOTO    LOOP

    MOVLW   D'50'
    MOVWF   REFRESH
    INCF    COUNTER,F
    MOVF    COUNTER,W
    SUBLW   D'100'             ; == 100?
    BTFSS   STATUS,Z
    GOTO    LOOP
    CLRF    COUNTER
    GOTO    LOOP

; ============================================================
; SPLIT_DIGITS: COUNTER → DIGIT0 (unità) + DIGIT1 (decine)
; Tecnica: sottrazione ripetuta (no istruzione DIV sul PIC)
; ============================================================
SPLIT_DIGITS
    MOVF    COUNTER,W
    MOVWF   TEMP
    CLRF    DIGIT1
SPLIT_LOOP
    MOVLW   D'10'
    SUBWF   TEMP,F             ; TEMP -= 10
    BTFSS   STATUS,C           ; C=0 → sottratto troppo → stop
    GOTO    SPLIT_DONE
    INCF    DIGIT1,F           ; decine++
    GOTO    SPLIT_LOOP
SPLIT_DONE
    MOVLW   D'10'
    ADDWF   TEMP,F             ; ripristina ultima sottrazione
    MOVF    TEMP,W
    MOVWF   DIGIT0
    RETURN

; ============================================================
; SEG_TABLE (identica al programma singola cifra)
; ============================================================
SEG_TABLE
    ADDWF   PCL,F
    RETLW   0x3F  RETLW   0x06  RETLW   0x5B  RETLW   0x4F  RETLW   0x66
    RETLW   0x6D  RETLW   0x7D  RETLW   0x07  RETLW   0x7F  RETLW   0x6F

; ============================================================
; DELAY_5MS ≈ 5ms @ 4 MHz
; 7 × 256 × 3 cicli = 5376 ≈ 5.4ms
; ============================================================
DELAY_5MS
    MOVLW   D'7'
    MOVWF   TEMP
D5_OUTER
    CLRF    D_LO
D5_INNER
    DECFSZ  D_LO,F
    GOTO    D5_INNER
    DECFSZ  TEMP,F
    GOTO    D5_OUTER
    RETURN

    END

5 · Riepilogo — Punti chiave

✓ Lookup table con RETLW
ADDWF PCL,F come primo elemento. La tabella deve stare in una singola pagina. Il valore di ritorno è in W dopo RETURN.

✓ Multiplexing
Una PORTB per N display. Un pin di abilitazione per cifra. Frequenza ≥ 50 Hz. Blanking tra le cifre contro il ghosting.

✓ SPLIT_DIGITS
Divisione senza DIV: sottrazione ripetuta di 10. Il numero di sottrazioni = quoziente; il residuo = resto. Tecnica usabile per qualsiasi base.

🧪 Prova tu

  1. Carica il programma singola cifra e osserva il ciclo 0→9→0. Premi il pulsante su RA0 durante la visualizzazione di “7”: il display torna subito a 0?
  2. Nella SEG_TABLE, cambia RETLW 0x07 (cifra 7) con RETLW 0x4F (cifra 3). Cosa vedi sul display alla terza posizione del ciclo?
  3. Sfida: aggiungi le lettere A e b alla tabella (indici 10 e 11) e porta il limite da D'10' a D'12'. Codici: A = 0x77, b = 0x7C.
  4. Sfida multiplexing: modifica il programma 00–99 per incrementare ogni 200ms invece di 500ms. Quante iterazioni da 10ms servono?

❓ Quiz rapido

1. Perché ADDWF PCL,F può causare errori se la tabella è mal posizionata?

Mostra risposta

PCL è solo gli 8 bit bassi del Program Counter. Se la tabella attraversa il confine 0x0FF/0x100, l’addizione fa traboccare PCL da 0xFF a 0x00 senza cambiare PCLATH, saltando nella pagina sbagliata (tipicamente ORG 0x000). Soluzione: posizionare la tabella subito dopo il GOTO iniziale.


2. Qual è il valore da scrivere su PORTB per visualizzare il numero 4 su un display Common Cathode?

Mostra risposta

Il 4 è composto dai segmenti b, c, f, g → bit RB1, RB2, RB5, RB6 = 0b01100110 = 0x66. È la quinta RETLW nella tabella (indice 4).


3. Nel multiplexing, perché si fa CLRF PORTA / CLRF PORTB tra DIG0 e DIG1?

Mostra risposta

Senza blanking, nel breve istante in cui si cambia il pin di abilitazione entrambi i display sono attivi con lo stesso pattern (quello di DIG0), causando il ghosting: DIG1 mostra per un attimo la cifra delle unità invece delle decine. Il blanking elimina la sovrapposizione.


4. In SPLIT_DIGITS, dopo il ciclo si aggiunge 10 a TEMP. Perché?

Mostra risposta

Quando il ciclo si ferma (C=0 dopo SUBWF), TEMP contiene il risultato di una sottrazione di troppo ed è “andato sotto zero” (wrapped a un valore alto). Aggiungere 10 annulla quell’ultima sottrazione errata e recupera il resto corretto. Esempio: 23→13→3→253 (C=0, stop) → 253+10=263 mod 256=7? No: 3+10=13… Verifica: 23, decine=2 (due sottrazioni da 10 riuscite), TEMP dopo due sottrazioni = 3, terza sottrazione 3-10 = -7 = 249 unsigned (C=0), ADDWF 249+10 = 259 mod 256 = 3. Corretto: resto = 3.

Prossima lezione →

Lezione 14 — Multiplexing LED e Matrice 4×4
Come controllare una matrice 4×4 di LED con soli 8 pin, scansione per riga, framebuffer in RAM e indirizzamento indiretto con FSR/INDF.

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