chiarimenti sugli interrupt

ieri mi chiedevo come funzionassero gli interrupt, perchè forse dovrebbero avermelo insegnato a scuola... e quindi ho letto queste 2 pagine sul reference:
http://arduino.cc/en/Reference/Interrupts
http://arduino.cc/en/Reference/AttachInterrupt
ma mi restano ancora dei dubbi, cosa sono fisicamente? cosa hanno di così diverso dal fare un if(digitalRead(X)){...} ? perchè dicono che solo alcuni pin possono essere utilizzati per fare ciò? che differenza c'è tra le 2 funzioni AttachInterrupt e Interrupts? potete spiegarmi un po'?
grazie mille in anticipo! :wink:

sostanzialmente il digitalread devi farlo tu ogni volta per vedere se è cambiato qualcosa, l'interrupt ti avvisa lui :slight_smile:

ah ok quindi è più veloce del digitalRead, no?
posso usare qualsiasi pin per fare questo lavoro? cosa significa questa frase "Most Arduino boards have two external interrupts: numbers 0 (on digital pin 2) and 1 (on digital pin 3). The Arduino Mega has an additional four: numbers 2 (pin 21), 3 (pin 20), 4 (pin 19), and 5 (pin 18)." ? che differenza c'è tra le 2 funzioni AttachInterrupt e Interrupts?
grazie ancora!

Gli interrupt sono delle routine (funzioni) che vengono eseguite quando c'e' l'evento associato, indipendentemente dal punto in cui il main "scorre".
In alcuni casi pero' si puo' richiedere che un blocco del codice del main non si debba interrompare a causa di un interrupt, per fare cio' prima del blocco ci metti un detachInterrupt. Quando ad esempio il blocco critico e' finito e vuoi riabilitare gli interrupt ci metti un interrupts().
Abbiamo detto che se c'e' un interrup viene eseguita una funzione il "gestore dell'interrupt" con attachInterrupt(interrupt, function, mode)associ l'interrupt alla sua funzione "gestore dell'interrup".

ok grazie capito :slight_smile: e la storia dei pin? sono tutti disponibili per fare ciò?

di default si possono usare solo i pin che tu stesso hai riposrato, quindi su arduinoUNO solo due pin.

pero' c'e' una libreria che ti permette di usarli su tutti (vado a memoria, per ora non l'ho mai usati)

allora, arduino mette a disposizione 2 pin, che hanno interrup CHANGE, LOW, FALLING, RISING ovvero in cambio stato, quando basso, solo quando il segnale "cade" da 1 a 0, solo quando il segnale "sale" da 0 a 1.

in realtà tutti i pin sono attivabili come CHANGE impostando un paio di registri. In pratica i PIN sono raggrupptai in gruppi da 8, e ogni gruppo ha il controllore di interrupt CHANGE:
a questo punto se confronti lo stato degli 8 pin con quello dell'ultimo interrupt, ti accorgi di quali pin sono cambiati e intraprendi le azioni specifiche.

se cerchi sul forum c'è del codice, molto tempo fa ho messo il codice per leggere 6 canali PPM con gli interrupt, che in pratica non è altro che contare quanto tempo rimane HIGH un pin :slight_smile:

Gli interrupt possono essere fatti partire da molti avvenimenti; esterni (pin 2 e 3) o interni tipo contatori timer ecc.

La diffrenuza tra interrupt e pull é il seguente:

Con la tecnica pull devi guardare regolarmente se un evento si é verificato ( quardi ogni minuto se i spagetti sono pronti e percui devi essere presente e non puoi fare altre cose). Con un interrupt viene interrotto il programma principale e eseguito il codice Interrupt ( metti la sveglai e quando suona gli spaghetti son pronti. nel fratempo puoi apparecchiare la tavola, andare in cantina a prendere la birra, leggere il nostro Forum ecc.)

Ciao Uwe

lesto quindi se ho capito il LOW, FALLING, RISING non si puo' fare su tutti ?

bhe, puoi ricavarlo via software :slight_smile:

