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...
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.
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
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
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.
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.
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.
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à
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()...
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?