Controllo Stato Sensore Laser : Interrupt o Loop ?

Dopo aver risolto il problema posto in questo topic:
http://forum.arduino.cc/index.php?topic=241622.0

Vi spiego in poche parole cosa dovrebbe fare questo semplice progettino:
Arduino utilizzato: Arduino Uno Rev3
Ho due fotocellule irraggiate da due fasci laser, quando la prima si chiude prendo il tempo così:

start = micros();

E quando si chiude la seconda fotocellula prendo il tempo così:

end = micros();

La distanza delle fotocellule la conosco, quindi non mi rimane che stampare la velocità media di passaggio calcolata in questo modo:

Serial.print ( distance / ( ( (double)(timeEnd - timeStart) ) / 1000000 ) );
Serial.println ( " m/s" );

La velocita' naturalmente e' in metri al secondo ( per i km/h basta moltiplicarla per 3.6 ).

Arrivato a questo punto tutto sembra funzionare molto bene, però mi pongo un dubbio, siccome la distanza delle 2 fotocellule sarà abbastanza corta ( 10 - 20 cm ) e la velocità di passaggio può essere abbastanza alta ( 60 Km/h ~= 18m/s ) mi chiedo se Arduino fa in tempo a verificare entrambi i passaggi e calcola un valore con non molta approssimazione, perchè nel caso migliore ho:

Distanza = 0.20m;
Velocità = 18 m/s;
Tempo di passaggio che dovrà controllare Arduino ~= 11ms;

Ho buttato giu un po di codice ancora da ottimizzare per poter calcolare il tempo trascorso dal passaggio dalla prima fotocellula alla seconda fotocellula e data la distanza delle fotocellule calcolarne la velocità media di passaggio.

Questo è il codice che ho scritto per ora:

int sensorPin1 = 3;
int sensorPin2 = 2;

boolean _start = false, _stop = false;
long timeStart, timeEnd;
const double distance = 0.07;

void setup ( ) {
    pinMode ( sensorPin1, INPUT );
    pinMode ( sensorPin2, INPUT );
    Serial.begin ( 9600 );
    
}

void loop ( ) {
    int value1 = digitalRead ( sensorPin1 );
    int value2 = digitalRead ( sensorPin2 );
    if ( value1 == HIGH && _start == false && _stop == false && value2 == LOW ) {
      _start = true;
      timeStart = micros ( );
      Serial.println ( "Start!" );
    }
    value1 = digitalRead ( sensorPin1 );
    value2 = digitalRead ( sensorPin2 );
    if ( value2 == HIGH && _start == true && _stop == false && value1 == LOW ) {
      _stop = true;
      timeEnd = micros ( );
      Serial.println ( "Stop!" );
    }
    if ( _start == true && _stop == true ) {
      Serial.print ( "Tempo trascorso: " );
      Serial.print ( distance / ( ( (double)(timeEnd - timeStart) ) / 1000000 ) );
      Serial.println ( " m/s" );
      _start = false;
      _stop = false;
    }
    delay ( 1 );
}

Ora vi chiedo, secondo voi è più veloce utilizzare la funzione loop come ho fatto in questo primo test per prendere i vari tempi, oppure sarebbe molto più ottimizzato usare una callback legata a un Interrupt, dato che utilizzo Arduino Uno e ho a disposizione 2 PIN di interrupt ?

Ciao e grazie :smiley:

Se puoi evita la callback e usa direttamente la ISR, questo per ridurre al minimo possibile l'overhead della chiamata a funzione. Nella ISR puoi prendere solo i tempi, occhio che il timer di millis() si ferma quando il PC salta dentro la ISR quindi la prima cosa da fare è salvare il tempo.

Per salvare il tempo potresti usare un array di due elementi e incrementare l'indice 0, 1, 0, 1
questo ti permette di scrivere meno codice nella ISR, es:
ISR {
startEnd[index] = micros();
index = index + 1 % 2;
}

su startEnd[0] ci sarà il tempo di start e su startEnd[1] il tempo di end
Quando index == 1 il tempo di end è stato già preso e nel loop puoi fare il calcolo e allora azzeri index.

Ciao.

Grazie mille, si con la parola callback mi riferivo proprio alla ISR ( piccola confusione nel linguaggio, scusa ), avevo letto del problema del non incremento di micros ( ) e millis ( ) all'interno di una ISR ma non l'avevo compreso molto bene, con quello che mi hai scritto mi è chiarissimo, grazie.
Per quanto riguarda invece la parte di programmazione, dato che ho due sensori differenti, scrivo due diverse ISR, e sto pensando a fare una cosa così:

volatile long start = 0, end = 0;
double distance = 0.10; // 10 cm di distanza

void setup ( ) {
  attachInterrupt ( 0, ISR_1, RISING );
  attachInterrupt ( 1, ISR_2, RISING );
  Serial.begin ( 9600 );   
}

void loop ( ) {
  if ( end != 0 && start != 0 ) {
    Serial.print ( distance / ( ( (double)(end - start) ) / 1000000 ) );
    Serial.println ( " m/s" );
    end = 0;
    start = 0;
  }
}

ISR_1 { // Fotocellula Iniziale
  start = micros ( );
}

ISR_2 { // Fotocellula Finale
  end = micros ( );
}

L'ho buttato giù ora mentre scrivo quindi ci potrebbe essere qualche imprecisione, però mi sembra ancora più ottimizzato, lui vede che deve fare il calcolo quando ha entrambi i tempi, volendo si potrebbe migliorare utilizzando una boolean nelle ISR per controllare che sia passato prima dalla prima fotocellula e poi dalla seconda.
Grazie, ciao :slight_smile:

Duecce:
volendo si potrebbe migliorare utilizzando una boolean nelle ISR per controllare che sia passato prima dalla prima fotocellula e poi dalla seconda.

... oppure, senza appesantire le ISR, con un IF nell loop :

if ((start == 0) && (end != 0)) { ... }

... vuol dire che c'è qualche cosa che non va ... qualcuno è passato prima davanti a end che a start XD

Guglielmo

Ahahah grazie gbp01, sapevo che non avevo pensato a tutto :smiley:

Ciao :slight_smile:

Grazie mille, si con la parola callback mi riferivo proprio alla ISR

No, la callback è proprio la funzione utente attaccata alla ISR.
Nel codice interno di arduino c'è qualcosa di simile a:

ISR(INT0) 
{
     int0userFunction();
}

Senza usare attachInterrupt, non c'è costo di chiamata a userFuction.
Il codice sarebbe simile a:

ISR(INT0) 
{ // Fotocellula Iniziale
  start = micros ( );
}

Solo che devi configurare gli interrupt manipolando i registri interni dell'atmega, non è molto complesso
ma richiede comunque una lettura del datasheet.

Io lascerei tutto così, visto che funziona e che il costo della chiamata a funzione è una costante, più in la ti documenti sulla ISR nuda e cruda e apporti la modifica. Il costo della chimata a micros non puoi eliminarla e comunque visto che non prende parametri e ridotto al minimo indispensabile a restituire un valore sullo stack, in ogni caso non si può fare di meglio. Ora non posso controllare, ma millis() resistuisce unsigned long, micros() forse anche controlla e se il caso modifica il tipo di start e end.

Visto che ci sono, io preferirei calcolare il valore indipendentemente dalla volontà di stamparlo.

Ciao.

Grazie MauroTec, non sapevo si potesse direttamente usare la ISR nuda e cruda, per adesso lascio così, più in quà mi informo e vedo cosa si può migliorare

Ciao :slight_smile: