mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik exact timing - 8051 - synchronisierte Zustandsmaschine


Autor: daniel (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die Fakten: AT89C2051 - 11.0952 Mhz(Timer 1 fuer Baudrate) - Timer0 fuer 
5 Sekunden-Zyklus( Zyklus beinhaltet 4 Zustaende aller 2,5 sek, 2 x 0,5 
sek, und 1,5 sek)

fuer den Baudratengenerator (9600Baud) verwende ich den Wert
0xFD, ist dieser richtig? der Timer arbeitet im Autoreload Modus

nun das Hauptproblem:

der Timer 0:
Ich habe mich auf den  Zaehlerwert von 9246dez festgelegt, somit habe 
ich einen 10 ms Interrupt. Timer wird also mit 65536 - 9246 = 56290dez 
(0xDBE1) geladen und gestartet.
Um mein Zuklus mit einzelnen Zustandszeiten zu erhalten habe ich noch 
ein Register fuer die Zaehlerwerte
somit fuer:
2500ms -> 250 im Register, 500ms -> 50 und 1500ms -> 150
Die ISR sieht folgendermassen aus: SIEHE CODE.TXT

Wie muss ich das Nachladeproblem behandeln ? muss zum zahler noch 6 
Maschienenzyklen dazuaddiert werden? Muss ich bei DEC TCOUNT etwas mit 
den Startwerten beachten, also Startwert + 1 ?

Nun zu den Zustaenden im Zuklus nach dem das STATEBIT gesetzt ist geht 
es zurueck in die Main: SIEHE CODE.TXT

Kann ich das besser loesen? Ausserdem ist hier das Problem das ich 
undefinierte Zeit verliere, wenn das Statebit gesetzt ist. Da ich nicht 
weiss bei welchen Befehl er sich gerade befindet, bzw wie lange es 
dauert bis das STATBIT mittels JBC seine Wirkung zeigt.

Dann springt er in die Auswertung fuer die Zustaende. Ich wollte auf die 
Bits in jedem State die mit SYNC gekennzeichnet sind, die genauen Zeiten 
von 2,5 nachster Zustand 0,5 nachster 1,5 dann nochmal 0,5 und wieder 
von vorn, erreichen. Wie ist es moeglich an diesen SYNC - Punkten den 
genauen Zeitwert zu haben? Probleme dazu macht mir der Code in der Main, 
da es unmoeglich wird, zu wissen, wieviel Zeit in der Main verbraucht 
wird bis zu den Sprung in die Zustandsauswertung.

Ausserdem muss in der Zustandanweissung alle Zustaende fuer eine 
synchronisation aufeinander abgeglichen werden. So dass (CJNE 
Auswertungen) bis zur Abarbeitung von Zustand 1 genausviel Zeit vergeht 
wie zur Abarbeitung von Zustand 3. Dies habe ich versucht mit NOP 
Befehlen auszugleichen. Ist die Ueberlegung richtig oder hab ich da 
Fehler im Code, bzw. kann man das einfacher machen?

Kann es Probleme geben bei einem leicht ungenauen Zeittiming, dass mir 
der Zyklus von 5 Sekunden auseinander drifftet oder die Zeiten der 
inneren Zustaende sich verschieben?

Ist ein bisschen viel aber diese Fragen beschaftigen mich schon sehr 
lange und es will mir keine Loesung dazu einfallen.

Fuer Tipps und Hinweise bin ich sehr dankbar und auch fuer das Erdulden 
diesen langen Beitrages ;)

Daniel

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
daniel wrote:

> Wie muss ich das Nachladeproblem behandeln ? muss zum zahler noch 6
> Maschienenzyklen dazuaddiert werden?

Mit "MOV TH0, #T0TH0", "MOV TL0, #T0TL0" hast Du schon verloren, da die 
Interrupteintrittszeit keine Konstante ist, richtig ist nur die 
Addition:

http://home.tiscali.de/peterd/appl/soft/clock/index.htm


Um mein Zuklus mit einzelnen Zustandszeiten zu erhalten habe ich noch
ein Register fuer die Zaehlerwerte
somit fuer:
2500ms -> 250 im Register, 500ms -> 50 und 1500ms -> 150

Dann nimm doch einfach 3 Bytes zum zählen:
dseg at 30h
  countdown50:  ds 1
  countdown150: ds 1
  countdown250: ds 1
cseg
..
  djnz   countdown50, m1
  mov    countdown50, #50
.. do 0,5s tasks
m1:
  djnz   countdown150, m2
  mov    countdown150, #150
.. do 1,5s tasks
m2:
  djnz   countdown250, m3
  mov    countdown250, #250
.. do 2,5s tasks
m3:
..


Peter

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
ich danke Ihnen erst mal sehr fuer Ihre schnelle Antwort.