l'unico interrupt ottenibile su tutti i pin è il pinchange interrupt, credo che ci sia una libreria apposita che si chiama pcint o qualcosa di simile.

ciao
tempo fa avevo scritto questo ma non lo ho mai provato

// versione con 3 shiftout 74hc595

// PinChangeIntExample, version 1.2 Fri Feb  3 19:41:08 CST 2012
// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information.
//-------- define these in your sketch, if applicable ----------------------------------------------------------
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
// on any one or two of the three ports.  If only a single port remains, the handler will be declared inline
// reducing the size and latency of the handler.
#define NO_PORTB_PINCHANGES // to indicate that port B will not be used for pin change interrupts
#define NO_PORTC_PINCHANGES // to indicate that port C will not be used for pin change interrupts
// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine
// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or
// PCintPort::arduinoPin are set and made available to your interrupt routine.
// #define NO_PIN_STATE        // to indicate that you don't need the pinState
// #define NO_PIN_NUMBER       // to indicate that you don't need the arduinoPin
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
// #define       DISABLE_PCINT_MULTI_SERVICE
//-------- define the above in your sketch, if applicable ------------------------------------------------------
#include <PinChangeInt.h>

// This example demonstrates a configuration of 3 interrupting pins and 2 interrupt functions.
// All interrupts are serviced immediately, but one of the pins (pin 4) will show you immediately
// on the Terminal.  The other function connected to 2 pins sets an array member that is queried in loop().
// You can then query the array at your leisure.
// This makes loop timing non-critical.

// Add more Pins at your leisure.
// For the Analog Input pins used as digital input pins, and you can use 14, 15, 16, etc.
// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's
// for the Analog Input pins and will properly recognize e.g., pinMode(A0, INPUT);
#define PIN1 2
#define PIN2 3
#define PIN3 4
#define PIN4 5
#define PIN5 6
#define PIN6 7


int statosensore1=HIGH;
int statosensore2=HIGH;
int statosensore3=HIGH;
int statosensore4=HIGH;
int statosensore5=HIGH;
int statosensore6=HIGH;

int i=0;
int j=0;
int count[6]={0,0,0,0,0,0};
float freq[6]={0,0,0,0,0,0};
//float freq1=0;
unsigned long tempo[10]={0,0,0,0,0,0,0,0,0,0};

//shiftout
int latchPinOUT = 10;//ST_CP di 74HC595 pin 12
int clockPinOUT =9;//SH_CP di 74HC595 pin 11
int dataPinOUT = 8;//DS di 74HC595  pin 14
byte ledRossiH = B00000000;  
byte ledVerdi = B00000000; 
byte ledRossiL = B00000000; 

void setup() 
{
    pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN1, sensore1, FALLING);  
    pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN2, sensore1, FALLING);
    pinMode(PIN3, INPUT); digitalWrite(PIN3, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN3, sensore1, FALLING);
    pinMode(PIN4, INPUT); digitalWrite(PIN4, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN4, sensore1, FALLING);
    pinMode(PIN5, INPUT); digitalWrite(PIN5, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN5, sensore1, FALLING);
    pinMode(PIN6, INPUT); digitalWrite(PIN6, HIGH);  // add more attachInterrupt code as required
  PCintPort::attachInterrupt(PIN6, sensore1, FALLING);
  
  Serial.begin(9600);
  
    //shiftout
  pinMode(latchPinOUT, OUTPUT);
  pinMode(clockPinOUT, OUTPUT);
  pinMode(dataPinOUT, OUTPUT);
 }

