Problema con frequenzimetro fatto con ARDUINODUE

Salve ho realizzato un frequenzimetro con ARDUINODUE fatto con il seguente codice Libero - Community - I siti personali
L'ho adattato per funzionare con 6 display sette seg. col seguente codice modificato da me.
Ho notato nel codice originale che ci sono degli errori nell' algoritmo riscontrato caricando e mandando in esecuzione il codice.
Ad esempio la riga "if (pulseCount == 1000)" si trova dentro a loop(){} ed invece dovrebbe trovarsi dentro ad una chiamata ogni 1msec altrimenti non si dovrebbe visualizzare la frequenza.
Con la modifica apportata nel mio codice ho constatato che non visualizzo un bel niente.
Per favore aiutatemi.

frequenzimetro.ino (9.62 KB)

Nella loop hai troppo poco codice.
Hai creato ad esempio la funzione "millisec" e poi non la chiami MAI nel loop

Leggi bene il codice la "millisec" è chiamata in Timer3.attachInterrupt(millisec); ogni 1msec.

Vero. Sei sicuro venga chiamata ?
Hai provato a levare un pò di codice e nella chiama a fare solo una semplice serial.print ?

Inoltre, di solito il codice di una routine interrupt dovrebbe essere il più breve possibile. Tu in quella millisec fai conversioni/scritture su pin/etc molto dispendiose.

Comunque il codice a quel primo tuo post, su digilander, perchè dici che non dovrebbe funzionare ? Lo hai provato ? proprio la pulseCount che arriva a 1000 è la base del conteggio

Si ho provato la millisec() solo con la funzione converti().
Nella mia versione c'è solo converti() che visualizza le cifre.
Mi succcede che se metto solo conveverti() con prima la formula di conversione per ottenere la "frequency" ottengo
un countdown da un valore molto alto fino alla frequenza del generatore di funzioni ossia in questo caso 32Hz ed una volta
arrivato a quel valore riparte al valore molto alto per ritornare a fare il coutdown.
Ciò succede solo con pulseCount = 0 ed alert = 0 nella funzione loop(){}.

Perché non usi pulseIn()?

Inoltre, tutto il codice mi sembra pesante... I pin andrebbero scelti sapientemente, per sfruttare porte intere. In questo modo, ad esempio, con Arduino Uno (328p) ho fatto la conversione a 7 segmenti semplicemente così:

void displ()
{
switch(n)      // Anodo comune
               // D 76543210
  {            //  dpGFEDCBA (0: segmento acceso)
  case 0: {PORTD|=0b01000000;  // Spegne i segmenti a 1
           PORTD&=0b11000000;} // Accende i segmenti a 0
  break;
  case 1: {PORTD|=0b01111001;
           PORTD&=0b11111001;}
  break;
  case 2: {PORTD|=0b00100100;
           PORTD&=0b10100100;}
  break;
  case 3: {PORTD|=0b00110000;
           PORTD&=0b10110000;}
  break;
  case 4: {PORTD|=0b00011001;
           PORTD&=0b10011001;}
  break;
  case 5: {PORTD|=0b00010010;
           PORTD&=0b10010010;}
  break;
  case 6: {PORTD|=0b00000010;
           PORTD&=0b10000010;}
  break;
  case 7: {PORTD|=0b01111000;
           PORTD&=0b11111000;}
  break;
  case 8: {PORTD|=0b00000000;
           PORTD&=0b10000000;}
  break;
  case 9: {PORTD|=0b00010000;
           PORTD&=0b10010000;}
  break;
  }
}

Considera, poi, che i valori che scriviamo vengono già convertiti in binario! Scrivendo, ad esempio, 9 sulla porta D con PORTD&=9+0xF0; PORTD|=9; già hai scritto 9 in binario sui 4 LSB (bit 0, 1, 2, 3). :slight_smile:

PORTD&=9+0xF0: moltiplico bit per bit il contenuto della porta D con 11110000 (0xF0: 1111=F e 0000=0) più 9, che è il valore che mi interessa. In questo modo, i bit a 0 del valore 9 andranno a 0, mentre gli altri resteranno invariati.

PORT|=9: sommo bit per bit il contenuto della porta D con il valore 9. In questo modo, i bit a 1 del valore 9 verranno posti a 1.

