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?
> 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.
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.
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.
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
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
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.
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
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.
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.
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