www.mikrocontroller.net

Forum: Codesammlung DCF-Uhr mit LCD in C


Autor: Johannes M. (johannesm)
Datum:
Angehängte Dateien:

Hallo,
ich bin gerade dabei in die C-Programmierung für Mikrocontroller
einzusteigen und habe mir als erstes Projekt eine DCF77-Uhr ausgewählt.
Sicherlich ist mein Programmcode noch nicht so ausgefeilt wie von
erfahrenen Programmierern, ich könnte mir aber vorstellen das andere
Einsteiger/Anfänger durch meinen primitiven Code durchsteigen :-)
Meine bisherigen Programmierkenntnisse beschränken sich auf etwas
PIC-Assembler (vor ~3 Jahren in der Ausbildung) und einem Semester Java,
also bitte nicht gleich steinigen wenn da ein paar grobe Schnitzer drin
sind.

Also jetzt ein paar Infos zu dem was ich fabriziert habe:
Programmiert im AVR Studio (mit AVR-GCC)
Hardware:
STK500
ATmega16
16MHz Quarz
20x4 Zeichen LCD (HD44780 kompatibel)
DCF77-Empfangsmodul von Conrad (641138) (PullUp-Widerstand 10k zwischen
Anschluß 2 und 4)

Die LCD-Ansteuerung habe ich aus dem Tutorial übernommen, vielen Dank an
den Autor an dieser Stelle
Ansteuerung Hardware/Software entsprechend dem AVR-GCC-Tutorial:
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Nun zu meinem Programm

Abtastung erfolgt mit einem 1ms Trigger.

Die Synchronisation zum DCF-Signal erfolgt durch einen High-Pegel
zwischen 1750-1950ms, das ist das 59Bit einer Minute, welches nicht
abgesenkt wird.

Danach beginnt die Auswertung der Datenbits, dabei habe ich folgende
Werte angenommen
Pegel-Absenkung von 60-140ms entspricht einer logischen 0
Pegel-Absenkung von 160-240ms entspricht einer logischen 1
keine Absenkung für 1750-1950ms entspricht dem Beginn einer neuen Minute

Abbruchkriterien (bzw. Start einer neuen Synchronisation)
...wenn mehr oder weniger als 59Bits pro Minute erkannt werden.
...Pegel-Zeiten die außerhalb der oben genannten Toleranzen (für
0/1/neue Minute) liegen
...Überprüfung der Minuten-, Stunden- und Datums-Parität

Über einen externen Quarz wird ein 1ms Takt erzeugt, damit läuft die
Uhrzeit/das Datum auch weiter, wenn keine korrekten DCF-Daten empfangen
werden.

Abgleich zwischen der internen Uhr und den DCF-Daten bei korrektem
Empfang (hier plane ich noch eine Plausibilitätsüberprüfung einzubauen)

Auf das LCD werden folgende Daten ausgegeben:
Wochentag (Mo/Di/Mi/Do/Fr/Sa/So)
Datum (dd.mm.yyyy) (Meine Lösung für die Jahreszahl ist noch etwas
rudimentär, ob in 90 Jahren noch jemand DCF77-Uhren benutzt? ;-) )
Zeitzone (MEZ/MESZ)
Zeit (hh:mm:ss)
Status der Synchronisation (?/!)

ToDo's:
Behandlung der Schaltsekunde (DCF-Auswertung "case 19")
Behandlung der angekündigten Zeitumstellung (DCF-Auswertung "case 16")
Anpassung für die zweistelligen Jahreszahlen

Geplante Erweiterungen:
Grafik-LCD (320x240) damit ich später noch andere Dinge anzeigen kann

Zum Einstieg war das bisher eine tolle Sache und ich habe viel über die
Eigenheiten/das Eigenleben der Hardware und Software gelernt, mal
schauen wie es weitergeht.

vg
Johannes

Edit: Eigentlich sollte der Anhang nur einmal dran, ist aber beides das
Gleiche.
Autor: Martin Thomas (mthomas) (Moderator) Benutzerseite
Datum:

Johannes M. schrieb:
>...
> Edit: Eigentlich sollte der Anhang nur einmal dran, ist aber beides das
> Gleiche.
Habe einen der Anhänge gelöscht.
Autor: Marius S. (lupin) Benutzerseite
Datum:

