Arduino e interrupt

Ciao a tutti,
inizio con il fare una piccola premessa per spiegare lo scenario in cui mi trovo, poi esporrò i miei dubbi in modo da essere più chiaro possibile...

Al momento dispongo di un Arduino (UNO o Mega al momento è irrilevante) che effettua diverse operazioni, come lettura di sensori (operazioni veloci) o comunicazione con altri dispositivi via seriale (operazioni lente), quello che vorrei aggiungere è una funzionalità di "antifurto" ed è proprio a questo punto che iniziano le mie perplessità..

Ad antifurto inserito, infatti tutti gli eventi provenienti dai sensori di allarme dovrebbero avere la priorità su tutto il resto così ho pensato agli interrupt esterni e mi sono documentato un po', se può essere utile anche ad altri riporto i 4 principali link che mi sono segnato:

AttachInterrupt
Simple Pin Change Interrupt on all pins
Interrupt
Arduino gestione degli interrupt

Da quello che ho visto esistono principalmente 2 tipologie di interrupt esterni, singoli o di gruppo (perdonatemi la semplificazione :stuck_out_tongue: ).

Con i "singoli" posso avere un'associazione uno a uno tra PIN e funzione, tuttavia sulla UNO sono presenti solo 2 PIN con questa funzionalità, mentre sulla MEGA solo 6 PIN.

Con quelli di "gruppo" ho un'associazione molti a uno dove n PIN convergono su un'unica funzione, tuttavia è possibile utilizzare ogni PIN presente sulla scheda.

Pensando ad un antifurto con solo 2 PIN (o 6 nel caso della MEGA) sono un po' stretto e non riuscirei a monitorare tutti i sensori direttamente (con dei sensori NC se li metto in serie a gruppi potrei ridurre il numero di ingressi da utilizzare, tuttavia perderei l'informazione puntuale relativa al sensore in allarme).

Così ho pensato agli interrupt di "gruppo", in questo modo al cambio di stato di ogni sensore il microcontrollore smette di fare quello che sta facendo e verifica se deve partire l'allarme.

Ma qui il primo problema, visto che l'Arduino fa anche altre cose e se il PIN di un sensore è nello stesso gruppo di un PIN di un sensore cosa succede? Parte un interrupt ad ogni variazione del sensore?? :fearful:

Fatte queste premesse al momento vedo solo 3 soluzioni possibili:

1-Riservo dei gruppi di PIN solo ed esclusivamente per la funzionalità di antifurto (questa soluzione non mi piace del tutto soprattutto visto che i pin non sono infiniti)

2-Collego i sensori sia ad un PIN di Arduino che ad un secondo (o terzo a seconda del numero di sensori) microcontrollore come ad esempio un attiny84 a sua volta è collegato ad un interrupt "singolo" di arduino.
In questo modo l'attiny non farà altro che controllare lo stato dei sensori senza che nient'altro lo rallenti, facendo scattare l'interrupt quando trova una variazione. (quest'altra soluzione invece ha lo svantaggio di aumentare la complessità del circuito)

3-Prendo un Arduino dedicato solo alla funzionalità di antifurto (questa in realtà non è una soluzione ma un ripiego)

Cosa mi consigliate? Esistono altre soluzioni valide oltre a quelle che mi sono venute in mente?

Grazie a tutti in anticipo

Gli interrupt su un microcontrollore sono di 2 tipi, interni ed esterni. Gli interrupt interni sono sollevati da una periferica integrata nel microcontrollore, ad esempio la seriale hardware, l'I2C, un timer, ecc... Gli interrupt esterni sono quelli agganciati ai piedini del chip. Sono di 2 tipi: INT e PCINT. I primi sono agganciati a specifici pin (visibili dalla piedinatura allegata come INT0 e INT1), i PCINT sono disponibili su tutti i pin (PCINTxx):

La differenza è che gli INT hanno un livello di priorità maggiore e possono essere sollevati per un segnale di "falling" (passaggio da stato alto a stato basso), di "rising" (da basso ad alto) o di tipo "low" (quando il piedino assume lo stato low). Gli altri permettono un generico "change", ossia passare da alto a basso o viceversa. Hanno un livello di priorità inferiore e non sono capaci di svegliare il chip da tutti gli stati di sleep (ad esempio, nella modalità più profonda, solo gli INT con segnale "low" ci riescono). Sono poi gestiti a gruppi, come hai detto, ossia ci sono 3 registri da 8 bit l'uno, ognuno per un gruppo di 8 pin quindi per sapere quale pin ha fatto scattare l'interrupt serve una lettura dei pin gestiti dal registro connesso fatta prima e dopo l'interrupt. Viene però in aiuto una libreria, detto PinChangeInt, con la quale puoi dimenticarti del lavoro sporco e pensare solo a scrivere la routine di ISR (gestione dell'interrupt).

