Hallo! Zu meinem verwendeten System: Ich habe ein Mikrocontroller System mit einen 12Mhz Mikrocntroller. Die Ausgabe der Stoppuhr erfolgt auf der BCD-7-Segment Anzeige (welche an Port 1 und 2 des Mikrocontrollers angeschlossen ist). Außerdem ist eine Schalterplatine an Port 0 angeschlossen für Zusatzfunktionen. Zur besseren Veranschaulichung können diese einzelne Elemente meines Systems untern folgenden Links angeschaut werden: Mikroconroller: http://dbi-design.de/clemens/projekt/mikrocontroller.jpg BCD-7-Segment Anzeige: http://dbi-design.de/clemens/projekt/bcd_7_seg.jpg Tasterplatine: http://dbi-design.de/clemens/projekt/taster.jpg Mit diesem System will ich nun eine Stoppuhr in Assembler realisieren. Die Stoppuhr soll folgende Funktionen aufweisen: 1. Start 2. Stopp 3. Speichern einer gestoppten Zeit 4. Abrufen einer gespeicherten Zeit Diese 4 Funktionen sollten für die Schalter aufgerufen werden können. Das Stoppuhr Programm muss in Assembler geschrieben werden und muss ebenso mit Interrupts arbeiten. Dies ist vorgeschrieben daran kann ich nichts ändern. Tipps für andere Sprachen oder ohne Interrupts bringen mir deswegen relativ wenig, trotzdem danke schonmal! Nun zu meinem eigenlichen Problem. Ich habe natürlich schon angefangen mit diesem Projekt komme aber einfach nichtmehr weiter. Ich steh irgendwie total auf dem Schlauch wie man gerne zu sagen pflegt ich hoffe deswegen auf eure Hilfe. Vielen Dank schoneinmal für Tipps oder Programmänderungsvorschläge! -------------------------------------------------------------------- Nun das bisherige Programm: $include (reg52.inc) org 0000h MOV R0,#0 ; Zähler MOV R1,#0 MOV R2,#0 ; bestimmt welche der Anzeigen aktiv ist MOV R3,#0 MOV R4,#0 ; für Segment rechts Hundertstel Zehner MOV R5,#0 ; für Segment Mitte rechts Sekunden Einer MOV R6,#0 ; für Segment Mitte links Sekunden Zehner MOV R7,#0 ; für Segment links Minuten Einer loop: ; hier wird zukünftig die neue Uhrzeit berechnet call neueUhrzeit ;aufrufen Hauptprogramm neueUhrzeit call ausgabe ;aufrufen Ausgabe Programm call wait ; aufrufen Warte Programm, wartet 4 ms LJMP loop ; aufrufen loop neueUhrzeit: INC R1 ; erhoehe Register 1 CJNE R1,#250d,weiter ;Springe nach weiter wenn der Inhalt von R1 ungleich 250d ist MOV R1,#0d ;Lade Register 1 mit dem Wert 0 INC R2 ; erhoehe Register 2 CJNE R2,#10d,weiter ;Springe nach weiter wenn der Inhalt von R2 ungleich 10d ist MOV R2,#0d ;Lade Register 2 mit dem Wert 0 INC R3 ; erhoehe Register 3 CJNE R3,#10d,weiter ;Springe nach weiter wenn der Inhalt von R3 ungleich 10d ist MOV R3,#0d ;Lade Register 3 mit dem Wert 0 mSek: INC R4 ; erhoehe Register 4 CJNE R4,#10d,weiter ;Springe nach weiter wenn der Inhalt von R4 ungleich 10d ist MOV R4,#0d ;Lade Register 4 mit dem Wert 0 INC R5 ; erhoehe Register 5 CJNE R5,#10d,weiter ;Springe nach weiter wenn der Inhalt von R5 ungleich 10d ist MOV R5,#0d ;Lade Register 5 mit dem Wert 0 mMin: INC R6 ; erhoehe Register 6 CJNE R6,#6d,m2 ;Springe nach m2 wenn der Inhalt von R6 ungleich 6d ist CJNE R7,#10d,m2 ;Springe nach m2 wenn der Inhalt von R7 ungleich 0d ist MOV R6,#0 ;Lade Register 6 mit dem Wert 0 MOV R7,#0 ;Lade Register 7 mit dem Wert 0 m2: CJNE R6,#10,weiter ;Springe nach weiter wenn der Inhalt von R6 ungleich 10 ist MOV R6,#9 ;Lade Register 6 mit dem Wert 9 INC R7 ; erhoehe Register 7 LJMP weiter ;aufrufen von weiter weiter: RET ;Rücksprung aus dem Unterprogramm ;Unterprogramm wait benötigt genau 4ms bei 12MHz wait: PUSH 01 PUSH 02 MOV R1,#1 wait1: MOV R2,#164 wait2: DJNZ R2,wait2 NOP DJNZ R1,wait1 NOP NOP NOP POP 02 POP 01 RET ausgabe: INC R0 CJNE R0,#04,rechts MOV R0,#0 rechts: CJNE R0,#0,mire MOV A,R4 CALL BCD7Seg MOV P1,A MOV P2,#00001110b mire: CJNE R0,#1,mili MOV A,R5 CALL BCD7Seg MOV P1,A MOV P2,#00001101b mili: CJNE R0,#2,links MOV A,R6 CALL BCD7Seg MOV P1,A MOV P2,#00001011b links: CJNE R0,#3,m1 MOV A,R7 CALL BCD7Seg MOV P1,A MOV P2,#00000111b m1: RET ; Wandelt vom BCD-Code in den 7-Segment-Code BCD7Seg: INC A MOVC A,@A+PC RET DB 3Fh, 06h, 5Bh, 4Fh, 66h, 6Dh, 7Dh, 07h, 7Fh, 6Fh end
Wie wärs, wenn Du erstmal nen Programmablaufplan schreibst, also verbal ausdrückst, was wann und wie zu tun ist. Eine Aufgabe in Code umzusetzen ist leicht. Das Schwierige am Programmieren ist jedoch, erstmal diese Aufgaben sinnvoll zu formulieren. Peter
@Clemens, das wichtigste fehlt ja wohl..oder ???? Was ist denn mit Timer ????? Normalerweise läßt man den bei Betätigung einer Taste laufen. Incrmentiert im Interrupt eine Variable ( RAM Zelle) auf. Überträge auf die nächtse und so weiter. Ne Kette eben. Allerdings wirst Du wohl um Interruptprioritäten nicht herum kommen. Die Anzeige ist ja nicht so wichtig wie die exakte Erfassung der Zeit. Den LED Multiplex inkl. oder exkl. Decoder kann der 2. Timer machen. Wie Peter sagt, Dir fehlt das eigentliche Konzept. So denn..... ran ans Schmierblatt....
Clemens, in einer Elektor Ausgabe der letzten 2 Jahre war eine solche Stoppuhr realisiert. Hier kannst Du Dich einmal konzeptionell befruchten lassen, denn da scheinst Du etwas Nachholbedarf zu haben. Der "Schwierigkeitsgrad" einer solchen Aufgabenstellung ist nicht sehr anspruchsvoll, wenn man sich etwas einliest und vor allem einen "Plan" entwickelt. Die Zutaten sind ja schon genannt: Timer geschickt initialisieren (Quarztakt beachten, denn die Uhr soll ja nicht zum Pfandhaus gehen), Interruptserviceroutine (fehlt bei Dir völlig) für den Timer (Kurz halten, am besten mit einem "Flag" arbeiten) drumherum baust Du dann schön Dein Zählwerk auf !! In der Hauptroutine kannst Du dann prima Deine 7-Segmentanzeige (Multiplexen oder statisch) versorgen und auch die Tasten befragen. Hier mußt Du selbst bewerten ob ein paar Mikrosekunden Verzögerung bis zur Abfrage des Tastenstatus ein Problem darstellen. Du kannst die Tasten natürlich auch per Interrupt befragen, aber ein paar Befehlszyklen gehen Dir immer verloren, wahrsagen können die Controller leider nicht. Hau rein ! D.S.
Hallo Danke schoneinmal für die Tipps. Also die obige Datei habe ich bekommen und mit der soll ich Arbeiten. Ehrlich gesagt versteh ich das mit dem Interrupt allerdings selbst nicht so ganz. Kennt jemand von euch eine gute Erklärung dazu? Das würde mir auch sehr weiterhelfen. Ansonsten habe ich mittlerweile mal einen sehr allegemeinen Programmablaufplan erstellt: http://dbi-design.de/clemens/projekt/pap.jpg Ich weiß allerdings nicht wie ich das Abrufen der gespeicherten Zeit bewerkstelligen soll.
naja geht doch schon. So nun brauchst Du noch nen Timer,der Dir ne Zeitbasis macht. zB alle 100ms oder alles 10ms etc.pp Dazu nimmt man Timer 1 oder Timer 0. Deine Quarzfrequenz kennst Du ja oder wählst sie entsprechend zB 12 Mhz. Dann hast du 1uS Zykluszeit, damit läßt sich prima arbeiten, da der 8051 12 Takte für einen Zyklus ( Befehl) braucht. Der Start des Stoppuhrprogramms macht nichts anderes als einfach den Timer zu starten. Und bei Stopptaste einfach den Timer stoppen. Wie gesagt, das Schlüsselwort lautet "Timer" !! Beim Überlauf löst der Timer nen Interrupt (Programmunterbrechung) aus. Im Interrupt muß Du nur noch ne Variable oder Speicherstelle hochzählen und wieder raus. Wenns ganz genau sein soll dann setzt Du im Interrupt nur nen Flag und springst mit RETI gleich wieder raus. Der Rest siehe oben....
ähhmm!
Das Programm sollte nicht gerade bei Adresse 0000h starten, weil gleich
dahinter ja die Interrupt Einsprungadressen befinden.
Könnte  so unerwünschte effekte geben.
Lieber mal so machen:
org  0000h
     LJMP Start