Datman:
Inoltre, tutto il codice mi sembra pesante... I pin andrebbero scelti sapientemente, per sfruttare porte intere. In questo modo, ad esempio, con Arduino Uno ...

Guarda che su Arduino DUE (SAM) la cosa è piuttosto più complessa e NON così banale come è su MCU AVR !

Guglielmo

:frowning:

:smiley: :smiley: :smiley: ... ohi, nulla di impossibile eh, ma sicuramente più articolato ...

There are 5 ports, A/B/C/D/E, each 32 bit, mapped to the pins as documented in variant.cpp.

There are several registers for each port that read and write it, and each pin can
be updated atomically so no special interrupt handling is needed:

PIO_PER - write 1's here to override other peripherals and allow GPIO use for pins
PIO_OER - write 1's here to set pins as OUTPUT
PIO_ODR - write 1's here to set pins as INPUT

  • PIO_SODR - write 1's here to set output pins HIGH
  • PIO_CODR - write 1's here to set output pins LOW
  • PIO_PDSR - read's actual state of the pins on the port.
    PIO_PUDR - write 1's here to switch off internal pull-up for pins
    PIO_PUER - write 1's here to switch on internal pull-up for pins

There are other registers to do with input filtering, interrupts, etc etc,
but these are the main ones.

Altro suggerimento che si trova è:

In fact to ensure the port(s) is enabled its simplest to call pinMode() for each
pin you want to directly manipulate, otherwise you have to configure the PIO
controller in more detail which isn't needed once pinMode() has done its thing.

Guglielmo

Non c'entra nulla con il problema del OP, ma è sempre bene ricordare che sulla DUE c'è anche il problema della corrente fornibile da ogni singolo pin, che varia per gruppi di pin ... per alcuni gruppi massimo 3mA e per altri massimo 15mA ... valori ben lontani dalle MCU AVR. :slight_smile:

QUESTA bella chart in formato A4 aiuta a riconoscere i suddetti pin :wink:

Guglielmo

Ho creato il seguente codice con pulseIn().
Noto che mi da valori del tipo 1, 2,5 ,7 4,2 etc.. pure se la frequenza è impostata a 220Hz.
Perchè?

frequenzimetro.ino (9.25 KB)

Scusate ho modificato un dettaglio del codice è giusto il secondo sorgente.

frequenzimetro.ino (9.25 KB)

C'è qualcosa che non va:
void loop() {
t1 = pulseIn(43, HIGH);
t2 = pulseIn(43, LOW);
}

Si hai ragione dovrei leggere dal pin fisico n 7 ossia pin n 23 della nomenclatura.
Giusto?

Il loop misura solo t1 e t2, non fa altro!

Ho aggiunto pure la formula per ottenere la frequenza partendo da t1 e t2 ossia frequenza=1/(t1+t2); poi aggiungo la converti(frequenza); e mi da tutti i display spenti.
Perchè?

frequenza=1/(t1+t2); e converti(frequenza); stanno in sec(), non in loop()!

Che portata ha questo frequenzimetro?
Ho messo converti frequenza nel loop, e sempre nel loop ho messo il serial.print, funzionicchia, ma da valori sballati di circa un 10%, sbaglio qualcosa?
Grazie

Ecco di seguito il mio sorgente modificato io al posto della funzione per scrivere nella seriale che invia i dati a
monitor ho utilizzato la converti(freq) che invia i dato ad un display 7 seg.
Mi succede che con questo codice visualizzo una serie ci cifre senza significato che lampeggiano ad alta velocita'.
Cosa ho sbagliato?

frequenzimetro.ino (9.26 KB)

come prima cosa ti suggerirei di rallentare un pelo la scrittura sui display, la fai ad ogni iterazione del loop.
Anche solo con i 50Hz la faresti 50 volte al secondo che sarebbe velocissimo per avere senso su un display.

poi potresti calcolare una media in un certo lasso di tempo e magari stampare solo quella, diciamo una volta al secondo.

E tenere traccia dei tempi min e max per vedere se all'interno del tempo di campionamento c'è stata una grossa variazione della frequenza. Magari è normale, magari hai captato dei disturbi...