Tornando alle domande, visto che stai lavorando con un antifurto, esso sarà attivo 24 ore al giorno per cui non penso che avrai problemi di risparmio energetico. Ti va bene quindi anche la gestione dei PCINT, con cui puoi in teoria leggere tutti i pin del chip (eliminati quelli che non puoi usare perché destinati ad altro). Oppure se hai bisogno di tanti ingressi, potresti anche suddividere i compiti fra più micro, deputando qualche chippino tipo l'Attiny85 o l'Attiny84 a semplici "rilevatori" di interrupt che passano poi i dati alla "centralina".

Ups... il link:
https://code.google.com/p/arduino-pinchangeint/

Innanzitutto grazie per le precisazioni relative agli interrupt e per il link alla libreria :wink:

Per la limitazione di poter monitorare solo il "change" con i PCINT non è un grande problema, anzi in realtà già pensavo di usare quella modalità.

La mia perplessità è se all'interno di uno stesso gruppo utilizzo un PIN per un sensore dell'antifurto che 2 PIN per la trasmissione seriale (SoftwareSerial) rischio di far "esplodere tutto" ogni volta che c'è uno scambio di dati su questi ultimi due? Ovviamente si tratta di un caso limite, ma mi serve per capire.

La SoftwareSerial usa i PCINT, andrebbe visto se è compatibile con la libreria che ti ho linkato perché entrambe indirizzano le relative ISR di gestione degli interrupt e non si possono mettere 2 vettori ISR identici nello stesso sketch.

leo72:
La SoftwareSerial usa i PCINT, andrebbe visto se è compatibile con la libreria che ti ho linkato perché entrambe indirizzano le relative ISR di gestione degli interrupt e non si possono mettere 2 vettori ISR identici nello stesso sketch.

:roll_eyes: mhh.... Dopo quello che mi hai detto sono andato a guardare i sorgenti delle due librerie, di cui riporto qui solo un pezzo..

SoftwareSerial.cpp

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

PinChangeInt.h

#ifndef NO_PORTA_PINCHANGES
ISR(PCINT0_vect) {
 #ifdef PINMODE
 PCintPort::s_PORT='A';
 #endif
 PCintPort::curr = portA.portInputReg;
 portA.PCint();
}
#define PORTBVECT PCINT1_vect
#define PORTCVECT PCINT2_vect
#define PORTDVECT PCINT3_vect
#else
#define PORTBVECT PCINT0_vect
#define PORTCVECT PCINT1_vect
#define PORTDVECT PCINT2_vect
#endif

#ifndef NO_PORTB_PINCHANGES
ISR(PORTBVECT) {
 #ifdef PINMODE
 PCintPort::s_PORT='B';
 #endif
 PCintPort::curr = portB.portInputReg;
 portB.PCint();
}
#endif

#ifndef NO_PORTC_PINCHANGES
ISR(PORTCVECT) {
 #ifdef PINMODE
 PCintPort::s_PORT='C';
 #endif
 PCintPort::curr = portC.portInputReg;
 portC.PCint();
}
#endif

#ifndef NO_PORTD_PINCHANGES
ISR(PORTDVECT){ 
 #ifdef PINMODE
 PCintPort::s_PORT='D';
 #endif
 PCintPort::curr = portD.portInputReg;
 portD.PCint();
}
#endif

#ifdef __USE_PORT_JK
#ifndef NO_PORTJ_PINCHANGES
ISR(PCINT1_vect) {
 #ifdef PINMODE
 PCintPort::s_PORT='J';
 #endif
 PCintPort::curr = portJ.portInputReg;
 portJ.PCint();
}
#endif

#ifndef NO_PORTK_PINCHANGES
ISR(PCINT2_vect){ 
 #ifdef PINMODE
 PCintPort::s_PORT='K';
 #endif
 PCintPort::curr = portK.portInputReg;
 portK.PCint();
}
#endif

A questo punto direi proprio che non sono compatibili le due librerie o sbaglio?

Quindi partendo dal presupposto che non posso eliminare la SoftwareSerial, l'unico sistema che mi rimane sarebbe quella di far gestire gli interrupt ad un altro integrato/microcontrollore collegato ad uno dei pin che supportano gli interrupt INT?

