Forum: Mikrocontroller und Digitale Elektronik Zeitschleife at89s52


von Christian (Gast)


Lesenswert?

Hallo,

ich versuche mich gerade an der Programmierung eines at89s52, mein Ziel 
ist es eine Uhr zu programmieren,

leider funktioniert meine Zeitschleife nicht ganz... :(

hier mal mein code:



;####### Programm für AT89S52 #########;


org 000h
    jmp 0100h
org 01bh
    djnz r1, zurueck
    mov r1,  #0a0h       160 000 Hz/ 160 = 100
    djnz r2, zurueck
    mov r2,#64h             100Hz/ 100 = 1s
    ljmp 0300h

zurueck:
    reti




;####### Initialisierung #########;

org 0100h

mov IE,#88h           für Interrupt T1 enable und EA Interrupts
mov  PCON,#00h
mov SCON,#00h
mov TCON,#00h
mov TMOD,#60h         für Timer1 auto Reload
mov TL1,#06h          4 000 000 Hz / 250 = 160 000
mov TH1,#06h
setb TR1

mov P0,#00h
mov P1,#00h
mov P2,#00h
mov P3,#00h
mov R0,#00h
mov R1,#0a0h
mov R2,#64h
mov R3,#00h
mov R4,#00h
mov R5,#00h


;####### Hauptprogramm #########;

mainloop:
  ljmp mainloop

org 0300h
    mov a,p1
    inc a
    mov p1,a
  jmp mainloop


das hier soll nur eine Zeitschliefe sein mit der ich den 1s-Takt nutze 
um den Port1 jede sekunde um eins zu erhöhen ich verwende einen 4Mhz 
Oszillator.

Sieht von euch jemand einen Fehler?
Laut Keil ist kein Fehler (also Tippfehler ausgeschlossen)

Danke Gruß

Christian

von Georg G. (df2au)


Lesenswert?

Verfolge doch mal den Weg des Programms, wenn eine Sekunde abgelaufen 
ist.

von Christian (Gast)


Lesenswert?

Finde den Fehler leider nicht, nach 1s springe ich zu 0300h dort erhöhe 
ich den port um 1 und anschlißend springe ich zum hauptprogramm zurück.

Was meinst du genau was nicht passt?

von Georg G. (df2au)


Lesenswert?

Du kommst aus der Interrupt Routine zu 300h, gehst aber nie wieder 
dorthin zurück. Also hängt sich der Kasten nach 1s in die 
Endlosschleife.

von Davis (Gast)


Lesenswert?

RETI wird nicht ausgeführt.

von Christian (Gast)


Lesenswert?

Danke für die schnellen und hilfreichen Antworten
habe es nun einmal umgeschrieben, jedoch funktioniert es immer noch 
nicht, liegt jetzt noch ein fehler im Programm versteckt?


;####### Programm für AT89S52 #########;


org 000h
    jmp 0100h

org 01bh

    ljmp 0300h


;####### Initialisierung #########;

org 0100h

mov IE,#88h
mov  PCON,#00h
mov SCON,#00h
mov TCON,#00h
mov TMOD,#60h
mov TL1,#06h
mov TH1,#06h
setb TR1

mov P0,#00h
mov P1,#00h
mov P2,#00h
mov R0,#00h
mov R1,#0a0h
mov R2,#64h
mov R3,#00h
mov R4,#00h
mov R5,#00h


;####### Hauptprogramm #########;

mainloop:
  setb p2.2
  ljmp mainloop

;##### Interrupt Zeitschleife###;

org 0300h

    djnz r1, zurueck
    mov r1,  #0a0h
    djnz r2, zurueck
    mov r2,#64h

    mov a,p1
    inc a
    mov p1,a

    inc p0

    jmp zurueck

zurueck:
    reti

von Georg G. (df2au)


Lesenswert?

Ich war der Meinung, dein Prozessor sei MCS51 kompatibel.

Dieser Befehl
> inc p0
erscheint mir illegal. Aber ich lasse mich gern belehren. Läuft der 
Assembler wirklich ohne Fehler durch?

von Davis (Gast)


Lesenswert?

1. Statt "... jedoch funktioniert es immer noch nicht, ...", schreibe 
was du erwartest und was nicht funktioniert.

2. Du arbeitest mit Keil: der hat einen Simulator, mit dem du Schritt 
für Schritt durch dein Programm gehen kannst.

von Davis (Gast)


Lesenswert?

"inc p0" funktioniert. Aber: P0 ist ein wenig tükisch, da der Port keine 
PullUps hat.

von Christian (Gast)


Angehängte Dateien:

Lesenswert?

ja danke für die Infos, anscheinend springt mein Mikrocontroller nicht 
in die Interruptschleife, da dieser mir die Ports in der 
Sekundenschleife nicht einschaltet.

Ich teste es bisher real an meinem MC.
Verschaltung sollte passen.

Weis einer von euch ob und wenn ja wie ich einen Counter am besten mit 
4Mhz in Keil simulieren kann?

Habe in Keil soweit es ging auch alles durchgetest dort hat es 
funktioniert

Ich tendiere dazu dass entweder das die Initialisierung des MC falsch 
ist oder es falsch aufgespielt wird.
Das mit den Pull ups schaue ich mir auch mal an


Ich werde mal einige tests durchführen^^

Vielen dank für eure gute Hilfe

von Ralf (Gast)


Lesenswert?

> hier mal mein code:
Sorry, aber das ist kein Code, das sind Assembler-Instruktion, mehr 
nicht. Code ist dokumentiert. Das machst du so gut wie gar nicht.
Ist nicht bös gemeint. Aber mach bitte folgendes, bevor du weiter den 
Fehler suchst:
Schreib hinter jede Instruktion einen Kommentar, was du da bezwecken 
willst.
Das bedeutet nicht, dass bei einem 'mov x,y' dann 'schreibe y in x' 
hinten dran steht, das sieht man anhand der Instruktion.
Viel mehr sollte da dran stehen, was du bezwecken willst. So steht 
beispielsweise bei 'org 01bh' dann 'Timer 1 Interrupt Vektor', bei 'mov 
TMOD,#60h' steht 'Timer 1 konfiguriert als ...' und bei 'mov TL1,#06h' 
steht 'Reloadwert für xxx ns/µs'.
Glaub's mir, das bringt's wirklich, denn danach gehst du Instruktion für 
Instruktion durch, und vergleichst, ob sie tatsächlich das macht, was 
der Kommentar angibt.

Was deinen Timer-Interrupt betrifft: Ohne Kommentar (da wären wir schon 
bei o.g. Thema) sehe ich nicht, in welcher Betriebsart der läuft, und 
ich bin jetzt zu faul nachzuschauen.
Ich vermute 8-Bit mit automatischer Rückladung. Keine Ahnung ob das 
Interrupt-Flag hierbei automatisch gelöscht wird wenn er in den 
Interrupt springt. Falls nicht, musst du manuell löschen, sonst bist du 
nach dem RETI + 1 Instruktion wieder im Interrupt.
Zweitens: in einem Interrupt macht man niemals Verzögerungsschleifen, 
im Timer-Interrupt schon gar nicht. Das blockt nur den 
Nicht-Interrupt-Ablauf - bitte gleich abgewöhnen. Interrupts müssen 
immer so lang wie nötig und noch mehr so kurz wie möglich sein.

Lege stattdessen eine Variable an. Diese Variable gehört nur dem 
Interrupt. Sie wird vom restlichen Programm nur während der 
Initialisierung mit einem Wert vorbelegt.
Diese Variable zählst du in deinem Interrupt runter, nimm dazu die 
DJNZ-Instruktion, sie soll auf RETI springen, wenn die Variable nach dem 
Dekrementieren ungleich Null ist.
Nach der DJNZ-Instruktion belegst du die Variable wieder mit dem 
ursprünglichen Wert, und führst deine restlichen Aktionen mit deinen 
Ports aus.
Fertig.

Eine Erweiterung wäre dann, wenn das Programm mal so läuft wie 
gewünscht, dass du beispielsweise die Portgeschichte ebenfalls ins 
Hauptprogramm auslagerst und der Timer-IRQ wirklich nur solche Sachen 
wie Zeitmessungen etc. macht, das zeigen wir dir dann, wenn dein 
Programm läuft.

Mach erstmal das von mir vorgeschlagene Kommentieren, damit hast du 
nämlich eine 1A-Möglichkeit zu vergleichen was du meinst zu tun und 
was tatsächlich passiert.

Ralf

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Da du den MC anscheinend mit 4 Mhz taktest, läuft der Timer doch mit 
4.000.000/12 = 333.333,333 Hz. Mir ist nicht klar, wie du da auf eine 
genaue Sekunde kommen willst.
Bedenke, ein Maschinenzyklus beim AT89C52 und AT89S52 ist 1/12 Fosc.
Besser ist es, den MC mit z.B. 12 oder 24 MHz zu takten.

von Achim S. (Gast)


Lesenswert?

Wenn ich mich nicht täusche, hast du TMOD unsinnig gesetzt. 
(Counter-Mode statt Timer). Den Hinweis von Ralf zur Nutzung von 
Kommentaren würde ich unterstützen...

Ralf schrieb:
> beispielsweise bei 'org 01bh' dann 'Timer 1 Interrupt Vektor', bei 'mov
> TMOD,#60h' steht 'Timer 1 konfiguriert als ...' und bei 'mov TL1,#06h'
> steht 'Reloadwert für xxx ns/µs'.

von Christian (Gast)


Lesenswert?

Bei meinem ersten Post habe ich versucht es ein wenig zu erkären, war 
aber anscheinend nich ausführlich genug, hier etwas ausführlicher, das 
reicht erstmal für heute.
Wie gefällt euch dieser Aufbau des Programmes?
Und danke für den Hinweis des Maschinenzyklus 12/osz hatte ich nicht 
gewusst.

;####### Programm für AT89S52 #########;


org 000h
    jmp 0100h     ;Sprung zu Haupt;


org 01bh

    ljmp 0300h     ;Sprung zu Interrupt Zeitschleife



;####### Initialisierung #########;

org 0100h

mov IE,#88h  ;1xEA bit7 und 1x ET1 bit3 (Freigabe Timer 1-interrupt);
mov  PCON,#00h    ;nicht benötigt;
mov SCON,#00h
mov TCON,#00h
mov TMOD,#60h     ;1xC/T bit6 und Mode 8-bit-Timer/Counter Reload M1 
bit5;
mov TL1,#06h     ;Reloadwert für ein 12MHz Oszi, Berechnung: 12M / 12 = 
1M= Maschinenzyklus, 1M / 250 = 4 000 Bitwert: 256-250 = 6;
mov TH1,#06h
setb TR1       ;start Timer 1;

mov P0,#00h       ;Nullsetzen;
mov P1,#00h
mov P2,#00h
mov R0,#00h
mov R1,#0a0h     ;R1 für Zeitberechnung 4 000 / 160 = 25 deshalb 
Register Vorladen mit 160 = 64hex;
mov R2,#19h       ;R2 für Zeitberechnung 25 / 25 =1 deshalb Register 
vorladen mit 25 (19hex)
;
mov R3,#00h
mov R4,#00h
mov R5,#00h


;####### Hauptprogramm #########;

mainloop:
    mov a,r1  ; Abfrage ob erster Register 0
                           wenn ja Abfrage des 2. Registers auf 0;

    jz wert1

    jmp mainloop

  jmp mainloop

wert1:
    mov r1,#0a0h  ;Reloadwert für Register 1 ( 4 000 Hz / 160);
    dec r2
    mov a,r2

    jz wert2
    jmp mainloop

  jmp mainloop

wert2:  mov r2,#64       ;Reloadwert für Register 2 (25Hz /25);


    mov a,p1  ;Ausgabe des Sekundentaktes an P1;
    inc a
    mov p1,a

  jmp mainloop


;##### Interrupt Zeitschleife###;

org 0300h

    dec r1       ;Register 1 erhöhen bei jeden Interrupt;

    reti

von Achim S. (Gast)


Lesenswert?

stimmt, ganz oben war der Kommentar in der entscheidenden Zeile ja schon 
drin. Aber

> mov TMOD,#60h     ;1xC/T bit6 und Mode 8-bit-Timer/Counter Reload M1

ist meiner Ansicht nach immer noch die falsche Initialisierung. Damit 
zählst du nicht Maschinenzyklen, sondern Flanken am Pin T1. Und weil 
dort nicht viele Flanken ankommen, wird der Counter-Überlauf nicht 
erreicht.

Versuchs mal mit

mov TMOD,#20h     ;Timer 1 im Mode 8-bit-Timer mit Auto-Reload

von Christian (Gast)


Lesenswert?

Achim S. schrieb:
> stimmt, ganz oben war der Kommentar in der entscheidenden Zeile ja schon
> drin. Aber
>
>> mov TMOD,#60h     ;1xC/T bit6 und Mode 8-bit-Timer/Counter Reload M1
>
> ist meiner Ansicht nach immer noch die falsche Initialisierung. Damit
> zählst du nicht Maschinenzyklen, sondern Flanken am Pin T1. Und weil
> dort nicht viele Flanken ankommen, wird der Counter-Überlauf nicht
> erreicht.
>
> Versuchs mal mit
>
> mov TMOD,#20h     ;Timer 1 im Mode 8-bit-Timer mit Auto-Reload



Okay, danke für den Hinweis, ich dachte das ich Counter auswählen muss 
wenn ich einen externen Quarz verwende bei XTAL1/XTAL2, und Timer wäre 
der interne Takt gewesen.

Erkennt der MC automatisch das ein Quarz angesteckt ist?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Christian schrieb:
> Wie gefällt euch dieser Aufbau des Programmes?

Noch nicht gut - du gibst z.B. die Interrupts frei, bevor überhaupt 
etwas initialisiert ist. Programmierst du in HEX? Warum benutzt du keine 
Sprunglabel?
z.B.:
1
   org   01bh 
2
   ljmp  TIMER_OVF
3
; und dann  später im Programm
4
TIMER_OVF:
5
    dec r1
6
    reti

Code markierst du in diesem Forum mit einen 'c' Tag in eckigen Klammern 
und ohne die Anführungsstriche. Beenden tust du den Codeschnipsel mit 
'/c' in eckigen Klammern.
Die nächste rätselhafte Stelle ist für mich diese hier:
1
  mov R1,#0a0h     ;R1 für Zeitberechnung 4 000 / 160 = 25 deshalb 
2
  Register Vorladen mit 160 = 64hex;
Wie jetzt? Du sagst, das du mit 64hex lädst, tust aber 0a0h ins 
Register?
Definiere dir lieber Konstanten oder schreibe die Werte in Dezimal. Das 
stört den Assembler normalerweise gar nicht und du sparst dir das 
Umrechnen.

Christian schrieb:
> Erkennt der MC automatisch das ein Quarz angesteckt ist?

Ohne Quarz startet ein AT89S52 gar nicht, da er keinerlei internen 
Oszillator besitzt. Du musst einen Quarz oder Resonator an XTAL1 und 
XTAL2 lt.Datenblatt beschalten.

von Christian (Gast)


Angehängte Dateien:

Lesenswert?

Danke für die Info, mein Problem ist erstmal glöst der Sekundentakt 
läuft! dafür besten dank an Achim und den danderen fleißigen Ratgebern.
Die anderen Ratschläge werde ich mir auch zu Herzen nehmen.

Gruß Christian

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.