Lezione 12 di 43

Lezione 11

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

I/O Digitale — PORTA e PORTB 🔌

Come configurare ogni pin come input o output, leggere pulsanti, pilotare LED.
RA4 open-drain, pull-up di PORTB, e tutto ciò che il datasheet non spiega chiaramente.

🎯 Cosa impari in questa lezione

  • Il modello TRIS → PORT: come ogni pin è controllato da due registri
  • PORTA: 5 pin (RA0–RA4), reset all-input, bitmask 5 bit
  • PORTB: 8 pin (RB0–RB7), pull-up interni, interrupt-on-change su RB4–RB7
  • La peculiarità di RA4: open-drain, non può erogare corrente in uscita
  • Come leggere un pin di input con BTFSC/BTFSS
  • Come scrivere un pin di output con BSF/BCF
  • Il fenomeno read-modify-write e perché può sorprendere
  • Come usare il pannello I/O del simulatore per testare

📋 Scheda lezione

Modulo2 — I/O Digitale
Lezione11 di 42
Livello⭐⭐ Principiante+
Tempo~40 minuti
RegistriPORTA, PORTB, TRISA, TRISB, OPTION_REG
PrerequisitiLezioni 07–10 (STATUS, BSF/BCF, bank switching)

🧠 Il modello TRIS → PORT

Ogni pin I/O del PIC16 è controllato da due registri: il registro TRIS (direzione) e il registro PORT (dato). TRIS vive in Bank 1, PORT in Bank 0. La regola mnemonica: TRIS 1 = Input, TRIS 0 = Output. Oppure: “1 = dito puntato verso di te (input), 0 = dito puntato verso l’esterno (output)”.

Modello TRIS / PORT — direzione e dato per ogni pin TRIS (Bank 1) Ogni bit = direzione pin bit = 1 → pin è INPUT bit = 0 → pin è OUTPUT Reset: TRISA=0x1F, TRISB=0xFF (tutti input) PORT (Bank 0) Ogni bit = dato pin Scrittura → aggiorna il latch Lettura → legge i pin fisici Latch reset: PORTA=0x00, PORTB=0x00 Pin fisico TRIS=0 (output): pin = valore del latch PORT TRIS=1 (input): lettura PORT = livello sul pin (scrittura PORT su pin input → latch aggiornato ma pin fisicamente non cambia) dir. dato ⚠️ Read-Modify-Write: BSF/BCF PORTB leggono i PIN fisici prima di scrivere il latch — su pin input con rumore, il valore letto può essere sbagliato. Usa sempre variabili shadow per i bit di output.
Figura 1 — TRIS controlla la direzione, PORT gestisce il dato. La lettura di PORT legge i pin fisici, non il latch — distinzione critica.

① PORTA — 5 pin, RA0–RA4

PORTA — 5 pin e registro TRISA TRISA (0x85, Bank 1) — solo 5 bit bit 7 bit 6 — — RA4 RA3 RA2 RA1 RA0 Caratteristiche pin: RA0–RA3 I/O digitale standard push-pull output RA4 / T0CKI — ATTENZIONE! Output OPEN-DRAIN: non eroga corrente! Richiede resistenza pull-up esterna per HIGH TRISA reset = 0x1F tutti i 5 bit = 1 → INPUT bitmask = 0x1F (5 bit)
Figura 2 — PORTA: 5 pin utili, TRISA a 5 bit. RA4 è open-drain e non può pilotare un LED senza pull-up esterno.

⚠️ RA4 open-drain: in configurazione output, RA4 può portare il pin a 0V (LOW) ma non a +5V (HIGH). Per avere HIGH in uscita è necessaria una resistenza di pull-up esterna verso VDD. Nel simulatore questa limitazione è segnalata visivamente ma non blocca l’esecuzione. Nel circuito reale, non connettere un LED direttamente su RA4 aspettandosi che si accenda con BSF PORTA,4.

; === Configurazione PORTA ===
BSF STATUS, RP0 ; Bank 1

; Caso 1: tutta PORTA come output
CLRF TRISA ; 0x00 = tutti output (bit=0)