...
;Ab Adresse 03h Hardware Einsprung Aderessen für Interrupt
org  000Bh              ;Interrupt Timer 0
     LJMP ISR_Timer0
...  oder
org 001bh              ;Interrupt Timer 1
    LJMP ISR_Timer1
...
...
von mir aus ab Adresse 100 oder höher egal
org   0100h
Start:  ...
        ...
        mov  TMOD,#00000010b  ;Timer0 aktivieren
  mov  TL0,#0
  mov  TH0,#0
  SETB  EA
  SETB  ET0
  clr  TF0
  SETB  TR0
        LJMP Start
ISR_Timer0:
         clr  TF0
         ...
         ...
         RETI    ;Return Interrupt
oder
ISR_Timer1:
       clr  TF1
       ...
       ...
       RETI   ; Return Interrupt
Gruß norad
  der, der Ahnung davon hat und Recourcenaufteilung der MCU nicht einem Compiler überlassen will !
Sorry Stephan. Aber es bleibt dabei, heute schreibt niemand mehr in Assembler. Zu mindestens Keiner der der ernst genohmmen werden will. PS.: Bei mir in der Firma & Kunden hat es sich immer weider rausgestellt das ein Prozessor zu klein/schwach war und er gewechselt werden musste. Wie geht den das in Assembler? Also bitte nie Assembler, Ihr macht euch das leben nur unnötig schwer.
Mittlerweile habe ich das Programm ergänzt, so gut es ging.
Könntet ihr das mal durchschauen?
Wäre nett!
$include (reg52.inc)
Starttaster    bit P0.1
Resettaster    bit P0.2
org 0000h
  LJMP start
;Einsprungadresse Interrupt-Serviceroutine für Timer 0
org 000Bh
      LJMP isrTimer0
org 0100h
Start:      SETB P1.0      ;Taster an P1, um Port als Eingang zu haben
abfrage:    MOV a,p1
            JB  a.0,abfrage
      Hilfsflag ist 1? Wenn ja dann
      SETB TR0
      wenn nein
      SETB Taster
      CALL neueUhrzeit
      CALL ausgabe
      CALL wait
      JMP abfrage
initRegister:
      MOV  R0,#0      ;bestimmt welche der Anzeigen aktiv ist
      MOV  R1,#0       ;Zähler von o bis 249 -->4ms*250 = 1 Sec
      MOV  R2,#0      ;Sekunden Einer
      MOV  R3,#0       ;Sekunden Zehner
      MOV  R4,#0       ;für Segment rechts          Minuten Einer
      MOV  R5,#0       ;für Segment Mitte rechts      Minuten Zehner
      MOV  R6,#0       ;für Segment Mitte links      Stunden Einer
      MOV  R7,#0       ;für Segment links          Stunden Zehner
      RET
initTimer0:
      SETB  ET0          ;Freigabe Timer0. Bit im Reg IE, Reg.-Adresse
      SETB   EA            ;Globale INT Freigabe. Bit im Reg IE
      MOV  TMOD,#00000001b  ;Timer0 konfiguieren, Modus1: 16bit
Zaehler
      MOV  TL0,#060h      ;Timer0 vorladen
      MOV  TH0,#0F0h      ;65536 - 4000 = 61536 = F060h (4ms)
      CLR  TF0
      SETB  TR0          ;Start Timer0
      LJMP start
;Interrupt-Service-Routine für Timer0
isrTimer0:
  CLR TF0
      MOV  TL0,#060h      ;Timer0 erneut vorladen
      MOV  TH0,#0F0h      ;65536 - 4000 = 61536 = F060h (4ms)
      CALL  neueUhrzeit      ;neue Uhrzeit berechnen
      CALL  ausgabe        ;Ausgabe auf Anzeige
      RETI              ;Ende der Interrupt-Service-Routine
neueUhrzeit:
         INC R1        ; erhoehe Register 1
         CJNE R1,#250d,weiter      ;Springe nach weiter wenn der
Inhalt
von R1 ungleich 250d ist
         MOV  R1,#0d            ;Lade Register 1 mit dem Wert 0
         INC R2              ; erhoehe Register 2
         CJNE R2,#10d,weiter      ;Springe nach weiter wenn der Inhalt
von
R2 ungleich 10d ist
         MOV R2,#0d            ;Lade Register 2 mit dem Wert 0
         INC R3              ; erhoehe Register 3
         CJNE R3,#10d,weiter      ;Springe nach weiter wenn der Inhalt
