Calcolare il tempo tra due eventi

Ciao, attualmente il mio problema è questo: devo calcolare il tempo tra un frote di salita ed un altro attraverso un interrupt e poi riportare il valore in un vettore che poi gli farò compiere una media.
Attualmente ho provato così ma non è quello che voglio. ualche consiglio, GRAZIE

// GESTIONE ENCODER 
#define encoderCLK 2 // CLK (canale A) connesso al pin D2
#define encoderDT 4  // DT (Canale B) connesso al pin D4
#define encoderSW 3  // SW (bottone) connesso al pin D3
#define interrupt0 0 // Associato al pin 2 di Arduino
#define interrupt1 1 // Associato al pin 3 di Arduino

unsigned long istante_finale = 0; 
unsigned long istante_iniziale = 0;
unsigned long differenza = 0;

int i = 0;


void setup() {
  // I pin sono impostati come ingresso
    pinMode(encoderCLK, INPUT);    // interrupt 0
    pinMode(encoderDT, INPUT);   

    // Attivo l'interrupt 0 quando lo stato del canale CLK "cambia" poiche'
    // questo devo effettuare la lettura di DT ad ogni variazione di CLK
    attachInterrupt(interrupt0, CLKChanged, RISING);
   
  
  
    
    Serial.begin(9600);
   

}

void loop() {
if(i==1){
  istante_iniziale = millis();
  }
if(i==2){
  istante_finale = millis();
  }
if(i==0){
  differenza = istante_finale - istante_iniziale;
  Serial.println(differenza);
  }
}
void CLKChanged()
{
 valore_stato_DT = digitalRead(encoderDT);
 if(valore_stato_DT == HIGH){
  i++;
  if(i==2){
    i=0;
    }
  }

    }

Hai dato poche informazioni circa il problema, vado ad intuito, non ti calcola la differenza :slight_smile:
Il problema è nel nell'ISR se i vale 2 la azzeri e quindi nel loop non entri mai dove assegni istante_finale, inoltre essendo i a zero all'inizio stampi all'infinito.
Ti consiglio di non azzerare i nell'ISR e di cambiare il loop in questo modo:

if(i==2)
{
  istante_finale = millis();
  i = 0;
  differenza = istante_finale - istante_iniziale;
  Serial.println(differenza);
  
}

Se poi la variabile istante_finale non ti serve per altre cose puoi fare:

if(i==2)
{
  differenza = millis() - istante_iniziale;
  i = 0;
  Serial.println(differenza);
  
}
  1. sarebbe utile avere almeno un'idea dell'ordine di grandezza della distanza (millisecondi, secondi, minuti...)
  2. sarebbe bene fare la funzione di interrupt il più corta possibile. Di conseguenza (avendone il tempo) si potrebbero dichiarare due variabili unsigned long, una "tempo attuale" che contiene i millisecondi allo scatto dell'interrupt, ed una "tempo precedente" che contiene la vecchia tempo attuale.
    Oltre a questo una variabile unsigned long, o anche più piccola, "differenzaa" che ne conterrà la differenza (così facendo, però NON hai una media, per la quale serve almeno un array di differenze, un suo indice e più tempo). La funzione di interrupt contiene soltanto una assegnazione di valore, prova a indovinare quale, e la loop esegue poche e semplici operazioni:
    Se tempo attuale e tempo precedente sono diverse (vale a dire se...) "differenza" ottiene il valore di...(indovinello facile) e tempo precedente diventa uguale a tempo attuale.

Tra l'altro mi sono scordato che la variabile i devi definirla volatile

semplifica la ISR
così dovrebbe andare, conta solo se interrupt rising e DT alto
e cicla i tra 0 e 2

void CLKChanged()
{
 
 
  i=(i+digitalRead(encoderDT))%3;
  

}

inoltre se i viene usata per sola lettura fuori dalla ISR potrebbe non essere necessario metterla VOLATILE

ah, te lo avranno già detto: come la avevi fatta tu la ISR nonfaceva mai i=2 e non faceva quindi il calcolo

fabpolli e Silente mi spiego meglio, io ho un encoder con due canali A e B e devo calcolare il tempo tra un passo e il successivo (se va in senso orario cioè durante il fronte di salita del canae A il valore del canae B deve essere lo stesso del canale A cioè HIGH) e in questo caso ci calcolo il deltatempo, nel caso in cui il verso sia antiorario entra in un sottoprogramma che blocchi l'interrupt e aspetti un tempo k qualsiasi.

Attualmente ho scritto il programma che scrive il tempo tra un passo e l'altro nella seriale e scrive errore nel caso vada in senso antiorario MA questo non accade e sembra che scrive valori errati.

volatile int valoreB = 0;
volatile int i =0, j=0;
volatile int k=0;

unsigned long inizio_A = 0;
unsigned long fine_A = 0;
unsigned long diff_A = 0;

