Frequenzimetro

Devo leggere una frequenza che vedo come un pin che diventa alto. Ho visto pulseIn che però non so se va bene perchè misura la durata dell'impulso, ma se la frequenza và misurata vedendo il numero degli impulsi in un certo tempo con la durata dell'impulso posso farci qualcosa?

Forse potrei fare un interrupt RISING e ogni volta aumentare una variabile, poi in loop() vedere quanto vale la variabile e rapportarla con il tempo trascorso...

Qual'è il modo migliore?

ma a che frequenza massima ?

Puoi fare così:

  • leggi la durata dell'impulso
  • trovi il numero di impulsi facendo 60/durata dell'impulso
  • Calcoli la frequenza facendo 1/numero di impulsi

Il vantaggio del misurare il periodo di una frequenza é che la misura dura poco. Per misurare bene il numero di periodi durante un certo tempo il tempo deve essere molto maggiore del periodo percui una misura dura molto.

Misura con pulseIn(pin, HIGH, timout) e dopo con pulseIn(pin, LOW, timout) il perodo della onda. Non é importante che la misura la fai su 2 onde diverse. La frequenza é 1 fratto il periodo.

Ciao Uwe

volendo ci sono anche due librerie della Teensy che funzionano anche su arduino

http://www.pjrc.com/teensy/td_libs_FreqCount.html

Tenete presente che Arduino non riesce a leggere correttamente frequenze che superino i 7MHz.

se la frequrenza che vuoi misurare non è stabile, allora non si può usare il sistema della pulseIN, ma puoi usare un interrupt in RISING o FALLING

ma per NON STABILE cosa intendi di preciso?

intendo di frequenza variabile nel tempo. Se conti gli impulsi hai la media della frequenza, con la mpulsein hai una lettura a caso

Nessuno strumento normale è in grado di misurare una frequenza variabile, che valore dovrebbe dare? Al più ci sono quelli che hanno il multidisplay e mostrano in contemporanea le ultime 3-4 misure, ma come dato memorizzato. La misurazione viene fatti ad intervalli periodici di durata fissa, la visualizzazione del dato misurato viene aggiornata tra un intervallo e l'altro, se la frequenza è stabile si avrà sempre lo stesso valore, se è instabile o variabile si avrà il valore misurato nell'ultimo intervallo. In genere si imposta la cosiddetta time base (base de tempi), un selettore che fissa gli intervalli a tempi che vanno da 10secondi (per misure di decimi di Hz) a 1sec, 0,1sec, 0,01sec, ecc; maggiore è la frequenza da misurare e minore è l'intervallo di misura da usare.