von
R3 ungleich 10d ist
         MOV R3,#0d            ;Lade Register 3 mit dem Wert 0
mSek:    INC R4              ; erhoehe Register 4
         CJNE R4,#10d,weiter      ;Springe nach weiter wenn der Inhalt
von
R4 ungleich 10d ist
         MOV R4,#0d            ;Lade Register 4 mit dem Wert 0
         INC R5              ; erhoehe Register 5
         CJNE R5,#10d,weiter      ;Springe nach weiter wenn der Inhalt
von
R5 ungleich 10d ist
         MOV R5,#0d            ;Lade Register 5 mit dem Wert 0
mMin:    INC R6              ; erhoehe Register 6
         CJNE R6,#6d,m2          ;Springe nach m2 wenn der Inhalt von
R6
ungleich 6d ist
         CJNE R7,#10d,m2        ;Springe nach m2 wenn der Inhalt von
R7
ungleich 0d ist
         MOV R6,#0            ;Lade Register 6 mit dem Wert 0
         MOV R7,#0            ;Lade Register 7 mit dem Wert 0
m2:      CJNE R6,#10,weiter      ;Springe nach weiter wenn der Inhalt
von
R6 ungleich 10 ist
      MOV R6,#9            ;Lade Register 6 mit dem Wert 9
      INC R7              ; erhoehe Register 7
      LJMP weiter            ;aufrufen von weiter
weiter:  RET                ;Rücksprung aus dem Unterprogramm
;Unterprogramm wait benötigt genau 4ms bei 12MHz
wait:    PUSH  01
      PUSH  02
      MOV  R1,#1
wait1:  MOV  R2,#164
wait2:  DJNZ  R2,wait2
      NOP
      DJNZ  R1,wait1
      NOP
      NOP
      NOP
      POP  02
      POP  01
      RET
ausgabe:  INC   R0
      CJNE  R0,#04,rechts
      MOV  R0,#0
rechts:  CJNE  R0,#0,mire
      MOV  A,R4
      CALL  BCD7Seg
      MOV  P1,A
      MOV  P2,#00001110b
mire:    CJNE  R0,#1,mili
      MOV  A,R5
      CALL  BCD7Seg
      MOV  P1,A
      MOV  P2,#00001101b
mili:    CJNE  R0,#2,links
      MOV  A,R6
      CALL  BCD7Seg
      MOV  P1,A
      MOV  P2,#00001011b
links:  CJNE  R0,#3,m1
      MOV  A,R7
      CALL  BCD7Seg
      MOV  P1,A
      MOV  P2,#00000111b
m1:    RET
; Wandelt vom BCD-Code in den 7-Segment-Code
BCD7Seg:  INC  A
      MOVC  A,@A+PC
      RET
      DB  3Fh, 06h, 5Bh, 4Fh, 66h, 6Dh, 7Dh, 07h, 7Fh, 6Fh
end
  @Clemens SETB P1.0 > ;Taster an P1, um Port als Eingang zu haben >abfrage: MOV a,p1 > JB a.0,abfrage Irgendwie unübersichtlich! Taster EQU 28h ;Bit Adressierbarer Bereich ab 25h (8 Bit) Vorschlag! MOV 25h,P1 ;Informationen von Prort 1 in 25h speichern MOV C,Taster ;Taster betätigt? JC abfrage ;Ja! ...
>>> Aber es bleibt dabei, heute schreibt niemand mehr in Assembler. Zu
mindestens Keiner der der ernst genohmmen werden will. <<<
Oh Gott, sind die Meisten mittlerweile zu Blöd für Assembler, statt
sinnvolle Hilfe zu geben, aber das kannst du ja nicht kannst ja nur C,
dann so einen Blödsinn zu verzapfen, sorry mußte mal gesagt werden.
Bei mir sinds 10% C und 90% Assembler.
Sorry das ich nichts zum Thema sage aber das mußte raus.
@ Clemens, sorry für den Mißbrauch.
  Dein Spaghetticode ist zwar angewachsen, aber trotzdem sieht da keiner durch. Nochmal, es bringt nicht, planlos drauflos zu coden, da kommt immer nur Mist raus. Du brauchst erstmal einen Plan. Also was soll wie erfolgen, wenn welche Tasten gedrückt wird, was soll wie dargestellt werden usw. Was Du brauchst, ist ein Timerinterrupt, um: - eine genaue Zeitbasis zu haben - Die Anzeige zu multiplexen - Die Tasten zu entprellen Im Hauptprogramm erfolgt dann die eigentliche Ablaufsteuerung, nachdem Du Dir einen Plan gemacht hast, wie diese überhaupt aussehen soll. Code kannst Du erst schreiben, nachdem Du weißt, was gemacht werden muß. Peter
