Lezione 31 di 43

Lezione 30

🎓 PIC16 — From Zero to Hero · Lezione 30 · Modulo 5

Comparatori e Tensione di Riferimento 🔋

I moduli C1/C2 e VRCON del PIC16F628A. Rilevamento di soglie analogiche
senza ADC, tensione di riferimento programmabile e interrupt su cambio comparatore.

🎯 Cosa impari in questa lezione

  • Cosa fa un comparatore analogico: confronta due tensioni, output digitale (0 o 1)
  • Registro CMCON: modalità CM2:CM0, bit C1INV/C2INV, lettura C1OUT/C2OUT
  • Le 8 modalità di configurazione del doppio comparatore del PIC16F628A
  • Registro VRCON: tensione di riferimento interna a 32 passi (0–3.5V @ Vdd=5V)
  • Due range VRCON: VRR=1 (basso, passo 208mV) e VRR=0 (alto, offset +1.25V)
  • Interrupt CMIF: trigger ad ogni cambio di stato dei comparatori
  • Applicazione: rilevatore di soglia con allarme LED — zero CPU overhead

📋 Scheda lezione

Lezione: 30 / 42
Modulo: 5 — Periferiche avanzate
Device: PIC16F628A
Prerequisiti: Lez. 21 (Interrupt), Lez. 26 (ADC)
Difficoltà: ★★★☆☆
Periferica: Comparatori C1/C2 + VRCON
Alternativa all’ADC per rilevamento soglie


1 · Il comparatore analogico — schema e CMCON

Un comparatore analogico è il circuito più semplice per prendere una decisione su una tensione: confronta due ingressi (IN+ e IN−) e produce un output digitale — 1 se IN+ è maggiore di IN−, 0 altrimenti. A differenza dell’ADC che misura quanto vale una tensione, il comparatore risponde solo alla domanda è sopra o sotto la soglia?. Il PIC16F628A include due comparatori (C1 e C2) configurabili in molti modi tramite il registro CMCON, con output disponibili sia come bit leggibili via software sia come segnali fisici sui pin RA4 e RA5.

Schema funzionale comparatore analogico — C1 del PIC16F628A RA0 IN+ RA2 IN- C1 C1INV C1OUT RA4 pin out C1OUT = 1 se RA0 (IN+) > RA2 (IN-) | C1OUT = 0 se RA0 (IN+) < RA2 (IN-) | C1INV=1 inverte oppure Vref interna CMCON bit layout C2OUT C1OUT C2INV C1INV CIS CM2 CM1 CM0 7 6 5 4 3 2 1 0

2 · Codice — configurazione base CMCON

; ============================================================
; Lezione 30 — Comparatori: configurazione base CMCON
; PIC16F628A @ 4 MHz
; C1: RA2(IN-) vs RA0(IN+) → uscita su RA4 (C1OUT)
; C2: RA2(IN-) vs RA1(IN+) → uscita su RA5 (C2OUT)
; CMCON (0x1F) Bank0:
;   CM2:CM0 = modo comparatori (000-111)
;   CIS = input switch
;   C1INV = inverti output C1
;   C2INV = inverti output C2
;   C1OUT = stato output C1 (read only)
;   C2OUT = stato output C2 (read only)
; ============================================================
    LIST    P=16F628A
    #INCLUDE <p16f628a.inc>
    __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _LVP_OFF

; Modalità CMCON (CM2:CM0):
; 000 → comparatori OFF, tutti pin I/O digitali
; 001 → 3 ingressi / 2 uscite
; 010 → 4 ingressi / 2 uscite (modalità più comune)
; 011 → 2 comparatori + Vref interna
; 100 → C2 con mux input
; 101 → 2 comparatori indipendenti con Vref
; 110 → 1 comparatore
; 111 → comparatori OFF (reset default per 16F628A)

    ORG     0x0000
    GOTO    INIT

    ORG     0x0005
INIT
    BSF     STATUS,RP0         ; Bank 1
    ; RA0, RA1, RA2 come input analogici per comparatori
    ; RA4, RA5 come output (C1OUT, C2OUT)
    MOVLW   b'00000111'        ; RA2:RA0 input, resto output
    MOVWF   TRISA
    CLRF    TRISB              ; PORTB output (LED debug)
    BCF     STATUS,RP0         ; Bank 0

    ; CMCON: CM=010 → 2 comparatori, 4 ingressi
    ; C1INV=0, C2INV=0 → output non invertiti
    ; C1: RA0(+) vs RA2(-), C2: RA1(+) vs RA2(-)
    MOVLW   b'00000010'        ; CM=010
    MOVWF   CMCON

