Pulsante, interrupt e led

Buonasera a tutti.
Qualcuno mi sa sistemare questo sketch in modo che non sbagli un colpo?

info:
Pulsante normalmente aperto chiude verso massa il pin 2 di interrupt, alla pressione del tasto il led sul pin 13 deve cambiare di stato.
A me succede che una volta su 10 pigiando il pulsante il led cambia di stato, ma rilasciando il pulsante il led cambia ancora di stato! dov'è l'inghippo?

questo è il codice:

void setup()
{
pinMode(LED, OUTPUT); // definiamo pin output
pinMode(int0, INPUT); // definiamo pin input
attachInterrupt(0, blink, FALLING);

void loop()
{
if(change)
{
noInterrupts();
change=false;
state=!state;
digitalWrite(LED, state);
while(digitalRead(int0)==LOW)
{
delay(200);
}
delay(200);
interrupts();
}
}
void blink()
{
change=true;
}

Buongiorno,
prima di tutto, essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con MOLTA attenzione il su citato REGOLAMENTO ...

... poi, in conformità al suddetto regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

In pratica, tutto il tuo codice dovrà trovarsi racchiuso tra due tag: [code] _il _tuo_ codice_ [/code] così da non venire interpretato e non dare adito alla formazione di caratteri indesiderati o cattiva formattazione del testo. Grazie.

Guglielmo

Dopo che avrai fatto quanto richiesto da Guglielmo ti do un consiglio:
Mettiti di fronte ad un foglio di carta e scrivici quello che vuoi che succeda nei diversi stadi del tasto, che sono:
Nulla successe, tasto rilasciato
Tasto appena prenuto
Tasto in pressione
Tasto in rilascio.
Scrivi quello che vuoi che succeda, e NON pensare a come farlo succedere.
Poi mostra il risultato che ne parliamo

... più che altro, DOPO aver fatto quello che ti ho chiesto al post #1, dicci se hai messo una rete RC per il debouncing del pulsante, altrimenti è ovvio che NON ti funzioni bene ... ::slight_smile:

Guglielmo

... ho letto la tua presentazione e, considerando quello che scrivi, aggiungo ... sicuro che per uno come te NON sia più adatto utilizzare Atmel Studio (è gratuito) invece che l'IDE di Arduino?

Vista sia la preparazione che il tuo piacere a lavorare a livelli più "bassi" ... è l'ambiente ideale :wink:

Guglielmo

Grazie della info Guglielmo, probabilmente si, tempo permettendo proverò anche l'Atmel Studio...sfruttando la board di arduino e i suoi vari shield, per non ricadere nel paranoico incubo di realizzare gli stampati.

Comunque sono daccordo con te, se mettessi una rete RC funzionarebbe, ma in questo momento non mi interssa tanto farlo funzionare quanto capire a fondo perchè con quelle 4 righe di codice ogni tanto sbarella.
Il debouncing dovrei riuscire a farlo con il delay(200) nel ciclo di while (con interrupts disattivati).
Al rilascio del pulsante ho inserito un ulteriore delay di 200ms prima che vengono riattivati gli interrupts, non vedo motivi per il quale al rilascio del pulsante ogni tanto si retriggera l'interrupt commutandomi l'uscita LED.

Per Silente:

Tasto rilasciato---> Led nello stato della precedente pressione.
Tasto premuto---> Led inverte di stato.
Tasto in pressione---> Led mantiene lo stato aquisito.
Tasto in rilascio---> Led DEVE mantenere lo stato già aquisito...fino a prossima pressione.

Grazie ancora

... devi sistemare il codice come ti ho chiesto al post #1. Grazie

Guglielmo

..e comunque a parte il discorso dei tag (che devi sistemare), il codice non è completo perché manca la graffa di chiusura della setup() e soprattutto DOVE hai definito la variabile "change" (che tra l'altro deve essere dichiarata "volatile" se la modifichi nell'ISR)???

Devi postare tutto il codice esatto altrimenti se anche uno volesse provarlo per proprio conto deve poterlo compilare, senza dover impazzire ad "indovinare" cosa tu ti sia scordato...

Fallo, correggi il tuo primo post con il codice completo ed esattamente quello che usi per provare, poi scrivi qui un nuovo post per spiegare esattamente cosa fa QUELLA versione (e quindi cosa NON fa), così possiamo aiutarti.

Scusate, sistemato il sorgente con la procedura corretta.
Ho dimenticato di specificare che sul pulsante non c'è rete RC ma è comunque presente una R di pullup.
So bene inoltre di essere paranoico con questa richiesta, ma è un sassolino nella scarpa che mi voglio levare capire perchè mi parte l'interrupt al rilascio del pulsante.
Ciao

void setup() 
{
  pinMode(LED, OUTPUT); // definiamo pin output
  pinMode(int0, INPUT); // definiamo pin input
  attachInterrupt(0, blink, FALLING);


void loop() 
{
  if(change)
  {
    noInterrupts();
    change=false;
    state=!state;
    digitalWrite(LED, state);
    while(digitalRead(int0)==LOW)
    {
      delay(200);
    }
    delay(200);
    interrupts();
  }
}
void blink()
{
  change=true;
}

gpb01:
... poi, in conformità al suddetto regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

Ma è così difficile capire che devi editare il tuo PRIMO post e non scriverne uno nuovo ? ? ? Eppure è scritto in modo chiaro ...

Guglielmo

mefore:
capire perchè mi parte l'interrupt al rilascio del pulsante.

L'interrupt parte sul fronte di discesa.

Se parte al rilascio vuol dire che sente un fronte di discesa.

E perché al rilascio, dopo cui dovrebbero esserci i 200ms di pausa per togliere i rimbalzi c'è comunque un fronte di discesa?

L'errore è nel dare per scontato di restare nel while che attende il rilascio.

Ma così evidentemente non è.

Hai filtrato solo i rimbalzi al rilascio, ma non quelli alla pressione.

Quindi al rilascio qualche volta si è già fuori dal while, e i rimbalzi del rilascio vengono visti come pressione.

Tanto per incominciare ti chiedo se non sarebbe più utile e comodo effettuare il debounce hardware e non software de pulsante (chiedi e guarda in giro per maggiori info in merito). Credo che, infatti, il debounce software NON sia strada percorribile.
In secondo luogo ti ringrazio di avermi chiarito quello che deve succedere. In sintesi nel momento in cui sento che il tasto sta venendo premuto il led deve cambiare di stato.
In terzo luogo mi domando e dico, non potrei scrivere un programma con una loop vuota, che contiene soltanto la setup() e una funzione di interrupt di una sola riga chiamata al RISING de pin del tasto?
Inoltre mi rispondo che si potrebbe, e sarebbe facile. Devi però avere una serie di conoscenze base:
1)sapere comandare un interrupt
2)sapere scrivere una funzione
3)sapere comandare un led
4)sapere leggere un pulsante.
Lo sai fare?
Infine, PRIMA DI RISPONDERE ti chiedo di seguire le direttive di lord Guglielmo, che temo sia intenzionato a chiudere la baracca se non lo fai