Ich zähle mal die Sekunden bis der Herr Dannegger seinen Code hier
verlinkt.
Autor: Johannes M. (johannesm)
Datum:
Angehängte Dateien:

Martin Thomas schrieb:
> Habe einen der Anhänge gelöscht.

Danke


Andere Beispielprogramme die man hier im Forum bzw. im Netz finden kann
sind mir natürlich auch bekannt. Aber es macht doch viel mehr Spaß etwas
selbst zu machen und dabei dazuzulernen.

Im Anhang noch ein Foto mit der Darstellung auf dem LCD.
Autor: Simon K. (simon) Benutzerseite
Datum:

Marius S. schrieb:
> Ich zähle mal die Sekunden bis der Herr Dannegger seinen Code hier
> verlinkt.

Immerhin hat er die Abtastung in einem Timer-Interrupt (1ms Trigger)
gemacht und nicht per Interrupt.
Das ist schon löblich!
Autor: Johannes M. (johannesm)
Datum:
Angehängte Dateien:

Mir sind gerade eben beim Datumswechsel ein paar Fehler aufgefallen.
Fälschlicherweise hatte ich beim Datumsübergang schon beim 28.02. direkt
auf den nächsten Monat umgeschaltet und der Monat begann bei Tag 0.
Sollte nun behoben sein, noch nicht schön aber es funktioniert :)
Autor: Bernd (Gast)
Datum:

Hallo,
verstehe deine Routine "void dcf_sync(void)" nicht ganz.
Habe sie versucht im Simulator zu testen aber selbst bei dem Versuch
If(counter_high>=2) laüft der Simulator nicht weiter.(counter_high wurde
auf 2 hochgezaehlt).
Liegt das am Simulator oder wie kann das sein?
Gruss Bernd
Autor: Johannes M. (johannesm)
Datum:

Hi,
im Simulator habe ich das noch nicht getestet, aber ich kann ja mal
versuchen die Anweisungen in der "void dcf_sync(void)" etwas genauer zu
beschreiben.
Als Randbedingung noch zu beachten: die Variable counter_high wird in
der ISR erhöht, also alle 1ms um +1.
if(!(dataPC & (1<<PINC1))) //ist am Eingangspin ein Low-Pegel wird dieser Zweig ausgeführt
  {
    if(counter_high>=1750 && counter_high<=1950) //ist der Wert der Variable counter_high zwischen 1750 und 1950 so kennzeichnet dies den Anfang einer neuen Minute (alle Counter, das DCF-Datum/Uhrzeit werden zurückgesetzt und es geht zurück in den normalen Programmablauf)
    {
      //LCD-Ausgabe: Synchronisation erfolgreich
      set_cursor(19,1);
      lcd_data('!');

      counter_reset(); //setzt sowohl counter_high, counter_low und die bit_number auf 0
      dcf_date_reset(); //setzt nach erfolgreicher Synchronisation den Datumswert auf 0
      dcf_time_reset(); //setzt nach erfolgreicher Synchronisation den Uhrezeitwert auf 0
    }
    else //ist der Wert counter_high kleiner als 1750 oder größer als 1950, liegt entweder ein Übertragungsfehler des DCF-Moduls vor oder es ist der ganz normale High-Pegel zwischen den Sekunden-Absenkungen.
    {
      counter_high=0; //hier wird wieder der counter_high zurückgesetzt, sonst ist bei mehrmaligem durchlauf der Wert immer >1950.
      dcf_sync();
    }
  }
  else //ist am Eingangspin ein High-Pegel wird wieder dcf_sync() aufgerufen. Es werden keine Counter zurückgesetzt.
  {
    dcf_sync();
  }

Ich hoffe das hat mehr geholfen als verwirrt, bin ja selbst noch am
lernen. :)
Autor: Bernd Bömer (behbeh)
Datum:

Hallo,
ja, so hatte ich das auch verstanden.
Problem schein zu sein, das ich A) einen anderen PIN nehmen, und B) den
gesamten Port im Interupt einlesen. Die und Verknüpfung klappt dann wohl
nicht mit dem (1<<PINC5).
Also muss ich auch beim einlesen nur das eine BIT einlesen.
Mal sehen ob das klappt.
Danke
Bernd
Autor: Mnemonic82 (Gast)
Datum:
Angehängte Dateien:

