Forum: Mikrocontroller und Digitale Elektronik Uhr mit Atmega8 und LED


von schoeni (Gast)


Lesenswert?

Hallo,

wie wohl jeder Anfänger habe ich eine Uhr mit Atmega8 und 4stellige LED 
(Multiplex)Anzeige gebaut, funktioniert gut.
Möchte jetzt noch periodisch Monat und Jahr zur Anzeige bringen. Hat 
jemand mal einen Ansatzpunkt. Die Stunden und Minuten sollen nach einer 
Seite raus und Monat und Jahr von der anderen Seite rein. Bei der 
Programmierung (assembler) habe ich mich weitestgehend an die Beispiele 
im Tutorial gehalten. Es sollte auch noch ein DCF-Modul von Conrad ran. 
Die Beiträge die ich bisher laß benutzen den INT-Eingang für das 
DCF-Signal. Leider sind die INT Eingänge mit der Anzeige belegt. 
Irgendwie stecke ich fest, kann mir jemand ein Beispiel oder Tip geben?

von Karl H. (kbuchegg)


Lesenswert?

> Bei der
> Programmierung (assembler) habe ich mich weitestgehend an die Beispiele
> im Tutorial gehalten.
</blockquote>

Das lässt immer noch jede Menge Spielraum.

Grundvoraussetzung:
Das du das Multiplexen über einen Timerinterrupt gemacht hast.
Ohne diesen Ansatz würde ich das überhaupt nicht angehen wollen.

Also zeig mal, was du bisher hast, damit man mal überlegen kann, wo man 
am besten eingreift um Tag und Monat regelmässig einzublenden.

von schoeni (Gast)


Angehängte Dateien:

Lesenswert?

Das Multiplexen geht über einen Timer Interrupt. Hab hier mal die 
asm-Datei angefügt. Die Uhr wird nur über einen Taster gestellt, wobei 
nur der 100stel Zähler überbrückt wir. Das ist nicht das gelbe vom Ei 
aber die Zeitkorrektur sollte später über DCF laufen. Die Anzeige ist 
eine 7-Segmentanzeige mit gem. Katode. Monat und Jahr sind noch nicht 
eingebunden. Das sollte auch vom DCF abgegriffen werden. Aber um die 
Funktion des ein und ausblendens zu erlernen würde ich es auch ohne DCF 
probieren.
Danke für die angebotene Hilfe.

von Karl H. (kbuchegg)


Lesenswert?

Der Code kommt soweit schon ganz gut hin. Du musst jetzt nur überlegen, 
wie eigentlich die Anzeige zustande kommt.

Da sind die 4 Segment-Bytes (Segment0 ... Segment3) und was auch immer 
für Codes in diesen Speicherzellen steht, wird von der Multiplexroutine 
angezeigt.

Wo werden diese Bytes befüllt? In der Routine out_nummer. Dort geschieht 
die Umsetzung der tatsächlichen Uhrezeit in die Codes für die Anzeige. 
Wenn du also von der Hauptschleife aus  nicht out_nummer aufrufst, 
sondern eine andere Funktion, die nicht die Uhrzeit entsprechend 
aufbereitet, sondern etwas anderes, zb das Datum und die entsprechenden 
Codes dafür in Segment0 ... Segment3 hinterlässt, dann zeigt die 
Multiplexroutine eben dieses an.

Das reinfahren bzw. rausfahren ist eine unangenehme Steuerung, ich würde 
das erst mal lassen und nur zwischen Zeit und Datum umschalten (aufgrund 
welches Auslösers eigentlich?). Auf 4-stelligen 7-Segmentanzeigen, sieht 
das auch nicht besonders gut aus, weil es so dermassen hüpft.


Also: entsprechende Speichermöglichkeit für Tag und Monat bereitstellen, 
Ausgaberoutine dafür schreiben und in der Hauptschleife einen 
Umschaltmechanismus dafür implementieren.

von Jasson J. (jasson)


Lesenswert?

Hi,

habe ich das richtig verstanden mit dem rein und raus wander, das das so 
aussehen soll, wie bei diesen laufschriften?
Ich nehm das mal an.

Wie machst du denn Zeitstruktur in der ISR? Je eine Speicherstlle für 
Stunde, Minute etc.? Vermute ich fast, ist ja die gängige Methode.

Mir ist folgendes durch den Kopf gegangen, was eher eine qualitative 
Skizze ist. Hast du schon mal mit den Pointer Registern X Y Z also r26 
bis r31 gearbeitet?

Idee:
Mach für jede Ziffer der Zeitstruktur eine eigene Speicherstelle. Dann 
musst du natürlich für jede Ziffer die überläufe behandeln, also ob es 
jetzt z.B. die Einer der Minuten sind oder die Zehner...
Vorteil ist aber, du kannst die Zeitstruktur im Grunde als Array im RAM 
ablegen und recht bequem jede einzelne Ziffer holen. Dann kannst du ja 
auch hinter die Zeit die Ziffern von Datum usw. legen. Bevor du eine 
Zahl anzeigst, musst du sie natürlich über eines der Pointerregister 
holen.