Silente:
...
Inoltre mi rispondo che si potrebbe, e sarebbe facile. Devi però avere una serie di conoscenze base:
1)sapere comandare un interrupt
2)sapere scrivere una funzione
3)sapere comandare un led
4)sapere leggere un pulsante.
Lo sai fare?

Se leggi la sua presentazione (cosa da fare sempre prima di rispondere) vedi che ha un ampia esperienza anche a basso livello :wink:

Guglielmo

Buonasera a tutti.
Tra impegni famigliari e rogne sul lavoro è sempre difficile entrare qua, ma quando riesco (anche alle 3 di notte) è sempre un piacere.
Postando nel precedente messaggio il mio codice in effetti è mancato qualcosa...nel ripulirlo dai commenti e dalle parti di codice di debug ho ranzato un po troppo. Riallego quindi il codice completo di tutti i commenti e del led verde aggiunto solo come debug.
Infinitamente semplice nella sua essenzialità...ma non funziona!

int pin = 13; // Led rosso
int verde =12;  // Led verde...test point per il ciclo di while
int int0 = 2; // pin di interrupt esterno
volatile bool change=false;  // abilita cambio di stato del led rosso
int state = LOW;  //Variabile di stato del led rosso

void setup() 
{
  pinMode(pin, OUTPUT);
  pinMode(verde, OUTPUT);
  pinMode(int0, INPUT);
  attachInterrupt(0, blink, FALLING);
  digitalWrite(verde,LOW);
}

