Forum: Mikrocontroller und Digitale Elektronik Projekt Stoppuhr in 8051 Assembler


von Clemens (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Stephan H. (stephan-)


Lesenswert?

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

von Jasmin (Gast)


Lesenswert?

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.

von Clemens (Gast)


Lesenswert?

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.

von Stephan H. (stephan-)


Lesenswert?

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

von norad (Gast)


Lesenswert?

ä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

von buz11 (Gast)


Lesenswert?


von Oliver (Gast)


Lesenswert?

Wer bitte schreibt heute noch Programme in Assembler?

von Stephan H. (stephan-)


Lesenswert?

der,
der Ahnung davon hat und Recourcenaufteilung der MCU nicht einem
Compiler überlassen will !

von Oliver (Gast)


Lesenswert?

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.

von Stephan H. (stephan-)


Lesenswert?

jedem das Seine ......

von Clemens (Gast)


Lesenswert?

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

von norad (Gast)


Lesenswert?

@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!
             ...

von Joe (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Stephan H. (stephan-)


Lesenswert?

@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 !!!!!...

von Peter D. (peda)


Lesenswert?

"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

von Clemens (Gast)


Lesenswert?

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!

von Stephan H. (stephan-)


Lesenswert?

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

von Stephan H. (stephan-)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Clemens (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Clemens (Gast)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Clemens (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

> --------------------
> 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 :-)

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Clemens (Gast)


Lesenswert?

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?

von Stephan H. (stephan-)


Lesenswert?

@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

von Karl H. (kbuchegg)


Lesenswert?

> 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

von Clemens (Gast)


Lesenswert?

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!

von Karl heinz B. (kbucheg)


Lesenswert?

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?

von Stephan H. (stephan-)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

@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)

von Pieter (Gast)


Lesenswert?

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

von Stephan H. (stephan-)


Lesenswert?

natürlich zählen wir nur von 0 bis 9,

kommt davon wenn man auf Arbeit....

von Clemens (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

> 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
Noch kein Account? Hier anmelden.