@Clemens, das soll doch ne Stoppuhr werden ?? Oder ???? Wie kannst Du es Dir dann leisten in der ISR ne komplette Ausgabe zu machen ?? SO NICHT !!!!! In der ISR: Timer neu laden INC Variable oder Flag setzen und raus !!!! Das wars ... mehr gehört da nicht rein falls Du Zehntel un Hunderstel messen willst. Die Wait Schleife würde ich in den 2 Timer packen. Allerdings muß die Priorität von Timer 1 höher sein !!! Oder Du mmußt dafür sorgen das Timer 2 den Timer 1 nicht unterbricht. Dann bringe alles mal etwas in Form. Habe jetzt nicht viel Zeit, bin auf Arbeit. Hast Du schon simuliert ??? @Oliver "PS.: Bei mir in der Firma & Kunden hat es sich immer weider rausgestellt das ein Prozessor zu klein/schwach war und er gewechselt werden musste. Wie geht den das in Assembler? " Da fragt man sich doch wer die Planung gemacht hat ...... Mitten im Projekt wird die MCU zu klein ?? Und kein Ersatz aus der gleichen Familie ??? AU MAN !!!!!...
"Da fragt man sich doch wer die Planung gemacht hat ......" Warscheinlich gar keiner. Manchmal wird die Planung notgedrungen erst dann gemacht, wenn der Spaghetticode zu groß und zu undurchschaubar geworden ist. Und oh Wunder, plötzlich paßt das Programm sogar in nen kleineren MC. Peter
OK, also alle reden in diesem Thema davon aber ich selbst habe keine Ahnung: Was ist ein Timer Interrupt? Könnte mir jemand mal ein Beispiel geben? Ich selbst habe wenig Ahnung von Interrupts und verstehe ehrlich gesagt die Interrupt Programmierung in meinem Programm nichteinmal (was u.a. auch dazu führt das bis jetzt noch kein gutes Programm dabei rauskam). Der Ablauf des Programms habe ich ja schoneinmal festgelegt, in einem Pap ( http://dbi-design.de/clemens/projekt/pap.jpg ). Ich weiß ja also was ich programmieren will, nur fehlt mir irgendwie das Verständnis dazu. Das Problem ist eben auch das ich ja mit Interrupts arbeiten muss (wurde mir so vorgegeben). Ich sollte wohl am besten mich ersteinmal in Interrupts einlesen. Kennt jemand von euch eine gute Beschreibung, Homepage, o.ä zum Thema Interrupts? Danke im Voraus!
au Backe Clemens,,, sag mal verlangen die Lehrer heute unmögliches oder ist das "Lernen" zur Unsitte erklärt worden und deshalb unschicklich ??? Bist Du der einzige von Euch der nix versteht oder alle ?? Schau mal hier, da steht alles über Interrupts für 8051. http://www.goblack.de/desy/mc8051chip/datenblatt/interupt/index.html
@Peter, und was soll ich sagen, meistens haben solche Leute auch noch Jobs von denen wir nur träumen können :-( armes Deutschland...wo können wir nur noch ein par Terraflops herbekommen ???
Na der Plan sieht ja gar nicht so schlecht aus, daran kann man
weiterarbeiten:
1.
Im allgemeinen möchte man eine Aktion beim Drücken, wie es scheint
startest Du den Zähler aber erst beim Loslassen (Start = 1).
2.
Was passiert beim Speichern ?
Wird der alte Wert überschrieben ?
3.
Nirgends wird der gespeicherte Wert angezeigt.
4.
Was wird überhaupt angezeigt, Du hast ja nur 4 Stellen.
Hier wird ein Interrupt angesprungen:
;Einsprungadresse Interrupt-Serviceroutine für Timer 0
org 000Bh
      LJMP isrTimer0
Ist also wohl nicht von Dir, wenn Du das nicht erkannt hast.
Unterprogramme im Interrupt sind: naja.
Aber die selben Unterprogramme im Interrupt und im Main sind oberpfui
!
Du unterbrichst ein Programm mit sich selbst. Aufm PC gibts dafür
knallhart nen Blue-Screen.
Peter
  Ich denke, dein Hauptproblem ist, dass du dich zu sehr
verzettels indem du an 5 Baustellen gleichzeitig arbeitest.
Weiters glaub ich, dass du das was dir vorgegeben wurde
(den Code, nicht die Aufgabenstellung) zu sehr als
Gesstzbuch ansiehst, dass unveränderbar ist. Meiner
Ansicht nach solltest du den Code so wie er war studieren,
rausfinden wie die einzelnen Teile funktioniert haben
(zb der ganze Bereich der die eigentliche Uhr in 7 Registern
realiert hat: Wie wird diese 'Uhr' um eine Einheit hochgezählt,
wie funktioniert die Ausgabe auf die Anzeige), ev. mal ein
bischen damit spielen und ihn verändern, und dann:
fang neu an.
Als allererstes solltest du mal im Originalcode die ganzen
Kommentare, ala:
      INC R1                   ; erhoehe Register 1
      CJNE R1,#250d,weiter     ; Springe nach weiter wenn der
                               ; Inhalt von R1 ungleich 250d ist
      MOV  R1,#0d              ;Lade Register 1 mit dem Wert 0
rauwerfen. Diese Kommentare sind naemlich ein Musterbeispiel
dafuer, wie man nicht kommentiert: Der Kommentar erzaehlt dir
nichts, er wiederholt nur dass was ohnehin in der Anweisung
auch steht.
Aber was ist R1? R1 enthaelt offensichtlich einen Sub-Sekunden
Zaehler. Durch den Interrupt Mechanismus wird die Funktion eine
bestimmte Anzahl mal in der Sekunde angesprungen. An einer
anderen Stelle im Programm findet sich der Kommentar
;Unterprogramm wait benötigt genau 4ms bei 12MHz
das deckt sich genau mit den 250 die da oben vorkommen.
Wenn ein Durchlauf 4 ms dauert, dann ist nach genau 250
durchläufen exakt 1 Sekunde vergangen. Das sollte
man dokumentieren, zb so:
      INC R1                   ; erhoehe den 4ms Zaehler
      CJNE R1,#250d,weiter     ; Wenn die Routine noch nicht 250
                               ; mal aufgerufen wurde, dann gibt
                               ; es nichts zu tun: -> weiter
      MOV  R1,#0d              ; den 4ms Zaehler wieder auf 0 setzen
     INC R2                    ; und zur Sekunden-Einerstelle
                               ; 1 dazzuzaehlen
     CJNE R2,#10d,weiter       ; Gibt das einen Überlauf 9->10?
                               ; Wenn nein, alles getan: -> weiter
     MOV R2,#0d                ; Es gab einen Überlauf. d.h. die
                               ; Einerstelle der Sekunden fängt wieder
                               ; bei 0 an zu zählen ...
     INC R3                    ; ... und bei den Zehnern der Sekunden
                               ; gehst um 1 hoch
     CJNE R3,#6d,weiter        ; Wenn hier noch nicht 6 (also 6*10
                               ; Sekunden) zusammengekommen sind:
                               ; nichts weiter zu tun: -> weiter
     ....                      ; wieder bei 0 Sekunden anfangen
                               ; und bei dem Minuten Einer um
                               ; 1 hochzählen
                               ; Übertrag 9->10 bei den Einern?
                               ; Zehner um 1 hochzählen,
                               ; usw
                               ; usw
Siehst du den Unterschied in der Qualitaet der Kommentare.
Jetzt erzählt mir der Kommentar, was an dieser Stelle warum
geschieht. Und zwar in meinen Worten. Das ein Sprung nach
weiter erfolgt, wenn R2 den Wert 10 erreicht hat, das sehe ich
auch im Quelltext. Die Frage ist nur: Warum macht man diesen
Sprung, bzw. warum macht man ihn manchmal nicht? Was ist
die Idee hinter diesem Sprung?
Das sind Dinge die ein Kommentar beantworten muss, nicht die
Klartext-Wiedergabe des Assembler-Befehls.
Und solange du diese Transformation der Kommentare nicht machst,
wirst du auch nie verstehen, was in deinem Programm zur Zeit
eigentlich vor sich geht und vor allen Dingen: was die Idee
hinter diesem urspruenglichen Program war. Erst wenn du das
komplett verstanden hast, weil du es studiert hast und von
jeder Anweisung sagen kannst welchen Zweck sie hat (wiederum:
Zweck im Sinne von: was passiert auf höherer Ebene. Ein INC R4
hat natuerlich den 'Zweck' R4 hochzuzaehlen. Das meine ich
nicht. Der Zweck ist die 'Einer der Minuten' hochzuzählen, weil
eben 1 Minute vergangen ist) erst dann wirst du in der Lage sein,
einen neuen Programmplan für deine Stoppuhr zu erstellen. Ein
Plan der auch realistisch ist und umgesetzt werden kann.
  So, ich habe das ganze Programm nun (nach meinen Vorstellungen) mal
sinnvoll überarbeitet.
Ich denke das die Grundfunktion (die laufende Zeit) nun funktionieren
sollte.
Da ich das Programm dank der Hilfe von vielen hier mittlerweile nun
endlich verstehe möchte ich nun die eigentlich wichtigen Funktionen
einbauen.
So soll das Programm ja auf Tastendruck gestartet, gestoppt, die
aktuelle Zeit gespeichert und die Zeit resettet werden.
Die Taster dazu liegen an Port 3 (wegen Interrupt). Diese abzufragen
stellt auch kein Problem dar, das Problem besteht nur darin das ich
nicht weiß was ich wie unterbrechen kann (z.B. für Start/Stopp).
Es wäre natürlich gut den Interrupt Timer zu unterbrechen mit Stopp
oder zu Starten mit Start aber wie könnte ich sowas bewerkstelligen.
Starten wäre wohl das kleinere Problem, aber so zu Stoppen das die
aktuelle Zeit auch stehenbleibt und weiter angezeigt wird.
Ich hab mir überlegt ob ich die Start und Stopp Funktion über ein
Externes Interrupt laufen lassen soll (EX0 und EX1).
Aber ich weiß nicht wie ich das dann mit dem unterbrechen und dem
einspringen machen kann bzw. ob ich überhaupt mehrere Interrupts in
einem Programm verwenden kann.
Ich bräuchte da deswegen Ideen wie ich so etwas bewerkstelligen kann.
Das Resetten wäre wohl das einfachste, in dem ich einfach die Register
alle 0 setze.
Danke im Voraus mal wieder für eure sehr hilfreiche Hilfe!
-----------------------------------------------------------
Hier noch das aktuelle Programm:
$include (reg52.inc)
Starttaster    bit P3.2
Stopptaster    bit P3.3
Resettaster    bit P3.4
Merkertaster   bit P3.5
org 0000h
  LJMP start
;Einsprungadresse Interrupt-Serviceroutine für Timer 0
org 000Bh
      LJMP isrTimer0
org 0100h
start:  CALL initRegister  ;Register initialisieren
  CALL initTimer0    ;Interrupt Timer initialisieren
loop:  LJMP loop    ;Endlosschleife
initRegister:
      MOV  R0,#0        ;bestimmt welche der Anzeigen aktiv ist
      MOV  R1,#0         ;Zähler von o bis 249 -->4ms*250 = 1 Sec
      MOV  R2,#0        ;für Segment rechts     Sekunden Einer
      MOV  R3,#0         ;für Segment Mitte rechts   Sekunden Zehner
      MOV  R4,#0         ;für Segment Mitte links   Minuten Einer
      MOV  R5,#0         ;für Segment links     Minuten Zehner
      RET
initTimer0:
      SETB  ET0            ;Freigabe Timer0. Bit im Reg IE,
Reg.-Adresse
      SETB   EA              ;Globale INT Freigabe. Bit im Reg IE
      MOV  TMOD,#00000001b    ;Timer0 konfiguieren, Modus1: 16bit
Zaehler
      MOV  TL0,#060h        ;Timer0 vorladen
      MOV  TH0,#0F0h        ;65536 - 4000 = 61536 = F060h (4ms)
      SETB  TR0            ;Start Timer0
      RET
;Interrupt-Service-Routine für Timer0
isrTimer0:
      MOV  TL0,#060h        ;Timer0 erneut vorladen
      MOV  TH0,#0F0h        ;65536 - 4000 = 61536 = F060h (4ms)
      CALL  neueUhrzeit        ;neue Uhrzeit berechnen
      CALL  ausgabe          ;berechnete Uhrzeit ausgeben
      RETI                ;Ende der Interrupt-Service-Routine
neueUhrzeit:
        INC R1      ;erhöht den Zaehler
        CJNE R1,#250d,weiter    ;Wenn die Routine noch nicht 250 mal
aufgerufen wurden
        ;dann springe nach weiter, da nichts geschieht
        MOV  R1,#0d             ;den Zaehler wieder auf Null
zurücksetzen
        INC R2                ;Sekunden Einerstelle um Eins erhöhen
        CJNE R2,#10d,weiter  ;Entsteht dadurch kein Überlauf (9->10)
springe
        ;nach weiter
        MOV R2,#0d        ;Es gab einen Überlauf, dadurch muss die
Einerstelle
        ;zurückgesetzt werden
        INC R3             ;Die Zehnerstelle der Sekunden muss um Eins
erhöht werden
        CJNE R3,#6d,weiter      ;Wenn die Zehnerstelle noch nicht 6
anzeigt springe
        ;nach weiter
        MOV R3,#0d              ;Es gab einen Überlauf, dadurch muss die
Zehnerstelle
         ;zurückgesetzt werden
  INC R4                ;Die Minuten Einerstelle um Eins erhöhen
        CJNE R4,#10d,weiter     ;Entsteht dadurch kein Überlauf (9->10)
springe
        ;nach weiter
        MOV R4,#0d              ;Es gab einen Überlauf, dadurch muss die
Einerstelle
        ;zurückgesetzt werden
  INC R5                ;Die Minuten Zehnerstelle um Eins erhöhen
        CJNE R5,#6d,weiter      ;Entsteht dadurch kein Überlauf (5->6)
springe
        ;nach weiter
        MOV R2,#0d              ;Es gab einen Überlauf, dadurch muessen
Register 2 bis 5
  MOV R3,#0d    ;zurückgesetzt werden. Da die Uhr nicht mehr
  MOV R4,#0d    ;anzeigen kann
  MOV R5,#0d
ausgabe:  INC   R0    ;erhöht die Anzeigennummer
        CJNE  R0,#04,rechts  ;Ist der Wert ungleich 4 springe nach
rechts
        MOV  R0,#0    ;Setzt den Wert der Anzeigennummer zurück
rechts: CJNE  R0,#0,mire  ;Ist die Anzeigennummer ungleich 0 springe
nach mire
        MOV  A,R2    ;Schreibe den berechneten Uhrzeitwert in den Akku
        CALL  BCD7Seg    ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A    ;Gibt den im Akku gespeicherten Wert auf P1 aus
        MOV  P2,#00001110b  ;gibt die Anzeigenposition auf P2 aus
mire:   CJNE  R0,#1,mili  ;Ist die Anzeigennummer ungleich 1 springe
nach mili
        MOV  A,R3    ;Schreibe den berechneten Uhrzeitwert in den Akku
        CALL  BCD7Seg    ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A    ;Gibt den im Akku gespeicherten Wert auf P1 aus
        MOV  P2,#00001101b  ;gibt die Anzeigenposition auf P2 aus
mili:   CJNE  R0,#2,links  ;Ist die Anzeigennummer ungleich 2 springe
nach links
        MOV  A,R4    ;Schreibe den berechneten Uhrzeitwert in den Akku
        CALL  BCD7Seg    ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A    ;Gibt den im Akku gespeicherten Wert auf P1 aus
        MOV  P2,#00001011b  ;gibt die Anzeigenposition auf P2 aus
links:   CJNE  R0,#3,m1    ;Ist die Anzeigennummer ungleich 3 springe 
nach
m1
        MOV  A,R5    ;Schreibe den berechneten Uhrzeitwert in den Akku
        CALL  BCD7Seg    ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A    ;Gibt den im Akku gespeicherten Wert auf P1 aus
        MOV  P2,#00000111b  ;gibt die Anzeigenposition auf P2 aus
m1:      RET      ;Rücksprung aus dem Unterprogramm
; Wandelt vom BCD-Code in den 7-Segment-Code
BCD7Seg:  INC  A
      MOVC  A,@A+PC
      RET
      DB  3Fh, 06h, 5Bh, 4Fh, 66h, 6Dh, 7Dh, 07h, 7Fh, 6Fh
END
  > So soll das Programm ja auf Tastendruck gestartet, gestoppt, die > aktuelle Zeit gespeichert und die Zeit resettet werden. Das ist schon mal falsch. Dein Programm läuft die ganze Zeit. Du kannst es nicht starten, stoppen oder was auch immer. Das Programm läuft bereits. Was du willst ist den Zeitgeber starten und stoppen! Der Zeitgeber ist in deinem Programm der Timer. Soblad der Timer nicht mehr läuft steht auch deine Uhr. Sobald du den Timer wieder laufen lasst, läuft auch deine Uhr. Das ist ein bischen so wie bei den altmodischen (schönen) Pendeluhren. Die werden gestoppt indem man das Pendel stoppt. In deinem Programm ist der Timer mit dem Pendel gleichzusetzen. Nur gibt es in deinem Programm eine klitzekleine Eigenheit: Mit dem Zeitgeber gekoppelt ist auch das Anzeigen der Uhrzeit. Das muesste man entkoppeln, da ja die Anzeige auch weiterhin gemacht werden soll, auch wenn der Zeitgeber selbst nicht läuft. Das ist aber kein Problem, denn letztendlich läuft ja dein Hauptprogramm irgendwann hier auf: loop: LJMP loop ;Endlosschleife und bleibt hier hängen. Alles was jetzt noch passiert ist, dass in regelmässigen Zeitabstaenden die Timer-Interrupt Funktion aufgerufen wird, die das 'Uhrwerk' weiterschaltet und die Anzeige bedient. Und natürlich läuft das Hauptprogramm wie wild in dieser Schleife weiter. Das eigentliche Hochzählen der Uhr erfolgt, wie bereits gesagt ja in einem Interrupt. Es spricht aber nichts dagegen, das Anzeigen der Uhrzeit genau in diese Schleife zu verlagern ... loop: call ausgabe LJMP loop und dafür die Ausgabe aus dem Interrupt wegzulassen: ;Interrupt-Service-Routine für Timer0 isrTimer0: MOV TL0,#060h ;Timer0 erneut vorladen MOV TH0,#0F0h ;65536 - 4000 = 61536 = F060h (4ms) CALL neueUhrzeit ;neue Uhrzeit berechnen RETI ;Ende der Interrupt-Service-Routine Siehst du. Jetzt zeigt dein Programm ständig die Uhrzeit an, und durch starten bzw. stoppen des Timers, läuft die Uhr oder sie läuft eben nicht. (Ich muss jetzt ein bischen raten, da ich den 8051 nicht kenne) dh. das hier SETB ET0 ;Freigabe Timer0. Bit im Reg IE, lässt die Uhr laufen. Während das hier CLRB ET0 die Uhr wieder anhält. Alles was du jetzt noch tun musst ist, die Schalter abfragen und je nachdem welcher Schalter gedrückt wurde, entweder das eine oder das andere ausführen.
danke für die schnelle und gute Antwort ich werde es gleich testen! Sollte das einwandfrei funktionieren muss ich mir nur noch was für den "Merker" überlegen.
> Alles was du jetzt noch tun musst ist, die Schalter abfragen
Ich vergass:
Das Abfragen der Schalter kannst du natuerlich auch in
der Hauptschleife machen. Wenn du 2 verschiedene Schalter
für starten und stoppen nimmst, brauchst du das dann auch
nicht entprellen. Sobald der 'Start'-Schalter gedrückt wurde,
gibst du den Timer frei, sobald der 'Stop'-Schalter gedrückt
wurde, stoppst du den Timer wieder:
loop:   call  ausgabe   ; damit wir auch was sehen
        hole Zustand vom Schalter 'Start'
        ist er gedrückt?
          wenn ja: SETB ET0
        hole Zustand vom Schalter 'Stop'
        ist er gedrückt?
          wenn ja: CLRB ET0
        jmp loop
Die Details wie man an die Schalter rankommt, bzw. woran man
erkennt ob einer gedrückt ist, weis ich nicht. Das musst
du wissen. Aber vom Prinzip her müsste das schon so funktionieren.
In diese Schleife baust du dann auch noch eine 3.te Schalterabfrage
ein, die das Uhrwerk wieder auf 0 zurücksetzt. Da du mitlerweile
ja verstehst wie die Uhr funktioniert, sollte das kein grosses
Problem mehr sein: Einfach alle Register die das Uhrwerk bilden
auf 0 setzen.
  In der schnelle mal Programmiert:
--------------------
org 0100h
start:     JB Starttaster,SETB ET0  ;Startaster abfragen
     LJMP start        ;Endlosschleife
     CALL initRegister      ;Register initialisieren
     CALL initTimer0      ;Interrupt Timer initialisieren
loop:     CALL Ausgabe        ;berechnete Uhrzeit immer ausgeben
     JB Stopptaster,CLRB ET0  ;Stopptaster abfragen
     JB Resettaster,springer  :Resettaster abfragen
     LJMP loop        ;Endlosschleife
springer:  CALL initRegister      ;Register zurücksetzen
------------------------------
Kann mir jemand bestätigen ob sowas funktionstüchtig wäre?
Ich hatte schoneinmal Probleme mit dem springen (also JB Befehl).
  start:     JB Starttaster,SETB ET0  ;Startaster abfragen
das kann ich mir nicht vorstellen.
Wie alle Srungbefehle, wird JB eine Ziel haben wollen
zu dem es springt. SETB ET0 ist aber kein Ziel. SETB ET0
ist eine Anweisung!
Aber du kannst die Logik ja auch umdrehen. Springen wenn
der Taster nicht gedrückt ist:
loop:             call ausgabe
                  JNB Starttaster, Weiter_bei_Stop
                  SETB ET0
Weiter_bei_Stop:  JNB Stopptaster, Weiter_bei_Reset
                  CLRB ET0
Weiter_bei_Reset: JNB Resettaster, Weiter_bei_
                  CALL initRegister
Weiter_bei_:      jmp loop
  > -------------------- > org 0100h > start: JB Starttaster,SETB ET0 ;Startaster abfragen > LJMP start ;Endlosschleife Warum willst du unbedingt, dass deine Stoppuhr nur ein einziges mal zu gebrauchen ist, wenn du mit demselben Aufwand auch eine Stoppuhr bauen kannst, die man starten, stoppen, starten, stoppen, resetten, starten, stoppen etc. kann? Und zwar in dieser oder jeder anderen beliebigen Reihenfolge. Ausserdem ist es keine sehr gute Idee einen Timer freizugeben noch bevor man ihn initialisiert hat :-)
