interrupt interni arduino uno

Salve a tutti!

Possiedo una ARDUINO Uno r3 e una ARDUINO Mega 2560, ma i vincoli di progetto mi impongono di usare la prima piattaforma. L'idea finale del progetto è ancora da definirsi: 1 volevo arrivare a leggere senza problemi i dati provenienti da una imu (mpu6050 acc+gyro) 2 volevo comandare due motori lego nxt con encoder (vedi link) https://www.google.it/search?q=motore+lego+nxt&client=firefox-a&hs=StL&rls=org.mozilla:it:official&channel=sb&source=lnms&tbm=isch&sa=X&ei=09poU9raIpSGyQOziYG4DA&ved=0CAkQ_AUoAg&biw=1280&bih=688

L'idea iniziale era fare un mini segway o balancing robot, ma non sono + certo di cosa diventerà e non mi pongo il problema ora che di problemi ne devo ancora risolvere tanti!

DEVO QUINDI USARE ARDUINO UNO REV3 LA MEGA RISOLVEREBBE TUTTO :0 :0 :0 :0 :0 Il punto 1 è stato risolto, non senza difficoltà: la IMU per funzionare sfrutta un interrupt, esattamente l'int 0 pin 2. Posto soltanto un pezzetto di codice per chiarezza; proviene dall'ormai famoso codice di jeff rowberg riadattato per SPI.

    // enable Arduino interrupt detection, this will execute dmpDataReady whenever there is an interrupt,
    // independing on what this sketch is doing at that moment
    // http://arduino.cc/en/Reference/AttachInterrupt
    Serial.print("Enabling interrupt detection... ");
    // attachInterrupt(interrupt, function, mode) specifies a function to call when an external interrupt occurs
    // ArduIMU+ V3 has ATMEGA328 INT0 / D2 pin 32 (input) connected to MPU-6000 INT pin 12 (output)
    [b]attachInterrupt(0, dmpDataReady, RISING); // the 0 points correctly to INT0 / D2[/b]
    // -> if there is an interrupt from MPU-6000 to ATMEGA328, boolean mpuInterrupt will be made true
    byte mpuIntStatus = SPIread(0x3A, ChipSelPin1); // by reading INT_STATUS register, all interrupts are cleared

VENIAMO AL PROBLEMA VERO E PROPRIO... Ora si tratta di comandare [u]UN[/u] motore nxt con encoder: ognuno di questi encoder ha 5 fili, due dei quali servono x leggere l'encoder, ma non credo si possa usare uno solo di questi due fili CAUSA perdita di risoluzione o proprio mancato funzionamento. Giusto?

Secondo le specifiche Arduino uno possiede 2 pin di interrupt:

External Interrupts: 2 and 3. These pins can be configured to trigger an interrupt on a low value, a rising or falling edge, or a change in value. See the attachInterrupt() function for details.

quindi se voglio usare sulla mia piattaforma la imu e l'encoder insieme sono obbligato ad usare un interrupt x l'una e uno x l'altra: MA IO NE VORREI USARE DUE X L'ENCODER PER OTTENERE MIGLIORI RISULTATI.

