Info generica interrupt (mega2560)

Ciao,

vorrei utilizzare 2 interrupt contemporaneamente, e volevo capire se è possibile.

Un interrupt conta degli impulsi da un pin ingresso; vorrei non utilizzare millis() per verificare il tempo di conteggio degli impulsi (circa 50 ms), in quanto a seconda di quando viene verificato millis() a causa del codice, il tempo non sarebbe sempre preciso; è possibile utilizzare un’altro interrupt che conteggia il tempo internamente?

Altrimenti altra soluzione è una funzione che con delay attende 50 ms e mi conteggia gli impulsi, ma in questo caso blocco il codice per 50 ms…

Grazie per i suggerimenti :slightly_smiling_face:

Certo che è possibile, fai due ISR e le colleghi a due intrerrupts diversi ... solo ... su che MCU sei? Perché su AVR gli interrupts NON sono nidificabili ...

Guglielmo

Arduino atmega 2560…

Quindi AVR ... finita una ISR viene eseguita la prossima ...

Guglielmo

Quindi potrei farlo comunque, in pratica quando esegue la ISR interna del tempo, non può eseguire il conteggio impulsi (ne perderei qualcuno), ma penso sempre più preciso che usare millis() nel codice.

Nell’altro caso, se dovesse scattare ISR per il tempo mentre è già dentro ISR per contare impulsi, finirebbe l’istruzione che conta gli impulsi e poi farebbe ISR del tempo (l’istruzione contaimpulsi è unica, aumenta una variabile, quindi molto breve)

Dove posso trovare come impostare l’ISR del contatore interno? Ci sarà da impostare un Timer?

Grazie :slightly_smiling_face:

Ci sono diversi modi per farlo. Fondamentalmente, si desidera contare gli impulsi che passano entro i 50 ms.
È possibile creare un ISR che conta gli impulsi e controlla se millis() è trascorso più di 50 ms (o micros(), per quanto riguarda questo). Nessuno dei due viene aggiornato durante l'ISR, ma nel frattempo sì. Si potrebbe anche creare un interrupt del timer che controlla il conteggio degli impulsi dal primo, ma non sarà più preciso e probabilmente più complesso. Qual è il tuo obiettivo finale? Cioè, a quale scopo vuoi farlo?

Il contatore interno È quello che millis() ti ritorna ed è già gestito con un interrupt che incrementa il contatore ...

... invece di usare millis() usa micros() che è più preciso ed avanza anche se ci sono delle ISR.

Guglielmo

Analogamente a millis(), micros() non viene aggiornato durante un ISR, ma i tick finiscono in una coda e quindi il valore viene aggiornato dopo l'uscita dall'ISR. Ciò non influirà in alcun modo sulla funzionalità.

FALSO,
micros() viene aggiornato anche durante una ISR ... è la differenza fondamentale (oltre la precisione).

Guglielmo

Eccomi,

si, per questi tempi meglio usare micros(), anche se, leggendo in giro, non si capisce bene se effettivamente micros() funziona anche durante la ISR, chi dice in un modo, chi dice nell’altro. Allego 2 esempi, ci si capisce meglio: questo è il metodo che utilizzo ora, ma mi blocca il programma abbastanza spesso

#include <util/atomic.h>        // libreria per sospendere interrupt in modo sicuro           

#define pinGiri  2              // pin ingresso giri= 1 impulso/giro

volatile int Giri;              // ISR incrementa con impulsi
int giri_m = 0;                 // ci salvo i giri minuto
int giri_s = 0;                 // ci salvo i giri secondo

unsigned long t_lett = 0;       // per millis, tempo lettura giri

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

  pinMode(pinGiri, INPUT);

  attachInterrupt(0, rpm, FALLING); //interrupt sul pin 2

  t_lett = millis();            // azzero millis
}

void loop()
{
  if (millis() - t_lett >= 150) // ogni 150 ms
  {
    leggi();
    t_lett = millis();          // azzero millis
  }

  // codice programma
}


void leggi()
{
  Giri = 0;                           // azzero var
  delay (50);                         // aspetta 50 ms per contare gli impulsi
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)   // sospendo gli interrupt
  {
    giri_s = (Giri * 20);             // moltiplico e salvo il dato nella mia var
    giri_m = (giri_s * 60);
  }
}

void rpm()      //Funzione che viene cambiata dall'interrupt
{
  Giri++;                             // incremento la variabile
}

questo il metodo con micros() nel loop, ma questo a seconda della lunghezza del programma, va a ritardare i 50 ms che si controllano con l’if:

#include <util/atomic.h>        // libreria per sospendere interrupt in modo sicuro           

#define pinGiri  2              // pin ingresso giri= 1 impulso/giro

