(Funk-)Echtzeituhr-Modul mit I2C-Schnittstelle

Beschreibung

Bei vielen Anwendungen ist die Information über die aktuelle Zeit hilfreich oder notwendig. Sie sollte möglichst genau sein, am besten keines manuellen Eingriffs (Einstellung) bedürfen und ab Start sofort verfügbar sein. Im hier vorgestellten Projekt sorgt eine Funkuhr für eine stets genaue Zeit und den Komfort einer automatischen Einstellung.

Der Kern der Schaltung ist ein ATmega-Mikrocontroller. Zum Einsatz kommt hier ein ATmega168; ein ATmega88 oder ein ATmega8 (evtl. mit kleinen Software-Anpassungen) sollten aber auch ausreichend sein. Neben dem Controller befinden sich noch ein Echtzeit-IC (DS1307+) mit Pufferbatterie und ein DCF77-Modul (zum Beispiel bei Pollin Electronic, reichelt elektronik oder Conrad Electronic erhältlich) auf der Platine.
Der Controller fragt regelmäßig den Echtzeit-Chip und das DCF-Modul ab. Die entsprechenden Funktionen für den Zugriff auf das DCF77-Modul sind hier beschrieben. Mit der über das DCF-Modul bestimmten Zeit wird der Echtzeit-Chip aktualisiert, der Echtzeit-Chip in Verbindung mit der Puffer-Batterie hingegen bietet bereits ab Start des Moduls die aktuelle Zeit. Über eine I2C-Schnittstelle (zwei Ports auf der Platine) ist dann von außen jederzeit die aktuelle Zeit abfragbar. Über den I2C-Bus ist es aber auch möglich, das DCF-Modul abzuschalten oder die Zeit manuell einzustellen.
Die Software bietet daneben noch die Möglichkeit der Ansteuerung eines LCD oder von 7-Segment-Anzeigen zur direkten Darstellung der Zeit und des Datums.

rtc_platine_v0.2.jpg rtc_platine_ueberblick.png

Die Abbildungen zeigen die Platine ohne aufgestecktes DCF-Modul und deren Grundstruktur.

Schaltung

Die Schaltung des RTC-Moduls ist in der folgenden Abbildung dargestellt.

realtime_clock.png

Sie enthält unter anderem die Beschaltung für den Power-On-Reset inklusive Reset-Taster (SW1), einen Quarzoszillator, Stiftleisten und Jumper, um Controller-Ports nach außen zu führen bzw. die Beschaltung zu konfigurieren. Die Stiftleisten und Jumper im Überblick: Die Schaltung ist für eine Versorgungsspannung von 5 V ausgelegt, das DCF77-Modul wird von der Platine über einen Low-Drop Regler LM3940 mit 3,3 V versorgt. Die Versorgung erfolgt über die I2C-Anschlüsse P2 oder P3. Für die Programmierung des Controllers steht der ISP-Anschluss P5 zur Verfügung.

Hinweis: Der Controller liest das Ausgangssignal des DCF77-Moduls über einen Komparator-Eingang (auf A/D-Wandler-Eingänge gemultiplext) ein, um die Pegelwandlung durchzuführen und gleichzeitig ein wenig mehr Robustheit gegenüber Störungen des DCF77-Ausgangssignals zu gewährleisten als dies bei einem normalen Controller-Eingang der Fall wäre.

Quellen

Verfügbar sind die Schaltplanquellen für KiCAD:
Im folgenden Archiv sind alle Quellen für die Software auf dem ATmega-Controller enthalten:

rtc_control0.3.tgz

Eine Doxygen-Dokumentation der Funktionen ist hier zu finden.

Die Quellen sind als AVR-Projekt für die Entwicklungsumgebung Code::Blocks organisiert. In den Projekteinstellungen ist derzeit als Zielplattform ein ATmega168 mit 20 MHz Taktfrequenz eingestellt. Bei abweichendem Takt sind die Definitionen F_CPU in den Projekteinstellungen bzw. in main.c und die Definition RTC_OCR0A in der Datei rtc_control.h entsprechend anzupassen.

Die Funktion des Moduls kann über Debug-Ausgaben auf einem LCD am Port P4 kontrolliert werden. Zur Zeit sind die Quellen an die Verwendung eines LCD mit 4 Zeilen und je 20 Zeichen angepasst.
Eine weitere Möglichkeit besteht in der Ansteuerung von sechs 7-Segment-Anzeigen mit Hilfe von drei SAA1064 ICs, um diese Platine für sich alleine schon als Uhr benutzen zu können. Diese Ansteuerung der drei SAA1064 erfolgt über den gemeinsamen I2C-Bus. Diese Funktion kann der Nutzer in der Datei rtc_control.h mittels der Definition RTC_SAA1064_OUTPUT an- oder abschalten.