; Caso 2: tutta PORTA come input
MOVLW 0x1F ; 0001 1111 = tutti input (è già il reset)
MOVWF TRISA

; Caso 3: RA0-RA2 output, RA3-RA4 input
MOVLW 0x18 ; 0001 1000 = bit3 e bit4 = input, resto output
MOVWF TRISA

BCF STATUS, RP0 ; Bank 0
CLRF PORTA ; azzera latch output


② PORTB — 8 pin, pull-up interni, interrupt-on-change

PORTB — 8 pin con funzioni speciali TRISB (0x86, Bank 1) — 8 bit RB7 IOC ↕ int-change RB6 IOC ↕ int-change RB5 IOC ↕ int-change RB4 IOC ↕ int-change RB3 I/O std RB2 I/O std RB1 I/O std RB0 / INT interrupt esterno fronte ↑ o ↓ Pull-up interni su tutta PORTB Abilitati impostando RBPU=0 in OPTION_REG (Bank 1, bit 7). Attivi solo su pin configurati come INPUT (TRIS=1). TRISB reset = 0xFF (tutti input) · PORTB latch reset = 0x00 · OPTION_REG bit7 RBPU reset = 1 (pull-up disabilitati) Per abilitare pull-up: BSF STATUS,RP0 → BCF OPTION_REG,7 → BCF STATUS,RP0
Figura 3 — PORTB: RB0 ha interrupt esterno, RB4–RB7 hanno interrupt-on-change (IOC), tutta la porta ha pull-up interni opzionali.

; === Configurazione PORTB tipica ===
BSF STATUS, RP0 ; Bank 1

; RB0–RB6 = output, RB7 = input (pulsante)
MOVLW 0x80 ; 1000 0000: solo bit7 = input
MOVWF TRISB

; Abilita pull-up interni (necessario per leggere pulsante)
; OPTION_REG bit 7 = RBPU (0=abilitati, 1=disabilitati)
BCF OPTION_REG, 7 ; RBPU=0 → pull-up abilitati

BCF STATUS, RP0 ; Bank 0
CLRF PORTB ; azzera latch output

📌 Perché i pull-up? Un pulsante connesso tra il pin e GND: quando non è premuto il pin è “in aria” (floating) e può leggere valori casuali. Il pull-up interno porta il pin a +5V quando il pulsante è aperto → legge 1. Quando il pulsante è premuto → pin va a GND → legge 0. Questa è la logica negata: premuto = 0, rilasciato = 1.


③ Leggere e scrivere i pin — pattern operativi

Scrivere un singolo pin output

; Accendi LED su RB0
BSF PORTB, 0 ; RB0 = 1 (HIGH)

; Spegni LED su RB0
BCF PORTB, 0 ; RB0 = 0 (LOW)

; Toggle LED su RB0 (inverti stato)
MOVLW 0x01 ; maschera bit 0
XORWF PORTB, F ; XOR con bit0: se era 0→1, se era 1→0

; Scrivere un byte intero su PORTB
MOVLW 0b10101010 ; pattern alternato (notazione binaria)
MOVWF PORTB ; applica a tutti i pin output di PORTB

Leggere un pin input

; Leggi RB7 (pulsante) — logica negata con pull-up
BTFSS PORTB, 7 ; salta se RB7=1 (pulsante rilasciato)
GOTO PREMUTO ; RB7=0 → pulsante premuto
GOTO RILASCIATO ; RB7=1 → pulsante rilasciato

; Copia un pin di input in una variabile (bit-mirror)
BTFSC PORTB, 7 ; salta se RB7=0
BSF FLAGS, 0 ; bit0 di FLAGS = copia di RB7
BTFSS PORTB, 7 ; salta se RB7=1
BCF FLAGS, 0 ; bit0 di FLAGS = 0

; Leggi l’intero PORTB in W e salva
MOVF PORTB, W ; W = valore letto dai pin fisici
MOVWF INPUT_SNAP ; salva snapshot in RAM


④ Read-Modify-Write — la trappola silenziosa