Come avevo scritto, inizialmente avevo pensato ad un AtTiny84 in questo modo posso gestire il cambio di stato di fino a 10 sensori (dai 12 pin disponibili tolgo uno per il reset e un altro per il collegamento all'arduino).
Visto che mi interesserebbe monitorare soltanto i "CHANGE" ovvero i cambi di stato, per caso esiste anche qualche altro integrato o microcontrollore in grado di fare la stessa cosa ma con un maggior numero di pin?

Non vorrei dover arrivare ad aggiungere un Atmega328 solo per far da passacarte (tra l'altro facendo una breve ricerca su ebay ho visto che si trovano a meno di 2€, ma è affidabile? mi verrebbero a costare addirittura meno degli Attiny84 :astonished: Dal numero di vendite dell'oggetto e dai feedback sembra di si..)

Non conosco quel venditore ebay, ma non lo userei per applicazioni importanti...

Secondo il mio parere, l'uso dell'AtTiny (o di più AtTiny) è la scelta migliore, deleghi totalmente ad un altro processore il compito di verifica dei sensori.

Altra piccola curiosità... ma sei sicuro che ti servano veramente gli interrupt?
Hai qualcosa di bloccante nel tuo codice?
Intendo dire... anche se il loop durasse un secondo, male che vada, avresti una reazione ritardata di un secondo.
Ma per fare durare un loop un secondo ce ne vuole.

La certezza al 100% sul dover usare per forza degli interrupt al momento non c'è, però l'arduino principalmente dovrà fare anche molte altre cose come letture/scritture su seriale (SW/HW che sia), lettura di valori da sensori o periferiche, e naturalmente l'elaborazione dei dati raccolti.

La funzionalità dell'antifurto in teoria non dovrebbe impegnare molte risorse (semplificando al massimo quando è attivo se lo stato dei pin da monitorare cambia faccio qualcosa, altrimenti continuo a leggere e basta), tuttavia deve essere molto reattiva e mettendomi nel caso peggiore dove per un qualsiasi motivo il microcontrollore è impegnato ad eseguire dei calcoli lunghi e complicati, oppure semplicemente ho scritto male del codice magari piazzando una serie di delay in una funzione, risulta bloccato per qualche secondo potrei perdere troppo tempo e perdermi la segnalazione di allarme di un sensore.

Quindi diciamo che al momento sto cercando di capire quale possa essere il sistema migliore per risolvere il caso peggiore :wink:

Le due lib mi paiono proprio incompatibili, entrambe inizializzano i medesimi vettori.
L'uso dell'Attiny84 ti porta ad avere di utile solo 9 pin massimo. 14 sono i piedini del chip, a cui devi togliere quello di reset ed i 2 dell'alimentazione. E siamo a 11. A questi devi scalarne altri 2 per la comunicazione col chip principale. C'è sempre un problema. Come comunichi con il chip principale? Scartando l'I2C, con cui ci fai pochi decimetri, resta l'RS485 gestito tramite software serial. Ma qui siamo nuovamente al problema di cui prima, SoftSerial e PinChangeInt non vanno d'accordo.

Però vale anche quanto detto da paulus: ti servono veramente gli interrupt? Con un loop che costantemente legge le porte di I/O e, per ogni variazione, memorizza l'allarme e spedisce il dato alla centralina, a meno che tu non abbia un sensore che manda un segnale che dura meno del tempo di loop, anche senza interrupt sei a posto

leo72:
Scartando l'I2C, con cui ci fai pochi decimetri, resta l'RS485 gestito tramite software serial.

Sull'Attiny non avrei la necessità di utilizzare gli interrupt visto che non farebbe altro che leggere gli stati delle porte e modificare lo stato di un pin se nota delle variazioni.
Per controllare se tutto funziona bene tramite I2C effettivamente non ci avevo pensato dato il compito banale che faceva, però effettivamente in caso di guasto non me ne accorgerei mai :wink: , per la distanza comunque non ci sarebbero problemi visto che nel mio immaginario l'Attiny sarebbe stato su una basetta accando all'Arduino.

leo72:
Con un loop che costantemente legge le porte di I/O e, per ogni variazione, memorizza l'allarme e spedisce il dato alla centralina, a meno che tu non abbia un sensore che manda un segnale che dura meno del tempo di loop, anche senza interrupt sei a posto

La centralina sarebbe l'arduino che oltre a leggere lo stato delle porte farebbe molte altre cose per quasto ho paura che potrebbe non intervenire in tempo, ed è li che volevo utilizzare gli interrupt.
Se poi una volta fatto tutto vedo che non servono meglio così, mi risparmio uno o più attiny in cascata

Scusa se rispondo solo ora, mi ero scordato di questo thread.

però effettivamente in caso di guasto non me ne accorgerei mai

Devi fare il polling dalla centralina, vale a dire periodicamente devi interrogare i Tiny periferici e controllare che rispondano entro un certo lasso di tempo. Se non rispondono o la linea è stata interrotta (taglio del filo del sensore) o il Tiny è spento (batteria scarica?) o il Tiny è partito per qualche altro problema. In ogni caso devi azionare lo stesso l'allarme, oppure segnalare un guasto/anomalia (qui dipende dal grado di sicurezza).

La centralina sarebbe l'arduino che oltre a leggere lo stato delle porte farebbe molte altre cose per quasto ho paura che potrebbe non intervenire in tempo, ed è li che volevo utilizzare gli interrupt.

Se un Tiny spedisce un dato in maniera indipendente, ossia non come risposta ad una interrogazione (polling), hai comunque sempre un dato che ricevi da una periferica dell'Atmega328. La periferica può essere impostata per sollevare un interrupt all'arrivo di un dato sul canale, col quale rispondere alla condizione di allarme in modo immediato.

shake84:
La centralina sarebbe l'arduino che oltre a leggere lo stato delle porte farebbe molte altre cose per quasto ho paura che potrebbe non intervenire in tempo, ed è li che volevo utilizzare gli interrupt.
Se poi una volta fatto tutto vedo che non servono meglio così, mi risparmio uno o più attiny in cascata

mah,,, usare interrupt per rilevare un sensore antifurto pare inappropriato ???
un sensore antifurto serio se va "on" deve restare on per un minuto almeno,
se non addirittura
ritornare off solo a comando

Mi pare che per legge gli antifurti non possano suonare per più di 3 minuti continuativi, dopo di che devono fermarsi per qualche secondo, e comunque per non più di 15 minuti complessivi. Quindi non può tornare off a comando, deve comunque disattivarsi da solo.

La sirena deve suonare 3 minuti, ma credo lui si riferisse allo stato del sensore.

Ehm aspettate un attimo, ho paura di essere stato frainteso..

Al momento non sono ancora alla fase di gestione della sirena, sto valutando soltanto quale sia il sistema migliore per controllare lo stato dei sensori per evitare di perdere delle segnalazioni.

Seconda cosa l'Arduino e l'Attiny nel mio immaginario sono all'interno della stessa scatola a pochi centimetri di distanza, quindi la comunicazione tra i due che inizialmente pensavo solo in un verso Attiny ---> Arduino su pin 2 o 3 (per l'UNO) potrebbe fallire solo se uno dei due si rompe o se viene tagliato 1 filo.