Du musst noch sehr viel genauer werden.
Durch einen Cut&Paste Fehler hast du dir dein Pgm
zerstört.
Wenn ich mir den Pgm-Teil 'ausgabe' anschaue,
denn sehe ich dort im wesentlichen 4 mal den
gleichen Abschnitt:
rechts: CJNE  R0,#0,mire  ;Ist die Anzeigennummer ungleich 0
                          ;springe nach mire
        MOV  A,R2    ;Schreibe den berechneten Uhrzeitwert in den Akku
        CALL  BCD7Seg    ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A    ;Gibt den im Akku gespeicherten Wert auf P1 aus
        MOV  P2,#00001110b  ;gibt die Anzeigenposition auf P2 aus
lediglich das Register im
       MOV A,R2
und das Label als Sprungziel, wo es weiter geht variiert.
Das ist aber seltsam, eigentlich würde ich erwarten, daß es
einen Unterschied in der Ausgabe gibt, je nachdem auf welche
Stelle du ausgibst. Da ist aber keiner.
Jett scrolle ich mal hoch, ganz an den Anfang:
Und siehe da. In jedem der 4 Abschnitte wird ein anderer
Wert an P2 ausgegeben. Ganz so wie man es erwarten würde.
Denn irgendwo muss es ja einen Unterschied geben, sonst landen
ja alle Ausgaben immer an der LED-Anzeige.
  > sonst landen ja alle Ausgaben immer an der LED-Anzeige. da fehlt ein sinnentstellendes Wort: sonst landen ja alle Ausgaben immer an derselben LED-Anzeige und verteilen sich nicht auf die 4 vorhandenen.