BSF e BCF su PORTB eseguono un’operazione in tre fasi: leggono il registro PORT (cioè i pin fisici, non il latch), modificano il bit, riscrivono. Se su un pin di input il livello fisico differisce dal latch, la lettura può corrompere lo stato degli altri bit. Esempio classico: hai RB0 come output (LED), RB7 come input (pulsante). Se esegui BSF PORTB, 0 mentre RB7 è a 0 per rumore esterno, potresti “leggere” RB7=0 e scrivere 0 anche nel latch di RB7 — che però è configurato come input e non cambia nulla elettricamente, ma la lettura successiva di PORTB potrebbe dare risultati inattesi.

🔍 Soluzione: variabile shadow. Mantieni una copia del valore di output in una variabile RAM (PORTB_SHADOW). Invece di BSF PORTB direttamente, modifica il shadow e poi MOVF PORTB_SHADOW, W / MOVWF PORTB. Così eviti di leggere i pin fisici durante la scrittura. Questa tecnica è standard nei firmware professionali.

; Pattern shadow register — evita read-modify-write
PORTB_SHADOW EQU 0x10 ; copia del latch di PORTB

; Inizializzazione
CLRF PORTB_SHADOW ; shadow = 0x00
MOVF PORTB_SHADOW, W
MOVWF PORTB

; Accendi RB0 senza leggere i pin fisici
BSF PORTB_SHADOW, 0 ; modifica lo shadow (RAM, sicuro)
MOVF PORTB_SHADOW, W ; carica shadow in W
MOVWF PORTB ; scrivi su PORTB (solo write, nessuna lettura pin)


📊 Riepilogo registri I/O

RegistroIndirizzoBankResetDimensioneFunzione
PORTA0x0500x005 bitLatch output / lettura pin di PORTA
TRISA0x8510x1F5 bitDirezione PORTA (1=input, 0=output)
PORTB0x0600x008 bitLatch output / lettura pin di PORTB
TRISB0x8610xFF8 bitDirezione PORTB (1=input, 0=output)
OPTION_REG0x8110xFF8 bitBit 7 = RBPU (0=pull-up abilitati)

💻 Programma — LED e pulsante con shadow register

; ============================================================
; Lezione 11 — I/O Digitale con shadow register
; RB0–RB5: LED output
; RB6, RB7: pulsanti input (pull-up interni)
; Pulsante RB7: accende/spegne RB0
; Pulsante RB6: accende/spegne RB1
; ============================================================
LIST p=16F84A
#include <p16f84a.inc>

CBLOCK 0x0C
PB_SHADOW ; → 0x0C: shadow register PORTB
BTN_PREV ; → 0x0D: stato precedente pulsanti
ENDC

LED0 EQU 0 ; RB0
LED1 EQU 1 ; RB1
BTN0 EQU 7 ; RB7
BTN1 EQU 6 ; RB6

ORG 0x0000
GOTO START
ORG 0x0004
RETFIE

START:
BSF STATUS, RP0 ; Bank 1
MOVLW 0xC0 ; 1100 0000: RB6,RB7=input, RB0-5=output
MOVWF TRISB
CLRF TRISA ; PORTA tutta output (non usata)
BCF OPTION_REG, 7 ; RBPU=0 → pull-up abilitati su RB6,RB7
BCF STATUS, RP0 ; Bank 0
CLRF PORTB ; LED spenti
CLRF PB_SHADOW ; shadow = 0x00
MOVLW 0xC0 ; pulsanti rilasciati = 1 (pull-up)
MOVWF BTN_PREV

MAIN:
; Controlla BTN0 (RB7) — toggle LED0 (RB0)
BTFSS PORTB, BTN0 ; salta se RB7=1 (rilasciato)
CALL TOGGLE_LED0 ; RB7=0 → premuto → toggle

; Controlla BTN1 (RB6) — toggle LED1 (RB1)
BTFSS PORTB, BTN1 ; salta se RB6=1 (rilasciato)
CALL TOGGLE_LED1 ; RB6=0 → premuto → toggle

GOTO MAIN

; — Toggle LED0 via shadow —
TOGGLE_LED0:
MOVLW 0x01 ; maschera bit 0
XORWF PB_SHADOW, F ; toggle bit0 del shadow
MOVF PB_SHADOW, W ; carica shadow
MOVWF PORTB ; applica — nessun read-modify-write
RETURN