void setup() {
pinMode(2, INPUT);
pinMode(4,INPUT);
Serial.begin(9600);

attachInterrupt(digitalPinToInterrupt(2), fronte, RISING);
}

void loop() {
if(k==0) {

if(j < i){
  fine_A = inizio_A;
  inizio_A = micros();
  diff_A = inizio_A - fine_A;
  j = i;
  Serial.print(diff_A/100000);
  Serial.print (" ");
  Serial.println(i);
}
if(k==1){
  Serial.print ("erroreeeeeeeeeeeeeeeeeeee    ");
  Serial.println (i);
  inizio_A = 0;
  fine_A = 0;
}
}
}
void fronte(){
  valoreB= digitalRead(4);
 if(valoreB==HIGH){
   
    i ++;
    k = 0;
    }
   if(valoreB==LOW){
   
    k=1;
   }
  
  }

Chiedo aiuto

spiega bene in che senso non va
non ti dice mai errore?
in che senso stampa valori errati, come fai a saperlo che sono errati?

vebbe' proviamo:
la ISR deve essere veloce il più veloce possibile
ti avevo lasciato un esempio, ma forse non ti era chiaro

non leggere il pin in una variabile che poi usi per un test
usa direttamente il pin per il test
una cosa del genere

void fronte()
{
    i = digitalRead(4);
    k = 1;
}

come vedi non uso variabili inutili
per prima cosa leggo immediatamente il pin e lo uso per restituire i
poi alzo la variabile K, che serve per sapere che c'è stato un interrupt

quindi nella loop tu controlli se c'è alta la k
se si e solo se si abbiamo avuto iìun interrupt
se la i è alta significa che era alto il pin che ci interessa
altrimenti no
non serve fare due test, perchè se fallisce la prima condizione l'altra è obbligata
una cosa del genere

void loop()
{
    if (k) // è accaduto un interrupt
    {
        // ora o abbiamo i alto oppure no
        if (i)
        {
            fine_A = inizio_A;
            inizio_A = micros();
            diff_A = inizio_A - fine_A;
            Serial.println(diff_A / 100000);
            Serial.println(" ");
            // i è alto, calcolo e stampo il tempo
            k = i = 0;
            // ricordiamoci di azzerare i e k
        }
        else
        {
            // i non è alto, stampo errore
            Serial.println("erroreeeeeeeeeeeeeeeeeeee    ");
            inizio_A = 0;
            fine_A = 0;
        }
    }
}

come vedi faccio un test, chiamiamolo implicito
if (k) che vale vero solo se k è alta
e poi, solo se il primo è ok testo i
che sarà alta solo se era alto il piedino che ti interessa
se non era alto....
non serve un secondo if , basta la else
fammi sapere

edit
credo di aver trovato
tu fai inizio meno fine
essendo fine posteriore a inizio il risultato sarà negativo
se usi una variabile unsigned i numeri sono certamente strani
prima legge di Nelson (che sono io) sulle variabili:
una variabile usata una volta sola è inutile, metti direttamente la sua formula generante

         //   diff_A = inizio_A - fine_A;
            Serial.println((inizio_A - fine_A) / 100000);

che anche così non è sicuro, se cerchi (e le cerchi tu) le regole di cast automatico del C dovrebbe chiarirsi il dubbio
ti è chiaro vero che stai usando variabili intere e che quindi sarà intero (senza parte frazionaria e troncato) il risultato stampato?

Standardoil ho appena provato con il tuo sketch e, durante il senso antiorario posso dire che funziona (a parte in alcuni momenti che non capisco perchè mi da un valore di tempo) invece nella parte in senso orario mi da dei valori strani (sbagliati) forse per il decounce dell'encoder (anche se dubito)

un errore che si può vedere nella seriale faccio scattare di un passo l'encoder e scrive tre file di errore e un valore

questo non va bene

posta il codice completo e attuale
e un esempio di stampata

volatile int valoreB = 0;
volatile int i =0, j=0;
volatile int k=0;

unsigned long inizio_A = 0;
unsigned long fine_A = 0;
unsigned long diff_A = 0;

void setup() {
pinMode(2, INPUT);
pinMode(4,INPUT);
Serial.begin(9600);

attachInterrupt(digitalPinToInterrupt(2), fronte, RISING);
}

void loop()
{
    if (k) // è accaduto un interrupt
    {
        // ora o abbiamo i alto oppure no
        if (i)
        {
            fine_A = inizio_A;
            inizio_A = micros();
            diff_A = inizio_A - fine_A;
            Serial.println(diff_A / 100000);
            Serial.println(" ");
            // i è alto, calcolo e stampo il tempo
            k = i = 0;
            // ricordiamoci di azzerare i e k
        }
        else
        {
            // i non è alto, stampo errore
            Serial.println("erroreeeeeeeeeeeeeeeeeeee    ");
            inizio_A = 0;
            fine_A = 0;
        }
    }
}

