Datalogger per impulsi in tensione

Si sembra che l'oggetto faccia al caso mio. Quindi i punti fondamentali per i quali emerge la necessità di questa espansine sono:

  • la necessità di una memoria non volatile con un minimo di capacità
  • il fatto che l'arduino non fornisce un RTS
    giusto?

Esatto.

Tieni cmq conto che il DS1307, come ti ho detto, da un tempo in secondi, non in decimi come da te richiesto. Io personalmente non conosco RTS che diano tale intervallo.

Capisco, ma se il microcontrollore lavora a frequenze elevate quindi è possibile programmare con l'arduino una sorta di timer che mi discretizzi il secondo? Quello che mi servirebbe sarebbe avere è la distanza di tempo tra due impulsi successivi con una risoluzione del decimo di secondo (oltre che il timestamp fornito dal shield).
Comunque risolta la questione timestamp e cronometro per il resto l'hardware arduino uno è in grado di fare questo tipo di acquisizione dell'impulso in tensione di un segnale proveniente da due fili con la funzione di interrupt?

Sì, puoi "attaccare" un interrupt ad un pin e far sì che all'arrivo di un impulso sia eseguito un compito.
Per la risoluzione temporale potresti anche implementare una piccola funzione che conti i millesimi/centesimi/decimi tra 1 secondo e l'altro.

Ricordati che max su un pin di Arduino può arrivare una tensione di 5V ed una corrente di 40 mA.

Se avvessi una tensione maggiore in ingresso posso usare un accoppiatore ottico per ridurla, giusto?
Chiedo scuso per la banalità di alcune mie domande ma sono davvero inespero nell'argomento :slight_smile:

Se avvessi una tensione maggiore in ingresso posso usare un accoppiatore ottico per ridurla, giusto?

si, oppure anche un partitore di tensione, anche se non è così sicuro

Per fare quello che vuoi esiste già un comando chiamato pulseIn(): http://arduino.cc/en/Reference/PulseIn, che ritorna il tempo trascorso tra il passaggio di stato del pin in microsecondi (1/1.000.000 di secondo).
Attenzione: "Works on pulses from 10 microseconds to 3 minutes in length"

in questo modo, hai il tuo valore in microsecondi, leggi l'ora dall'RTC e salvi le info su eeprom. (in realtà la sincronizzazione con l'RTC è più complessa, ma dovresti venirne fuori, ,magari usando un allarme dall'RTC allo scoccare di ogni minuto)

considerando 1kB di eeprom (1024 bytes)
e il salvataggio delle info in questo modo:
giorno(1byte)mese(1byte)anno(ultime 2 cifre)(1byte)ora(1byte)minuti(1byte)secondi(1byte)millisecondiPartenza(2byte)durata(2byte)
totale:10byte
quindi 1024/10=102 registrazioni e 4 byte che avanzano

lesto:
Per fare quello che vuoi esiste già un comando chiamato pulseIn(): http://arduino.cc/en/Reference/PulseIn, che ritorna il tempo trascorso tra il passaggio di stato del pin in microsecondi (1/1.000.000 di secondo).
Attenzione: "Works on pulses from 10 microseconds to 3 minutes in length"

in questo modo, hai il tuo valore in microsecondi, leggi l'ora dall'RTC e salvi le info su eeprom. (in realtà la sincronizzazione con l'RTC è più complessa, ma dovresti venirne fuori, ,magari usando un allarme dall'RTC allo scoccare di ogni minuto)

Ho capito, vedo dal link che è una funzione che vale solo se ho impulsi che cadono a distanza non maggiore di 3 minuti l'uno dall'altro, altrimenti il timer si disativa. Il problema è che nel mio caso spesso e volentieri cadono a tempi maggiori l'uno dall'altro.

Ti ringrazio.

Ciao,

potresti utilizzare una soluzione mista per i tempi, ossia utilizzare l'ora del RTC e i millis (o micros se hai bisogno di maggior risoluzione) per conteggiare gli intervalli fra impulsi.

Mi spiego meglio:

All'avvio (nella funzione di setup) leggi ata e ora e le memorizzi nella memoria esterna (SD card, EEPROM, ...)