Aggiungere una comunicazione costante tra i due come suggerito da leo72 mi permetterebbe di verificare appunto casi di "rottura" e di gestire quindi opportuni avvisi.

Passiamo ora al collegamento dei sensori..

I fili di ogni sensore (magnetico, pir, tapparelle, ecc..) arriva nella mia scatola dove viene effettuata una conversione per passare dai 12V (con cui lavorano i sensori) ai 5V (con cui lavora l'arduino), si potrebbe usare un partitore di tensione, ma probabilmente con un optoisolatore garantirei una sicurezza maggiore (se per caso viene attaccato il 220 sul cavo di un sensore al massimo mi salta l'optoisolatore).

A questo punto il mio sensore dovrebbe essere collegato sia ad un pin dell'Arduino che ad un pin dell'Attiny.

Questo perchè?

All'Arduino perchè in ogni caso voglio sapere con precisione qual'è il sensore in allarme.

All'Attiny che ad ogni variazione di stato dei sensori farà scattare l'interrupt sull'arduino per essere sicuro che la gestione della modalità "Antifurto" abbia la precedenza su tutte le altre operazioni.

Ora nel caso in cui il loop più lungo possibile compiuto dall'Arduino (considerando possibili delay, errori, ecc.) duri meno di 0.5 sec probabilmente la gestione degli interrupt non serve, ma non avendo ancora questo dato mi sono messo nello scenario peggiore possibile.

elrospo:
mah,,, usare interrupt per rilevare un sensore antifurto pare inappropriato ???
un sensore antifurto serio se va "on" deve restare on per un minuto almeno,
se non addirittura ritornare off solo a comando

Purtroppo non ho una vasta esperienza nel mondo degli antifurti, ma ti posso assicurare che esistono sensori seri che segnalano l'allarme solo quando è in corso e non hanno memorie di alcun tipo, nella fattispecie mi riferisco ai comuni contatti magnetici, sarà poi premura della centralina gestire tempistiche e comportamenti.