void loop() 
{
  if(change)
  {
    noInterrupts(); // disabilito interrupts per non farmi buttar fuori dall'if se ci fosse un rimbalzo del pulsante.
    change=false; // riarmo change per non farmi rientrare nel if fino alla prossima pressione del pulsante
    state=!state; // not della variabile di stato del led rosso
    digitalWrite(pin, state); // che poi vado a scrivere sull'uscita del led rosso
    while(digitalRead(int0)==LOW) // mentre ho ancora gli interrupt disabilitati giro in questa while finche il pulsante è premuto
    {
      digitalWrite(verde,HIGH); // accendo un led verde per conferma che non esco dal while
      delay(200); // delay di 200ms tanto per fare qualcosa
    }
    digitalWrite(verde,LOW);  // se sono qui è perchè il pulsante è stato rilasciato...
                              // mentre l'interrupt è ancora disabilitato
    delay(200);       // al rilascio del pulsante aspetto ulteriori 200ms di debuncing prima di riabilitare gli interrupt
    interrupts();   // se sono qui è perchè il pulsante è stato rilasciato e sono terminati tutti i rimbalzi...quindi posso riarmare l'interrupt
                  // e attendere, senza far nulla,  la nuova pressione del pulsante.
  }
}
void blink()
{
  change=true; // triggerato interrupt sul pin int0. Al prossimo ciclo della loop()il program counter mi entra nel condizionale if(change)
}

Claudio_FF:
L'errore è nel dare per scontato di restare nel while che attende il rilascio.

Claudio ti ringrazio per l'interessamento ma purtroppo non è nemmo come dici tu...se rileggi il codice colpleto che ho postato qui sopra vedi che nel ciclo di while ci ho messo un led verde che rimane acceso fintanto che sono nel while, ed in effetti cosi è. Quindi ho la prova che nella while ci rimango finche c'è il pulsante premuto, quando lo rilascio il led verde si spegne e solo dopo che il led verde si è spento e sono trascorsi ulteriori 200ms vado a riarmare l'iterrupt. Ma spesso, oltre a spegnersi il led verde, mi cambia ancora di stato il led rosso.

Silente:
Tanto per incominciare ti chiedo se non sarebbe più utile e comodo effettuare il debounce hardware e non software de pulsante (chiedi e guarda in giro per maggiori info in merito). Credo che, infatti, il debounce software NON sia strada percorribile.

Perdonami silente ma temo di non essere daccordo, con un pic e piu o meno le stesse righe di codice tutto funziona alla grande.
Ma a parte questo so bene che un debuncing hardware con una RC, o meglio ancora un flip flop con due nand, sarebbe la soluzione ideale...ma confidando un giorno di poter fare con arduino qualcosa di piu utile di un led che cambia stato, sviscerare a fondo il comportamento degli interrupt è un passo fondamentale, piu precisamente è fondamentale capire come il compilatore dell IDE di arduino traduce in assembler le righe di codice che gli diamo in pasto.

Per Guglielmo, ti ringrazio infinitamente per la stima che ti ha suscitato in me la mia presentazione, ma credimi, alla soglia dei 50 quando riguardo il codice che scrivevo 15/20 anni fa mi gia la testa!
Ad oggi (complice anche mansioni diverse di lavoro che mi trovo a fare rispetto a tempo fa) mi ritengo un esordiente totale!
Ciao a tutti e grazie per l'interssamento.