void loop()
{
  if (statosensore1==LOW)       // sensore 1
{ 
  count[0]++;
//   Serial.println(count[0]);
  //  Serial.println(i);
  tempo[i]=millis(); i++; 
    //   Serial.println(tempo[0]);
      //   Serial.println(tempo[9]);
   if(i==10)
  {
    freq[0]=count[0]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[0]=0;
     // Serial.println(freq[0]);
   }
  statosensore1=HIGH;
}
 if (statosensore2==LOW)        // sensore 2
{ 
  count[1]++;
    tempo[i]=millis(); i++; 
 if(i==10)
  {
    freq[1]=count[1]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[1]=0;
   }
  statosensore1=HIGH;
}
 if (statosensore3==LOW)        // sensore 3
{ 
  count[2]++;
    tempo[i]=millis(); i++; 
 if(i==10)
  {
    freq[2]=count[2]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[2]=0;
   }
  statosensore1=HIGH;
}
 if (statosensore4==LOW)        // sensore 4
{ 
  count[3]++;
    tempo[i]=millis(); i++; 
 if(i==10)
  {
    freq[3]=count[3]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[3]=0;
   }
  statosensore1=HIGH;
}
 if (statosensore5==LOW)        // sensore 5
{ 
  count[4]++;
    tempo[i]=millis(); i++; 
 if(i==10)
  {
    freq[4]=count[4]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[4]=0;
   }
  statosensore1=HIGH;
}
 if (statosensore6==LOW)        // sensore 6
{ 
  count[5]++;
    tempo[i]=millis(); i++; 
 if(i==10)
  {
    freq[5]=count[5]*1000/(tempo[9]-tempo[0]);
   i=0;
   count[5]=0;
   }
  statosensore1=HIGH;
}


for(j=0;j<=5;j++)
{
 //Serial.println(j);
 Serial.println(freq[0]);
  
if(freq[j]>10 && freq[j]<15)
{
  Serial.println("ok");
     bitWrite(ledRossiH,j,0);
   bitWrite(ledVerdi,j,1);
     bitWrite(ledRossiL,j,0);
}
else
{  
  Serial.println("ko");
   bitWrite(ledRossiH,j,1);
   bitWrite(ledVerdi,j,0);
     bitWrite(ledRossiL,j,1);
}
     digitalWrite(latchPinOUT,1);                       //Pull latch LOW to start sending data
      shiftOut(dataPinOUT, clockPinOUT, MSBFIRST, ledRossiL);    //Send the data byte rossi
      shiftOut(dataPinOUT, clockPinOUT, MSBFIRST, ledVerdi);    //Send the data byte verdi
      shiftOut(dataPinOUT, clockPinOUT, MSBFIRST, ledRossiH);    //Send the data byte rossi
     digitalWrite(latchPinOUT,0);                      //Pull latch HIGH to stop sending data

}

}

//---------------------------funzioni
void sensore1()
{
  statosensore1=LOW;
}
void sensore2()
{
  statosensore2=LOW;
}
void sensore3()
{
  statosensore3=LOW;
}
void sensore4()
{
  statosensore4=LOW;
}
void sensore5()
{
  statosensore5=LOW;
}
void sensore6()
{
  statosensore6=LOW;
}

PinChangeInt.h (15.3 KB)

keywords.txt (301 Bytes)

Testato:
lesto quindi se ho capito il LOW, FALLING, RISING non si puo' fare su tutti ?

Gli interrupt, trad. "interruzione", come ha detto Uwe sono sia interni che esterni. Parlando di pin, dobbiamo parlare di interrupt esterni. Questi sono di 2 tipi: INT e PCINT. Il primo tipo consta di 2 interrupt, INT0 e INT1, agganciati a 2 specifici pin, il pin D2 ed il pin D3. Ogni interrupt ha il suo registro. Il secondo tipo si compone di 24 interrupt, da PCINT0 a PCINT23, uno per ogni pin di I/O del micro. Questi ultimi sono gestiti cumulativamente da 3 interrupt, PCI0, PCI1 e PCI2, ognuno tramite un apposito registro, ogni registro controlla 8 pin (8 bit=8 pin=8 PCINT). Ogni interrupt può "sentire" il passaggio a LOW, il FALLING del segnale (da HIGH a LOW), il RISING (da LOW a HIGH) oppure qualunque altro cambiamento di stato (anche lo stato HIGH).
I PCINT sentono invece un qualsiasi cambio di stato sul pin.

