Go Down

Topic: Frequenzimetro (Read 5 times) previous topic - next topic

BrainBooster

#15
Oct 24, 2012, 10:06 am Last Edit: Oct 24, 2012, 10:16 am by BrainBooster Reason: 1
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:
Code: [Select]


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  :smiley-mr-green:
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.

lesto

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.

2.
Quote
detachInterrupt(0);
  attachInterrupt(0, hitime, HIGH);  //aspettiamo l'evento high
inutile il detach, poichè l'attach va a sostituire la funzione precedente.

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

4.
Code: [Select]
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.

5. perchè elimini 3.00 da freq?
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

BrainBooster

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

lesto

3. 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à
4. 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()...
5. 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.
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

BrainBooster

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?

lesto

no, dico che è la stessa cosa per i tuoi fini, però dico che è meglio accorpare la logica in una sola funzione generica sia per RISING che per FALLING.

così se devi modificare qualcosa in un colpo solo lo fai per entrambi i picchi, evitando stupidi errori di copia-incolla che fanno solo perdere tempo.

poi trovo che la stesura del codice va sempre fatta con un occhio di rigurado al fine che si vuole ottenre, si chiama architettura.
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

BrainBooster

#21
Oct 24, 2012, 04:10 pm Last Edit: Oct 24, 2012, 04:12 pm by BrainBooster Reason: 1
chiaramente hai ragione quando parli di architettura, ma l'avevo detto all'inizo, è solo un primo abbozzo della logica, poi tutto si può migliorare :)
quindi in pseudocodice (anche non pseudo :)  ) tu che faresti?
edit:
... pensavo che se uso l'evento change, poi non devo andare a leggere "a mano" lo stato del pin introducendo ulteriore ritardo?

BrainBooster

Rifatto e semplificato con pinchange interrupt

Code: [Select]

#include <PinChangeInt.h>
volatile unsigned long previousMillis = 0;  //variabile per controllo tempo di scan
long interval = 1000;  //intervallo invio letture
volatile unsigned long hticks=0;
volatile unsigned long  lticks=0;

void setup()
{
  PCintPort::attachInterrupt(2, &quicfunc, CHANGE);
   Serial.begin(9600);
}

void loop()
{
   unsigned long currentMillis = millis(); //prendo il tempo
  if(currentMillis - previousMillis > interval) {  //se è trascorso l'intervallo impostato
   
    previousMillis = currentMillis;  //resetto il tempo
    unsigned long totale=hticks+lticks;
    Serial.print("numero ticks Hi= ");
    Serial.println(hticks);
   
    Serial.print("numero ticks lo= ");
    Serial.println(lticks);
   
     Serial.print("frequenza (Hz)= ");
    Serial.println((totale/2));
     Serial.print("periodo (uS)= ");
    Serial.println(((1.0/hticks)*1000000.0),4);
    hticks=0;
    lticks=0;
  }}
 
 
  void quicfunc() {
  if (digitalRead(2)==HIGH){
  hticks++;}
  else{
  lticks++;
  }
}

legge bene fino a da 1 a 15000hz (limite dell'oscillatore che uso per le prove)
che ne dite?

lesto

il llimite della digitalRead è di 145KHz. Se usi direttamente i registri, sali a 4MHz

more info:
http://www.billporter.info/ready-set-oscillate-the-fastest-way-to-change-arduino-pins/

piccola chicca per il tempo:
Code: [Select]

#define byte MAX 10 //una define non ha bisogno di essere volatile!
volatile byte contatore = 0;
volatile unsigned long lastDuration[MAX];

void quicfunc() {
 lastDuration[contatore<MAX-1?contatore+1:0;] = millis(); //inizializza il tempo di inizio impulso del PROSSIMO segnale
 lastDuration[contatore] = millis()-lastDuration[contatore]; //calcola la durata del segnale attuale
 contatore=contatore<MAX-1?contatore+1:0;

 if (digitalRead(2)==HIGH){
   hticks++;
 }else{
   lticks++;
 }
}


NON È TESTATO! ma in pratica riempo un array delle ultime 10 durate di impulso (però non c'è modo per sapere se il primo impulso è alto o basso, potresti usare un boolenao di quelli che avanzano per indicare se la lettura in posizione 0 è high o low e da li ricavare il resto)
al posto di un int e dei vari if, potresti usare una union, quindi crei una variabile di X bit (e quindi che con l'overflow va a 0 da sola) e sfruttare uno dei bit che ti avanza al posto della booleana.
ottimizzazione spinta, senza andare troppo nel basso livello :)
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Michele Menniti

Lesto, può essere che ciò che dico sia a sproposito in questa discussione, e mi pare peraltro di averlo già scritto. Io e te abbiamo parlato tempo fa di una lib, che a te non piace molto (ma le motivazioni erano troppo ostiche per le mie misere conoscenze), ma con la quale io leggo senza problemi fino a poco più di 7MHz, riferendomi ad un segnale prodotto da un Generatore.
Questa mi affermazione contrasta con la tua (4MHz con digitalRead) o sono due cose differenti?
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

lesto

sì ricodo ma non ricordo la libreria. forse era la digitalWriteFast?

un alibreria non può essere più veloce del semplice codice, per il semplice fatto che si perdono clock nel salto a funzione (e non credo proprio che la cosa sia ottimizzata nel compilatore)

ho controllato, e il link da cui ho preso i dati si parla di SCRITTURA, mentre noi parliamo di lettura.

quindi ho cercato nei menadri del forum: http://arduino.cc/forum/index.php/topic,47106.0.html

Code: [Select]
PIND & B00000010

impiega 2 cicli macchina, quindi frequenza massima di 8MHz con quazo a 16MHz

edit: e comunque non stiamo tenedo conto del coide di contorno, mooolto più pesante.
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Michele Menniti

E' una lib specifica per far funzionare Arduino come frequenzimetro, ho dovuto metttere mano ad alcuni parametri, forte del fatto che avevo strumentazione seria per i confronti, ma alla fine ho letto oltre 7MHz senza problemi, visualizzandoli su un LCD; l'avevo vista sul tuo sito e ti ho chiesto se l'usavi anche tu e mi hai risposto che non ti piaceva, comunque parliamo sempre e comunque di lettura e misurazione, NON di scrittura
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

lesto

ah, ho capito, parli della freqcounter di Martin Nawrath.. in pratica usa il timer0 come contatore, per un intervallo di tempo deciso da timer2.

per questo raggiunge la precisione di una pura lettura digitale (2 clock circa). In effetti quel codice è più ottimizzato dell'uso dell'interrupt, ma ci perdi 2 timer e la possibilità di studiare l'andamento PWM.
certo è un bel esercizio di stile, ma non la userei mai in un codice che debba fare pure altro.
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Michele Menniti

Nel mio caso non deve fare altro, dovrò solo implementare la lettura di un pulsante ed il controllo di due pin, il resto è tutto sano hardware :)
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

lesto

se implementi l'anti-bounce in hardware hai fatto cinquina :)
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Go Up