Ciao
Tricka90,
volevo aggiungere un suggerimento a quelli, peraltro ottimi, che ti hanno già dato i "colleghi di forum".
Quando vuoi ottenere una frequenza accurata a lungo termine ma:
1) la frequenza dell'oscillatore che hai, pur essendo stabile, non ha il valore nominale esatto che ti aspetti; e/o
2) la frequenza dell'oscillatore, divisa da prescaler e contatori, non è un multiplo della frequenza che vuoi (e quindi non puoi effettuare una divisione intera per ottenerla);
ALLORA puoi utilizzare vantaggiosamente un "
trimmer software" (non ne conosco un nome ufficiale, ma io l'avevo battezzato così).
Facciamo un esempio:
hai una frequenza di oscillatore
Fosc di circa 4 MHz e vuoi ottenere una frequenza, accurata a lungo termine, di 1 Hz.
La tua frequenza di 4 MHz passa attraverso un primo divisore per 4, un prescaler e un contatore autoricaricante. Questa catena divide la frequenza per (supponiamo)

, cioè per 262144.
Quindi avrai a disposizione una frequenza che ti genera un interrupt

volte al secondo.
Da lì, per tirare fuori 1 Hz, devi dividere per 15,258789... che non è un numero intero, e quindi l'operazione non è così banale.
Qui viene in aiuto il trimmer software.
Definiamo una variabile intera senza segno da, supponiamo, 32 bit. Cioè un
unsigned long, sulla maggior parte delle macchine. La chiameremo
contatore.
Definiamo una unità di tempo (che in omaggio al tuo nickname chiamerò
trick 
), dove

, cioè

.
trick fanno 1 secondo, e dovrebbe essere ormai chiaro dove voglio andare a parare.
Se ad ogni interrupt sommiamo alla variabile
contatore il periodo, espresso in
trick, della frequenza di 15,258789... Hz, ecco che
contatore andrà in overflow una volta al secondo.
Il periodo della frequenza di 15,258789... Hz espresso in
trick è:

che arrotonderemo all'intero più vicino, cioè 281 474 977.
E adesso vediamo la procedura di interrupt:
- Codice: Seleziona tutto
#define SOFTWARE_TRIMMER_VAL 281474977
...
unsigned long contatore = 0;
unsigned long trimmer = SOFTWARE_TRIMMER_VAL;
...
void interrupt()
{
if (... /* È l'interrupt dell'auto-reload timer? */)
{
/* Sì! Incrementiamo il contatore: */
contatore += trimmer;
if (contatore < trimmer)
{
/* Overflow contatore: vuol dire che è passato 1 secondo! */
/* Eseguiamo le operazioni relative, per esempio aggiorniamo i secondi: */
...
}
/* Reset interrupt flag: */
...
}
}
Visto che facile?
Attenzione: l'aggiornamento dei secondi ha un leggero jitter.
Infatti a volte la procedura di interrupt aggiorna i secondi dopo 15 chiamate, altre volte dopo 16. Questo è necessario per mantenere un'alta accuratezza a lungo termine.
Possiamo calcolare anche la granularità del trimmer. In questo caso, aumentandolo o diminuendolo di una unità alla volta, la granularità è di circa una parte su 281 474 977, cioè circa 3,6 parti per miliardo. Tradotto in rapporto di tempi, circa un decimo di secondo all'anno. Se è ancora troppo, bisogna aumentare il numero di bit del contatore.
I quarzi hanno tolleranze generalmente misurate in parti per milione.
Per esempio, un quarzo da 4 MHz con una tolleranza di 20 p.p.m. potrebbe oscillare a 4 000 080 Hz anziché alla frequenza nominale di 4 000 000 Hz. Tralasciando derive per temperatura e simili, in questo esempio ciò darebbe un errore sistematico (un anticipo) di circa 53 secondi al mese.
Ma con il trimmer software possiamo compensare questo errore. Come?
Basta ricalcolare il valore del trimmer:

la cui parte intera ha una differenza di 5630
trick in meno rispetto al valore nominale calcolato in precedenza.
Se hai un buon frequenzimetro puoi misurare la frequenza dell'oscillatore e fare il calcolo a tavolino.
Ma si può anche pensare ad un aggiornamento "dinamico": ogni volta che regoli l'orologio, questo oltre a rimettere in sincronia l'orario potrebbe calcolare l'errore sul tempo trascorso dalle regolazioni precedenti. Quindi potrebbe
ricalcolare il valore del trimmer per minimizzare l'errore futuro.
E le temporizzazioni corte ???
Abbiamo detto che questo trimmer software mantiene una buona accuratezza a lungo termine, ma che a breve termine c'è un certo jitter (non tutti i secondi sono lunghi uguali).
Per temporizzazioni corte sarebbe bene avere una base dei tempi con periodo il più possibile vicino a tempi "standard" (1 ms o 10 ms per esempio). Per ottenere questi tempi si può scegliere un opportuno fattore di divisione del timer auto-ricaricante.
In questo modo si possono ottenere temporizzazioni corte ripetibili e senza jitter, se pure affette da un errore sistematico dovuto al fattore di divisione (che deve essere intero e fisso) e alla tolleranza del quarzo.
Per temporizzazioni lunghe, anziché far avanzare i secondi, per esempio, ogni 100 scadenze di un timer da 10 ms, si può applicare a questa base dei tempi da 10 ms il metodo già visto del trimmer software, ottenendo in questo modo il "meglio dei due mondi":
- temporizzazioni corte ripetibili e senza jitter;
- temporizzazioni lunghe accurate sul lungo periodo.
Buona notte a tutti!