void fronte()
{
    i = digitalRead(4);
    k = 1;
}

sulla seriale

leggere tutto il post mio è difficile vero?
la stampata non si vede
ma l'encoder non ha un debounce?, male mi limito a dire: male
inoltre se impegni la seriale a stampare un botto di caratteri dovresti essere come minimo sicuro che tra due differenti e successivi eventi di interrupt tu abbia il tempo si trasmettere

carica il link che ti ho mandato e puoi vedre la foto

Secondo te io non lo sapevo?
se ti dico che non si vede è perché io ho provato
metti a osto e poi magari prova a rispondere ad alcune delle domande che ti abbiamo fatto
chissa', il mio piede sinistro dice che la soluzione è nella risposta
lo accontentiamo?

scusa ma non riesco ad inviarla Standardoil

evidentemente non riesci nemmeno a rispondere
vedi tu, secondo te vale la pena di aiutare uno che NON risponde alle domande?

Certo c'hai ragione, quello che volevo dire è che non riesco inviarti la foto tutto qui, per il il problema del debounce non ci dovrebbe essere perchè l'ho appena provato sull' osciloscopio e non si vedono strani rimbalzi, quello che non capisco è che se uso il mio encoder da 1080 passi per giro e lo faccio girare a circa 1kHz i valori non si incrementano nel modo corretto cioè mi spiago meglio i valori dopo aver compiuto indicativamente lo stesso giro a diverse velocità la variabile che uso per il numero di passi è diversa
Perchè?

luchinho:
Certo c'hai ragione, quello che volevo dire è che non riesco inviarti la foto tutto qui

Per la foto hai due soluzioni ...

  1. la riduci a meno di 1.2MB e la alleghi (vd. punto 8 del regolamento)
  2. la metti su un servizio pubblico e usi il link per inserirla nel post con l'apposito bottone (vd. punto 9 del regolameto)

Guglielmo

luchinho:
la variabile che uso per il numero di passi è diversa

Questa variabile in quale codice la vediamo?


Considera che, per come sono strutturati i codici precedenti, il loop deve obbligatoriamente girare almeno alla stessa velocità di arrivo degli interrupt (in sostanza non deve contenere niente che lo faccia ritardare più di 1ms), e questo mi sembra vanificare un po' l'uso degli interrupt...

Un interrupt è un evento asincrono che accade in un momento qualsiasi, indipendente dal tempo o dalla logica del resto del programma.

A cosa serve qui l'interrupt? A identificare il momento in cui compare un fronte di salita, in quel momento esatto va letto il tempo (con micros, che ha un errore di +/-4µs).

Chi deve calcolare la differenza? Se il loop fosse sempre abbastanza veloce "da stare dietro agli interrupt" allora potrebbe farlo lui assieme a tutte le altre cose che servono (ma allora non servirebbe usare gli interrupt), altrimenti anche le altre operazioni vanno nella ISR.

Quindi:

  • Rilevamento fronte
  • Lettura tempo attuale
  • Lettura secondo ingresso
  • Eventuale calcolo tempo trascorso (+ event.inserim in array)
  • Eventuale incremento numero passi

Con 1000 fronti al secondo non dovrebbe esserci alcun problema, l'atmega è in grado di eseguire quanto sopra almeno 20mila volte al secondo se non di più.

Rimane però da pensare cosa deve succedere "al primo giro", ovvero quando da senso antiorario si riparte in senso orario, perché la prima differenza di tempo sarà necessariamente sbagliata (servono almeno due fronti in senso orario per calcolare la prima differenza valida).

Claudio_FF questo è il codice che uso per verificare il numero di passi

#define encoderCLK 2 // CLK (canale A) connesso al pin D2
#define encoderDT 4  // DT (Canale B) connesso al pin D4
#define interrupt0 0 // Associato al pin 2

int encoderCount = 0; // Contatore
int actCLKState;      // Lettura attuale del canale CLK (A)
int prevCLKState;     // Lettura precedente del canale CLK (A)
 
void setup()
{
    pinMode(encoderCLK, INPUT);    // interrupt 0
    pinMode(encoderDT, INPUT);   

    attachInterrupt(interrupt0, CLKChanged, CHANGE);
   Serial.begin(38400); 
    prevCLKState = digitalRead(encoderCLK);
}
 
void loop() {
  Serial.println(encoderCount);
  }


void CLKChanged()
{
    int actCLKState = digitalRead(encoderCLK);
    if (prevCLKState != actCLKState)
    {
       
        encoderCount += (actCLKState == digitalRead(encoderDT) ? 1 : -1);
        Serial.println(encoderCount);
        prevCLKState = actCLKState;
    }
}

Certo per verificare la differenza tra due fonti ma il fatto è che se non legge correttamente nemmeno l'encoder non ha senso calcolare il tempo. Te cosa ne dici ? A me verrebbe da dire prima risolvol il problema della lettura di ogni passo