das verstehe ich nicht ganz: MOV P2,#00001110b hier wird doch bestimmt auf welche der 4 Anzeigen der Wert angezeigt werden soll? Oder verstehe ich da irgendetwas falsch?
@Karl Heinz, woher Du Deine Ruhe nimmst und die Zeit..... Hut ab !!!! Mir gefällt Deine Art die Sachen zu erklären, die für uns so selbstvertsändlich sind..... Habe ich mal beim "44780 zeigt nur Kästchen" Tread versucht.. 100 Einträge nur für den Init des LCD !!! gab nur böse Haue von "Abgehobenen ".. als ich versucht habe es auf die Art " MCU an Display...habe Daten" zu erklären Mach weiter so.... stephan Henning
> das verstehe ich nicht ganz: > > MOV P2,#00001110b > > hier wird doch bestimmt auf welche der 4 Anzeigen der Wert angezeigt > werden soll? Autsch. Kommanmdo retour. Ich hab mich verlesen. War ein langer Tag, und heiss war's auch und ... Tschuldigung. Mein Fehler
Macht ja nix, Fehler sind Menschlich... Ich konnte das Programm leider noch nicht testen, da die Übertragungssoftware (Pony Prog) irgendwelche Fehler anzeigt (bezüglich I/O Schnittstelle oder sowas in die Richtung). Were das ganze jetzt aber mal bei mir zuhause ausprobieren... Vorerst gehe ich aber mal davon aus, dass das Programm so funktioniert. Was aber noch fehlt ist eben dieser Merker, welcher sich eine gestoppte Uhrzeit merken soll. Ich habe schon verschiedene Überlegungen angestellt, z.B. - Wert in akku schreiben (ist aber wohl zu groß, oder?) - Wert einem der beiden freistehenden Register (R6 und R7) zuweisen, aber da bekomm ich ja auch nicht die 4 Zahlen rein - Wert einfach an eine bestimmte Adresse schreiben, z.B. 060h Alles ist aber nach meinen Erkenntnissen nicht möglich, deswegen erbitte ich nun wiedereinmal eure Hilfe. Nur eine Idee würde mich eventuell schon weiter bringen! Danke im Voraus!
Na ja.
Zuerst mal musst du überlegen, dass du sowas wie
'die Zeit' als eine Einheit ja gar nicht hast.
'Die Zeit' ist verteilt auf (Moment, muss zaehlen gehen)
die Register R1 bis R5
    R1     der 4ms Zaehler
    R2     Einerstelle der Sekunden
    R3     Zehnerstelle der Sekunden
    R4     Einerstelle der Minuten
    R5     Zehnerstelle der Minuten
