Uso corretto di un interrupt di cambio pin


10

Sto cercando di utilizzare gli interrupt di cambio pin per rilevare i pulsanti premuti. Fino ad ora non ho mai lavorato con questo tipo di interruzioni e ci sono alcuni problemi, quindi voglio assicurarmi se questo è l'uso corretto.

Se il foglio dati è corretto, è necessario fare le seguenti cose per usare un interrupt di cambio pin:

  1. Imposta quali PIN vuoi controllare nel registro PCMSK
  2. Abilita il registro PIN per il controllo di interruzione cambio pin (PCICR)
  3. Abilita interrupt
  4. Utilizzare il vettore di interrupt corrispondente

Progetto: lampada semplice, colori controllati tramite 4 pulsanti.

Impostare:

  • Atmega168A-PU
  • 4 interruttori a pulsante mini
  • MOSFET per controllare il mio LED RGB da 3 Watt

Ecco il codice che sto usando che non funziona come previsto:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

Nota: i pulsanti devono essere rimossi. Dal momento che sto provando questo passo per passo e non dovrebbe essere necessario accendere il LED, l'ho ignorato qui.

Domanda: Il modo in cui sto cercando di utilizzare gli interrupt è corretto?

Problemi con la mia configurazione:

  • I pulsanti 1-3 sono totalmente ignorati.
  • Button4 sta innescando un reset di Atmega

Cose che ho controllato:

  • I pulsanti non sono in alcun modo collegati al PIN di ripristino
  • I pulsanti sono correttamente collegati a GND se premuti
  • I pulsanti non sono collegati a GND se non vengono premuti
  • I pulsanti funzionano bene se li utilizzo senza interruzioni, ad esempio:

    if (! (PINC & PULSANTE4)) {PORTB ^ = BLU; }

  • 16MHZ cristallo esterno / cristallo interno
  • Eventuali errori nel routing
  • Sto usando un condensatore da 100nF tra PWR e GND su atmega
  • VCC (7), GND (8), GND (22), AVCC (20) sono collegati (poiché non ho bisogno di AREF, non è collegato)

È necessario il flag PCIE1 (non PCIE2) e PCINT1_vect (non PCINT2)
microtherion

Perché PCIE1? Sto usando il registro C, quindi se conto sarebbe A (PCIE0), B (PCIE1), C (PCIE2)? Comunque, l'ho provato con PCIE1 e PCINT1_vect e non ho alcuna reazione se premo i pulsanti.
echox,

1
Può essere un po 'rischioso assumere l'ortogonalità in tali incarichi. In questo caso particolare, saresti quasi corretto, tranne per il fatto che l'ATmega168 non ha una porta A. In ogni caso, ho seguito il foglio dati e il pinout. Un altro suggerimento era che si utilizzava PCIE2, ma si impostavano bit in PCMSK1; non può essere giusto (purtroppo, non so perché il tuo schizzo modificato non funzioni ancora).
microtherion,

Grazie, capisco anche che la combinazione di software di debug che dipende dall'hardware di autocostruzione non è così facile ;-)
echox,

Risposte:


14

Gli interrupt di cambio pin di solito non sono un buon modo per rilevare le azioni dei pulsanti. Questo perché i pulsanti meccanici rimbalzano e otterrai un sacco di interruzioni insignificanti, quindi dovrai comunque comunque rimandare.

Un modo migliore è avere un interrupt periodico, come ogni 1 ms (frequenza di 1 kHz). È molto tempo per la maggior parte dei processori, quindi la frazione del tempo trascorso nell'interrupt sarà piccola. Basta campionare lo stato del pulsante ad ogni interruzione. Dichiarare un nuovo stato del pulsante se si è visto il nuovo stato 50 ms di fila. 50 ms è più lungo della maggior parte dei pulsanti che rimbalzano, ma è ancora abbastanza corto in modo che gli umani non si accorgano o si preoccupino del ritardo.

Si noti che in questo modo è anche possibile gestire più pulsanti nello stesso interrupt periodico da 1 ms. Tutto ciò che serve è un contatore per ciascun pulsante.

Altre informazioni sul tempo di rimbalzo:

Occasionalmente, come in questo caso, qualcuno dice che 50 ms è un tempo di rimbalzo troppo lungo. Questo non è vero per i normali pulsanti premuti dagli umani. Potrebbe essere un problema forse in applicazioni molto critiche come un cronometro, ma finora non ne ho mai incontrato uno. Ho fatto un test su questo nei primi anni '80, e anche molte altre persone.

È vero che il tempo di rimbalzo del pulsante tipico è di circa 10 ms, con quasi tutti i settaggi di 25 ms. Il fattore limitante sul tempo di rimbalzo è la percezione umana. 50 ms è un po 'più breve rispetto a dove le persone iniziano a notare un ritardo quando non lo stanno cercando. Anche allora, ci vuole molto più tempo per essere fastidioso. In alcuni casi può essere possibile che un essere umano rilevi una differenza tra 50 ms e 0 ms di ritardo se lo sta cercando in modo specifico , ma è molto diverso dal premere un pulsante e vedere accadere qualcosa e non pensare al ritardo.