Guten morgen...

Habe den Code übernommen und versucht ihn für nen Atmega 2560
anzupassen.
Bekomme auch keine Fehlermeldungen mehr raus. Die Anzeige auf dem
Display passt auch, nur leider krieg ich das DCF-Signal nich rein.
Hab schon alles mögliche ausprobiert...

Vielleicht ist ja doch noch nen Fehler im Code?

Danke im Vorraus...
Autor: Bernd Bömer (behbeh)
Datum:

Hallo,
habe mein Problem erkannt. Da ich den Code in ein extra File (clock.c)
gespeichert habe und auch  die Variablen in clock.h muss ich wohl die
counter_xx und die millisecond mit "volatile" versehen, dann geht es
auch im Simulator. Vorher wurde der Befehl if(counter_high<=xxx &&
counter_high>=xxx) nicht im Simulator ausgeführt..

Mein Datenlogger läuft jetzt (anscheinnend?!).

Bernd
Autor: Mnemnoni82 (Gast)
Datum:

Kann es vielleicht sein das ich bei der Timer init nen fehler gemacht
habe?
Autor: Johannes M. (johannesm)
Datum:

Hi,
habe mal über die Timer Einstellungen geschaut (mit dem 2560 habe ich
noch nicht gearbeitet), die sehen auf den ersten Blick aber richtig aus.
Ansonsten würden mir spontan folgende Fehlerquellen einfallen:
Läuft der mega2560 mit 16 MHz? (Fuses)
Welches DCF-Modul verwendest du? An PinC1 Angeschlossen?
Beim Conrad Modul habe ich den invertierten DCF-Ausgang mit einem
Pull-Up Widerstand verwendet.
Hast du schon mal direkt die Signale am DCF-Modul angeschaut? Ist der
Empfang i.O.?
Autor: Mnemonic82 (Gast)
Datum:

Hi...

Erstmal danke das du mir helfen möchtest.
Also die Taktfrequenz des mega2560 ist auch 16MHz, wie beim mega 16.
Kann es sein das ich doch was an den fuses machen muss?

Benutze auch das DCF-Modul von Conrad, hab aber die Pullups wie im
Datenblatt gezeigt angeschlossen. (d.h. an den Klemmen 3 und 4 jeweils
einen 10k, die Enden kurzgeschlossen und dann von da ne Brücke auf
Klemme 2)
Du hast nur am negierten Ausgang nen Pullup benutzt?

Den Empfang hab ich mit nem Multimeter überprüft. (5V angelegt und die
Ausgänge gemessen. Da hab ich ne Spannung gemessen die zwischen 0 und 5V
geschwankt hat)
Kann man das überhaupt so machen?

Gruss Fabian
Autor: Mnemonic82 (Gast)
Datum:

Hast du vielleicht nen Bild vom DCF mit angeschlossenem Pullup?
Autor: Johannes M. (johannesm)
Datum:
Angehängte Dateien:

Habe mal ein Foto gemacht und beschriftet.

Die Pegel mit einem Multimeter zu messen ist natürlich etwas "ungenau",
beim DCF-Signal kommt es ja hauptsächlich auf die Zeiten zwischen den
Pegeländerungen an. Dazu habe ich zwei Bilder (im pdf) vom Oszi
angehängt, so müsste es in etwa aussehen.

Auf der 1ten Seite die Abbildung einer kompletten Minute, auf der 2ten
Seite noch mal als Detail die "fehlende" Absenkung für die Kennzeichnung
einer neuen Minute und die Unterscheidung zwischen der logischen 0 und 1
ist auch gut zu erkennen.
Autor: Mnemonic82 (Gast)
Datum:

Hi...

Vielen Dank erstmal für die Bilder. Das die Sache mit dem Multimeter
ziemlich ungenau ist war mir ja klar, hab aber leider kein oszi zur
Verfügung. So konnte ich wenigstens sehn ob überhaupt was rauskommt.