Wenn du also 'die Zeit' irgendwo zwischenspeichern möchtest,
dann wirst du halt die Inhalte dieser 5 Register irgendwo
sichern muessen. Wieso soll das nicht möglich sein, die Inhalte
dieser 5 Register im Speicher in 5 Bytes abzulegen?
  warum ist das nicht möglich ??? 4 dezimale Zahlen passen doch in 2 Bytes rein. 2 Register oder 2 Speicherstellen. 1 Byte besteht aus 2 Nibbles und mit einem Nibble kann man Zahlen von 0-15 darstellen richtig ?? da wir aber dezimal nur bis 10 zählen nimmst Du ein Nibble und dividierst es durch 15 (0Ah) und hast den dez. Wert. Dann das 2. Nibble usw.
@stephan Machs nicht unnötig kompliziert. Clemens schwimmt doch sowieso schon hinten und vorne. Wenn du ihn jetzt auch noch in Nibble-verarbeitung reinjagst ... (Ich weiss das das alles in Wirklichkeit nicht kompliziert ist. Nur: Wie erklärst du das jemanden der noch nicht mal 5 Bytes irgendwo im Speicher ablegen und wieder laden kann)
moin moin, @Stephan >> da wir aber dezimal nur bis 10 zählen nimmst Du ein Nibble und >> dividierst es durch 15 (0Ah) und hast den dez. Wert. 2Zeile = 2Fehler, bitte nochmal. @Clemens Merker? Machs wie mit einem Spickzettel in der Schule, da kommt doch auch das rauf was Du Dir nicht merken kannst/willst. Nimm also Speicherzellen z.B. ab 60H und lege da die Werte ab, welche Du später noch gebrauchen tust. Vergleiche das mit dem, so wie Du es manuell tun würdest. Mit Gruß Pieter
natürlich zählen wir nur von 0 bis 9, kommt davon wenn man auf Arbeit....
So, mittlerweile bin ich fertig mit diesem Projekt und möchte mich bei
euch für eure Hilfe bedanken.
Weiter unten findet ihr den funktionierenden, getesteten Code.
Da ich immernoch einen Drang habe eine bessere Note zu erreichen, frage
ich mich ob es nicht möglich wäre statt den Minuten und den Sekunden,
die Sekunden und die Hundertstel auszugeben.
Wie wäre das mit meinem Programm realisierbar?
Habt ihr irgendwelche gute Vorschläge?
Für Antworten wie immer dankbar!
Gruß
Clemens
-----------------------------------------------
Aktuelles Programm (getestet!):
$include (reg52.inc)
Starttaster    bit P0.0
Stopptaster    bit P0.1
Resettaster    bit P0.2
Merkertaster   bit P0.3
Abruftaster    bit P0.4
org 0000h
  LJMP start
;Einsprungadresse Interrupt-Serviceroutine für Timer 0
org 000Bh
      LJMP isrTimer0
org 0100h
start:            CALL initRegister  ;Register initialisieren
                CALL initTimer0    ;Interrupt Timer initialisieren
loop:            CALL ausgabe                  ;berechnete Werte 
ausgeben
                   JB Starttaster, Weiter_bei_Stop      ;Abfrage des
Starttasters
                    SETB ET0                      ;Freigabe Timer 0
Weiter_bei_Stop:    JB Stopptaster, Weiter_bei_Merker    ;Abfrage des
Stopptasters
                      CLR ET0                      ;Zuruecksetzen des 
Timers
Weiter_bei_Merker:  JB Merkertaster, Weiter_bei_Abruf    ;Abfrage des
Merkertasters
              MOV  061h,R0                  ;Schreibe die gestoppten 
Werte
              MOV  062h,R1                          ;an Stellen im 
Speicher
              MOV  063h,R2
              MOV  064h,R3
              MOV  065h,R4
              MOV  066h,R5