Die Quellen können selbstverständlich auch (manuell) in eine andere Entwicklungsumgebung (zum Beispiel AVR-Studio) eingebunden werden. Ein Makefile ist ebenfalls in den Quellen enthalten, mit dem sich die Quellen zumindest unter Linux auch ohne Code::Blocks übersetzen lassen sollten. Voraussetzung ist natürlich eine installierte avr-gcc-Umgebung, die alle Linux-Distributionen als fertiges Paket bereits mitbringen sollten.

Für die Konfiguration des Systems wichtige Dateien sind:

I2C-Interface

Die Kommunikation mit dem RTC-Modul erfolgt über eine I2C-Schnittstelle. Das RTC-Modul arbeitet dabei als Slave und simuliert die in der folgenden Abbildung dargestellte Registerstruktur.

rtc_platine_register.png

Vor dem Lese- oder Schreibzugriff ist immer das Adressregister zu setzen. Das RTC-Modul inkrementiert die Registeradresse bei fortgesetzten Lese- oder Schreibzugriffen automatisch.

Das Auslesen der Zeit kann (bei Verwendung der I2C-Funktionen der im Projekt ebenfalls enthaltenen Dateien i2c.c und i2c.h) zum Beispiel folgendermaßen erfolgen:

#define I2C_RTC_SLAVE_ADDRESS 0xaa //Slave Address for RTC on I2C Bus (including Write Bit)
                                   //(7 Bit address + Write Bit)

uint8_t rtc_data[I2C_MASTER_BUFFER_LENGTH];
uint8_t i2c_retry_counter;
real_time_data real_time;          //see dcf77.h
uint8_t return_value;


///get current time and date:
/// 1. set register address
/// 2. read data
/// 3. extract time/date information


//set address///////////////////////////////////////////////////

rtc_data[0] = I2C_RTC_SLAVE_ADDRESS; //SLA+W
rtc_data[1] = 0x00;

//I2C communication
i2c_retry_counter = I2C_MAX_RETRYS;
return_value = I2C_IO_ERROR;

while (return_value != I2C_IO_SUCC && i2c_retry_counter > 0) {
  return_value = i2c_master_data_io(rtc_data, 2, FALSE);
  i2c_retry_counter--;
  _delay_us(I2C_RETRY_WAIT_US);
}


//read from data registers if setting address was successful////

if (return_value == I2C_IO_SUCC) {

  rtc_data[0] = I2C_RTC_SLAVE_ADDRESS | 0x01; //SLA+R

  //I2C communication
  i2c_retry_counter = I2C_MAX_RETRYS;
  return_value = I2C_IO_ERROR;

  while (return_value != I2C_IO_SUCC && i2c_retry_counter > 0) {
    return_value = i2c_master_data_io(rtc_data, 7, FALSE);
    i2c_retry_counter--;
    _delay_us(I2C_RETRY_WAIT_US);
  }

}


//extract time/date information/////////////////////////////////

if (return_value == I2C_IO_SUCC) {

  //extract real time from I2C registers
  real_time.current_second = rtc_data[2] & 0x3f;
  real_time.current_minute = (rtc_data[2] >> 6) & 0x03;
  real_time.current_minute = real_time.current_minute | ((rtc_data[3] & 0x0f) << 2);
  real_time.current_month = (rtc_data[3] >> 4) & 0x0f;
  real_time.current_hour = rtc_data[4] & 0x1f;
  real_time.current_weekday = (rtc_data[4] >> 5) & 0x07;
  real_time.current_day = rtc_data[5] & 0x1f;
  real_time.current_year = rtc_data[6] & 0x7f;

}


Die Anzahl der Kommunikationsversuche bei belegtem Bus ist durch die Definition I2C_MAX_RETRYS spezifiziert. Die Wartezeit zwischen zwei Versuchen bestimmt I2C_RETRY_WAIT_US (Mikrosekunden). Die aktuellen Zeitdaten befinden sich nach erfolgreicher Abarbeitung in der Struktur real_time.

Fehlerquellen

Folgende potentielle Fehlerquellen sind beim Benutzen des RTC-Moduls zu beachten:

Lizenz

Alle hier veröffentlichten Quellen stehen unter der LGPLv3.

Alle hier veröffentlichten Schaltungen können natürlich nach Belieben nachgebaut oder verändert und veröffentlicht werden.
Zu beachten ist dabei allerdings, dass dies ein Hobby-Projekt ist und keinerlei Gewährleistung für Fehlerfreiheit oder gar eine Haftung für eventuelle Schäden übernommen werden kann.
Die Schaltungen sollen eine Anregung darstellen und sie nachbauen und in Betrieb nehmen sollte nur der, der weiß, was er tut..

Versionsinfo (Webseite)

Anregungen oder weitere Informationen

Für Anregungen oder die Beantwortung von Fragen zum Projekt steht ein Forum zur Verfügung.