Auto-calibrazione clock micro ATmega

Uno dei problemi ricorrenti nell’utilizzo di un sistema a micro-controllore è la precisione del clock utilizzato per i moduli Timer: su schede a basso costo frequentemente l’hardware prevedere un oscillatore a bassa frequenza da utilizzare in modalità basso consumo per implementare la funzionalità RTC senza l’utilizzo di un modulo esterno dedicato.

Questo schema di principio obbliga l’utilizzo su target AVR ATmega di una fonte di clock alternativa basata sull’oscillatore RC interno, calibrato dalla fabbrica con precisione 10 %, estremamente suscettibile di variazioni in base alle condizioni di temperatura. Basta dare un’occhiata alla caratteristica riportata sul datasheet del costruttore per rendersi conto a questo punto di quanto poco siano affidabili i timer alimentati dall’oscillatore RC interno.

Nel seguito verrà proposto un metodo per compensare i problemi legati all’utilizzo di tale oscillatore basandosi sul riferimento del quarzo esterno a 32768 Hz, dalle proprietà di tolleranza assoluta e drift in temperatura misurabili in parti per milione (a seconda della qualità) piuttosto che in punti percentuali.

Procedura utilizzata

Il concetto è semplicissimo: utilizzeremo due contatori free running basati sulle due sorgenti di clock, quindi applicheremo il risultato del rapporto di comparazione al registro OSCCAL che permette di agire sulla velocità dell’oscillatore RC interno ottenendo un precisione assoluta nell’ordine dell’1 % su tutto il range di temperatura. Nel seguito viene utilizzato come riferimento il datasheet del microcontrollore ATmega328p (quello comunemente utilizzato sulle schede Arduino UNO) disponibile sul sito del produttore al seguente link.

Sicuramente il modulo timer TC2 è configurato in modalità di clock asincrona, cioè utilizza il quarzo a 32768 Hz collegato sui pin TOSC1 e TOSC2 che fornisce il clock al timer avendo impostato il bit AS2 a 1 nel registro ASSR del microcontrollore ATmega. Stabiliamo un interrupt periodico in modo da calcolare un rapporto intero tra il contaggio di un timer gestito dagli 8 MHz dell’RC interno (ad esempio TC1, che è un timer 16 bit) e TC2. Nel caso più semplice generiamo un interrupt al secondo sull’overflow del timer2 utilizzando il divisore per 128 (frequenza interrupt [Hz] = 32768 [Hz] / 128 / 256 = 1 [Hz]). Nel codice seguente viene anche configurato Timer1 in modalità free-running con un prescaler da 256, essendo questo timer gestito da un clock che dovrebbe aggirarsi sugli 8 MHz ci attendiamo un conteggio in un secondo di 8000000 / 256 = 31250.

// Configurazione Timer 2 - 8bit clock 32768 Hz
TCCR2B |= (1 << CS22) | (1 << CS20);  // prescaler / 128
TIMSK2 |= (1 << TOIE);                // Overflow Interrupt Enable

// Configurazione Timer 1 - 16 bit clock 8 MHz
TCCR1B |= (1 << CS12) | (1 << CS10);

L’algoritmo da eseguire ad ogni secondo è il seguente:

  1. Registra valore timer counter 1
  2. Calcola differenza rispetto al valore registrato al secondo predente
  3. Se differenza > 300 (+1 %) decrementa OSCCAL, altrimenti se differenza < -300 (-1 %) incrementa OSCCAL

Attenzione: L’operazione di incremento / decremento su OSCCAL deve agire solo sui 7 bit meno significativi, il bit più significativo CAL7 stabilisce il range di frequenza, passare da un range all’altro può essere fatto tenendo conto della discontinuità che ne deriva.

Utilizzo in sistemi safety

La procedura descritta è utilizzabile anche in applicazioni che hanno caratteristiche di sicurezza purché la compensazione sul registro OSCCAL sia limitata: in tale tipo di applicazioni è infatti già presente un controllo di congruenza tra due fonti distinte di clock, viene semplicemente aggiunta la calibrazione continua della fonte RC. E’ possibile argomentare che limitando l’azione su OSCCAL viene comunque garantita l’indipendenza delle due fonti di clock, che sarebbe invece compromessa qualora l’algoritmo utilizzato agisse liberamente sul controllo di calibrazione dell’oscillatore RC.

 

Lascia un commento