Per il conteggio del tempo e calcoli sugli impulsi lavori coi millis (millis() da il numero di millisecondi da quando si e' avviato il programma in esecuzione)

Puoi fare i calcoli direttamente sull'ATmega dell'ora di ogni impulso, ma lo puoi anche fare dopo avendo uno start time ed il valore dei millis.

Attento all'utilizzo della funzione millis(), non va utilizzata all'interno della funzione di interrupt in quando non funziona (millis si basa su interrupt di un timer, ma questi vengono disabilitati per il periodo di tempo dell'esecuzione della funzione di interrupt in essere ISR).

Ciao,
Marco.

Ho capito, vedo dal link che è una funzione che vale solo se ho impulsi che cadono a distanza non maggiore di 3 minuti l'uno dall'altro, altrimenti il timer si disativa. Il problema è che nel mio caso spesso e volentieri cadono a tempi maggiori l'uno dall'altro.

no problema: quando si disattiva ritorna il valore 0. Semplicemente fai ripartire il comando :slight_smile: ma poi ti devi ricordare di sommare il valore di timeout (per essere sicuro di tale valore lo puoi impostare tu, magari ogni minuto, ovvero 6000000) moltiplicato per il numero di volte che sei andato in timeout!

Ah, ricordati che lavorando con i micros(), un unsigned long al massimo può contenere circa 60 minuti, quindi PRIMA di fare moltiplicazioni, somme etc, ti conviene convertire i valori in millis(). Un unsigned long usato con i millis va in overflow ogni circa 55 giorni.

Proprio per questi motivi è importante che l'RTC aggiorni la data più spesso dell'overflow(sopratutto dei micros), altrimenti avrai inconsistenza dei dati.

Perfetto è proprio quello a cui stavo pensando dopo le varie indicazioni ricevute.

Questa è la parte che mi causa problemi concettualmente. Se è l'impulso che arriva genera l'interrupt e la funzione millis() viene disabilitata come faccio a poterla usare? La posso riprendere (salvando il suo valore in una variabile) subito dopo che l'interrupt smette di essere eseguito? In questa maniera però per in accuratezza, se però il tutto si svolge nell'arco di un decimo di secondo è accettabile.

la millis la puoi usare all'interno di un interrupt, semplicemente non viene aggiornato il suo valore.
Ecco perchè bisogna scrivere interrupt veloci: se no il timer sfasa troppo (ed ecco anche perchè sui tempi lunghi si usa un RTC)

Ti ringrazio Lesto, tutto molto chiaro

Ciao,

volevo dirti che per quanto riguarda la funzione millis() nella funzione di interrupt ma non puoi utilizzarla per farci calcoli sul tempo trascorso, ma la puoi utilizzare per avere il tempo di quando si e' avuto l'interrupt.

Per conteggiare l'intervallo fra gli impulsi puoi utilizzare una variabile in cui carichi il tempo in millis dell'interrupt ed una variabile che ti indica se vi e' stato o meno un interrupt

void my_interrupt_handler()
{
interrupt_on = 1 ;

interrupt_time = millis(); // da il valore di millis al momento dell'interrupt (gli interrupt dei timer sono arrestati al momento dell'interrupt = millis non conteggia piu')

// gestione delle altre eventuali variabili necessarie ...
}

Poi nel loop inserisci la logica di gestione

if(interrupt_on){
// logica di gestione dei tempi di interrupt per gli impulsi
...
}

La variabile interrupt_time deve essere dichiarata come volatile (lo inserigli all'inizio dello sketch)
volatile unsigned long interrupt_time = 0 ; // interrupt

Buon lavoro,
Marco.

A dire il vero la funzione millis() può funzionare correttamente all'interno di un interrupt, purchè gi interrupt siano ri-abilitati nella funzione che gestisce l'interrupt con l'utilizzo della funzione interrupts().

int pin = 2; //pin corrispondente all'interrupt 0

void setup()
{
//eventuale codice di setup per seriale o lcd
attachInterrupt(0, interpt, FALLING); // è stato posto un pulsante sul pin 2 collegato a massa da una parte ed in pull-up con una resistenza
// dall'altra
}

void loop() {
...
lcd.print(millis()/1000);
...
}

void interpt()
{
interrupts();
while (digitalRead(pin) == LOW);
}

Premendo il pulsante si entra nella routine di servizio interpt() dove vengono riabilitati gli interrupts e si resta in attesa che il pulsante venga rilasciato.
La funzione millis() avrà fatto il suo lavoro correttamente e l'output sulla seriale o lcd ne darà conferma.

Ciao a tutti
Giovanni

A dire il vero la funzione millis() può funzionare correttamente all'interno di un interrupt, purchè gi interrupt siano ri-abilitati nella funzione che gestisce l'interrupt con l'utilizzo della funzione interrupts().

Se c'è necessità di mantenere gli interrupt abilitati durante l'esecuzione della funzione la cosa più corretta è staccare la funzione dall'interrupt abilitare globalmente gli interrupt ed all'uscita meglio dopo un ciclo del loop riattaccare la funzione all'interrupt. Diversamente abilitare gli interrupt globalmente dentro la funzione si rischia che un'altro interrupt esegua la funzione che si sta già eseguendo e prima di ritornale ad eseguire il loop ci sono di mezzo più di un return.

Ciao.

non vedo il problema di più di un return prima di riatornare al codice. E' il sistema usato dai PC normali in cui la recursività degli interrupt è cosa normale.. Il vero problema è se l'interrupt viene lanciato più spesso di quanto il suo codice venga eseguito.

lesto:
non vedo il problema di più di un return prima di riatornare al codice. E' il sistema usato dai PC normali in cui la recursività degli interrupt è cosa normale.. Il vero problema è se l'interrupt viene lanciato più spesso di quanto il suo codice venga eseguito.

L'interrupt nel mio caso serve semplicemente per dire al controllore di scrivere su una memoria non volatile il tempo dell'evento e risolvere due o tre espressioni algebriche. Sicuramente nella peggiore delle ipotesi il successivo interrupt non può arrivare prima di tre secondi.
Scusatemi, non ho esperienza sulla programmazione delle funzioni di interrupt, mi potete consigliare una guida o una discussione che spieghi ciò che mi serve? Ho ordinato l'arduino ed il shield con microsd e RTS, ci siamo quasi :slight_smile:

Buongiorno a tutti, sono riuscito con successo a fare un programma in grado di soddisfare i requisiti illustrati ad inizio topic. Vi scrivo perchè è sorto un problema di diversa natura, riguardante lo stesso sketch, ma mi sembrava comunque il caso di postarlo in questo stesso topic anzichè aprirne uno nuovo.
Ho integrato la scheda arduino uno con l'adafruit data logger shield, per la gestione dell'ora e per la scrittura dei dati in una scheda sd. Fin qua tutto bene, riesco a gestire il tutto.
Il problema sorge quando vado ad inserire nel mio sketch principale delle righe di codice riguardanti l'acquisizione di alcuni parametri da file (input.txt).
Specifico che questa acquisizione di parametri è verificata funzionare bene tramite un codice che ho fatto girare da solo sul mio arduino e che riporto di seguito.
Anche il programma "madre" è verificato lavorare bene da solo prima dell'inserimento del codice di lettura dei parametri da file (in questo caso i parametri li inizializzo ad un valore fissato in fase dichiarativa)

Riporto di seguito il mio codice della lettura da file dei tre parametri di tipo float (i_ref, res, tol):

#include <SD.h>
#include <stdio.h>
  char intensity[4];
  char resolution[4];
  char tollerance[4];
  float i_ref, res, tol;
  int j;
  char EndFile;
 
File myFile;

void setup()
{
  Serial.begin(9600);
  Serial.print("Initializing SD card...");

   pinMode(10, OUTPUT);
   
  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
     SD.open("input.txt");

  wptread();
 
}

void loop()
{
}

void wptread()
{
      j = 0;
      intensity[j] = myFile.read();
      while (intensity[j] != '\r' && j <=5)
        {
          j++;
          intensity[j] = myFile.read();
        } 
      j = 0;
      resolution[j] = myFile.read();
      while (resolution[j] != '\r' && j <=5)
        {
          j++;
          resolution[j] = myFile.read();
        } 
      j = 0;
      tollerance[j] = myFile.read();
      while (tollerance[j] != '\r' && j <=5)
        {
          j++;
          tollerance[j] = myFile.read();
        } 
      i_ref = atof(&intensity[0]);
      res = atof(&resolution[0]);
      tol = atof(&tollerance[0]);
      Serial.println(i_ref,1);
      Serial.println(res,1);
      Serial.println(tol,1);
}

Il problema sorge quando inserisco il codice quotato nel programma madre (e lo faccio senza errori, la compilazione da esito positivo). Nonostante la classe di setup del programma completo lavori bene come atteso, il void loop mi da dei risultati sbagliati, nella sostanza mi restituisce dei risultati di alcune equazioni nulli quando invece dovrebbe venire un numero non nullo (come verificato dall'esecuzione senza il codice quotato).

Ora la mia domanda è: non è che allungando il programma princiale sto caricando troppe variabili e l'arduino non riesce a gestirle per limitazioni fisiche di memoria?

supercolli:
Il problema sorge quando inserisco il codice quotato nel programma madre (e lo faccio senza errori, la compilazione da esito positivo).

ciò vuol dire che non ci sono errori sintattici, ma ci possono essere errori logici.

supercolli:
Nonostante la classe di setup del programma completo lavori bene come atteso, il void loop mi da dei risultati sbagliati, nella sostanza mi restituisce dei risultati di alcune equazioni nulli quando invece dovrebbe venire un numero non nullo (come verificato dall'esecuzione senza il codice quotato).

codice?

supercolli:
Ora la mia domanda è: non è che allungando il programma princiale sto caricando troppe variabili e l'arduino non riesce a gestirle per limitazioni fisiche di memoria?

possibile, esistono metodi per vedere la ram disponibile, però inizia a postare il codice completo e vediamo come conviene agire