aiuto sugli interrupt

Ciao
sto cercando di usare gli interrupt sull’arduino. Mi interessa intercettare il cambio di stato (da 0 a 5v) su almeno 6 pin. Ho trovato poca documentazione, il manuale dell’atmega e qualche spezzone nel playground ma ognuno usa il proprio metodo e nessuno lo spiega bene ::slight_smile:
Quello che ho capito per ora è che devo abilitare l’interrupt sull’EICRA settando gli ultimi 4 bit a 0101 (abilito gli interrupt di cambio di stato su INT0 e INT1), e metterea 1 gli ultimi due bit di EIMSK
Poi a questo punto setto gli utimi 3 bit di PCICR a 1 a seconda di se voglio leggere i pin 23-16, 14-8 e 7-0
con PCMSK0-1-2 posso abilitare o disabilitare gli interrupt sui singoli pin
ora, ammesso e non concesso che possa lavorare direttamente su questi registri da arduino (ora è tardi per testare) non ho ancora capito come dire al chip di eseguire un determinato codice.
In un esempio che ho trovato quì: http://www.arduino.cc/playground/Main/PcInt gli passa l’indirizzo della funzione, ma la cosa mi sembra molto elaborata.
Invece in un sorgente che possiedo usa:

ISR(PCINT2_vect)
{[codice da eseguire all'interrupt]}

e nel setup:

// pin change interrupt enables
      PCICR |= (1 << PCIE0);                  // PCINT0..7            
      PCICR |= (1 << PCIE2);                  // PCINT16..23

      // pin change masks
      PCMSK0 |= (1 << PCINT7);            // PB7
      PCMSK2 |= (1 << PCINT17);            // PD1
      // external interrupts
      EICRA  = (1 << ISC00) | (1 << ISC10);      // Any change INT0, INT1
      EIMSK  = (1 << INT0) | (1 << INT1);            // External Interrupt Mask Register
      EIFR |= (1 << INTF0) | (1 << INTF1);

direi che quì non ci sono tutti i passaggi che ho detto sopra… Insomma un piccolo aiuto o un esempio di interrut basicissimo terra terra per capire il concetto e prendere il volo?

'notte ;D

ciao lesto

Hai provato con questo?
http://arduino.cc/en/Reference/AttachInterrupt
http://arduino.cc/en/Reference/DetachInterrupt

Per ampiare i pin che possono generare un Interrupt usa una porta OR della serie cmos 4000 e una volta generato l’interrupt controlla quale delle entrate é attiva.
Se l’ interupt é generato da un tasto é necessario fare un debouce via hardware üer esempio il MC14490

Ciao Uwe

si, le ho viste ma sono solo per 2 pin sul 328p... invece col codice che ho postato puoi, senza hardware esterno, controllare tutti i pin, pure se settati su output :-)

Ciao Lesto Il vantaggio della mia soluzione é che non devi programmare direttamente i registri del Controllore. Ciao Uwe

ok, ma funziona solo su due pin, io ho la necessità di almeno 4, meglio 6... altrimenti non insisterei... senza contare che gli interrupt li stiamo studiando in uni, quindi sarebbe "un integrazione alla lezione" ;D ::)

Ciao Lesto: * Quello che avevo suggerito era una porta logica OR con 8 entrate per esempio la CD4078. L' uscita la porti sul pin 3 o 2 e le entrate in paralello a 8 entrate arduino. Quando un entrata del OR va a Hight l' uscita del OR va a High e produce un interupt. Nella funzione di interrupt controlli le 8 entrate per sapere quale entata ha prodotto l' interrupt. é possibile che ti serve un NAND per realizzare la logica che hai bisogno. http://www.datasheetcatalog.org/datasheets/70/109327_DS.pdf

Tanti Saluti Uwe

grazie, ma con il codice in http://www.arduino.cc/playground/Main/PcInt riesci a leggere un'interrupt su qualsiasi pin senza bisogno di hardware esterno. Facendo i passi descritti sopra funziona, più o meno. Allora si può settare di avere un interrupt quando il segnale è basso, è in salita, è in discesa o quando cambia. Gli interrupt li puoi dividere o in INT0 e INT1 oppure in PCINT0, PCNT1 e PCINT2 Fin quì tutto ok. Se usi PCINT sai quale gruppo di pin ha attivato l'interrupt ma come fare a sapere quale pin l'ha attivato? esistono le maschere per accendere/spegnere l'interrupt su di un pin... Riuscissi a capire quello che combina PcInt.. magari le maschera a ciclo? però per attivare l'interrupt da un determinato pin vuol dire che in qualche modo sa da quale pin arriva l'interrupt...

ahhhh forse ho trovato, basta usare static void PCint(uint8_t port){} bisogna care attenzione che probabilmente usa le PCINT e non i pin di numerazione o quelli usati da arduino. Comunque son segnati negli schemi ;D appena arrivo a casa provo :)