; ============================================================
; MAIN: leggi stati C1OUT e C2OUT, mostra su PORTB
; ============================================================
MAIN
    MOVF    CMCON,W
    ; C1OUT è bit6, C2OUT è bit7
    ANDLW   b'11000000'        ; isola C1OUT e C2OUT
    MOVWF   PORTB              ; mostra sui LED
    GOTO    MAIN

    END

⚠️ Reset default PIC16F628A: al reset CMCON = 0x07 (CM=111), che significa comparatori OFF ma con RA0–RA3 configurati come ingressi analogici dedicati ai comparatori. Se si vogliono usare RA0–RA3 come GPIO digitali normali, bisogna esplicitamente scrivere CMCON = 0x07 (paradossalmente lo stesso valore — ma anche CM=000 disabilita i comparatori lasciando i pin digitali). La confusione comune: chi non inizializza CMCON e legge PORTA ottiene sempre 0 su RA0–RA3 perché i pin sono in modalità analogica.


3 · Tensione di riferimento interna VRCON

Il PIC16F628A include un DAC interno a 4 bit chiamato modulo di tensione di riferimento, controllato da VRCON. Con 16 passi e due range si coprono tensioni da 0V a ~3.6V (con Vdd=5V). Il riferimento interno è utile per soglie fisse che non variano — elimina la necessità di un divisore resistivo esterno. La tensione può anche essere portata fisicamente sul pin RA2 tramite VROE=1, permettendo di misurarla con un multimetro o usarla come ingresso per circuiti esterni.

VRCON — tensione di riferimento interna programmabile (Vdd=5V) VRR=1 — Range basso Vref=(VR/24)×Vdd VR=0 0.000 V VR=4 0.833 V VR=2 0.417 V VR=6 1.250 V VR=3 0.625 V VR=7 1.458 V ← soglia lez. VR=8 1.667 V VR=12 2.500 V VR=10 2.083 V VR=15 3.125 V Step = Vdd/24 = 208mV VRR=0 — Range alto Vref=(VR/32+0.25)×Vdd VR=0 1.250 V VR=8 2.500 V VR=2 1.563 V VR=10 2.813 V VR=4 1.875 V VR=12 3.125 V VR=6 2.188 V VR=14 3.438 V VR=7 2.344 V VR=15 3.594 V Step = Vdd/32 = 156mV Offset +25% Vdd
; ============================================================
; Lezione 30 — Tensione di riferimento interna VRCON
; PIC16F628A @ 4 MHz
; VRCON (0x9F Bank1):
;   VREN = abilita modulo Vref
;   VROE = Vref su RA2 (pin esterno)
;   VRR  = range: 1=basso (Vdd/24 per step), 0=alto (Vdd/32)
;   VR3:VR0 = valore 0-15
; Vref (range basso,  VRR=1): Vref = (VR3:0 / 24) × Vdd
; Vref (range alto,   VRR=0): Vref = (VR3:0 / 32 + 0.25) × Vdd
; Con Vdd=5V, VRR=1:
;   VR=0  → 0.000V    VR=8  → 1.667V
;   VR=4  → 0.833V    VR=15 → 3.125V
; Con Vdd=5V, VRR=0:
;   VR=0  → 1.250V    VR=8  → 2.500V
;   VR=15 → 3.594V
; ============================================================
    LIST    P=16F628A
    #INCLUDE <p16f628a.inc>
    __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _LVP_OFF

    CBLOCK  0x20
VREF_STEP       ; passo corrente (0-15)
DELAY_CNT
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0005
INIT
    BSF     STATUS,RP0
    MOVLW   b'00000111'
    MOVWF   TRISA              ; RA0-RA2 input
    CLRF    TRISB

    ; VRCON: abilita Vref, range alto (VRR=0), VR=0
    ; VROE=1 → porta Vref su RA2 per misurarlo
    MOVLW   b'11100000'        ; VREN=1, VROE=1, VRR=0, VR=0000
    MOVWF   VRCON
    BCF     STATUS,RP0

    ; CMCON: CM=011 → C1 con Vref interna come ingresso -
    ; C1(+)=RA0, C1(-)=Vref interna
    MOVLW   b'00000011'        ; CM=011
    MOVWF   CMCON

    CLRF    VREF_STEP

; ============================================================
; MAIN: scala Vref da 0 a 15 in loop, legge C1OUT
; ============================================================
MAIN
    ; Aggiorna VRCON con nuovo valore VR
    BSF     STATUS,RP0
    MOVF    VREF_STEP,W
    ANDLW   0x0F               ; solo bit3:0
    IORLW   b'11100000'        ; VREN=1, VROE=1, VRR=0
    MOVWF   VRCON
    BCF     STATUS,RP0

    ; Attendi stabilizzazione (~10µs)
    MOVLW   D'3'
    MOVWF   DELAY_CNT