Il massimo aiuto che ti so dare, visto che non so programmare a basso livello, è dirti che:
1)credo che il tuo programma non vada perché, tra l'altro, la funzione di interrupt è chiamata FALLING, quindi agisce quando il pulsante viene rilasciato (almeno se esso risulta normalmente aperto), mentre a te pare interessi funzioni alla pressione.
2)con il debounce hardware dei pulsanti il programma sarebbe potuto venire (penso) una cosa del tipo:

#define LED 13//define dovrebbe sostituire a ogni volta che trova la stringa scritta prima tra i due spazi (nel caso LED) con quello scritto dopo di essa (nel caso il numero tredici, ma può essere una stringa o un insieme di funzioni)
void setup()
{
attachInterrupt(0, funzione, RISING);
pinMode (13, OUTPUT);//su Arduino Uno un led sul pin 13 è già posto
}
void loop()
{
}
void funzione()
{
digitalWrite (LED, !digitalRead (LED));
}

Mi scuso per la non indentazione ma non ho usato ide o altro
3)senza i debounce hardware devo fare un minimo di quello software. Per cominciare esso mi basta che sia sulla pressione, e non sul rilascio, quindi posso aggiungere queste cose:
dichiaro una boolean globale volatile che mi dica se la interrupt è intervenuta (indovina dove la alzo)
la loop si compone di un controllo su quella flag, se alta ...
se alta la abbassa, ferma gli interrupts, aspetta abbastanza tempo, e gli riarma.
Non testato, ma a livello teorico va

Domanda della mattina presto:
ma siamo sicuri (io non lo so) che i delay si comportino come si devono comportare a interrupts disattivati?
mi sembra di ricordare che la delay si basi su un interrupts, o faccio confusione?

Silente:
quindi agisce quando il pulsante viene rilasciato

Dipende da come è collegato. La condizione del ciclo while mi fa supporre che chiude verso massa una resistenza di pull-up (a margine: per evitare questi tipi di dubbi io preferisco non usare esplicitamente HIGH LOW ma definire delle label come PRESSED_LEVEL ecc che imposto all'inizio a seconda del collegamento hardware realizzato).

mefore:
Quindi ho la prova che nella while ci rimango finche c'è il pulsante premuto, quando lo rilascio il led verde si spegne e solo dopo che il led verde si è spento e sono trascorsi ulteriori 200ms vado a riarmare l'iterrupt. Ma spesso, oltre a spegnersi il led verde, mi cambia ancora di stato il led rosso.

Forse i fronti di discesa vengono catturati anche in condizione noInterrupts, e vengono gestiti alla riattivazione con interrupts. Le funzioni noInterrupts/interrupts agiscono solo sul bit di abilitazione globale, o anche su quelli specifici delle singole periferiche?

Claudio_FF:
Forse i fronti di discesa vengono catturati anche in condizione noInterrupts, e vengono gestiti alla riattivazione con interrupts. Le funzioni noInterrupts/interrupts agiscono solo sul bit di abilitazione globale, o anche su quelli specifici delle singole periferiche?

credo che tu qui ci abbia preso...
stavo proprio pensando quello
secondo me le delay sembrano funzionare male perchè al ripristino degli interrupt esegue un (almeno uno, non so se tutti) uno degli interrupt che aveva in pancia, memorizzati ma non eseguiti
Guglielmoooo pensaciii tuuuuuuuuuu....
sulla musichetta del gigante buono Ferrero

Standardoil:
almeno uno, non so se tutti) uno degli interrupt che aveva in pancia, memorizzati ma non eseguiti

Direi uno solo per ogni periferica. Non ho mai visto architetture che accumulano/accodano in hardware più int request dello stesso tipo... anche perché se la richiesta precedente non è stata gestita quella seguente indica un nuovo evento (dello stesso tipo) che si sovrappone... la logica complessiva a questo punto può essere totalmente compromessa.

Resta il fatto che un segnale interrupt non deve mai essere un segnale "sporco".