Nicht immer ist es möglich, einen Quarz zur Taktgenerierung anzuschließen. Benötigt man in diesem Fall dennoch einen genauen Takt, z.B. um per RS232 Daten auszutauschen, hat man das Problem, dass die Frequenz des internen RC-Oszillators nicht passend eingestellt ist. Der Oszillator wird zwar werksseitig kalibriert, jedoch ist findet diese Kalibrierung unter Bedingungen statt, die von denen in der Praxis abweichen (Betriebspannung, Temperatur, etc.).

Hier hilft es nur den Oszillator nach zu justieren. Beim AVR gibt es für diesen Zweck das OSCCAL-Register. Durch Änderung des Registerinhalts kann die Frequenz angepasst werden. Mit Hilfe des folgenden Programms kann man einen passenden Wert für das Register ermitteln. Die Funktionsweise ist recht simpel.

Der initiale Wert des OSCCAL-Registers wird ausgelesen. Danach wird der Wert in einer Schleife von +/-10 ausprobiert. Bisher war diese Spanne ausreichend, da die werksseitige Kalibrierung i.d.R. nur leicht verändert werden muss. Im Zweifelsfall kann die Schleife aber auch über den kompletten Wertebereich laufen. Zu Bedenken ist allerdings, dass nicht jeder Chip den kompletten Bereich verträgt. Es kann sein, dass er sich aufhängt. Da OSCCAL aber nach einem Reset wieder mit der Werkseinstellung geladen wird, ist das kein größeres Problem.

Mit jedem Wert des OSCCAL-Registers wird versucht einen Text per RS232 zu übertragen, z.B. an einen PC. Zusätzlich zu dem Text wird sinnvollerweise auch der aktuelle OSCCAL-Wert gesendet. Passt die Frequenz, ist der Text lesbar. Den angezeigten Wert kann man dann für Folge-Projekte nutzen. Meist ist ein größerer OSCCAL-Wertebereich passend. Hier nimmt einen der mittleren Werte. Das Ganze sieht dann so aus:

Terminalausgabe

Das Programm hierzu ist auch recht einfach:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "GeneralDefinitions.h"
#include "AppVersion.h"
#include "susat.h"

int main(void)
{ int16_t MinOSCCAL = OSCCAL -10;
  int16_t MaxOSCCAL = OSCCAL +10;
  
  if (MinOSCCAL < 0)    MinOSCCAL = 0x00;
  if (MaxOSCCAL > 0xFF) MaxOSCCAL = 0xFF;

  usart_init();
  sei();

  for(;;)	   // main loop
  { for(int16_t i = (MinOSCCAL & 0xFF); i <= (MaxOSCCAL & 0xFF); i++)
    { OSCCAL = i & 0xFF;
      _delay_ms(300);
      usart_puts_P("\n\rThe quick brown fox jumps over the lazy dog! --> ");
      usart_puti(i);
      usart_puts_P(" <---");
    }
    _delay_ms(2000);
  }
}