volatile int Giri;              // ISR incrementa con impulsi
int giri_m = 0;                 // ci salvo i giri minuto
int giri_s = 0;                 // ci salvo i giri secondo
unsigned long t_lett = 0;       // per millis, tempo lettura giri

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

  pinMode(pinGiri, INPUT);

  attachInterrupt(0, rpm, FALLING); //interrupt sul pin 2

  t_lett = micros();            // azzero micros
}

void loop()
{
  if (micros() - t_lett >= 50000)     // ogni 50 ms
  {
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) // sospendo gli interrupt
    {
      giri_s = (Giri * 20);           // moltiplico e salvo il dato nella mia var
      giri_m = (giri_s * 60);
      Giri = 0;                       // azzero var
    }
    t_lett = micros();                // azzero micros
  }

  // il codice lungo può ritardare l'intervento dell IF sopra
}

void rpm()      //Funzione che viene cambiata dall'interrupt
{
  Giri++;                             // incremento la variabile
}

Funziona e chi dice il contrario NON si è mai studiato i sorgenti del "core" Arduino per AVR ...

unsigned long micros() {
	unsigned long m;
	uint8_t oldSREG = SREG, t;
	
	cli();
	m = timer0_overflow_count;
#if defined(TCNT0)
	t = TCNT0;
#elif defined(TCNT0L)
	t = TCNT0L;
#else
	#error TIMER 0 not defined
#endif

#ifdef TIFR0
	if ((TIFR0 & _BV(TOV0)) && (t < 255))
		m++;
#else
	if ((TIFR & _BV(TOV0)) && (t < 255))
		m++;
#endif

	SREG = oldSREG;
	
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

Guglielmo

Mentre, se si esamina millis() si vede che dipende NON dal contatore del timer0 (come nel caso di micros()) che comunque cammina sempre, ma dall'interrupt generato al suo overflow ...

#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

unsigned long millis()
{
	unsigned long m;
	uint8_t oldSREG = SREG;

	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	cli();
	m = timer0_millis;
	SREG = oldSREG;

	return m;
}

Guglielmo

NON azzeri micros(), correggi il commento, aggiorni la variabile t_lett :wink:

Guglielmo

Guglielmo grazie,

mi sono tolto il dubbio di micros() :wink:

Per il commento della variabile micros(), l’ho sempre scritto così per comodità, ma naturalmente è la mia variabile che viene aggiornata al contatore micros(), vedrò di abituarmi al commento corretto :grinning_face: grazie

Altra cosa, più da smanettoni di hardware, per la precisione meglio una scheda che abbia il quarzo piuttosto che il risuonatore ceramico.

Ciao,

bhè, si, sicuramente è più preciso il quarzo, anche se in questo caso, per quello che devo fare io, non ho poi necessità di questa precisione :smiley:

Però alla fine, dalle vostre risposte non ho capito se quindi è meglio usare un interrupt interna per misurare il tempo dei 50 ms; o se va bene anche l’if nel programma.

Poi non avevo risposto a @Deva_Rishi, vorrei misurare la velocità di un motorino e contare i giri che fa da quando inizia a girare.

Ok. Ci sono due metodi ovvi.
Misurare il numero di impulsi in un intervallo di tempo fisso, oppure misurare il tempo di un numero fisso di impulsi. Il modo più semplice è il primo, ma a velocità inferiori potrebbe causare molta imprecisione.
Quindi userei un pinChangeInterrupt, ma molto probabilmente misurerei solo se aumenta o diminuisce (non cambia). Il primo comando dell'ISR è leggere l'ora. Questa variabile sarà statica per l'ISR. Quindi si controlla l'ora dall'interno del pinChangeInterrupt.
Se supera la soglia (e gli impulsi superano una soglia minima per le basse velocità), si fa il calcolo. Quanti impulsi in quale intervallo di tempo esatto. Si memorizza il risultato in una variabile volatile e si imposta un flag "pronto per la lettura" (anch'esso volatile, ovviamente).

Nello sketch principale, si legge la variabile (se possibile, si mantenga una dimensione di 8 bit) quando il flag mostra true, quindi si reimposta il flag.

Provaci e mostraci cosa riesci a inventare.
Penso che tu abbia tutto il tempo per elaborare i dati in entrambi i casi. Si tratta solo di contare e fare qualche calcolo quando si verificano le condizioni giuste.

Prima di complicarti la vita, inizia con questo sistema e verifica se hai la precisione che ti serve ... :wink:

Guglielmo

Guglielmo buongiorno,

questo sistema con l’if nel programma è quello che sto utilizzando, sinceramente mi funziona bene; volevo solo migliorare la precisione in quanto il programma è lungo ed è probabile che i 50 ms diventino di più; però se utilizzare un’altro interrupt complica di molto le cose, il gioco non vale la candela :slightly_smiling_face:

Se proprio ti vuoi complicare la vita, usa un altro Timer (es. Timer1) per generare un interrupts esattamente a 50msec. ... ci sono varie librerie che ti permettono una gestione semplificata dei timers e le loro impostazioni :wink:

Guglielmo