50 ms è quindi un buon tempo di rimbalzo perché il ritardo è inferiore al limite di percezione nelle applicazioni ordinarie, molto al di sotto del limite di disturbo e ben al di sopra del tempo di rimbalzo della maggior parte degli switch. Ho trovato interruttori che sono rimbalzati per così tanto tempo, quindi potresti anche spingere al limite di percezione poiché non c'è nulla da perdere.

Ho realizzato molti prodotti con i pulsanti di debounce del firmware utilizzando un tempo di debounce di 50 ms. Nemmeno una volta un cliente ha menzionato nemmeno un ritardo. Tutti hanno accettato che i pulsanti funzionassero senza problemi.


1
50ms potrebbero essere troppo lunghi per alcuni casi (di solito, 10-20ms è il limite della percezione umana, e questo dovrebbe essere sufficiente per rimbalzare), ma il metodo qui descritto è la strada da percorrere.
Laszlo Valko,

1
@Laszlo: No, 50 ms non è troppo lungo per il caso ordinario. Vedi aggiunta alla mia risposta.
Olin Lathrop,

Ho provato 50ms che funziona bene per me :-) Sono ancora curioso di sapere perché l'interruzione del cambio pin non funziona (a parte le cose che rimbalzano), ma questo funziona :-) Grazie.
echox,

1

Gli interrupt di cambio pin sono un modo migliore per rimbalzare rispetto al polling. L'interruzione di solito passa attraverso una logica come un D-Flip Flop o D-Latch. Sebbene ciò sia vero, è più difficile implementare questa routine di debounce con compilatori di livello superiore. Una volta che si verifica l'interrupt, il flag di interrupt non viene cancellato e l'abilitazione dell'interrupt viene cancellata fino a quando non si verifica un ritardo. Una volta che si è verificato il ritardo, viene verificato lo stato del pin e se è ancora nello stato dato che ha attivato l'interrupt, lo stato del pulsante viene modificato e l'indicatore di interruzione viene cancellato e viene abilitata l'interruzione. Se non si trova nello stato che ha causato l'iniziato, viene abilitata l'interruzione e lo stato rimane lo stesso. Questo libera il processore per altre attività. Il periodico interrompe il tempo perso nel programma.


-1

"Gli interrupt di cambio pin di solito non sono un buon modo per rilevare le azioni dei pulsanti."

Sbagliato. PC INT è l'opzione migliore. Se si utilizza il polling per verificare lo stato di un pulsante, la maggior parte delle volte non verrà eseguito nulla. Sprechi molto tempo prezioso nella CPU. PC INT consente di eseguire le azioni solo su richiesta.

"Questo perché i pulsanti meccanici rimbalzano e otterrai un sacco di interruzioni insignificanti, e poi dovrai comunque rimandare."

Corretto sul rimbalzo. Tuttavia, non dovresti MAI rimbalzare un pulsante / interruttore all'interno di una routine di interrupt (stesso motivo: perdita di tempo della CPU). Gli ISR ​​devono essere veramente brevi ed efficienti, in termini di codice. Usa il debouncing hardware. Mantieni pulito il tuo software!

Il debouncing hardware è più conveniente, vedi qui / Debouncing RC + trigger Schmitt per riferimento. L'ho usato per innumerevoli volte con PC INT, non ha mai fallito.

Quindi sì, puoi (e dovresti) usare PC INT per ottenere lo stato di un pulsante. Ma devi anche utilizzare il corretto rimbalzo dell'hardware.


2
Il debouncing del software è un approccio valido e il più delle volte l'overhead aggiuntivo della CPU è irrilevante. Dire che in genere dovresti rimbalzare nell'hardware è discutibile nella migliore delle ipotesi. Dire che devi usare il debounc dell'hardware in tutti i casi è semplicemente sbagliato.
Olin Lathrop,

Nella maggior parte delle applicazioni il controller è comunque inattivo la maggior parte delle volte, eseguendo il loop principale. Inoltre, il tempo di CPU richiesto per eseguire un controllo dello stato IO e incrementare potenzialmente una variabile è minimo. L'implementazione del debouncing semplice nel software arriva quasi per "libero", l'hardware costa denaro. E non ridere di pochi centesimi, anche il montaggio costa denaro e se gestisci volumi medio-alti di un prodotto non è trascurabile. È vero che il tempo ISR dovrebbe essere breve, ma questo non è un argomento in questo caso. È probabilmente più critico se il PC INT ISR spara 50 volte di seguito a causa del rimbalzo.
Rev.1.0

@Nelson, lo 'spreco di tempo della CPU' conta in alcune applicazioni e non in molte altre. È necessario qualificare la risposta per la situazione in cui il tempo della CPU è critico.
user1139880
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.