Also ich hab jetzt das DCF so beschaltet wie du es auch gemacht hast.
Hab den Ausgang des DCF`s auch an PC1 gehängt. In der "main" von
"dcf-clock.c" definierst du PB0 als Ausgang. Mir ist leider nich ganz
klar wofür genau. Bitte nich auslachen, steh noch ziemlich am Anfang.

Zu meiner Hardware:
Benutze das MK3 von myAVR, da sollte doch eigentlich alles notwendige
drauf sein. (Quartz für den Takt des Timers, usw.)

Gruss Fabian
Autor: Johannes M. (johannesm)
Datum:

PB0 ist noch eine "Leiche" von meinem Testaufbau, hat keine Funktion.

Die Zeilen können beide gelöscht werden:
DDRB |= (1<<PB0);  //PB0 Ausgang (1)
PORTB &= ~(1<<PB0);  //PB0 auf low gesetzt (0)

Ich bin auch gerade dabei den Programm-Code zu überarbeiten, das wird
sich aber aus Zeitgründen noch ein paar Tage/Wochen hinziehen.
Der aktuelle Stand hier im Forum sollte aber ohne Probleme laufen.
Autor: Mnemonic82 (Gast)
Datum:

Vielen dank für deine Hilfe...
Wünsch dir weiterhin viel Erfolg und Spass bei deinem Projekt.
Werds mal beobachten

Gruss Fabian
Autor: HansDampf (Gast)
Datum:

hallo johannes,
gute entwicklung die du gemacht hast.

zu meiner frage:
ich will dieses projekt auch umsetzen. allerdings ohne dem
empfangsmodul. Anstatt dem empfangsmodul, kommt mein dcf signal direkt
von einer rs232 schnittstelle. sollte kein unterschied sein, oder?

mfg HansDampf
Autor: Hanna (Gast)
Datum:

Simon K. schrieb:
> Immerhin hat er die Abtastung in einem Timer-Interrupt (1ms Trigger)
> gemacht und nicht per Interrupt.
> Das ist schon löblich!

Da man ja immer gerne was dazulernen will:
Ich dachte eigentlich, das die Abtastung per Interrupt der Normalfall
wäre. Was bringt die Abtastung im Timer-Interrupt denn für Vorteile?
Autor: HansDampf (Gast)
Datum:

so jetzt hab ich hier ein
Atmega8 +
2x16 LCD
und was muss ich am code ändern (von 16bit auf 8bit).

cya
Autor: Niko Robna (Gast)
Datum:

HansDampf schrieb:
> (von 16bit auf 8bit)

Der Mega16 ist auch nur ein 8Bit-Controller.
Autor: Sauger (Gast)
Datum:

Nabend,

HansDampf schrieb:
> zu meiner frage:
> ich will dieses projekt auch umsetzen. allerdings ohne dem
> empfangsmodul. Anstatt dem empfangsmodul, kommt mein dcf signal direkt
> von einer rs232 schnittstelle. sollte kein unterschied sein, oder?

doch, du brauchst einen Pegelwandler. Die nächste Frage ist ob das
Empfangsmodul die Uhrzeit als String übergibt, oder an einer
Handshakeleitung rumzappelt.

Hanna schrieb:
> Da man ja immer gerne was dazulernen will:
> Ich dachte eigentlich, das die Abtastung per Interrupt der Normalfall
> wäre. Was bringt die Abtastung im Timer-Interrupt denn für Vorteile?

Du kannst jeden x-beliebigen Pin nehmen. Voraussetzung ist natürlich das
dein Programm diesen rechtzeitig prüfen (Pollen) kann. Ein delay_ms(500)
irgendwo in der Hauptschleife geht dann nicht mehr.

MfG
Autor: Sauger (Gast)
Datum:

Sauger schrieb:
> Voraussetzung ist natürlich das
> dein Programm diesen rechtzeitig prüfen (Pollen) kann. Ein delay_ms(500)
> irgendwo in der Hauptschleife geht dann nicht mehr.

ist natürlich mist, habe Timer-Interrupt überlesen

MfG
Autor: Julian (Gast)
Datum:

Hi,

ich habe hier ebenfalls (wie ein paar Beiträge zuvor) einen ATMega 8.
Ich verwende einen externen Quarz mit 8MHz. Die Frequenz habe ich
umgestellt sowie ein paar weiter Änderungen vorgenommen, allerdings
wirft der Timing-Absatz der Hauptmethode 4 Errors. Ist das irgendwie
ATMega16 spezifisch? Kann ich mir kaum vorstellen:
../dcf-clock.c:566: error: 'TCCR0A' undeclared (first use in this function)
../dcf-clock.c:566: error: (Each undeclared identifier is reported only once
../dcf-clock.c:566: error: for each function it appears in.)
../dcf-clock.c:566: error: 'WGM01' undeclared (first use in this function)
../dcf-clock.c:567: error: 'OCR0A' undeclared (first use in this function)
../dcf-clock.c:568: error: 'TIMSK0' undeclared (first use in this function)
../dcf-clock.c:568: error: 'OCIE0A' undeclared (first use in this function)

Gruß,
Julian
Autor: Gerd (Gast)
Datum:

Die Register für den Timer0 heissen beim Atmega16 anders als beim
Atmega8. Da musst du in die beiden Datenblätter schauen und deinen Code
anpassen.
Autor: Julian (Gast)
Datum:

Läuft! Danke!
Autor: Alexander Kr (bandit1200)
Datum:

Hallo!!
Wer kann mir den Schritt für Schritt erklären wie man dieses Projekt
mit Avr Studio umsetzt.
Bin Anfänger, habe das Tutorial durch, bekommer aber beim Build ständig
Fehler, habe bisher nur leds zum blinken und an-aus gebracht..
Ich weiss nicht genau was in AVR Studio geaddet werden soll.
Habe dcg-clock.c drin, lcd-routines.c und lcd routines.h.
Habe das Evaluationsboard von pollin mit LCD, benutze zum Übertragen
Ponyprog.

Suche nach Problemlösung hier in den Foren seit über 6 std ohne Erfolg.

/*Fehler*/

lcd-routines.o:D:\Dokumente und Einstellungen\Bandit_1200\Eigene
Dateien\Downloads\DCF-Clock_2010-02-28\DCF-Clock\default/../lcd-routines.c:134:
first defined here
make: *** [dcf-clock.elf] Error 1
Build failed with 1 errors and 4 warnings...

Bitte keine Links zu anderen Freds, habe sie alle durch.
Wer hilft mir?
BITTE!!!
Autor: Johannes M. (johannesm)
Datum:

Hi,
habe gerade mal getestet mit den 3 genannten Dateien das Projekt
aufzusetzen, hat bei mir funktioniert. Ich schreibe mal mein Vorgehen
auf, vielleicht hilft dir das:
Im AVR Studio:
- New Project -> AVR GCC -> Project name: dcf-clock
- AVR Simulator -> Device ATmega16
- Bei den Source Files die angelegte Datei dcf-clock.c aus dem Projekt
entfernen
- Nun die 3 Dateien (dcf-clock.c, lcd-routines.c und lcd-routines.h) im
Explorer in das Projektverzeichnis kopieren (hier liegt schon
"dcf-clock.aps")
- Wieder im AVR Studio die beiden c-Dateien bei den Source Files adden
und die h-Datei bei den Header Files
- Menü Projekt -> Configuration Options -> Frequency: 16000000 Hz -> OK
- F7 drücken -> fertig
Autor: Alexander Kr (bandit1200)
Datum:

Herzlichen Dank.
Hat wunderbar funktioniert!!
Autor: Maulwurf (Gast)
Datum:

Hallo Julian

Kannst du mal den Code für der Mega8 posten ?

Gruss und Danke Bernd
Autor: Dieter Hagl (hans691)
Datum:

Hallo Johannes,

erst mal vielendank für des Code.
Ich versuche gerade ihn zu verstehen und hätte eine Frage.
Wie springst du aus der Routine dcf_sync()heraus dcf_sync() wird ja
wieder von sich selbst aufgerufen?

Danke
Dieter
Autor: Johannes M. (johannesm)
Datum:

Dieter Hagl schrieb:
> Hallo Johannes,
>
> erst mal vielendank für des Code.
> Ich versuche gerade ihn zu verstehen und hätte eine Frage.
> Wie springst du aus der Routine dcf_sync()heraus dcf_sync() wird ja
> wieder von sich selbst aufgerufen?
>
> Danke
> Dieter

Hallo,

im Falle das die Bedingung der if-Anweisung in der dcf_sync() zutrifft,
also "Synchronisation erfolgreich", ist das Ende der Funktion erreicht
und es geht zurück zur Adresse, die auf dem Stack liegt. Also zur
while-Schleife in main.

Gruß
Johannes

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net