in teoria, applicando un poco di statistica, dai il numero di valori letti, il tempo, la media, la varianza (ovvero quanto è la variazione di frequenza nell'arco di tempo), la moda...

sono tutti dati utili. Sappiamo benissimo che la perfezione non esiste, e la varianza è quella che a colpo d'occhio ti dice di quanto stai fuori precisone. la moda ti dice che c'è un forte picco ad una determinata frequenza (da non confondere con la media!)

trovo che siano tutti dati utili, per esempio per verificare la qualità di un circuito PWM->analogico (in tal caso la frequenza sarebbero le variazioni dell'uscita analogica dato un PWM fisso in imput, causate dal circuito RC o RLC), oppure per valutare la qualità della 220V, o di un trasfromatore (che se fosse perfeto avrebbe frequenza 0)

però forse sto usando una definizione di frequenza che si discosta dalla definizione elettronica/elettrotecnica

ma facciamo un esempio reale:

motore sotto inverter (= che puo variare i giri 0-2800 minuto)

10 impulsì al giro quindi max frequenza 500 hz

la pulsein che problemi potrebbe avere a visualizzare i giri minuti anche se il motore è in accellerazione ?

pulsein ogni segnale alto/basso: cambiamento dell'ouput più veloce dell'occhio umano.

media degli impulsi nell'ultimo secondo: il valore non è reale ma leggibile da un essere umano.

uso di una pulseIn ogni secondo:possibili false letture per arrotondamento dei float e disturbi esterni

media delle pulseIn di un secondo: complicazione inutile del codice, possibilità di perdere impulsi

media degli impulsi + pulsein: controllo degli impulsi reale, e anche del loro andamento high/low

allora cosa consigli di usare per rcavare la velocita del suddetto motore (no interrupt) ?

senza interrupt? perchè? c'è la attachInterrupt apposta, più facile di così!

comunque considerando la quntità di impulsi, se sono per rotazione, anche con un polling (vedi digitalRead/analogRead) in un loop, con baud il più alto possibile, sei più che a posto

edit: 2 conti; durata analogRead (la più lenta) 200micros. 1secondo = 1.000.000 micros, / 200 = 5000Hz. in RPM (*60) = 300.000 RPM max. Aggiungici il delay per la serial (in 1 secondo a 119200baud scrive 119200/10 = 11920 byte, per semplicità facciamo che in un byte ci fai stare la lettura e poi analizzi il flusse seriale con un editor esadeciomale o con un programma che passa da byte a stringa rappresentate il numero) 11.920Hz di scruitture, ovvero 1.000.000/11920=84micors a carattere

quindi 200micos di analog read + 84 micros di seriale fa 284micros. quindi 3521Hz max, ovvero 211.000 RPM max

direi che il motore fa in tempo a prendere fuoco prima che il nostro (inefficiente) loop conta-rpm vada in crisi. Se hai X segnali per giro, allora gli RPM max sono 211.000/X, ma fai attenzione che un segnale non può durare MENO di 284micros.

se non erro una digitalRead fatta con le CBI e TWI mni pare avvenga in 10micros... a te i conti.

Sappi che io ho testao con successo una UNO (e anche una 2009) a 921600 baud (anche di più mi pare, devo controllare)

Se volete un esempio "porchereccio" di questo tipo di approccio, io sto provando a fare qualcosa di simie... Ecco il codice iniziale per le prove:

volatile unsigned long stateh = 0; //tick alti
volatile unsigned long statel = 0;  //tick bassi
long previousMillis = 0;  //variabile per controllo tempo di scan
unsigned long htime=0;  //tempo High
unsigned long ltime=0;  //tempo Low
unsigned long phtime=0; // tempo precedente High
unsigned long pltime=0; // tempo precedente Low
unsigned long freq=0;  // frequenza
long interval = 1000;  //intervallo invio letture
float offset=3.00;  //offset empirico
void setup()
{
  Serial.begin(9600);  //inizializzo la seriale
  attachInterrupt(0, hitime, HIGH);  //iniziamo da un livello alto
  
}

void loop()
{
  
  unsigned long currentMillis = millis(); //prendo il tempo
  if(currentMillis - previousMillis > interval) {  //se è trascorso l'intervallo impostato
   
    previousMillis = currentMillis;  //reetto il tempo
    //stampo i valori calcolati
  Serial.print("Hi= ");
   Serial.println(stateh);
    Serial.print("lo= ");
   Serial.println(statel);
     Serial.print("frequenza(Hz)= ");
     freq=((stateh + statel)/2)-offset;  //presunto dutycycle a 50%
       Serial.println(freq,9);
   Serial.print("periodo(ms)= ");
   Serial.println(((1.00/freq)*1000000.00),4);
   Serial.print("Htime(uS)= ");  
   Serial.println(htime);
   Serial.print("Ltime(uS)= "); 
   Serial.println(ltime);
  // azzeriamo tutto... si riparte
stateh=0;
statel=0;
freq=0;
phtime=0;
htime=0;
pltime=0;
ltime=0;
}

}

void hitime()  //interrupt per livello alto
{
  ltime=pltime-micros();
  stateh  = stateh + 1;  //incremento numero tick alti
  detachInterrupt(0); 
  attachInterrupt(0, lotime, LOW);  //aspettiamo l'evento low
  phtime=micros(); 
}

void lotime()  //interrupt per livello basso
{
  htime=phtime-micros();
  statel  =statel + 1; //incremento numero tick bassi
  detachInterrupt(0); 
  attachInterrupt(0, hitime, HIGH);  //aspettiamo l'evento high
  pltime=micros();
}

non badate allo stile ma segnalatemi eventuali errorazzi :grin: lo sto provando con un generatore di onda quadra (autocostruito) che mi spara fuori 57Hz collegato sul pin 2 e che il programmillo legge correttamente, ripeto è un inizio... anche se i DSO mi dice che funziona.

  1. tutte le variabili usate (anche in sola lettura) da due intterrupt differenti, o tra "main" e interrupt, sono da dichiarare volatili. Un paio le hai azzeccate, ma ti sei dimenticato di quasi tutti i long.

detachInterrupt(0); attachInterrupt(0, hitime, HIGH); //aspettiamo l'evento high

inutile il detach, poichè l'attach va a sostituire la funzione precedente.

  1. non vedo perchè usaere phtime e pltime; in realtà o usi una o usi l'altra, quindi tanto vale usarne una sola generica "ptime"

phtime=0;
htime=0;
pltime=0;
ltime=0;

perchè azzeri i tempi? questo farà impazzire la prima lettura dopo la print. Tanto l'overflow di queste variabili è legata alla micros(), quindi azzerarle è perfettamente inutile, anzi, come detto prima antiproduttivo. Anche azzerare freq è inutile, poichè non è un contatore ma viene riassegnata quando serve, ma almeno non crea casini.

  1. perchè elimini 3.00 da freq?
  1. hai ragione, in verità ero partito con un altro codice che poi ho adattato sempre più, adesso ho corretto con i tipi giusti.
  2. non lo sapevo :) li ho levati ed effettivamente sembra ininfluente
  3. le leggo entrambi per verifica e futuro calcolo del dutycycle
  4. azzerare i contatori serve per ripulire dai dati della lettura precedente dopo che l'ho stampata, non sembra far ipazzire nulla.
  5. quel tre è un valore di scostamento fisso che ho con la lettura sull'oscilloscopio, dopo vedrò di capire perchè c'è questa differenza di letture
  1. nache ai fini del dutycicle on ti serve avere le due variabili (leggeresti solo l'ultimo dutycicle), ma piuttosto un array degli ultimi X valori, oppure una media. quindi lì usa una variabile sola, che poi al massimo elabori a seconda delle necessità
  2. non ti accorgi che impazzisce perchè non stampi i duticicle. quando fai ltime=pltime-micros(); nell'interrupt, e hai appena azzarato la variabile, ti esce che l'impulso è durato micros()...
  3. se implementi una variabile booleana (ma devi essere certo dello stato del pin alla partenza) o una lettura (ma con twi / cbr o come si chiamano) nell'iterrupt, e hai seguito il mio consiglio al punto 3, scopirrai che i due codici sono perfettamente identici. Quindi anzichè avere 2 interrupt, RAISING e FALLING, ne avrai solo uno di CHANGE, che guarda caso è possibile attivare su qualsiasi pin lavorando a basso livello coi registri.

per adesso non sto facendo la media dei valori ottenuti ma faccio solo una lettura al secondo, quindi quello sarebbe solo l’ultimo dutycycle, poi si vedrà…
quindi alla fine tu dici che è meglio usare un pcint piuttosto che gli interrupt normali?