Aggiornamento dell'ultimo minuto, stavo cuiriosando tra i vari progetti in megatopic quando ho trovato un link ad un articolo che parla dell'integrato PCF8574.

Oltre ad essere usato come come I/O expander leggo che può "lanciare" un interrupt!
Riporto qui il paragrafo:

In ultimo si ricorda che ad ogni variazione di stato di un ingresso si ha la generazione di un interrupt, ovvero l'uscita INT viene posta a massa (Open Drain). Questo permette, d'interrompere il microcontrollore e forzare una lettura dello stato dei pin usati come ingressi. L'interrupt, viene generato ad ogni variazione del pin usato come ingresso, ovvero su ogni fronte. L'interrupt viene disattivato ad una lettura/scrittura della porta o al ripristino del livello logico originario.

Teoricamente mi eviterebbe di utilizzare un Attiny solo per sollevare un interrupt e mi farebbe risparmiare anche un po' di pin sull'arduino.

Quanto dico è corretto o vedete qualche controindicazione usandolo nel mio contesto (Arduino->PCF8574->Optoisolatore->Sensore)?

A questo punto il mio sensore dovrebbe essere collegato sia ad un pin dell'Arduino che ad un pin dell'Attiny.

Questo perchè?

All'Arduino perchè in ogni caso voglio sapere con precisione qual'è il sensore in allarme.

All'Attiny che ad ogni variazione di stato dei sensori farà scattare l'interrupt sull'arduino per essere sicuro che la gestione della modalità "Antifurto" abbia la precedenza su tutte le altre operazioni.

Secondo me è una cosa inutilmente ridondante. A te basta fare il polling dei sensori, se uno non risponde fai scattare l'allarme come se ricevessi un segnale di infrazione.

leo72:
Secondo me è una cosa inutilmente ridondante. A te basta fare il polling dei sensori, se uno non risponde fai scattare l'allarme come se ricevessi un segnale di infrazione.

Sinceramente spero tu abbia completamente ragione e che io mi stia complicando la vita per nulla, tuttavia la mia paura non è che un sensore dell'antifurto non mi risponda (in quel caso come hai giustamente detto dovrei far scattare l'allarme e basta), ma che se l'Arduino sia impegnato a fare altro mentre ricevo un messa di infrazione.

Supponi che nel mio loop ci sia qualcosa di questo tipo (codice puramente inventato)

//Controllo lo stato dei sensori dell'antifurto
checkAntifurto();
//Invio dei messaggi verso altre schede
sendMex(mex1, scheda1);
sendMex(mex2, scheda2);
sendMex(mex3, scheda3);
sendMex(mex4, scheda4);
sendMex(mex5, scheda5);
sendMex(mex6, scheda6);
sendMex(mex7, scheda7);
sendMex(mex8, scheda8);
sendMex(mex9, scheda9);

La funzione checkAntifurto potrebbe essere un'operazione veloce dove controlla lo stato dei sensori soli sensori dell'antifurto ed eventualmente fa partire la sirena, il cui tempo di esecuzione può essere trascurato.

Le funzioni di invio dei messaggi invece potrebbero essere decisamente più lente, supponendo di dover inviare 128byte in ogni messaggio a 9600bps impiegherei circa 0.11 sec e se non ho sbagliato i conti per 9 messaggi potrei arrivare a circa 1 secondo, tempistica che se aumentasse ancora mi farebbe storcere il naso.

Ora sarà mia premura durante la scrittura del codice evitare di fare qualcosa del genere ed evitando laddove possibile l'utilizzo di delay(), ma attualmente non avendo stabilito ancora tutte le funzionalità e quali librerie saranno necessarie non posso garantire che non si verifichi qualcosa di analogo.

Esagerato :grin:

Se lavori in I2C, sarà lo stesso attiny a dire al processore centrale quale è stato il sensore che ha generato l'allarme. Inoltre potrai collegare diversi attiny, dipende da quanti sensori vuoi.

Puoi anche ragionare al contrario, quindi far comportare il processore centrale come slave e definire una procedura da attivare quando si verifica l'evento "ricevo qualcosa su I2C".

paulus1969:
Esagerato :grin:

Mh.. ho paura proprio di si :cold_sweat:
Comunque per ora ringrazio tutti per avermi risposto, quanto meno ho avuto modo di studiare un po' di teoria sugli interrupt con Arduino. :wink:

Quindi per il momento il discorso interrupt lo accantono un attimo sperando di non doverlo ritirare fuori più avanti :wink: