Interrupt e Bottoni

Buongiorno a tutti

Ho la necessità di eseguire una cera funzione ogni qual volta ho un cambio di stato sull'interrupt 0 ovvero sul pinDigital2. Per intervenire sul pin appropiato ho messo un tasto collegato direttamente tra massa ed il pin e provando diversi pasametri CHANG, FALLING e RISING ma in questo caso ho dovuto connettere il tasto a + 5v

Ho due problemi :

1) L'interrupt funziona ma la funzione chiamata viene eseguita molte volte .... anche se il tasto è stato premuto e rilasciato !!!! Perchè "Rimbalza" in quetso modo ?

2) L'interrupt viene rilevato solo dopo certo tempo !!!!! Se premo velocemente il tasto arduino non si accorge di niente !!!! Roba da matti !

Dove sbaglio ? Di seguito allego il codice sorgente :

setup() { Serial.begin(9600); attachInterrupt(0, my_interrupt_handler, FALLING); }

void loop() {

}

void my_interrupt_handler() { Serial.println(" Interrupt !!!!"); }

metti un condesatore da 100n o poco più tra il pin 2 e massa.
come metti il pin a massa? resistenza da 10k tra pin e massa e pulsante tra pin e 5v, vero?

Ciao,

i bottoni meccanici creano tutti delle oscillazioni della conduzione quando vengono premuti, anche se noi non ce ne accorgiamo... il microcontrollore li rileva.

Devi cercare le soluzione per il "debounce" di un bottone, ve ne sono sia a livello hardware che software.

Qui trovi una soluzione a livello di codice: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1250318272/1

Hai attivato il pullup resistor (o inserito esternamente) sul pin che usi? Il pin, quando il bottone e' aperto, si trova altrimenti in uno stato indefinito, per cui si hanno risultati imprevedibili.

Ciao, Marco.

ciao Marco

Non puoi usare una soluzione di debounce software se vuoi usare un bottone per l'interrupt. Ti serve una soluzione hardware per fare il debounce.

Ciao Uwe

Dal link della discussione pubblicata da Marco arrivi ad uno schemettino dove c'è una soluzione HW.

Ciao drakelive

2 possibilitá:

il 14490 come deboucer per 6 pulsanti http://www.datasheetcatalog.org/datasheet/philips/HEF4528BF.pdf

il 4528 come multivibratore monostabile che puó essere retriggerato. Metti una lungheza di impulso tra 5 e 10 msecondi e il pulsante in modo che fa partire l' impulso all'azionamento. (I0 e Cd a H, su I1 il pulsante a +5V e una resistenza pull down. O al interrupt del arduino). http://www.datasheetcatalog.org/datasheet/philips/HEF4528BF.pdf

Ciao Uwe

Ho dato un'occhiata ai link e ho potuto vedere alcune possibilità. Sul seguente tutorial presente sul sito di Arduino si utilizza una sola resistenza : http://arduino.cc/en/Tutorial/Debounce

Nella seguente pagina si utilizza un circuito un po differente composto oltre che da una resistenza anche da un condensatore: http://www.all-electric.com/schematic/debounce.htm

Qual'è la soluzione migliore?

Ringrazio Drake

Ciao drakelive

Il primo link fa un debouce via software e percui non possibile se vuoi collegarlo al interrupt.

Sul secondo linkl puoi usare la soluzione col condensatore e il 7414.

Ciao Uwe

col mio arduino basta un condesatore

Dei segnali che cambiano lentamente in modo analogico possono dare problemi con entrate digitali. L' aggiunta di un Schmitt-Trigger 7414 preserva da tale problema.

In sostanza puó funzionare e anche no.

Ciao Uwe

uwefed perché non si può mettere il codice del debounce software in un interrupt?

Perché gli interrupt devono contenere codice "snello", se si infilano dentro loop od altro si rallenta tutto l'Atmega.

appunto! il codice è “snello” (reading è l’input, che ogni interrupt viene negato a priori, buttonState sarà la variabile volatile che verrà letta nel loop per conosce lo stato del pulsante)

// check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH),  and you've waited
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }
 
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonState = reading;
  }

Ma in un interrupt non puoi usare la funzione Millis, che è calcolata proprio da un interrupt. Durante un interrupt io credo che il conteggio del tempo si fermi.... o no?

si, durante un interrupt millis() restituisce sempre lo stesso valore, ovvero quello del tempo di inizio interrupt. Ma questo valore è più che sufficiente per svolgere quello di cui abbiamo bisogno...

esempio io schiaccio il pulsante, ogni frequenza di "bounce" viene chiamati 2 interrupt, uno per il fronte di salita e uno per quello di discesa (caso interrup CHANGE). ogni interrupt però avrà una millis() differente (se la frequenza è > di 1 millis, altrimenti capiteranno interrupt con lo stesso valore millis ma questo non è un problema)

e solo l'interrupt con la millis() che soddisfa la situazione if ((millis() - lastDebounceTime) > debounceDelay) cambierà il pulsante.

Ora mettiamo il caso il pulsante normalmente (non pressato) da GND stabile, e se premuto rimbalza tra +5v e GND gli interrupt verranno lanciati: quando premi il pulsante, poi a frequenza di bounce, e poi quando lasci il pulsante. possono nascere 2 problemi: 1. tieni premuto il pulsante meno tempo di lastDebounceTime; il pulsante rimarrà attivo fisso, quindi dal loop ogni tanto devi richiamare l'interrupt(*), e devi essere sicuro di farlo più spesso di lastDebounceTime 2. se la situazione if ((millis() - lastDebounceTime) > debounceDelay) è soddisfatta, ci sono 2 casi: 2.1 la lettura è alta, quindi il bottone è ancora premuto, senza ombra di dubbio 2.2 la lettura è bassa, il pulsante POTREBBE essere stato rilasciato OPPURE per caso hai beccato un rimbalzo basso, ma il bottone è ancora premuto. Il problema può essere eliminato via software così: l'interrupt non setta il bottone a basso, ma una booleana che dice che secondo lui è stato abbassato. Se avviene un'interrupt entro debounceDelay, il pulsante è ancora attivato e quello che ci fregava prima era una bounce, altrimenti il tasto è stato veramente abbassato...