COSA FARESTE? IO SONO PER IL LASCIARE STARE LA IMU E CERCARE DI RISOLVERE IL PROBLEMA LEGGENDO UN ENCODER BENE CON DUE FILI, ma significa, essendo solo due gli interrupt esterni, creare una routine di interrupt per pin diversi da pin2 e pin3, o almeno, questo è quello che ho capito nelle miei ''crociere'' online. Questo è il classico modo per leggere gli interrupt e funziona bene, ma usa il pin due che a me serve! =(

#define encoderPinA 2
#define encoderPinB 3

volatile int encoderCount = 0;
volatile float angle = 0;
volatile float angle_previous = 0;
volatile float angle_post = 0;

void doEncoderA(){  // interrupt 0 function
  if (digitalRead(encoderPinA) == HIGH) {  // look for a low-to-high on channel A
    if (digitalRead(encoderPinB) == LOW) { // check channel B to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  else {                                     // must be a high-to-low edge on channel A
    if (digitalRead(encoderPinB) == HIGH) { // check channel B to see which way encoder is turning 
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  angle = 0.00105*encoderCount;  // unit: radian
 // angle= angle*(180/?);
 // angle= angle* 57.295;
}

void doEncoderB(){  // interrupt 1 function
  if (digitalRead(encoderPinB) == HIGH) {   // look for a low-to-high on channel B
    if (digitalRead(encoderPinA) == HIGH) { // check channel A to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  else {                                    // must be a high-to-low edge on on channel B
    if (digitalRead(encoderPinA) == LOW) { // check channel B to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  angle = 0.00105*encoderCount;  // unit: radian
 // angle= angle*(180/?);
 // angle= angle* 57.295;
}

void setup() {

     pinMode(encoderPinA, INPUT);
     pinMode(encoderPinB, INPUT);
     attachInterrupt(0, doEncoderA, CHANGE); 
     attachInterrupt(1, doEncoderB, CHANGE); 

     Serial.begin (9600); // for debugging
}

void loop() {
  Serial.print("counter: ");
  Serial.print(encoderCount, DEC);
  Serial.print(" - ");
  Serial.print("angle: ");
  Serial.print(angle, 4);
  Serial.println(";");
  delay(500);
}

Grazie al cielo sul forum di arduino ho trovato qualcuno che utilizza con successo interrupt su pin diversi da pin 2 e 3: http://forum.arduino.cc/index.php?topic=80928.0 Qualche consiglio x capire il codice? Mi basta una linea guida per capire come scrivere un programma che legga gli encoder!

NXTfreeDOmF: Possiedo una ARDUINO Uno r3 e una ARDUINO Mega 2560, ma i vincoli di progetto mi impongono di usare la prima piattaforma.

Se è una questione di dimensioni ci sono MEGA compatibili delle stesse dimensioni della UNO --> http://www.seeedstudio.com/depot/Seeeduino-Mega-p-717.html Solo un pelino più lunghe. ;)

Altrimenti c'è la Leonardo che ha 5 interrupt esterni su pin 0,1,2,3 e 7 --> http://arduino.cc/en/Main/ArduinoBoardLeonardo I pin 0 e 1 sono utilizzati dalla Serial*1*, quindi se usi la Serial(senza numero) per comunicare col PC non hai interferenze.

Ciao PaoloP, purtroppo sono "obbligato" ad usare la arduino uno rev3 cm vincolo imposto dal progetto =( =(

Puoi vedere qui --> http://forum.arduino.cc/index.php?topic=111587.0 e qui --> https://www.newioit.com.au/archives/56

PaoloP: Puoi vedere qui --> http://forum.arduino.cc/index.php?topic=111587.0 e qui --> https://www.newioit.com.au/archives/56

Grazie dei link, ma avrei una domanda + basilare per la mia comprensione: sapendo che userò due interrupt per leggere correttamente un encoder (o addirittura due a questo punto) e che il programma che ho postato qui sopra funziona, ma sul pin 2 e 3 e questo sarà ciò su cui lavorero' ora, [u]COME FARO' POI[/u] a dire al motore: "muoviti di x gradi"?

Mi spiego: comando i miei due motori con un ponte h L293D che si prende il +e- dai due motori e dal quale collego due fili attaccati a due uscite digitali per la direzione. E' attaccato ai 5v di arduino x la sua alimentazione e ad una pila 9V x alimentare i due motori.

DAL MOTORE LEGO NXT due fili sono x gli encoder, la massa dell'encoder e i suoi 5v.

I collegamenti funzionano perchè il programma che usa pin2 e 3 restituisce valori corretti, MA COME FACCIO A COMANDARE L'ENCODER, OLTRE CHE LEGGERLO?

comandarlo? , cosa intendi? non lo devi comandare, lui gira quando gira il motore, ed in uscita ti ritrovi degli impulsi su due canali a seconda dello sfasamento tra gli impulsi tra un canale e l'altro si puo' rivelare il senso di rotazione del motore. in base agli impulsi, sai la velocita' del motore, (naturalmente devi sapere quanti impulsi ogni giro escono). sostanzialmente gli encoder si usano come trasduttori di posizione, o velocita'. come gestire gli impulsi, via software purtroppo non so aiutarti.

forse non ho interpretato correttamente quello che volevi dire, in tal caso, passa oltre :blush: ciao

goldx: comandarlo? , cosa intendi? non lo devi comandare, lui gira quando gira il motore, ed in uscita ti ritrovi degli impulsi su due canali a seconda dello sfasamento tra gli impulsi tra un canale e l'altro si puo' rivelare il senso di rotazione del motore. in base agli impulsi, sai la velocita' del motore, (naturalmente devi sapere quanti impulsi ogni giro escono). sostanzialmente gli encoder si usano come trasduttori di posizione, o velocita'. come gestire gli impulsi, via software purtroppo non so aiutarti.

forse non ho interpretato correttamente quello che volevi dire, in tal caso, passa oltre :blush: ciao

La direzione è una informazione importante, ma vorrei dire al mio motore: muoviti di x gradi (step) (in una direzione o nell'altra); come controllo quindi il mio encoder?? Qualcuno sa darmi un consiglio?

Nei micro Atmel ci sono 2 gruppi di interrupt, gli INT ed i PCINT, i primi sono detti interrupt esterni e solo solidalmente legati a determinati pin e basta. I secondi sono i cosiddetti Pin Change Interrupt, ossia interrupt legati ai cambi di stato di quasi tutti i pin. Che differenze ci sono? Diverse, ma a te basta solo sapere che gli INT sono con priorità maggiore e maggiormente gestibili nella modalità di utilizzo mentre i secondi sono raggruppati e bisogna lavorare di codice per sapere in un gruppo quale pin ha sollevato un PCINT. Entra in tuo aiuto la libreria che hai visto linkata a quella pagina. Grazie ad essa puoi attaccare un interrupt anche ad un pin diverso dal D2 e dal D3. I metodi di utilizzo sono simili a quelli degli interrupt gestibili con l'Arduino, come puoi vedere dagli esempi allegati alla libreria.

Salve a tutti! Sto ultimando il progetto del mio segway e per una questione di spazio voglio leggere i due segnali dell'encoder del mio motore lego nxt con [u]arduino uno/u

Questo è il programma (che spopola online) che uso per leggere gli encoder sui pin 2 e 3:

#define encoderPinA 2
#define encoderPinB 3

volatile int encoderCount = 0;
volatile float angle = 0;
volatile float angle_previous = 0;
volatile float angle_post = 0;

void doEncoderA(){  // interrupt 0 function
  if (digitalRead(encoderPinA) == HIGH) {  // look for a low-to-high on channel A
    if (digitalRead(encoderPinB) == LOW) { // check channel B to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  else {                                     // must be a high-to-low edge on channel A
    if (digitalRead(encoderPinB) == HIGH) { // check channel B to see which way encoder is turning 
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  angle = 0.00105*encoderCount;  // unit: radian
 // angle= angle*(180/π);
 // angle= angle* 57.295;
}

void doEncoderB(){  // interrupt 1 function
  if (digitalRead(encoderPinB) == HIGH) {   // look for a low-to-high on channel B
    if (digitalRead(encoderPinA) == HIGH) { // check channel A to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  else {                                    // must be a high-to-low edge on on channel B
    if (digitalRead(encoderPinA) == LOW) { // check channel B to see which way encoder is turning
      encoderCount = encoderCount + 1;
    }
    else {
      encoderCount = encoderCount - 1;
    }
  }
  angle = 0.00105*encoderCount;  // unit: radian
 // angle= angle*(180/π);
 // angle= angle* 57.295;
}

void setup() {
     pinMode(encoderPinA, INPUT);
     pinMode(encoderPinB, INPUT);
     attachInterrupt(0, doEncoderA, CHANGE); 
     attachInterrupt(1, doEncoderB, CHANGE); 

     Serial.begin (9600); // for debugging
}

void loop() {
  Serial.print("counter: ");
  Serial.print(encoderCount, DEC);
  Serial.print(" - ");
  Serial.print("angle: ");
  Serial.print(angle, 4);
  Serial.println(";");
  delay(500);
}

Da quello che ho potuto capire in giro x il web posso sostituire questi due pin (che sfrutto già) con due pin qualsiasi della mia arduino: io vorrei usare i pin analogici perchè ho tutti quelli digitali tranne uno occupati (e me ne servirebbero due). E' POSSIBILE QUELLO CHE VORREI? Potrò poi chiamare, secondo voi, queste due funzioni nel setup() per chiamare due interrupt continuando quindi a riuscire a leggere i valori dell'encoder?

attachInterrupt(0, doEncoderA, CHANGE); 
attachInterrupt(1, doEncoderB, CHANGE);

In particolare sto cercando di rendere usabili con interrupt i pin analogici, per poi usarne due.

void setup()
{
  Serial.begin(9600);
  InitialiseIO();
  InitialiseInterrupt();
}

void loop() {
}  

void InitialiseIO(){
  pinMode(A0, INPUT);     // Pin A0 is input to which a switch is connected
  digitalWrite(A0, HIGH);   // Configure internal pull-up resistor
  pinMode(A1, INPUT);     // Pin A1 is input to which a switch is connected
  digitalWrite(A1, HIGH);   // Configure internal pull-up resistor
  pinMode(A2, INPUT);     // Pin A2 is input to which a switch is connected
  digitalWrite(A2, HIGH);   // Configure internal pull-up resistor
}

void InitialiseInterrupt(){
 // cli();                     // switch interrupts off while messing with their settings  
  PCICR =0x02;              // Enable PCINT1 interrupt
  PCMSK1 = 0b00000111;
//  sei();                   // turn interrupts back on
}

ISR(PCINT1_vect) {    // Interrupt service routine. Every single PCINT8..14 (=ADC0..5) change
                                  // will generate an interrupt: but this will always be the same interrupt routine
  if (digitalRead(A0)==0)  Serial.println("A0");
  if (digitalRead(A1)==0)  Serial.println("A1");
  if (digitalRead(A2)==0)  Serial.println("A2");
}

(Questo codice è solo una base di partenza) come posso usare le isr x le due chiamate per gli interrupt? Non voglio la pappa pronta: vorrei solo capire se è la strada giusta e se posso in qualche modo usare le isr per settare interrupt su qualsiasi pin, come credo. Con le isr impostate, dopo tutti i miei pin cambiano stato poi devo capire quale....? mi sfugge qualcosa?

No! Perchè quel genere di Interrupt influisce su tutto il PortX, quindi potresti avere situazioni impreviste. Inoltre l'interrupt è sul cambiamento di stato, quindi devi inserire una routine per discriminare se l'Interrupt è sul LOW o sull'HIGH.

Leva roba dalla porta SPI (oppure usa la I2C, visto che è libera) e aggiungi un Port Expander.

Ne stavamo parlando qui: http://forum.arduino.cc/index.php?topic=238326.msg1712390#msg1712390 perché aprire un nuovo thread, scusa?

leo72: Ne stavamo parlando qui: http://forum.arduino.cc/index.php?topic=238326.msg1712390#msg1712390 perché aprire un nuovo thread, scusa?

ho fatto un errore io! come posso procedere? in realtà ho aperto un nuovo 3D app adesso con nuovi dubbi. Credevo che facendo troppe domande i membri non mi rispondessero, allora le ho divise. :cold_sweat: :cold_sweat: :cold_sweat:

Si sparpagliano solo le info.

NXTfreeDOmF: ho aperto un nuovo 3D app adesso con nuovi dubbi.

Se è questo: http://forum.arduino.cc/index.php?topic=238840.0

te l'ho [u]chiuso[/u]. Per favore continua in un SOLO thread.

EDIT: ho riunito e spostato i 2 thread qui.

leo72:

NXTfreeDOmF: ho aperto un nuovo 3D app adesso con nuovi dubbi.

Se è questo: http://forum.arduino.cc/index.php?topic=238840.0

te l'ho [u]chiuso[/u]. Per favore continua in un SOLO thread.

EDIT: ho riunito e spostato i 2 thread qui.

GRAZIE DELL'AIUTO MI SCUSO PER IL DISGUIDO TECNICO :cold_sweat: :cold_sweat: SCUSATE LA CONFUSIONE, CHIARISCO:

Ho finalmente ritrovato una vecchio 3D aperto da lesto in cui si parla di abilitare interrupt su qualsiasi pin. Ho apportato qualche piccola modifica per abilitare l'ISR soltanto sui pin analogici, ma ho un dubbio: ho la necessità di attivare l'interrupt su almeno due pin, in modo da leggere due segnali provenienti da un encoder MI SEMBRA DI CAPIRE CHE PER LEGGERE L'ENCODER POSSO CHIAMARE ANCHE UN SOLO INTERRUPT attaccando entrambi i fili dell'encoder. E' vero? SI OTTIENE UNA LETTURA MIGLIORE CHIAMANDO DUE INTERRUPT O E' SUPERFLUO E BASTA CHIAMARNE UNO? Ho liberi soltanto i pin analogici e il pin 4 e vorrei arrivare ad una cosa di questo genere [u]ipotizzando di usare due interrupt[/u]:

attachInterrupt(PinD4, doEncoderA, RISING); 
attachInterrupt(PinA0, doEncoderB, RISING);

Devo attivare le due routine ISR() per i due gruppi di cui ho bisogno giusto? Su A0-A5 (D14-D19) non ho nulla, ma sugli altri due gruppi tutti i pin (tranne il D4) sono occupati: non è che questo può compromettere l'utilizzo di quei pin? ad esempio pinD2 (int 0) lo uso con un interrupt(standard) per la lettura di dati da una imu e non vorrei venisse influenzato :roll_eyes:!

Parte dal fondo. Non puoi attaccare gli interrupt con attachInterrupt sui pin che vuoi tu, devi obbligatoriamente usare 0 e 1, che corrispondono a INT0 e INT1, ossia i pin digitali 2 e 3. Se vuoi usare i Pin Change Interrupt, devi usare

PCintPort::attachInterrupt(PIN, burpcount,RISING);

come da esempio: https://code.google.com/p/arduino-pinchangeint/wiki/Usage

Detto questo, non devi preoccuparti della porta su cui è partito l'interrupt, è la libreria che identifica il pin.

leo72:
Parte dal fondo. Non puoi attaccare gli interrupt con attachInterrupt sui pin che vuoi tu, devi obbligatoriamente usare 0 e 1, che corrispondono a INT0 e INT1, ossia i pin digitali 2 e 3. Se vuoi usare i Pin Change Interrupt, devi usare

PCintPort::attachInterrupt(PIN, burpcount,RISING);

come da esempio:
https://code.google.com/p/arduino-pinchangeint/wiki/Usage

Detto questo, non devi preoccuparti della porta su cui è partito l’interrupt, è la libreria che identifica il pin.

Considerando di dover usare due interrupt sto per testare questo programma (che usa la libreria):

//#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
// 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 PIN1 14 //voglio usare un pin analogico e il d4
#define PIN2 4
#define PIN3 16 //non lo uso

uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins

void quicfunc() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  interrupt_count[latest_interrupted_pin]++;
};

// You can assign any number of functions to any number of pins.
// How cool is that?
void pin3func() {
  Serial.print("Pin "); Serial.print(PIN1, DEC); Serial.println("!");
}

void pin4func() {
  Serial.print("Pin "); Serial.print(PIN2, DEC); Serial.println("!");
}

void setup() {
//  pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH);
//  PCintPort::attachInterrupt(PIN1, &quicfunc, FALLING);  // add more attachInterrupt code as required
//  pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH);
//  PCintPort::attachInterrupt(PIN2, &quicfunc, FALLING);
  pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH);
  PCintPort::attachInterrupt(PIN1, &pin3func, CHANGE);
  
  pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH);
  PCintPort::attachInterrupt(PIN2, &pin4func, CHANGE);
  
  Serial.begin(115200);
  Serial.println("---------------------------------------");
}

uint8_t i;
void loop() {
  uint8_t count;
  Serial.print(".");
  delay(1000);
  for (i=0; i < 20; i++) {
    if (interrupt_count[i] != 0) {
      count=interrupt_count[i];
      interrupt_count[i]=0;
      Serial.print("Count for pin ");
      if (i < 14) {
        Serial.print("D");
        Serial.print(i, DEC);
      } else {
        Serial.print("A");
        Serial.print(i-14, DEC);
      }
      Serial.print(" is ");
      Serial.println(count, DEC);
    }
  }
}

Vi aggiorno…

Posto il risultato: ‘

---------------------------------------
........Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
Pin 14!
..........................

Sul pin A0 vede l'interrupt ma soltanto se giro in una direzione, nell'altra non indica nulla-->perchè ha bisogno dell'altro interrupt, dell'altro segnale, che non trova! Se lo rovasse me lo stamperebbe giusto? Non lo trova xchè sn settati uno hign uno low? Il primo che lo setto high o low rileva cmq il change e questo mi sembra giusto... Dove sbaglio?

ps. credo ci sia anche un errore nel programma che stampa tante volte il pin invece che incrementare una variabile count ma investigherò meglio...

Forza ragazzi!! Non ci salto fuori.. :~ ^_^ :~ Davvero nessun consiglio???

Prima di tutto, con le ultime versioni dell'IDE per attivare le pull-up interne puoi fare così:

pinMode(pin, INPUT_PULLUP);

Poi, non vedo l'attivazione della pull-up sul pin 16. Forse dipende da questo.