DLY DECFSZ  DELAY_CNT,F
    GOTO    DLY

    ; Leggi stato comparatore
    MOVF    CMCON,W
    ANDLW   b'11000000'        ; C1OUT(bit6), C2OUT(bit7)
    MOVWF   PORTB

    ; Avanza step
    INCF    VREF_STEP,F
    MOVF    VREF_STEP,W
    SUBLW   D'15'
    BTFSS   STATUS,C
    CLRF    VREF_STEP          ; reset se &gt; 15

    CALL    DELAY_100MS
    GOTO    MAIN

DELAY_100MS
    MOVLW   D'100'
    MOVWF   0x70
DA  MOVLW   D'250'
    MOVWF   0x71
DB  DECFSZ  0x71,F
    GOTO    DB
    DECFSZ  0x70,F
    GOTO    DA
    RETURN

    END

4 · Rilevatore di soglia con interrupt CMIF

Il vantaggio reale del comparatore rispetto all’ADC è l’interrupt CMIF: il comparatore genera automaticamente un interrupt ogni volta che il suo output cambia stato (RA0 attraversa la soglia Vref in qualsiasi direzione). La CPU non deve fare polling — dorme o lavora su altro, e viene svegliata solo all’evento. Questo è zero overhead: il rilevamento è in tempo reale, senza ritardi di campionamento.

Rilevatore di soglia — segnale RA0, Vref=1.458V, interrupt CMIF Vref 1.46V RA0 (tensione misurata) C1OUT (alto = RA0 > Vref) CMIF CMIF CMIF CMIF CMIF
; ============================================================
; Lezione 30 — Rilevatore di soglia con interrupt comparatore
; PIC16F628A @ 4 MHz
; Trigger allarme quando RA0 scende sotto soglia (Vref=1.5V)
; Interrupt: CMIF (bit6 PIR1) con CMIE (bit6 PIE1)
; Soglia 1.5V @ Vdd=5V: VRR=0, VR = round((1.5/5 - 0.25)×32) = 3
; Vref = (3/32 + 0.25) × 5 = (0.09375 + 0.25) × 5 = 1.719V ≈ 1.5V (appross.)
; Soglia più precisa: VRR=1, VR=7 → (7/24)×5 = 1.458V
; ============================================================
    LIST    P=16F628A
    #INCLUDE <p16f628a.inc>
    __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _LVP_OFF

    CBLOCK  0x20
W_SAVE
STATUS_SAVE
ALARM_FLAG      ; 1 se allarme attivo
    ENDC

#DEFINE LED_ALARM PORTB,0      ; LED allarme
#DEFINE LED_OK    PORTB,1      ; LED normale

    ORG     0x0000
    GOTO    INIT

    ORG     0x0004
; ============================================================
; ISR — interrupt cambio comparatore
; ============================================================
ISR
    MOVWF   W_SAVE
    SWAPF   STATUS,W
    MOVWF   STATUS_SAVE

    ; Verifica sorgente: CMIF?
    BTFSS   PIR1,CMIF
    GOTO    ISR_EXIT

    BCF     PIR1,CMIF          ; azzera flag

    ; Leggi stato C1OUT (bit6 CMCON)
    BTFSS   CMCON,C1OUT        ; C1OUT=1 → RA0 &gt; Vref (normale)
    GOTO    ISR_ALARM

    ; Tensione OK: RA0 > soglia
    BCF     ALARM_FLAG,0
    BCF     LED_ALARM
    BSF     LED_OK
    GOTO    ISR_EXIT

ISR_ALARM
    ; Tensione bassa: RA0 < soglia
    BSF     ALARM_FLAG,0
    BSF     LED_ALARM
    BCF     LED_OK

ISR_EXIT
    SWAPF   STATUS_SAVE,W
    MOVWF   STATUS
    SWAPF   W_SAVE,F
    SWAPF   W_SAVE,W
    RETFIE

    ORG     0x0050
INIT
    BSF     STATUS,RP0         ; Bank 1
    MOVLW   b'00000111'
    MOVWF   TRISA              ; RA0-RA2 input
    CLRF    TRISB              ; PORTB output

    ; Vref: VRR=1 (range basso), VR=7 → ~1.458V @ Vdd=5V
    MOVLW   b'10010111'        ; VREN=1, VROE=0, VRR=1, VR=0111
    MOVWF   VRCON

    ; Abilita interrupt CMIE
    MOVLW   b'01000000'        ; CMIE=1
    MOVWF   PIE1

    BCF     STATUS,RP0         ; Bank 0

    ; CMCON: CM=011 → C1 con Vref interna
    ; C1INV=0: C1OUT=1 quando RA0 > Vref
    MOVLW   b'00000011'
    MOVWF   CMCON

    ; Stato iniziale: assume OK
    CLRF    ALARM_FLAG
    BCF     LED_ALARM
    BSF     LED_OK

    ; Abilita interrupt globale e periferica
    MOVLW   b'11000000'        ; GIE=1, PEIE=1
    MOVWF   INTCON