Siccome gli interrupt INT0 e INT1 possono lavorare in modo asincrono, ossia senza l'utilizzo del clock di sistema, quando devono rilevare il segnale LOW, questi interrupt possono essere utilizzati anche durante il risparmio energetico con modalità di sleep più aggressive dell'IDLE, l'unica che tiene agganciato il clock di sistema ai pin, l'unica quindi che fa funzionare i PCINT.

La libreria è scaricabile da qui:
http://www.arduino.cc/playground/Main/PcInt

stefa24 il tuo è un uso improprio degli interrupt, perchè misciato col metodo alternativo detto "polling", ovvero ciclare all'infinito per vedere i cambiamenti.

io tuo codice ha la stessa funzione del mio per leggere la durata di uno stato alto, ma è fortemente dipendente dalla durata del loop.

InputPin.cpp

#include "InputPin.h"

/*
Let You read digital pin 2,4,5,6,7
based on BIRONPILOTV59, tnx to ciskje
by Lesto
*/

#define MAXUNSIGNEDLONGCVALUE 4294967295
#define MASKPCINT0 (1<<2)
#define MASKPCINT1 (1<<4)
#define MASKPCINT2 (1<<5)
#define MASKPCINT3 (1<<6)

volatile byte _oldbit, _newbit, _changed;

InputPin::InputPin(){
  pinMode(2, INPUT); // 3 is used for esc
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  // interrupt on pin change PCINT2
  PCICR |= (1 << PCIE2);

  PCMSK2 = (1 << PCINT18) | // pin2
  (1 << PCINT20) | // pin4
  (1 << PCINT21) | // pin5
  (1 << PCINT22); // pin6

  _oldbit = PIND;
}

//putted here because interrupt dosn't want it in .h
unsigned long _startIn[4];
volatile  unsigned long _rawIn[4];
unsigned long _time;

ISR(PCINT2_vect) {
  _time=micros();
  
  _newbit=PIND;
  _changed=_newbit^_oldbit;
  
  if (_changed&MASKPCINT0){

    if (_newbit&MASKPCINT0) {
      _startIn[0]=_time;
    }else{
      _rawIn[0]=_time-_startIn[0];
    }
  }

  if (_changed&MASKPCINT1)
    if (_newbit&MASKPCINT1) 
      _startIn[1]=_time;
    else 
      _rawIn[1]=_time-_startIn[1];

  if (_changed&MASKPCINT2)
    if (_newbit&MASKPCINT2) 
      _startIn[2]=_time;
    else 
      _rawIn[2]=_time-_startIn[2];

  if (_changed&MASKPCINT3)
    if (_newbit&MASKPCINT3) 
      _startIn[3]=_time;
    else 
      _rawIn[3]=_time-_startIn[3];
  _oldbit=_newbit;
//  blockedRX=0;
}

int InputPin::getDuration(int i){
 // Serial.print("ok:");
//  Serial.print(rawIn[i]);
  //      Serial.println(rawIn[0]);
      // Serial.print("okIND2:");
        //Serial.println(rawIn, BIN);
  return _rawIn[i];
}

InputPin.h

#ifndef InputPin_h
#define InputPin_h


#include "WProgram.h"

class InputPin{
  
  public:
    InputPin();
    int getDuration(int i);
    int x, y, z;
  private:
};
#endif

come puoi notare il calcolo della durata è eseguito all'interno dell'interrupt, e il tutto è completamente indipendente dal loop, soprattutto il calcolo della durata del segnale. In questo modo nel loop posso dedicarmi a calcoli che mi portano via anche giorni, senza per questo andare ad influenzare la lettura. certo perderei tutte le letture tranne l'ultima effettuata, ma col tuo metodo avresti una sola lettura di segnale alto con durata X giorni + qualcosa.

