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.
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.
; ============================================================ ; 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 > 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.
; ============================================================ ; 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 > 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
- 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.
- 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.
- 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.
- 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