Devo misurare la frequenza dell'onda quadra che può variare tra 0 e 1 MHz e ha una risoluzione di 0,25 Hz.
Non ho ancora deciso quale controller, ma molto probabilmente sarà uno dei 20 pin di Attiny.
Normalmente come misurare i segnali a bassa frequenza sarebbe utilizzando due timer uno configurato in modalità di acquisizione timer per interrompere su diciamo i fronti di salita del segnale esterno e un altro timer impostato per interrompere ogni secondo, quindi il valore del registro contatore dei timer precedenti dopo 1 secondo sarebbe uguale alla frequenza del segnale.
Tuttavia questo metodo ovviamente non funzionerà per catturare segnali che vanno da 0 a 1 MHz con una risoluzione di 0,25 Hz per questo avrei bisogno di un contatore a 22 bit (i micro AFAIK a 8 bit hanno solo contatori a 8/16 bit).
Un'idea che avevo era quella di dividere il segnale prima di applicarlo al micro ma questo sarebbe impraticabile in quanto il segnale dovrebbe essere diviso per 61 quindi la frequenza potrebbe essere aggiornata solo ogni 61 secondi dove vorrei che fosse ogni pochi secondi .
Esiste un altro metodo che consentirebbe di aggiornare la frequenza ogni 4 secondi?
Aggiornare:
La soluzione più semplice consiste nell'utilizzare un interrupt esterno o una cattura con timer per interrompere sul fronte di salita del segnale e ottenere l' isr
incremento di una variabile di tipo long int
. Leggi la variabile ogni 4 secondi (per consentire frequenze fino a 0,25Hz da misurare).
Aggiornamento 2:
Come sottolineato da JustJeff, una MCU a 8 bit non sarà in grado di tenere il passo con un segnale da 1 MHz in modo da escludere l'interruzione su ogni fronte di salita e incrementare un long int
...
Ho scelto il metodo suggerito da Timororr. Una volta che riesco a implementarlo, invio post e condivido i risultati. Grazie a tutti per i vostri suggerimenti.
Rapporto sullo stato di avanzamento:
Iv ha iniziato a testare alcune delle idee presentate qui. In primo luogo ho provato il codice di Vicatcu. Si è verificato un evidente problema di TCNT1 non risolto dopo che la frequenza è stata calcolata, non un grosso problema ...
Poi ho notato durante il debug del codice che circa ogni 2 a 7 volte la frequenza è stata calcolata il conteggio di overflow del timer 1 (il timer configurato per contare gli eventi esterni) sarebbe breve di due. Ho messo questo alla latenza del Timer 0 ISR e ho deciso di spostare il blocco dell'istruzione if dall'ISR al principale (vedi frammento di seguito) e ho appena impostato un flag nell'ISR. Alcuni debug hanno mostrato che la prima misurazione sarebbe ok, ma con ogni lettura successiva il conteggio di overflow del Timer 1 sarebbe finito di 2. che non posso spiegare - mi sarei aspettato che non fosse finita ...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Successivamente ho deciso di provare a implementare il suggerimento di Timrorrs. Per generare l'intervallo necessario (di circa 15 ms tra ogni interruzione timer_isr) dovrei mettere in cascata i due timer a 8 bit poiché l'unico timer a 16 bit su Atmega16 viene utilizzato per catturare i fronti di salita del segnale esterno.
Ho pensato che questa soluzione avrebbe funzionato e sarebbe stata molto più efficiente in quanto la maggior parte delle spese generali è stata spostata sui timer e solo un breve tempo rimanente per la CPU. Tuttavia non era accurato come avevo sperato, le misurazioni si spostavano avanti e indietro di circa 70Hz, cosa che non mi dispiacerebbe alle alte frequenze, ma sicuramente non è accettabile alle frequenze più basse. Non ho trascorso molto tempo ad analizzare il problema, ma immagino che la disposizione a cascata del timer non sia così accurata in quanto ho implementato una disposizione simile alla proposta di timrorrs su un controller 8051 molto più lento che aveva 2 timer a 16 bit e i risultati erano abbastanza precisi.
Sono ora tornato al suggerimento di Vicatcu, ma ho spostato il calcolo della frequenza in Timer 0 isr (vedi frammento di seguito ), questo codice ha prodotto misurazioni coerenti e ragionevolmente accurate. Con un po 'di precisione, la precisione della calibrazione dovrebbe essere di circa +/- 10Hz.
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER0_PRELOAD; //Reload timer for 1KHz overflow rate
if(task_timer_ms > 0)
{
task_timer_ms--;
}
else
{
frequency_hz = 1.0 * TCNT1;
TCNT1 = 0;
frequency_hz += global_num_overflows * 65536.0;
global_num_overflows = 0;
frequency_hz /= (TASK_PERIOD_MS / 1000.0);
task_timer_ms = TASK_PERIOD_MS;
}
}
Se qualcuno ha altri suggerimenti, sono aperto a loro, ma preferisco non usare i range ... Inoltre, non sono più intenzionato a ottenere una risoluzione dello 0,25%, non sembra molto utile il livello di precisione che ho al momento .