Die Beispiele hatte ich mir gestern abend angeschaut, habe sie aber 
nicht verstanden. Doch das mit der Addition habe ich jetzt verstanden.
Diese `+2` steht wohl fuer den Befehl ADD a, th0 ?
in meinem Fall sozusagen:
PUSH  ACC
PUSH  PSW
; timer reload
CLR   EA
CLR   TR0 ; stop by 16 bit addition
MOV   A, #256 - TO_RELOAD_LOW + 6  ; 5 MC(MOV,ADD,..) + 1 MC(RUN) ??
ADD   TL0, A
MOV   A, #256 - TO_REALOAD_HIGH     ; 
ADDC  THO, A                       ; ?? braucht er die High -> Low   Reihenfolge? 
; 5 machine cycles since timer stop
SETB  TR0
SETB  EA

DJNZ  TIMER_REGISTER, IT0END     ; timer register leer nicht springen
SETB  TIME_EXEEDED               ; die main weiss nun das sie was machen soll...
POP   PSW
POP   ACC
RETI

Ist das richtig mit den Realoden unter Beachtung das der Timer gestoppt 
ist?
Ich denke in meinem Fall ist es nicht noetig, die drei Bytes zu nutzen, 
da ich bis zum naechsten Ueberlauf 9426 MC Zeit habe, das Register 
Nachzuladen. Ist die Annahme richtig?
Ausserdem hat er nur 128 kb RAM und ab 30h steht mein Serial Recieve 
Buffer , ab 40h mein Serial Send Buffer und ab 50h der STACK.

Vielen Dank
Daniel

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
Nun noch zu dem Problem der Main:
MAIN:  
  ; main task - execute States
  JBC  STATEBIT,  STATEM
  ; check of recieve datagram
  JBC    RECVBIT,  JRECV
  ; check of byte send finished
  JBC  SENDBIT,  JSEND
  SJMP   MAIN
  ; 8 MC
Wenn man vom Timer Interrupt zurueck kommt, ist es undefiniert bei 
welchen Befehl er gerade ist und wie lange es dauert, bis das STATEBIT 
(in der Timer 0 ISR gesetzt) seine Wirkung zeigt.
Kann man das auch anders loesen? ist es moeglich ein variablen 
Sprungbefehl zu erzeugen. Zum beispiel so in der Main:
Main:
        springe zu Adresse   ; adresse kann variabel sein

Adresse kann sein: MAIN oder STATEM oder RECVBIT oder ...

Ist sowas moeglich? Kann man die Adressen vorher wissen oder kann der 
Compiler solche MARKEN umwandeln fuer mein Sprungziel.
JMP arbeitet ja auch nur mit festen Adressen.
Muss so etwas mit einer Sprungtabelle geloest werden? Zum Beispiel so:
MOV   DPTR, #JUMP_TABLE  ; Adresse der Sprungtabelle
MOV   A,  INDEX_NUMBER  ; index welche sprung man aus Tabelle moechte
RL    A     ; teile durch 2 - Sprungeintraege brauchen 2 Byte platz
JMP   @A + DPTR
JUMP_TABLE:   ; hier beginnt die Tabelle
AJMP  MAIN
AJMP  STATEM
AJMP  RECVBYTE
AJMP  SENDBYTE
.
.
.

Das waere wohl die einzige loesung eines variablen sprungzieles oder 
geht es einfacher ?
Wie muss ich in meiner Zustandsabarbeitung, die Synchronisation zwischen 
alle SYNC punkten erstellen? geht es so mit dem auszaehlen und den NOP 
Befehlen?
Das auszaehlen der Machinenzyklen hat ein Problem wenn ich einen Sprung 
mache und ein Bit setze oder nicht setze. Wie mus ich das bearbeiten 
oder beachten ?
S0TASK:
  MOV  A,  CHBYTE;  ; HIGH-BYTE der Ausgabe nummer
  RRC  A    ; SHIFT RIGHT
  JNC  NO_BIT    ; wie ist diese Sprung bei der Zaehlung 
                                ;der Machinenzyklen zu beachten??
  SETB  NUMHB0         ; mitzaehlen oder nicht?!
  ; 5 MC
NO_BIT:
  MOV  NUML, CLBYTE   ; 8 BITS SET LOW BYTE
  SETB  SIGA   ;SIGNAL A SET- SYNC - auf das Bit soll die Zeit (SYNCHRONISATION)
                                ; des aktuellen Zustandes gelten
SIEHE CODE.TXT aus dem STARTBEITRAG


Vielen Dank fuer Tipps und Hinweise
Daniel

PS: Oben ist beim Source die Marke IT0END vergessen, muss natuerlich vor 
POP PSW !

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die Fragen zu dem Timer- Code hat sich erledigt habe das Beispiel fuer 
16 bit Timer 0  auf 
http://home.tiscali.de/peterd/appl/soft/clock/index.htm  uebersehen.
Statt XCH - Befehl kann man doch auch MOV nehmen oder ist es wichtig das 
sie getauscht werden?

Die Frage zu der Synchronisation besteht jedoch weiterhin.

Vielen Dank
Daniel

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
daniel wrote:

> Statt XCH - Befehl kann man doch auch MOV nehmen oder ist es wichtig das
> sie getauscht werden?

Ohne XCH muß man noch extra den ACCU sichern (PUSH+POP).


> Die Frage zu der Synchronisation besteht jedoch weiterhin.

Wie genau muß denn die Synchronität sein (in CPU-Zyklen) ?