Angenommen die Zehnerziffer der Stunden liegt bei dec.100, dann lädst du 
den Z-Pointer mit 100, und liest RAM.dec100 aus. Dann machst du sbiw 
r30,1
wodurch sich der Zeiger auf die Einer der Stunde verschiebt. Das machst 
du, bis der Pointer bei den Einern der Minuten angekommen ist.

Initialisierst du aber am Anfang den Z-Pointer nicht auf 100, sondern 
auf 101, läuft er natürlich über den Ram Bereich der Zeit raus und in 
den Ram Bereich von meinetwegem dem Datum rein.

Sei schon drauf vorbereitet, Arrays in ASM zu bauen kann echt Brainfu*** 
werden^^, vorallem, wenn man mehr als eine Stufe drin hat. Dann hat man 
aber echt gerafft, was Arrays sind :-)

viele Grüße,
Jasson

EDIT:
Entschuldige meine Frage ob, du die Pointer kennst, habe ich eben 
gesehen ;-) ich will niemandem auf den Schlips treten

von oldmax (Gast)


Lesenswert?

Hi
Ich habe etwas ähnliches gemacht. Mit dem Datum hab ich mich auch ein 
wenig schwer getan. Assembler bietet eben nicht so komfortable Routinen 
wie eine Hiochsprache, aber im Prinzip mußt du ja auch nur Zählen und 
Vergleichen. Du wirst deine Variablentabelle etwas aufweiten müssen, 
aber keine Angst, der Atmega8 steckt das locker weg. Die Zählerwerte 
würd ich in den unteren Registerbereich packen, da diese etwas begrenzt 
im Befehlsumfang sind. So ist ein "LDI R1, 60" oder "LDS R1, Variable" 
nicht möglich, aber ein "INC R1". Für die Grenzwerte 60 und 24 kannst du 
entweder ein Register mit Konstanten laden, oder aber du holst dann ein 
anderes Register zu Hilfe, weil ein "CPI R1, 60" auch nicht geht.
Bspl:
1
.Def  Sekunde =r1
2
.Def  Minute = r2
3
.Def  Stunde = r3
4
.Def  Tag    = r4
5
.Def  Monat  = r5
6
.Def  Jahr   = r6
7
.Def  sechzig = r7
8
.Def  vierundzwanzig = r8


Initialisieren mit :
1
LDI  r16, 60
2
MOV  sechzig, r16
3
LDI  r16, 24
4
MOV  vierundzwanzig, r16

dann geht auch :
1
  INC Sekunde1
2
  CP  Sekunde, sechzig 
3
  BRLO ende
4
  CLR  sekunde
5
  INC  Minute
6
  CP   Minute, sechzig
7
  BRLO ende
8
  CLR  Minute
9
  Inc  Stunde
10
; usw
Beim Datum hinterlegst du eine Tabelle für die Tage im Monat. Lediglich 
das Schaltjahr mußt du dann berechnen. Das Jahr würd ich auch fix mit 
"20" ergänzen, es sei denn, du willst in 88 Jahren den 
Jahrhundertwechsel mitberechnen.
Die Laufschrift erfordert etwas Hirnschmalz... aber es gibt ja die 
Adressregister X, Y und Z. Vielleicht solltest du den Anzeigepuffer 
etwas größer machen und z. B. 4 Leerstellen, 4 Werte und 4 Leerstellen 
definieren.
1
Leer_1:      .Byte4
2
Anzeige_3:   .Byte1   ; entspricht Wert x*10^3
3
Anzeige_2:   .Byte1   ; entspricht Wert x*10^2
4
Anzeige_1:   .Byte1   ; entspricht Wert x*10^1
5
Anzeige_0:   .Byte1   ; entspricht Wert x*10^0
6
Leer_0       .Byte4
So erhälst du fortlaufende Speicherzellen für die Ausgabe und kannst nun 
z. B. die Uhrzeit links rauslaufen lassen. dann trägst du die 
Datumswerte ein.
In der Timer-ISR setze ich mir immer ein Bit für eine Zeitaktion. Sollz. 
B. im Sekundentakt geschoben werden, so setze ich im Bereich der 
Sekunden in einer Variablen ein Bit. Im Hauptprogramm bin ich ja nicht 
zeitkritisch und das Ereignis kann dann abgefragt und bearbeitet werden. 
Das Bit wird nach Bearbeitung wieder gelöscht.
So, genug
Gruß oldmax

von Karl H. (kbuchegg)


Lesenswert?

oldmax schrieb:

> Die Laufschrift erfordert etwas Hirnschmalz... aber es gibt ja die
> Adressregister X, Y und Z. Vielleicht solltest du den Anzeigepuffer
> etwas größer machen und z. B. 4 Leerstellen, 4 Werte und 4 Leerstellen
> definieren.

Das ist eine ziemlich coole Idee. Treibt man das noch weiter (den 
Speicher dafür hat man ja)

   4 Leerstellen
   4 SegmentCodes     (Uhrzeit)
   4 Leerstellen
   4 SegmentCodes     (Datum)
   4 Leerstellen
   4 SegmentCodes     (Jahr)
   4 Leerstellen