Weiter_bei_Abruf:    JB Abruftaster,Weiter_bei_Reset      ;Abfrage
Abfragetaster
              MOV  R0,061h                  ;gespeicherte Werte den
              MOV  R1,062h                          ;Registern zuweisen
              MOV  R2,063h
              MOV  R3,064h
              MOV  R4,065h
              MOV  R5,066h
              CALL ausgabe                  ;gespeicherte Werte ausgeben
Weiter_bei_Reset:   JB Resettaster, Weiter_bei_nichts    ;Abfrage
Resettaster
                     CALL initRegister               ;Register 
zuruecksetzen
Weiter_bei_nichts:    LJMP loop                    ;Endlosschleife
initRegister:
      MOV  R0,#0          ;bestimmt welche der Anzeigen aktiv ist
      MOV  R1,#0           ;Zähler von o bis 249 -->4ms*250 = 1 Sec
      MOV  R2,#0          ;für Segment rechts     Sekunden Einer
      MOV  R3,#0           ;für Segment Mitte rechts   Sekunden Zehner
      MOV  R4,#0           ;für Segment Mitte links   Minuten Einer
      MOV  R5,#0           ;für Segment links     Minuten Zehner
      RET
initTimer0:
      SETB   EA                  ;Globale INT Freigabe. Bit im Reg IE
      MOV  TMOD,#00000001b      ;Timer0 konfiguieren, Modus1: 16bit
Zaehler
      MOV  TL0,#060h            ;Timer0 vorladen
      MOV  TH0,#0F0h            ;65536 - 4000 = 61536 = F060h (4ms)
      SETB  TR0                ;Start Timer0
      RET                ;Ruecksprung aus dem Unterprogramm
;Interrupt-Service-Routine für Timer0
isrTimer0:
      MOV  TL0,#060h          ;Timer0 erneut vorladen
      MOV  TH0,#0F0h          ;65536 - 4000 = 61536 = F060h (4ms)
      CALL  neueUhrzeit       ;neue Uhrzeit berechnen
      RETI                    ;Ende der Interrupt-Service-Routine
neueUhrzeit:
        INC R1                ;erhöht den Zaehler
        CJNE R1,#250d,weiter      ;Wenn die Routine noch nicht 250 mal
aufgerufen wurden
                          ;dann springe nach weiter, da nichts geschieht
        MOV  R1,#0d               ;den Zaehler wieder auf Null
zurücksetzen
        INC R2                  ;Sekunden Einerstelle um Eins erhöhen
        CJNE R2,#10d,weiter      ;Entsteht dadurch kein Überlauf (9->10)
springe
                         ;nach weiter
        MOV R2,#0d              ;Es gab einen Überlauf, dadurch muss die
Einerstelle
                          ;zurückgesetzt werden
        INC R3                  ;Die Zehnerstelle der Sekunden muss um
Eins erhöht werden
        CJNE R3,#6d,weiter        ;Wenn die Zehnerstelle noch nicht 6
anzeigt springe
                          ;nach weiter
        MOV R3,#0d                ;Es gab einen Überlauf, dadurch muss
die Zehnerstelle
                         ;zurückgesetzt werden
        INC R4                 ;Die Minuten Einerstelle um Eins erhöhen
        CJNE R4,#10d,weiter       ;Entsteht dadurch kein Überlauf
(9->10) springe
                          ;nach weiter
        MOV R4,#0d                ;Es gab einen Überlauf, dadurch muss
die Einerstelle
                          ;zurückgesetzt werden
        INC R5                 ;Die Minuten Zehnerstelle um Eins erhöhen
        CJNE R5,#6d,weiter        ;Entsteht dadurch kein Überlauf (5->6)
springe
                          ;nach weiter
         MOV R2,#0d              ;Es gab einen Überlauf, dadurch muessen
Register 2 bis 5
        MOV R3,#0d            ;zurückgesetzt werden. Da die Uhr nicht 
mehr
        MOV R4,#0d            ;anzeigen kann
        MOV R5,#0d
weiter:  RET              ;Ruecksprung aus dem Unterpogramm
ausgabe:  INC   R0          ;erhöht die Anzeigennummer
        CJNE  R0,#04,rechts    ;Ist der Wert ungleich 4 springe nach
rechts
        MOV  R0,#0          ;Setzt den Wert der Anzeigennummer zurück
rechts: CJNE  R0,#0,mire      ;Ist die Anzeigennummer ungleich 0 springe
nach mire
        MOV  A,R2          ;Schreibe den berechneten Uhrzeitwert in den
Akku
        CALL  BCD7Seg        ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A          ;Gibt den im Akku gespeicherten Wert auf P1
aus
        MOV  P2,#00001110b    ;gibt die Anzeigenposition auf P2 aus
mire:   CJNE  R0,#1,mili      ;Ist die Anzeigennummer ungleich 1 springe
nach mili
        MOV  A,R3          ;Schreibe den berechneten Uhrzeitwert in den
Akku
        CALL  BCD7Seg        ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A          ;Gibt den im Akku gespeicherten Wert auf P1
aus
        MOV  P2,#00001101b    ;gibt die Anzeigenposition auf P2 aus
mili:   CJNE  R0,#2,links    ;Ist die Anzeigennummer ungleich 2 springe
nach links
        MOV  A,R4          ;Schreibe den berechneten Uhrzeitwert in den
Akku
        CALL  BCD7Seg        ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A          ;Gibt den im Akku gespeicherten Wert auf P1
aus
        MOV  P2,#00001011b    ;gibt die Anzeigenposition auf P2 aus
links:   CJNE  R0,#3,m1      ;Ist die Anzeigennummer ungleich 3 springe
nach m1
        MOV  A,R5          ;Schreibe den berechneten Uhrzeitwert in den
Akku
        CALL  BCD7Seg        ;Ruft Werte für BCD7Seg Anzeige auf
        MOV  P1,A          ;Gibt den im Akku gespeicherten Wert auf P1
aus
        MOV  P2,#00000111b    ;gibt die Anzeigenposition auf P2 aus
m1:      RET              ;Rücksprung aus dem Unterprogramm
; Wandelt vom BCD-Code in den 7-Segment-Code
BCD7Seg:  INC  A
      MOVC  A,@A+PC
      RET
      DB  3Fh, 06h, 5Bh, 4Fh, 66h, 6Dh, 7Dh, 07h, 7Fh, 6Fh
END
  > Da ich immernoch einen Drang habe eine bessere Note zu erreichen, > frage ich mich ob es nicht möglich wäre statt den Minuten und den > Sekunden, die Sekunden und die Hundertstel auszugeben. > Wie wäre das mit meinem Programm realisierbar? Natürlich. Es ist aber an der Zeit, dass du selbst anfängst nachzudenken. Die Änderungen sind trivial sobald du verstanden hast was bei einer Uhr die Hunderstel/Sekunden anzeigt anders ist als bei einer die Minuten/Sekunden anzeigt. Hinweise: Wenn 59 Sekunden vergangen sind, was passiert dann? Muss dasselbe auch passieren wenn 59 Hunderstel Sekunden vergangen sind?
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
Mit Google-Account einloggen
  Noch kein Account? Hier anmelden.