allora ci sono riuscito…
In pratica ll’interrupt chiama una funzione che ritorna a 1 i pin che sono stati modificati, ma salvo due impulsi che cambiano praticamente allo stesso momento tutto ok, altrimenti, se l’ordine di arrivo dei segnali è importante, meglio metterli su un PCMSK diverso (ce ne sono 3, che raggruppato 8 pin a testa tranne uno che ne ha solo 7)

Allego lo schetch risultante dallo studio di http://www.arduino.cc/playground/Main/PcInt e del manuale. In ratica è lo stesso schetc, però non usa il minor numero possibile di funzioni esterne, in modo da vedere effettivamente quali e come va a modificare i registri. In effetti l’unica chiamate esterna è portInputRegister() che ho descritto prima (ritorna un byte corrispondente agli 8 pin del PCMSK, a 1 se sono cambiati)

Se lo schetc vi si continua a riavviare avete sbagliato qualcosa con gli intterupt.
p.s. Il codice non è testato, l’ho pulito un attimo al volo da un pò di scritte di debug (molte le ho lasciate).
Teoricamente è eresia usare una serial nell’interrupt (rallenta troppo) ma per capirci:

#include "pins_arduino.h"
uint8_t PCintLast[3];

ISR(PCINT0_vect) {
  int port=0;
  
  // get the pin states for the indicated port.
  uint8_t curr;
  uint8_t mask;
  
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port]; //evita che lo stesso pin venga estratto due o più volte se più interrupt si sovreppongono
  PCintLast[port] = curr;
  Serial.print("curr");
  Serial.println(curr, BIN);
  
  Serial.print("mask");
  Serial.println(mask, BIN);

  if ((mask &= PCMSK0) == 0) {
    //ritorna se l'iterrupt è lanciato da un pin già estratto o non presente in PCMSK0 (quindi un pin con interrupt non attivato)
    return;
  }
  
  uint8_t bitt;
  
  for (uint8_t i=0; i < 8; i++) {//per 8 volte
    bitt = 0x01 << i;//crea il valore 2^i
    if (bitt & mask) { //usato per estrarre l'iesimo bit
      uint8_t pin = port * 8 + i-1; //calcola il valore PICINT del pin
      Serial.print("Interrupt di PCINT:");
      Serial.println(pin, DEC);
    }
  }
}

ISR(PCINT1_vect) {
  //come codice ISR0 ma con port = 1
}
ISR(PCINT2_vect) {
  //come codice ISR0 ma con port = 2
}

void setup(){
  Serial.begin(9600);

/*I SEGUENTI SETTAGGI SONO MODIFICABILI A SECONDA DELLE NECESSITÀ , MAGGIORI INFO SUL MANUALE DELL'ATMEGA*/
  
  Serial.println("\nPCICR:");
  Serial.println(PCICR, BIN);
  // su quale serie di pin voglio abilitare gli interrupt?
  PCICR |= (1 << PCIE0); // PCINT0-5 DigitalPIN8-13
  //PCICR |= (1 << PCIE1); // PCINT8..13 AnalogPIN0-5
  PCICR |= (1 << PCIE2); // PCINT16..23 DigitalPIN0-7 ATTENZIONE!!! RILEVA COME INTERRUPT ANCHE LA COMUNICAZIONE SERIALE
  Serial.println(PCICR, BIN);
  
  /*ATTENZIONE, DI SEGUITO SI SETTANO GLI INTERRUPT SUI PIN. GESTIRE SEMPRE TUTTI QUELLI DICHIARATI CON L'APPOSITA FUNZIONE ISR(PCINTXX_VECT) o SIGNAL(PCINTXX_VECT) DOVE xx È IL NUMERO DEL PCMSK CORRISPONDENTE */

 
  Serial.println("PCMSK0:");  
  Serial.println(PCMSK0, BIN);
  //abilitiamo gli interrupt pin per digitalpin da 8 a 14, in questo caso solo 8 corrispondente a PCINT0 e 9 -> PCINT1
  PCMSK0 |= (1 << PCINT0) | (1 << PCINT1);
  Serial.println(PCMSK0, BIN);
/*
  Serial.println("PCMSK1:");  
  Serial.println(PCMSK1, BIN);  
  //abilitiamo gli interrupt pin per analogpin da 0 a 5, in questo caso tutti
  PCMSK1 |= B01111111; //PCMSK1 È L'UNICO REGISTRO PCMSK DI CUI SI PUÒ USARE AL MASSIMO SOLO 7 BIT
  Serial.println(PCMSK1, BIN);
*/
/*
  Serial.println("PCMSK2:");  
  Serial.println(PCMSK2, BIN);
  //abilitiamo gli interrupt pin per digitalpin da 16 a 23, in questo caso i PCINT 18,20,21,22,23 che corrispondono ai pin digitali 2,4,5,6,7
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT20) | (1 << PCINT21) | (1 << PCINT22) | (1 << PCINT23);
  Serial.println(PCMSK2, BIN);
*/
 /*FINE INTERRUPT SUI PIN*/
}