Tra l'altro si nota bene come funziona la storia degli interrupt a basso livello:
PARTE DI SETUP:

  1. l'attivazione di uno dei 3 registri (che contiene 8 pin) PCIE2, in questo caso
  2. l'attivazione dell'interrupt sul vero e proprio pin, attraverso un registo in cui ogni bit rappresenta un pin. in questo PCMSK2 viene attivato per i pin PCINT18, PCINT20, PCINT21, PCINT22
  3. il salvataggio dello stato attuale dei pin che stiamo analizzando (o meglio, del gruppo di 8 che contiene quelli che ci interessano) (_oldbit = PIND;)
    PARTE "ATTIVA":
  4. un itnterrupt in arrivo chiama la funzione di interrupt corrispondente, in questo caso ISR(PCINT2_vect)
  5. lettura stato attuale dei pin con _newbit=PIND;
  6. troviamo i pin che son stati modificati: _changed=_newbit^_oldbit;
  7. con un pò di logica a BIT estraiamo i pin cambiati(corrispettivo bit ad 1 in _changed), ed eventualmente anche il loro stato (da _newbit o PIND)
  8. esecuzione del codice che vi interessa, e salvataggio delle info (in questo caso la durata del segnale alto) in un array/variabile "volatile"
  9. salvataggio dell'attuale stato dei pin con _oldbit=_newbit;

ps. il codice, come potete leggere dai commenti è basato sul codice del bironpilot di ciskje, del forum baronerosso.it

scusate ma ho ancora dei dubbi, questi dubbi non riguardo a come gli interrupt funzionino ma come devo fare per usarli :blush:
allora io avevo bisogno di usare 4/5 pin di interrupt su un arduino mega2560, e possibile no?
in un'altra discussione mi era stato suggerito di usare la libreria PcInt per avere un maggior numero di pin di interrupt, la libreria dovrebbe essere questa:
http://arduino.cc/playground/Main/PinChangeInt
http://arduino.cc/playground/Main/PcInt
siccome ero in difficoltà Leo mi ha dato la sua libreria, ma quando vado a inserire la libreria dall'ide non scrive la riga include<...>, ho guardato un po' il codice della libreria e mi pare diverso da quella che c'è nel playground, sono molto confuso :~ spero che qualcuno qui possa aiutarmi a chiarirmi le idee :wink:
grazie a tutti in anticipo! :wink:

Come ti ho spiegato, esistono gli INT ed i PCINT. I primi sono interrupt di un livello superiore ma sono limitati a pochi e sono mappati su specifici pin.
I PCINT sono presenti invece per tutti i pin, quindi puoi usarli su qualsiasi uscita dell'Arduino. Hanno un limite (altrimenti sarebbe facile, no? :stuck_out_tongue: ): esiste un solo registro ad 8 bit per ogni porta logica del micro, ogni porta mappa 8 piedini. Inoltre quando si attiva un interrupt, se ne arriva un altro questo viene messo in attesa. Ma sui PCINT bisogna stare attenti: siccome appunto ogni registro PCINT mappa 8 porte, se si attiva il flag di un PCINT, il micro lo esegue, poi terminata l'esecuzione, resetta il flag relativo. Però se arrivano 2 o più segnali sullo stesso pin, solo il primo verrà eseguito, i successivi saranno persi.

La lib che ti ho linkato io puoi copiarla direttamente all'interno del tuo sketch in una tab aggiuntiva, sarà il compiatore a miscelarla al tuo programma.
Alternativamente usa quella presente nel link che hai messo tu:
http://arduino.cc/playground/Main/PinChangeInt
Scompatti l'archivio e metti la cartella PinChangeInt all'interno di /sketchbook/libraries.

sisi ma io ho la tua, ma se vado a fare "import library" dall'IDE non mi inserisce la riga include<...> non so perchè, io l'ho scompattata e messa nella cartella con le altre librerie...
se no di queste: Google Code Archive - Long-term storage for Google Code Project Hosting. quale dovrei prendere? :slight_smile:
potete postare un piccolo esempio di codice con i PcInt, per favore?
grazie mille! :wink:

perche' come gia' ti ha detto leo, non e' una libreria
e' uno sketch

mmmmm e quindi non vedo niente? come faccio a capire se è inserito o meno? :~

Se hai scompattato la cartella nel posto giusto, carica l'esempio allegato alla libreria.