; — Toggle LED1 via shadow —
TOGGLE_LED1:
MOVLW 0x02 ; maschera bit 1
XORWF PB_SHADOW, F
MOVF PB_SHADOW, W
MOVWF PORTB
RETURN

END

💡 Nel simulatore: nel pannello I/O trovi i pin di PORTB con un LED (stato output) e un checkbox (input esterno). Imposta TRISB=0xC0 con il programma, poi usa i checkbox di RB6 e RB7 per simulare i pulsanti (checkbox = livello del pin fisico). Nota che al reset i pin sono tutti input e il LED nel simulatore li mostra come input finché non cambi TRISB.

🏋️ Prova tu!

  1. Configura PORTB con RB0–RB3 output e RB4–RB7 input con pull-up. Scrivi il codice di inizializzazione completo e verifica nel simulatore che il pannello I/O mostri la direzione corretta per ogni pin.
  2. Scrivi un programma che accenda un LED su RB0 se RB4=1 (livello alto), lo spenga se RB4=0. Usa BTFSC/BTFSS su PORTB,4. Testa nel simulatore variando il checkbox di RB4.
  3. Sfida: implementa un contatore a 4 bit sui LED RB0–RB3. Ad ogni pressione di RB7 (con pull-up), incrementa il contatore e mostra il valore sui 4 LED. Usa il shadow register per evitare il read-modify-write e una variabile CONTA per il valore corrente. Il contatore deve resettarsi a 0 dopo 15.

❓ Quiz rapido

1. Al reset, TRISB=0xFF e OPTION_REG=0xFF. I pull-up interni di PORTB sono abilitati o disabilitati?

Mostra risposta

Disabilitati. OPTION_REG bit 7 = RBPU = 1 al reset (OPTION_REG reset = 0xFF). RBPU è active-low: RBPU=1 significa pull-up disabilitati, RBPU=0 significa pull-up abilitati. Quindi dopo il reset i pull-up sono off anche se i pin sono input. Per abilitarli devi fare BCF OPTION_REG, 7 mentre sei in Bank 1.


2. Scrivi MOVLW 0xFF / MOVWF TRISA. Quanti pin di PORTA diventano input?

Mostra risposta

Tutti e 5: RA0–RA4. TRISA è mascherato internamente a 5 bit (value & 0x1F), quindi 0xFF diventa 0x1F = 0001 1111 = tutti e 5 i bit a 1. I 3 bit superiori (bit 5, 6, 7) di TRISA sono non implementati e vengono ignorati silenziosamente.


3. Connetti un LED su RA4 con TRISA4=0 (output). Esegui BSF PORTA,4. Il LED si accende?

Mostra risposta

Nel circuito reale: NO (o sì, dipende dal circuito). RA4 è open-drain: in uscita può solo portare il pin a GND (LOW), non a +5V (HIGH). BSF PORTA,4 = valore latch = 1, ma il transistor open-drain è spento — il pin fluttua se non c’è pull-up esterno. Per fare HIGH su RA4 serve una resistenza pull-up verso VDD. Nel simulatore questa limitazione è segnalata ma non impedisce il cambio del LED simulato.


4. Perché usare un shadow register invece di BSF/BCF direttamente su PORTB?

Mostra risposta

BSF/BCF su PORTB eseguono un read-modify-write: leggono prima il valore dei PIN FISICI (non il latch), modificano il bit target, riscrivono. Se ci sono pin input con rumore o livelli incerti, la lettura può contenere valori sbagliati che vengono riscritti sul latch, potenzialmente alterando lo stato di pin output adiacenti. Il shadow register risolve: BSF/BCF lavorano su RAM (solo write, deterministico) e solo MOVWF PORTB applica il valore finale — scrittura pura, senza lettura pin.

Prossima lezione →

Lezione 12 — Lettura pulsanti e debounce
Il rimbalzo meccanico dei pulsanti, perché genera letture spurie,
e le tecniche software per eliminarlo in assembly.

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