Sinceramente, a pensarci bene, piuttosto che un'interrupt sul cambio stato del pin sarebbe meglio un interrupt basato sul tempo, così eviti di dover richiamare il codice dell'interrupt dal loop() (che alla fine è quello che si sta evitando di fare), che invece usa solo la variabile buttonState per sapere com'è messo il pulsante

(* dal loop NON è possibile chiamare una funzione interrupt, quindi il codice dell'interrupt dovrebbe essere inserito in una funzione chiamata sia dal loop che dall'interrupt. la funzione e tutte le variabili globali che utilizza dovrebbero essere dichiarate volatile.)

(* dal loop NON è possibile chiamare una funzione interrupt, quindi il codice dell'interrupt dovrebbe essere inserito in una funzione chiamata sia dal loop che dall'interrupt. la funzione e tutte le variabili globali che utilizza dovrebbero essere dichiarate volatile.)

Ma invece credo che si possa fare, cioè attachInterrupt lo fà e perchè non dovrebbe essere possibile richiamarla dal loop per prima di richiamarla dal loop bisogna spegnere l'interrupt con cli(), e riaccenderlo con sei() oppure usare deattach.

Il problema è che la routine viene comunque eseguita più volte, con la soluzione hardware si risolve meglio, una soluzione software e all'uscita della routine di interrupt disabiliti l'interrupt per poi riabbilitarlo nel loop, preferisco risolvere via hardware come già detto.

Ciao.

MauroTec: Ma invece credo che si possa fare, cioè attachInterrupt lo fà e perchè non dovrebbe essere possibile richiamarla dal loop per prima di richiamarla dal loop bisogna spegnere l'interrupt con cli(), e riaccenderlo con sei() oppure usare deattach.

io sto parlando delle funzioni ISR, non possono essere chiamate da codice, quindi si fa una funzione a parte, che viene chiamata sia da ISR che dal loop(), proprio quello che fa attachInterrupt(). Non bisogna spegnere l'interrupt perchè comunque essendo una funzione le variabili interne non si sovrappongo, bisogna fare attenzione alla consistenza dei dati di output, ma in questo codice non dovrebbero esserci problemi.

MauroTec: Il problema è che la routine viene comunque eseguita più volte, con la soluzione hardware si risolve meglio, una soluzione software e all'uscita della routine di interrupt disabiliti l'interrupt per poi riabbilitarlo nel loop, preferisco risolvere via hardware come già detto.

Ciao.

secondo me la soluzione hardware è sempre migliore, purtroppo non sempre si hanno sotto mano i componenti e si risolve così

io sto parlando delle funzioni ISR,

Parlavamo della stessa cosa chiamata diversamente, l'incomprensione nasce dal fatto che quelle che sembrano funzioni, sono invece macro, io per evitare di chiamarle macro mi riferisco a queste come le routine ISR.

Non bisogna spegnere l'interrupt perchè comunque essendo una funzione le variabili interne non si sovrappongo, bisogna fare attenzione alla consistenza dei dati di output, ma in questo codice non dovrebbero esserci problemi.

Mi sono espresso solo in parti, ciò che volevo dire è che se spegni (attacchi o stacchi) quell'interrupt all'uscita della funzione (sia che si tratti di routine ISR o funzione attaccata) ciò che si ottiene è interrupt one shot, quindi quando scatta l'interrupt la routine viene eseguita una sola volta, uscendo si ritorna nel loop, il manuale dice che in uscita dalle routine ISR un pò di codice (non ricordo quanto) nel loop (main) viene eseguito prima che una nuova richiesta di interrupt venga onorata.

Allora nella funzione attacata, a fine o anche ad inizio stacchi la funzione dall'interrupt, e salvi lo stato, "one_shot = true" nel loop usi, if (one_shot) one_shot = false, e riabbiliti l'interrupt.

Non l'ho testato e quindi non so se ci sono side effect, ma di base dovrebbe funzionare.

Ciao.

Buongiorno

Mi sto incasinando .... ognuno dice la propria e non si arriva ad una conclusione. :-D

Nel caso specifico la funzione di interrupt ( my_interrupt_handler()) deve semplicemente impostare a TRUE una variabile globale Boolean quando c'è una variazione delo stato del Pin controllato.

Quindi la variante composta da una resistenza da 10K e da un condensstore da 10nF potrebbe andare?

Ringrazio in anticipo Drake

Mi sto incasinando .... ognuno dice la propria e non si arriva ad una conclusione. :-D

Ognuno dice la sua e tutti hanno ragione.

void b0_interrupt()
{
  if (b0_state)
    b0_state = false;
  else
    b0_state = true;
}

void loop();
{
     //quicontrolli la variabile b0_state e aggisci.     
}

Lo schema è quello, con un resistore da 10k e un condensatore, il valore di questo dipende io avevo a portata di mano un 22nF e funziona ma se hai 100nF dovrebbe andare bene.

Nota che è meglio evitare di far fare cose impegnativi dentro le funzioni attaccate all'interrupt, da evitare il serial.print.

Ciao.