; ============================================================
; MAIN: loop principale, gli allarmi gestiti da ISR
; ============================================================
MAIN
    ; Qui potremmo fare altro lavoro (logging, display, etc.)
    ; Lo stato è sempre aggiornato dalla ISR
    NOP
    GOTO    MAIN

    END

🧪 Prova tu

  1. Carica il programma base (CM=010). Nel pannello comparatori del simulatore, imposta RA0=3.0V e RA2=2.0V. Leggi CMCON: C1OUT (bit6) deve essere 1 (RA0 > RA2). Ora imposta RA0=1.5V: C1OUT deve scendere a 0. Verifica PORTB.
  2. Nel programma VRCON, imposta VR=8 (VRR=0). Calcola Vref: (8/32 + 0.25) × 5 = (0.25 + 0.25) × 5 = 2.5V. Verifica nel simulatore che VRCON contenga 0b11101000. Poi prova VRR=1, VR=12: Vref = (12/24)×5 = 2.5V — stesso valore, diverso registro.
  3. Carica il rilevatore di soglia. Con RA0=2.0V (sopra 1.458V): LED_OK acceso, LED_ALARM spento. Abbassa RA0=1.0V: deve scattare CMIF, ISR attiva LED_ALARM. Verifica che ALARM_FLAG=1 in RAM.
  4. Sfida: aggiungi isteresi software per evitare oscillazioni attorno alla soglia. Implementa: soglia_alta=1.6V (VRR=1, VR=8), soglia_bassa=1.3V (VRR=1, VR=6). Nella ISR, se ALARM_FLAG=0 usa soglia_alta; se ALARM_FLAG=1 usa soglia_bassa. Aggiorna VRCON di conseguenza per creare un trigger di Schmitt digitale.

❓ Quiz rapido

1. Quale vantaggio ha il comparatore rispetto all’ADC per il semplice rilevamento di una soglia fissa?

Mostra risposta

Tre vantaggi principali. Primo, la velocità: un comparatore risponde in nanosecondi, mentre l’ADC del PIC richiede almeno 20µs di acquisizione + 10×Tad di conversione = circa 40µs totali. Secondo, il consumo: il comparatore può funzionare in SLEEP, l’ADC no — il modulo comparatore con interrupt può svegliare il PIC dal modo sleep, ottimo per sistemi a batteria. Terzo, la semplicità: non serve software di conversione, scaling o lookup table — il comparatore dà direttamente un bit. Lo svantaggio è che non si sa “quanto” è la tensione, solo “sopra o sotto”.


2. Il bit C1INV in CMCON inverte l’output del comparatore. In quale scenario è utile questo bit invece di usare BTFSC/BTFSS invertiti nel codice?

Mostra risposta

C1INV è utile quando l’output fisico del comparatore (il pin RA4) deve essere invertito rispetto alla logica interna — per esempio se si controlla direttamente un transistor che richiede logica negativa, senza passare per il software. Anche per l’interrupt: CMIF scatta ad ogni cambio di C1OUT; se si vuole l’interrupt solo sulla transizione da alto a basso (non da basso ad alto), si può usare C1INV combinato con un flag software. Infine, C1INV permette di realizzare comparatori con logica “active-low” (output normalmente alto, si abbassa quando IN+ supera la soglia) senza modificare il codice di gestione del flag.


3. CMIF scatta ad ogni cambio di C1OUT o C2OUT. Come si distingue nella ISR quale dei due comparatori ha generato l’interrupt?

Mostra risposta

Non esiste un flag separato per C1 e C2 — CMIF è un unico flag che si alza per qualsiasi cambio di entrambi i comparatori. Nella ISR bisogna leggere CMCON e controllare i bit C1OUT (bit6) e C2OUT (bit7). Confrontandoli con il loro valore precedente (salvato in RAM prima che scattasse l’interrupt) si determina quale comparatore è cambiato e in quale direzione. Schema tipico: salvare CMCON_PREV = CMCON all’uscita dalla ISR; al prossimo ingresso, confrontare il nuovo CMCON con CMCON_PREV usando XOR — i bit cambiati indicano quale comparatore ha generato l’evento.

Prossima lezione →

Lezione 31 — Timer1 e Timer2: usi avanzati
Timer1 come contatore esterno e timer a 16 bit. Timer2 come base per PWM e CCP. Misura di frequenza e periodo con capture mode del modulo CCP.

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