Peter

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
gerade noch einen fatalen Fehler entdeckt!
bei 11,0592 Mhz muss er 9216dez (2400hex) Takte zaehlen fuer 10ms 
Overflow. Damit meine Teilerwerte im Register stimmen
Also fuer die Reloadwerte:
TH0 = FF - 8 - 24
TL0 = FF - 00

Daniel

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
es muss
TH0 = FF + 8 - 24
TL = FF - 00
sein.

Daniel

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ja die Synchronitaet soll zwischen den einzelnen Zustaenden genau(in CPU 
Zyklen) sein. Das bedeutet das der Zustand 3 genau die selbe Zeit 
braucht wie der Zustand 1, da diese ja nacheinander abgefragt werden. Im 
Code-Beispiel Code.txt habe ich diese Zeilen mit SYNC markiert. Nach 
Ablauf der Zeit soll ein Zustand abgearbeitet werden. Sozusagen soll 
kein Unterschied in CPU-Zyklen zwischen den einzelnen Zustaenden sein.
Speicherplatz fuer die unzaehligen NOP Befehle habe ich im Code-Bereich.
Bis zu Abarbeitung bleiben alle Interrupts ausser der Timer gesperrt. Da 
auch in den Zustaenden die Teilerzeiten ins Register geladen werden.
Ich denke nicht das es passieren kann, dass der Timer Interrupt vor dem 
Nachladen der Teilerzeiten eintritt (somit eine ungueltige Zeit 
entsteht) da dieser erst nach 9000 CPU-Zyclen eintritt, der Code aber 
weniger als 100 CPU- Zyklen betraegt.
CPU-Zyklen sind im Code mit MC(Machine Cycles) geschrieben.

Vielen Dank fuer die guten Antworten
Daniel

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
vielen Dank fuer die Hilfe. Es war mir tatsaechlich moeglich die 
einelnen Zustaenden auf einander zu Synchronisieren mit plus/minus 2 CPU 
Zyklen verschiebung innerhalb des Gesammtzykluses.
Die Main wurde in soweit abgeandert das sie fast konstante Zeiten 
liefert.
;MAIN 
MAIN:
  JBC  JMPBIT,  MAINJMP
  SJMP  MAIN
MAINJMP:  
  MOV  DPTR,  #JUMP_TABLE
  MOV  A,  INDEX_NUMBER
  RL  A
  JMP  @A + DPTR
  ; 6 MC
JUMP_TABLE:
  AJMP   MAIN    ; INDEX 0 - MAIN
  AJMP  STATEM    ; INDEX 1 - STATEMACHINE
  AJMP  RECVBYTE  ; INDEX 2 - RECIVE BYTE
  AJMP  SENDBYTE  ; INDEX 3 - RECIVE BYTE
  SJMP  MAIN
  ; 6 MC

Sinn dieser Praezision ist nur der, das es moeglich ist Code mit 
prazisen Zeiten zu haben. Gibt ja schon genuegend andere 
Zeitungenauigkeiten, die man nicht ausmertzen kann. Wie der Quarz und 
die Temperaturabhaengigkeit.

Vielen Dank Peter fuer die sehr guten und sehr hilfreichen Antworten!
Daniel

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

es hat sich bei mir noch eine Frage ergeben.
Kann es passieren das sich die Gesammt-Zykluszeit aendert? Oder 
verschiebt? Zum Beispiel nach einem Jahr die nicht mehr Zykluszeit 5 sek 
betraegt sondern 5,5 sek ?
Es arbeitet nach diesem Prinzip:
Der Timer loest ein Ueberlauf aus -> ein Teilerregister wird 
decrementiert (-1) -> Teilerregister null, wird ein Bit gesetzt -> durch 
das Bit wird in der Main ein Sprung ausgeloest -> springt in den 
aktuellen Ablaufzustand -> hier wird ein Bit gesetzt was der User als 
Output zu sehen bekommt (synchronisiert auf diese Bit) -> Teilerregister 
wird mit neuem Wert geladen -> ... im aktuellen Zustand -> zurueck in 
Main, Bit- Polling

Somit haengt doch die Zeit alleinig vom Timer ab, also kann sie sich 
nicht verschieben (nur in Ungenauigkeiten zum Quarz hin ).

Das Bit fuer den User auf das er synchronisiert, kommt dann im 
Gesamtzyklus mal 1 CPU - Zyklus eher oder auch nicht, haengt von der 
Auswertung vorher ab.
An der Gesamtzykluszeit aendert sich dadurch aber nichts, oder?.

Wenn man nun fuer diesen Ablauf, nach einem Jahr die Gesamtzykluszeit 
misst, muesste diese doch immer noch gleich sein, oder?

Vielen Dank falls mir jemand meine Annahmen bestaetigen koennte oder mir 
sagen koennte ob ein Fehler in den Annahmen sind.

Daniel

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich nehme an es wird sich wohl niemand mehr zu meiner Frage aeussern. 
Der Beitrag ist wahrscheinlich schon unter der Deadline der ersten 
Seite.

Naja, aber trotzdem vielen Dank fuer das super Forum hier !

Daniel

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.