dann erhält man den Überblendeffekt fast gratis, je nachdem wo man mit 
der Ausgabe anfängt bietet sich ein anderes Bild einer Anzeige in 
verschiedenen Stadien des raus/reinscrollens.

Hält man sich diese Adresse zb im X-Pointer vor, dann kann man hier in 
der Multiplex-ISR anstelle von
1
    ldi    ZL, low(Segment0)       ; fuer diese Ziffer das Codemuster fuer
2
    ldi    ZH, high(Segment0)      ; die Anzeige in der Codetabelle nachschlagen

den Z-Pointer aus dem X-Pointer laden (der natürlich am Anfang mal 
korrekt geladen werden musste) und nur durch Erhöhen bzw. Verringern 
bzw. Umsetzen des X-Pointers ergeben sich die einzelnen 
Überblend-Scroll-Effekte.

von schoeni (Gast)


Lesenswert?

vielen Dank für eure Antworten, habe das Prinzip verstanden. Werde das 
mal ausarbeiten. Wenn das dann funkioniert können wir uns doch bestimmt 
über die DCF Auswertung unterhalten. Die Uhr ist an einer Stelle 
montiert, bei der  ein manuelles Stellen ungünstig ist.
bis bald
Ulf

von schoeni (Gast)


Lesenswert?

hallo oldmax,
hallo Herr Buchegger,

bin gerade dabei die Vorschläge in mein Programm einfließen zu lassen.
Wie setzt du im Bereich der Sekunden in einer Variablen ein Bit?
Meine Idee war jetzt die Funkion "OUT_Nummer" zu kopieren und in 
"Out_Nummer_2" umbenennen, dann mit Tag und Monat füttern und beide 
Funktionen abwechseld über "Multiplex" auszugeben. Dazu wäre eine einige 
Sekunden dauernde Schleife sinnvoll vieleicht ohne einen extra Timer zu 
aktivieren.

von Karl H. (kbuchegg)


Lesenswert?

schoeni schrieb:

> "Out_Nummer_2" umbenennen, dann mit Tag und Monat füttern und beide
> Funktionen abwechseld über "Multiplex" auszugeben. Dazu wäre eine einige
> Sekunden dauernde Schleife sinnvoll vieleicht ohne einen extra Timer zu
> aktivieren.

Vorschlag.
Man konnte sich zb das unterste Bit der Sekunden anzeigen. Ist es 0 wird 
in der Hauptschleife die Zeit ausgegeben, ist es 1 das Datum. Das ergibt 
dann einen 1 Sekundenwechsel zwischen Zeit und Datum. Wenn das zu 
schnell ist, könnte man auch das nächst höhere Bit nehmen.
Vorteil: Das ist einfach zu implementieren.

von oldmax (Gast)


Lesenswert?

Hi
OK, von KHB die einfache und von mir natürlich die komplizierte 
Version...
ALso, ich setz voraus, du hast es geschafft, den Timer mit einem 
Interrupt von 1 mSek. zu versehen. Das ist auch der Takt für den 
Multiplexer. Zumindest ist das bei mir immer so. Nun möchtest du aber 
noch ein paar Zeitereignisse.
Na dann wollen wir mal..
1
My_Timer_ISR:
2
   ....           ;zuerst die üblichen Push -Befehle für die Register
3
   RCALL  Multiplexer      ; zuerst den Aufruf für die Anzeige
4
   LDS    R16, MilliSek    ; Millisek als Zählvariable
5
   INC    R16 
6
   STS    MilliSek, R16    ; und erst mal abspeichern
7
   CPI    R16, 100         ; 100 mSek. ergeben einen Takt von 1/10 Sek.
8
   BRLO   Timer_ISR_Ende   ; wenn kleiner, dann brauchts keinen 
9
                           ; weiteren Schritt in der ISR
10
   CLR    R16
11
   STS    MilliSek, R16    ; nun ist eine 1/10 Sek. vergangen
12
   LDS    R 16, Timer_Flags ; Timerflags sind nun Bits, die den
13
                           ; bestimmten Zeitereignissen zuordnest
14
   ORI    R16, 0b00000011  ; so hast du 2 Ereignisbits, die du in
15
                           ; verschiedenen Unterprogrammen abarbeitest
16
   STS    Timer_Flags, R16
17
   LDS    R16, Zehntel     ; Zählervariable für Zehntelsekunden
18
   INC    R16
19
   STS    Zehntel, R16     ; wieder erst ablegen
20
   CPI    R16,10           ; dann vergleichen für Sekunde
21
                           ; wie es weitergeht denk ich, verstehst du
22
   .....
23
Timer_ISR_Ende:
24
     .....                 ; hier stehen die POP Befehle
25
RETI
 So, nun hast du was zum Üben. Ach ja, wenn du auf die Bits reagierst, 
vergiss nicht, diese entsprechend zurückzusetzen.
Gruß oldmax

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.