void loop(){
  Serial.println("loop");
  //Serial.println(changed);
}

Grazie assai lesto, era quello che mi serviva, ora però il link nel playground mi sembra completo, appena posso provo. Nota che dal datasheet dell'ATmega328 leggo che i PCINTxx hanno una priorità in base al loro numero.

Caspita sono mesi che cerco info e quel link nel playg per me l'hanno messo giorni fà, nel senso che non l'ho letto prima di adesso :) .

Ok saluti.

visto che mi capita spesso di linkare la discussione metto il codice che sto usando attualmente.
l’ho un pò commentato e generalizzato, quindi non è testato ma dovrebbe funzionare alla grande :slight_smile:
possono essere letti tutti i pin, tranne quelli di alimentazione e aref. fate attenzione che NON dovete confondervi tra PIN e PCINT: esempio per leggere il digital pin 2, dovete attivare il PCINT18!!! (questo è anche il motivo delle declare all’inizio che dico servono per chiarire il codice!)
fate riferimento a questo schema: http://arduino.cc/en/Hacking/PinMapping168

/*
Let You read digital pin 2,4,5,6, can be easily modified for others pins
Permette di leggere i pin 2,4,5,6, ma può essere facilmente modificato per gli altri pin
more info: http://www.atmel.com/dyn/products/product_card.asp?PN=ATmega328P
*/

//define pin mask, usefull for better code's readability. definisce le maschere per i pin, utile per una migliore leggibilitità del codice
#define MASKPCINT0 (1<<2)
#define MASKPCINT1 (1<<4)
#define MASKPCINT2 (1<<5)
#define MASKPCINT3 (1<<6)

InputPin::InputPin(){
  //activate global interrupt. attiva gli interrupt
  SREG&B10000000;

  pinMode(2, INPUT); 
  // pin 3 is unused, il pin 3 non viene usato
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);

  // interrupt on pins change PCINT2, this means digital pin from 0 to 7. l'interrupt viene attivato su PCINT2, ovvero i digital pin da 0 a 7
  PCICR |= (1 << PCIE2);

  //unmask pin to read, smaschero i pin da leggere
  PCMSK2 = (1 << PCINT18) | // pin2
  (1 << PCINT20) | // pin4
  (1 << PCINT21) | // pin5
  (1 << PCINT22); // pin6
}


//called every interrupt. chiamata ogni interrupt
byte _oldbit, _newbit, _changed;
ISR(PCINT2_vect) {
  //read actual pins status. legge lo stato attuale dei pin
  _newbit=PIND;
  //extrapolate changed pins. estrapola i pin che sono stati modificati
  _changed=_newbit^_oldbit;
  
  //if pin 2 has changed. se pin2 è stato modificato
  if (_changed&MASKPCINT0){
    //if pin2 now is high. se pin2 ora è in stato alto
    if (_newbit&MASKPCINT0) {
      //INTERRUPT STATO ALTO SUL PIN 2
    }else{
      //INTERRUPT STATO BASSO SUL PIN 2
    }
  }

  if (_changed&MASKPCINT1){
    if (_newbit&MASKPCINT1){ 
      //INTERRUPT STATO ALTO  SUL PIN 4
    }else{
      //INTERRUPT STATO BASSO SUL PIN 4
    }
  }

  if (_changed&MASKPCINT2){
    if (_newbit&MASKPCINT2){ 
      //INTERRUPT STATO ALTO  SUL PIN 5
    }else{
      //INTERRUPT STATO BASSO SUL PIN 5
    }
  }

  if (_changed&MASKPCINT3){
    if (_newbit&MASKPCINT3){
      //INTERRUPT STATO ALTO  SUL PIN 6
    }else{
      //INTERRUPT STATO BASSO SUL PIN 6
    }
  }
  //remember actal pins status for change extrapolation next call. Ricorda lo stato dei pin attuale per estrapolare i cambiamenti alla prossima chiamata
  _oldbit=_newbit;
}

fate attenzione che NON dovete confondervi tra PIN e PCINT: esempio per leggere il digital pin 2, dovete attivare il PCINT18!!!

Si infatti, anche se con il software che mi hai linkato non devo specificare il PCINT ma solo i pin di arduino.

Grazie a quel codice ho potuto gestire l'encoder in quadratura permettendo un CPR di 2880 (Count Per revolution) con un disco di 150LPI.

Purtroppo ancora non sono in grado di comprendere a pieno tutti i passagi, devo studiare.

Ok grazie di nuovo. Ciao