Forum: Mikrocontroller und Digitale Elektronik Timer0 beim AT90S8515


von Manuete (Gast)


Lesenswert?

hallo zusammen,

habe mein Programm soweit fertig bis auf diesen Timer0 (8Bit Timer) beim 
AT90S8515.

Ich brauch den Timer innerhalb einer Empfangsroutine, sprich beim 
Empfang von Bytes, dort initialisiere ich es ab dem 1.Byte-Empfang... da 
es nicht unendlich auf verlorene Bytes warten soll... das funktioniert 
soweit.. .aber beim 256mal tritt dieser Timeout immer wieder auf

Habe eine gute Erklärung im Forum gefunden, die passt einwandfrei nur 
mein Problem ist, dass eben beim 255 der Überlauf auftritt... ich 
versuche diesen Überlauf (Overflow) zu vermeiden, aber irgendwie bekomme 
ich es anscheinend nicht hin....

Eigentlich versuche ich im Code, den TCNT0 zurückzusetzen und den Timer 
zu deaktivieren, sprich dann dürfte es nicht zum Überlauf immer 
auftreten...

-----------Timer initialisieren und Zählregister auf 0 setzen------
Timer0:
ldi   temp1,0b00000101     ;Vorteiler  1024 einstellen
out   tccr0,temp1

ldi   temp1,1<<TOV0
out   TIFR, temp1

clr   temp1
out   TCNT0, temp1      ;TCNT0 mit 0x00 laden

ldi   temp1,(1<<toie0)
out   timsk,temp1    ; Timer0-Überlauf-Int aktivieren

-------Unterprogramm--------------------------------------------------
TimerStoppen:
ldi   temp1,0b00000000
out   tccr0,temp1    ;Timer0 wird gestoppt

clr   temp1
out   TCNT0, temp1  ;Zaehler wird auf 0x00 gesetzt

ldi   temp1,1<<TOV0
out   TIFR, temp1


----------Interrupt-Auftritt--------------------------------------------
TimerV0:             ;Auftritt bei Ablauf des Timer0
in     STAT,SREG           ; Statusregister sichern

ldi    temp1,1<<TOV0  ;Bitpsition des TOVO-Flags laden
out    TIFR,temp1    ;TOVO-Flag rücksetzen

TimerStoppen2:
ldi    temp1,0b00000000     ;Timer0 stoppen
out    tccr0,temp1

ldi    temp1,0x00    ;Zählregister TCNT0 mit 0x00 laden
out    TCNT0, temp1

out    SREG,STAT    ; Flags wiederherstellen
reti
------------------------------------------------------------------------ 
--

dankeschön für jegliche Hinweise


Erläuterung aus dem Forum (--Karl Heinz---)
Jetzt zählt also das Timerregister so vor sich hin.
Als solches ist das noch nicht sehr aufregend.
Interessanter wird die ganze Sache, wenn man weis
das dieser Timer bei bestimmten Zählerstanden Aktionen
auslösen kann. So eine bestimmter Zählerstand kann zb.
ein Overflow sein. Ein 8 Bit Timer kann ja nur bis 255
zählen. Danach findet ein Überlauf (Overflow) statt
und der Timer beginnt wieder bei 0. Man kann sich jetzt
an diesen Overflow klemmen und einen Programmteil ausfühen
lassen, wenn der Overflow eintritt.

von Gast (Gast)


Lesenswert?

Hallo Manuete,
das sind nur Auszüge des Programms, richtig?
Damit ist leider wenig anzufangen - denn das Problem
kann sonstwo liegen.

Was auffällt:

temp1 sowohl im Interrupt als auch in den anderen
Programmteilen zu verwenden, ist nicht gut.

Wenn der Überlauf des Timer0 nach 256 Zählschritten
genutzt wird, braucht es keine Rücksetzung auf 0 in
der Interruptroutine. Im Gegenteil, es wird die Zeit
bis zum nächsten Interrupt >256 gesetzt.

Wenn der Timer0 mit Vorteiler 1024 betrieben wird,
muss man zum Laden von tcnt0 den Timer nicht stoppen.

Die Interruptroutine wird bequem innerhalb der 1024
Takte des Vorteilers aufgerufen.

Der Timer0 wird im Interrupt gestoppt aber nicht
wieder gestartet - geschieht das anderswo im Programm?

Alles in allem wird nicht wirklich klar, worum es
nun wirklich geht...

von Manuete (Gast)


Lesenswert?

Das ist richtig, nur die Auszüge die den Timer0 betreffen.. den ohne ihn 
läuft das Programm einwandfrei... wobei es ja auch mit dem Timer0 soweit 
geht... wenn ich zum Beispiel über Docklight statt 11Bytes nur 3Bytes 
sende und über den Stk500 kommuniziere.. dann bekomme ich eine Antwort 
dass der Timeout überfällig wurde..

Aber wenn ich jetzt immer die 11Bytes sende über Docklight... dann tritt 
trotzdem beim 256mal der Timeout auf... was ja net richtig ist, da es 
keinen Grund dafür gibt...

Aber den TCNT0 will ich ja nur innerhalb der Empfangsroutine zählen 
lassen, da es dort nicht zu lange warten soll... als Kontrolle eben...

Ja in der Interruptroutine wirds gestoppt...

In der Empfangsroutine wird es immer gestartet, immer mit 
1Byte-Empfang...

von Manuete (Gast)


Lesenswert?

ausserhalb der Empfangsroutine (sprich auf die 11 Bytes nacheinander) 
wird der Timer immer gestoppt und der Zähler auf 0x00 gesetzt...

von Gast (Gast)


Lesenswert?

Das Problem muss nicht zwingend im Umfeld des Timer0
liegen. Der Code enthält zwar ein paar überflüssige
Anweisungen und hoffentlich wird temp1 nicht noch
anderweitig genutzt, aber ansonsten macht die
Struktur Sinn.
Ich würde mal den Rest des Programmes unter die
Lupe nehmen, warum "TimerStoppen" beim 256ten
Mal vielleicht nicht bedient wird.

von Gast (Gast)


Lesenswert?

Wozu dient TimerStoppen2: ?

von Manuete (Gast)


Lesenswert?

HI;)

Doch temp1 benutze ich fast überall als Zwischenspeicher, aber ich lade 
es immer nach jeder Benutzung davor und danach mit 0x00... das ist doch 
hoffentlich net so schlimm;)

also vielleicht habe ich ja ein paar Verständnisprobleme;)

1.)Habe eine Empfangsroutine
--->die erwartet 11 Bytes
-->innerhalb dieser Routine werden diese Einstellungen vorgenommen
Timer0:
ldi   temp1,0b00000101     ;Vorteiler  1024 einstellen
out   tccr0,temp1

ldi   temp1,1<<TOV0
out   TIFR, temp1

clr   temp1
out   TCNT0, temp1      ;TCNT0 mit 0x00 laden

ldi   temp1,(1<<toie0)
out   timsk,temp1    ; Timer0-Überlauf-Int aktivieren


Dann überprüfe ich die Checksumme dieser 11 Bytes
---> die mir später erlaubt etwas (Relais) schalten zu dürfen

Nach der Überprüfung lass ich auf meiner Platine Relais schalten
Oder bei falscher Checksumme eben nicht.

Jenach errechneter Checksumme sende ich entweder zu Docklight oder auch 
LabView eine Antwort, damit die nächsten 11Bytes (die sind fest) 
gesendet werden können.


Wenn aber bei Schritt 1.) was schief läuft dann springt er eben
ansonsten befinde ich mich immer im Empfangsbereitschaft...

----------Interrupt-Auftritt--------------------------------------------
TimerV0:             ;Auftritt bei Ablauf des Timer0
in     STAT,SREG     ; Statusregister sichern

ldi    temp1,1<<TOV0  ;Bitpsition des TOVO-Flags laden
out    TIFR,temp1     ;TOVO-Flag rücksetzen

TimerStoppen2:
ldi    temp1,0b00000000     ;Timer0 stoppen
out    tccr0,temp1

ldi    temp1,0x00    ;Zählregister TCNT0 mit 0x00 laden
out    TCNT0, temp1

out    SREG,STAT    ; Flags wiederherstellen
reti
------------------------------------------------------------------------ 
--

Die Frage ist jetzt, ich simuliere einen einwandfreien Betrieb und ich 
bekomme es nicht hin den Timer (TCCR0) und den TCNT0 zu stoppen...
weil ich immer wieder in diese Interruptroutine reinfalle;)
Das soll ja nur dann vorkommen wenn ich eben zu wenige Bytes empfange...

von Manuete (Gast)


Lesenswert?

achso, also TimerStoppen2 ...

jenachdem wo ich rausspringe, wenn ich die Empfangsroutine verlasse,nach 
dem ich meine Bytes empfangen habe.. dann sollte der Timer eben 
deaktiviert werden und der Zählstand wieder auf 0 gesetzt werden....

zur Sicherheit dachte ich ich dupliziere einfach dieses Label 
TimerStoppen


Wobei es bisher keinen grossen oder bessergesagt überhaupt keinen 
Unterschied gemacht hat

von Manuete (Gast)


Lesenswert?

also ich habe es gerade mit dem STK500 getestet...

wenn ich es nicht stoppe dann hängt mein Programm immer in der 
Timer-Interruptroutine der der Timer dann ständig fällig ist

von Manuete (Gast)


Lesenswert?

ist es egal ob ich zuerst TCCR0 deaktiviere und dann TCNT0 auf 0 lade 
oder muss ich bezüglich das was beachten????????

von Gast (Gast)


Lesenswert?

Schauen wir mal, wie viel Zeit bis zum Interrupt vergeht.
Vorteiler = 1024
Zähler = 256
Nach 1024 * 256 = 262144 Takten wird der Interrupt ausgelöst.
Welche Taktfrequenz benutzt die CPU?
Bei 1 MHz löst der Timer0 nach ca. 262 Millisekunden einen
Interrupt aus. Bei 12 MHz sind es nur noch 22 Millisekunden.

Welche Baudrate wird benutzt?

von Gast (Gast)


Lesenswert?

Ich gehe normalerweise so vor:

clr temp1
out TIMSK,temp1 ; Interrupt abstellen (hier pauschal alles!)
out TCCR0,temp1 ; dann timer0 stoppen
out TCNT0,temp1 ; dann zähler zurück setzen

ldi temp1,(1<<TOV0)
out TIFR,temp1  ; vorsorglich anhängiges Interrupt-
                ; Flag zurück setzen

von Manuete (Gast)


Lesenswert?

hi,
Taktfrequenz von 4MHZ
Baudrate 9600

out TIMSK,temp1 ; Interrupt abstellen (hier pauschal alles!)

hmmm den Interrupt muss ich doch nicht abstellen...

"Bei 1 MHz löst der Timer0 nach ca. 262 Millisekunden einen
Interrupt aus. Bei 12 MHz sind es nur noch 22 Millisekunde"

Und er soll ja nicht ständig ein Interrupt auslösen, sondern nur 
innerhalb des Empfanges, falls sich dieser verzögert

für 11 Bytes werden ca 11 Mikrosekunden benötigt...
Ich kann bis zu 100ms gewähren, das wäre okay..

Was genau macht dieser Vorteiler, ich benutze ja den 1024....


dann gute nacht und bis morgen

süsse Träume an alle

von Gast (Gast)


Lesenswert?

Nun ja, wir sind nun mal auf einer Ursachenforschung,
oder nicht? :-)
Dazu gehört natürlich - wenn sonst nichts direkt auffälliges
ins Auge springt - auch eine Durchkalkulation der Timeout-
zeiten. Manchmal hilft es schon zu wissen, wann ganz genau
der Interrupt überhaupt ausgelöst wird.

Bei 4 MHz Takt, einem Vorteiler von 1024 und einem Timer-
Zähler von 256, ergibt sich eine Auslösezeit von ca. 65
Millisekunden (abgerundet).

Bei 9600 Baud benötigt die Übertragung eines Bytes inkl.
Start- und Stoppbit etwa 1 Millisekunde - nicht 11 Mikro-
sekunden. Für 11 Bytes werden also mindestens 11 Milli-
sekunden benötigt.

Nun sind diese 11 Millisekunden aber sehr akademisch, wenn
ein PC mit im Spiel ist. Es hängt sehr davon ab, wie die
Daten zusammen gestellt und übermittelt werden. Wird jedes
Byte einzeln aufbereitet, ist mit wesentlich längeren Zeiten
als 1 Millisekunde zu rechnen. Bei Windows-Rechnern gilt
m.W. eine Granulation von 10 Millisekunden, bei Linux-
Systemen von 1 Millisekunde.
Es ist nicht ungewöhnlich, dass speziell Windows-Rechner
von Zeit zu Zeit einmal kurz "Luft holen"; sich mit zeit-
aufwendigen Festplattenverwaltungen, Indizierkram, usw.
beschäftigen.

Der Vorteiler teilt den CPU-Takt von 4 MHz erst einmal
durch 1024, bevor er ihn auf den Timer0-Zähler los lässt.
Timer0 tickt also mit einer Frequenz um die 3,9 kHz.
Und da der Timer seinerseits ein Teiler durch 256 ist,
wird der Interrupt (wenn man ihn nicht sperren würde)
mit ca. 15 Hz ausgelöst werden, was den bereits erwähnten
65 Millisekunden entspricht. Alles gerundete Zahlen.

Vielleicht wäre eine Umstellung auf einen 16-bit-Timer
mit kleinerem Vorteiler einen Versuch wert?

von Gast (Gast)


Lesenswert?

>out TIMSK,temp1 ; Interrupt abstellen (hier pauschal alles!)

>hmmm den Interrupt muss ich doch nicht abstellen...

Nein, in diesem speziellen Fall, wo der Interrupt innerhalb
der Interrupt-Routine abgestellt wird nicht.
Das Beispiel sollte nur verdeutlichen, wie man (ich) einen
Timer ganz allgemein "aufräumt".

Die Sperre des Interrupts hält einfach "den Rücken frei",
die nachfolgenden Aufräumarbeiten am Timer wirklich
ungestört abwickeln zu können.
Ich beschäftige mich seit Jahrzehnten mit Assembler-
Programmierung - da schleichen sich gewisse Strategien
aus der Erfahrung ein, die einem Neuling nicht unmittelbar
nachvollziehbar scheinen. :-)

Dazu gehörte zum Beispiel, die Interrupt-Festigkeit des
EEPROM-Zugriffes im AVR zu hinterfragen, noch bevor die
Datenblätter diese Schwachstelle überhaupt aufgenommen
hatten.

Dazu gehört z.B. auch, die Latenzzeiten eines Interrupts
im Zusammenspiel mit der sleep-Anweisung genau zu unter-
suchen, wenn man eine Jitter-freie Anwendung benötigt.
Und nicht einfach den Datenblättern zu vertrauen. Ein
AT90S2313 und ein ATtiny2313 erwiesen sich da schon mal
als recht ungleiche Brüder. Dies ist eine Meinungsbekundung
und keine Tatsachenbehauptung - muss ich hier vorsorglich
hinzufügen. ;-)

Kurzum - den Interrupt muss man hier nicht zwingend
abschalten, aber wenn man es trotzdem tut, fällt er einem
auch nicht unerwartet wegen irgendwelcher übersehener
Randbedingungen in den Rücken. Man kann die Anweisung
nachträglich dann immer noch auskommentieren.

von Manuete (Gast)


Lesenswert?

hello, vielen Dank für die netten Tipps und Erklärungen

yep stimmt, 11,46ms  für eine
Telegrammlänge von 11Bytes (11Byte * 10Bit =110Bit/9600 = 11,46ms)

Diese 11Bytes werden aufeinmal über LabView gesendet z.B.:
02FF FFFF FFFF FFFF FF003

Dies wird dann über das UART des AT90S8515 empfangen
Beim 1Byteempfang initialisiere und starte den Timer und lade das 
Zählregister mit 0
um eben zu überprüfen, ob diese 100ms schon rum sind, dann sendet der 
microcontroller an LabView, dass dieser nochmals diese 11Bytes senden 
soll, da 1Byte oder jegliches an Bits verloren gegangen ist...

Diese Telegrammlänge von 11,46ms zu gewährleisten,und noch mehr und zwar 
darf es 100ms dauern,
so habe ich gedacht, dass ich es über den Timer0 und den Vorteiler von 
1024 einstellen kann dass diese Zeit von 100ms gewährleistet

Oder muss dies viell. über ein extra Register indem ich die 100ms lade 
oder einstelle und runterzähle??

Zum Timer1, der scheint mir noch komplizierter zu sein als der Timer0, 
wobei ich dann halt den Timeout auftritt dort erst denke ich beim 65

von Manuete (Gast)


Lesenswert?

uuups die 100ms lassen sich mit dem Timer0 nicht einstellen

sondern nur 65ms, das wäre denke ich auch okay
das wäre ja fast die 6fache Zeit als die Telegrammlänge von 11,46ms für 
die 11Bytes

von Gast (Gast)


Lesenswert?

Ich würde nicht darauf vertrauen, dass der PC die 11 Bytes
in einem Rutsch aussendet.
Das wird schon seinen Grund haben, dass der Timer0 nach
65 Millisekunden zuschlägt. :-)
Timer1 als 16-Bit-Zähler ist nicht wirklich komplizierter.
Statt einmal temp1 nach TCNT0 zu laden, müssen nacheinander
TNCT1L und TNCT1H geladen werden. Das ist der einzige
wirkliche Unterschied. Lediglich auf die Reihenfolge muss
man aufpassen - steht im Datenblatt, wie das geht.

Zwischenvorschlag:
Setze doch den Timer0 nicht nur nach dem ersten empfangenen
Byte zurück, sondern nach jedem empfangenen Byte. So
brauchst Du auf Timer0 nicht verzichten und gibst jedem
Byte eine Frist von 65 ms. Auch kein Beinbruch.
Am Ende der 11 Bytes dann nicht vergessen, den Interrupt
zu sperren...

von Manuete (Gast)


Lesenswert?

okay,
dankeschön für den Tipp, dann probier ichs mal aus
mal schauen was dabei rauskommt

von Manuete (Gast)


Lesenswert?

ich glaube das mit dem rücksetzen funktioniert..

nach insgesamt 256 Kommunikationen, sprich 11Bytes übertragungen tritt 
erst mein Interrupt immer auf... das heisst nach jeder Kommunikation 
füllt sich anscheinend ein Register den ich nicht auf 0 setze...

von Manuete (Gast)


Lesenswert?

muss ich vielleicht den Prescaler zurücksetzen noch in meinem Code...

habe es hier im Forum gelesen, anscheinend bezüglich synchronisation


 "vom Prescaler nimmst, musst Du zusätzlich den Prescaler
zurücksetzen, um den Timer wieder zu synchronisieren..."

von Gast (Gast)


Lesenswert?

Hallo Manuete,

> muss ich vielleicht den Prescaler zurücksetzen noch in meinem Code...

Ich wüsste nicht, dass man beim AT90S8515 den Prescaler gezielt
zurück setzen kann.

Diese Finesse braucht es, wenn man mehrere Prozesse im "Gleichtakt"
betreiben möchte. In diesem Fall ist das nicht erfoderlich.
Der nicht zurück gesetzte Vorteiler sorgt gerade mal für eine
Unsicherheit von 256 Mikrosekunden (@ 4MHz und Vorteiler=1024),
was bei dem Gesamtproblem vernachlässigbar ist.

Die Unsicherheit, wann der PC das nächste Byte anliefert, liegt um
Größenordnungen höher. Kann dort auch schon mal > 50 Millisekunden
liegen, wenn Prozesse höherer Priorität abgearbeitet werden.

von Manuete (Gast)


Lesenswert?

hi,

also hab alles mögliche versucht, hab jetzt wirklich keine ahnung was 
ich noch versuchen soll... also zur Problemerinnerung... die 
Kommunikation erfolgt einwandfrei, nur bei der 256 Kommunikation, sprich 
wenn ich 256mal je 11Bytes sende tritt das Timeout(021103)
Der Timer wird richtig gestoppt und der Zähler wieder mit 0 geladen, 
ansonsten würde Timeout früher auftreten...
Die Zahl 256 ist bemerkenswert... warum tritt es nur nach 256mal, und 
das immer wieder?? Irgendwo lädt sich ein Register im AT90S8515, das mit 
dann den TimerInterrupt aufruft, was er eigentlich nicht machen soll;)

Das ist mein Code, vielleicht ein wenig gewöhnungsbedürftig, aber für 
einen Anfänger ganz okay;)



.include "8515def.inc"

.def  STAT  = R16
.def  Position= R17
.def  Wert_Y   = R18
.def  ZwiSpa  = R19
.def  Pos_UART =R22
.def  SchlZaY = R23
.def  schlZaX  = R24
.def  temp1   = R25


.EQU  nss  = PB4

.EQU   fqu   = 4000000    ; Quarzfrequenz des AVR
;.equ  fqu  = 3686000    ;STK500
.EQU   fbd   = 9600      ; Baudrate des UART

.EQU   bddv   = (fqu/(16*fbd))-1   ; Baudraten-Teiler



.org $0000
rjmp Init    ; Sprung zur Initialisierung
reti         ; Rücksprung aus Externer Interrupt0
reti         ; Rücksprung aus Externer Interrupt1
reti         ; Rücksprung aus Timer/Counter1 Capture Event
reti         ; Rücksprung aus Timer/Counter1 Compare Match A
reti         ; Rücksprung aus Timer/Counter1 Compare Match B
reti         ; Rücksprung aus Timer/Counter1 Overflow
rjmp TimerV0  ; Sprung auf Routine für Timer/Counter0 Overflow
reti           ; Rücksprung aus Serial Transfer complete
reti         ; Rücksprung aus UART, Rx Complete
reti         ; Rücksprung aus UART, Tx Complete
reti

;*********************************************************
Reset:

rjmp  Init


Init:
ldi R20,High(RAMEND)
out SPH,R20
ldi R20,LOW(RAMEND)
out SPL,R20


ldi R29,$02             ;High Byte Adresse (Y)
ldi R28,$20             ;Low Byte Adresse  (Y)

ldi temp1,0b00000011    ;PD0, PD1 gesetzt
out PortD,temp1         ;PortD
ldi temp1, 0b00000010   ;PD0 als Eingang und PD1 als Ausgang
out DDRD, temp1

ldi temp1,$70         ;SCK=0,MISO=PullUP,MOSI=1,/SS=1
out PortB,temp1       ;Latch=1,Bit 2=Pull Up,Led1,Led=0
ldi temp1,$B0         ;SCK,MOSI,/SS=OUTPUT,REST=INPUT
out DDRB,temp1        ;Datenrichtung PortB Konfigurieren
ldi emp1,$50          ;SPIE=0,SPE=1;DORD=0,MSTR=1,CPHA=0
out Spcr,temp1        ;Cpha=0,Cpol=0,f(SCK)=f(Sys)/4

sbi PortB,PB3         ;595 enable ein

sei                   ;Interrupts freigeben


Tab_0_setzen:
ldi R29,$02           ;High Byte Adresse (Y)
ldi R28,$20           ;Low Byte Adresse  (Y)

Loop5:
ldi temp1,0x00       ;temp mit 0x00 laden
st Y+,temp1          ;Speichern auf Speicherplatz Y (0220)
inc schlZaY          ;inkrementiere schlZaY
cpi schlZaY,11       ;vergleiche schlZaY mit 11
brne Loop5           ;springen, wenn SchlaZaY nicht gleich 11

ldi temp1,0x00      ;Zwischenspeicher temp1 mit 0x00 laden
ldi schlZaY,0x00    ;Schleifenzaehler schlZaY mit 0x00 laden
ldi Pos_UART,0x00   ;Position des UART Pos_UART mit 0x00 laden

SPI:
ldi R29,$02         ;High Byte Adresse (Y)
ldi R28,$21         ;Low Byte Adresse  (Y)
ldi schlZaY,0x00    ;Schleifenzaehler schlZaY mit 0x00 laden


LOOP3:
ld ZwiSpa,Y+       ;Byte zur Übertragung aus Speicher Y
cbi PortB,nss      ;/ss=aktiv(log.0)
out SPDR,ZwiSpa    ;Datenbyte ausgeben
spi1:
sbis SPSR,SPIF     ;Skip,wenn Übertragung beendet
rjmp spi1
inc schlZaY        ;inkrementiere schlZaY
cpi schlZaY,8      ;vergleiche schlZaY mit 8
brne Loop3         ;springen, wenn SchlaZaY nicht gleich 8

sbi portB,nss      ;/ss=inaktiv(log1)
ldi schlZaY,0x00   ;Schleifenzaehler schlZaY mit 0x00 laden

;wenn über UART empfangen wurde und über SPI die Relais geschalten 
wurden
;dann sende Antwort an LabView
cpi Pos_UART,1
breq LabView
brne UART


LabView:
rcall CheckSumOK

UART:
ldi temp1,bddv          ; Baudrate des UART einstellen
out UBRR,temp1

sbi UCR,RXEN           ;Receiver freigegeben
sbi UCR,TXEN           ;Trasmitter freigegeben


Tab_1_setzen:
ldi  R29,$02           ;High Byte Adresse (Y)
ldi  R28,$20           ;Low Byte Adresse  (Y)

Loop55:
ldi temp1,0x00   ;temp1 mit 0x00 laden
st  Y+,temp1     ;Speichern auf Speicherplatz Y(0220)
inc schlZaY      ;inkrementiere schlZaY
cpi schlZaY,11   ;vergleiche schlZaY mit 11
brne Loop55      ;springen, wenn SchlaZaY nicht gleich 11

ldi  temp1,0x00
ldi  schlZaY,0x00
ldi  schlZaX,0x00

EmpfangenS:
ldi  R29,$02           ;High Byte Adresse (Y)
ldi  R28,$20           ;Low Byte Adresse (Y)
ldi  SchlZaY,0x00
ldi  temp1,0x00
ldi  Position,0x00
cbi  USR, RXC          ;RXC-Flag loeschen falls gesetzt

ldi  temp1,0x00
out  TIMSK,temp1
ldi  temp1,0b00000000
out  tccr0,temp1          ;Timer0 wird gestoppt
ldi  temp1,0x00
out  TCNT0, temp1         ;Zaehler wird auf 0x00 gesetzt
ldi  temp1,1<<TOV0        ;Bitpsition des TOVO-Flags laden
out  TIFR,temp1           ;TOVO-Flag rücksetzen


Empfangen:
sbis USR,RXC              ;warten bis ein Byte empfangen wurde,
rjmp Empfangen            ;Byte steht im UDR-Register bereit

Weiter2:
cpi Position,0x01         ;vergleiche ob Fehler aufgetreten ist
breq EmpfangenS           ;springe bei Gleichheit auf neues EmpfangenS


FrameError:
sbic USR,FE              ;Framing Error Flag,überprüfen
rjmp EmpfangenS

in temp1,UDR            ;(empfangenes Byte) nach temp1 kopieren
st Y+,temp1             ;empfangenes Byte auf Speicherplatz Y (0220)

inc SchlZaY             ;inkrementiere schlZaY

;beim ersten Byte-Empfang wird Timer0 initialisiert und gestartet
;springe bei Gleichheit auf das Label Weiter das den Timer aufruft
cpi SchlZaY,1
breq Weiter
brne Weiter3

Weiter:
rcall  Timer0

Weiter3:
ldi  temp1,0x00
out  TCNT0,temp1

cpi  SchlZaY,11
brne Empfangen

ldi  Pos_UART,1              ;Position des UART mit 1 laden
ldi  schlZaY,0x00            ;Schleifenzaehler Y mit 00 laden

ldi  schlZaY,0x00            ;Schleifenzä Ymit 00 laden

ldi  temp1,0x00
out  TCNT0,temp1
out  TCCR0,temp1
out  TIMSK,temp1

rjmp  Checksumme          ;auf Checksumme springen zur Byte Ueberprüfung


Timer0:
ldi   temp1,(1<<toie0)
out   timsk,temp1    ;Timer/Counter-Interruptmaske aktivieren

ldi   temp1,0b00000101   ;Vorteiler  1024 einstellen
out   tccr0,temp1        ;Timer/Counter0 Control-Register einstellen

ldi  temp1,0x00
out  TCNT0,temp1

ret


Checksumme:
ldi   ZwiSpa,0x00
ldi  temp1,0x00
ldi  SchlZaX,0x00

ldi   R29,$02
ldi   R28,$29

ld  ZwiSpa,-Y        ;Lade Wert -X in Zwischenspeicher ZwiSpa
Loop7:
ld   temp1,-Y

eor  ZwiSpa,temp1    ;Ergebnis wird in ZwiSpa zwischengespeichert

inc   SchlZaX
cpi   SchlZaX,7      ;insgesamt 7Verknüfungen
brne  Loop7


ldi  R28,$29
ld  temp1,Y             ;empfangene CS laden

cp  ZwiSpa,temp1        ;Errechnete CS und empfangene CS vergleichen

brne   CheckSumFalse   ;springe bei Ungleichheit auf Falsche CS
rjmp   SPI             ;Relativer Sprung zur SPI


CheckSumOK:        ;3 Bytes zurücksenden,
ldi  SchlZaY,0x00
ldi  temp1,0x00

sendSTX1:
sbis  USR,UDRE    ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendSTX1

ldi  temp1,0x02    ;STX (Start of Text)
out  UDR,temp1     ;Daten in den Puffer schreiben und damit senden

inc  SchlZaY      ;Schleifenzaehler SchlZaY um 1 inkrementieren


sendOK:
sbis  USR,UDRE      ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendOK

ldi  temp1,0xFF     ;lade 0xFF für in Ordnung der Checksumme
out  UDR,temp1
inc  SchlZaY        ;Schleifenzaehler SchlZaY um 1 inkrementieeren


sendETX1:          ;ETX (End of Text)
sbis  USR,UDRE     ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendETX1

ldi  temp1,0x03     ;ETX
out  UDR,temp1

inc  SchlZaY        ;inkrementiere SchlZaY um 1
cpi  SchlZaY,3      ;vergleiche schlZaY mit 3
brne CheckSumOK     ;springen, wenn SchlaZaY nicht gleich 3

rjmp  EmpfangenS


CheckSumFalse:         ;3 Bytes zurück
ldi  SchlZaY,0x00
ldi  temp1,0x00

sendSTX0:
sbis  USR,UDRE        ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendSTX0

ldi  temp1,0x02      ;STX (Start of Text)
out  UDR,temp1       ;Daten in den Puffer schreiben und damit senden

inc SchlZaY


sendFalse:
sbis  USR,UDRE    ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendFalse

ldi  temp1,0x00  ;lade 0x00 für nicht in Ordnung der Checksumme
out  UDR, temp1
inc  SchlZaY


sendETX0:
sbis  USR,UDRE    ;Warten bis UDR für das nächste Byte bereit ist
rjmp  sendETX0

ldi  temp1,0x03  ;ETX
out  UDR, temp1

inc     SchlZaY          ;inkrementiere schlZaY
cpi     SchlZaY,3        ;vergleiche schlZaY mit 3
brne    CheckSumFalse    ;springen, wenn SchlaZaY nicht gleich 3

rjmp EmpfangenS   ;springe zum EmpfangenS


TimerV0:         ;Auftritt bei Ablauf des Timer0

in STAT,SREG    ; Statusregister zwischenspeichern in STAT Register

ldi  temp1,0x00
out  TIMSK,temp1

ldi   temp1,0b00000000
out   tccr0,temp1        ;Timer0 wird gestoppt

ldi  temp1,0x00
out  TCNT0, temp1       ;Zaehler wird auf 0x00 gesetzt

ldi  temp1,1<<TOV0     ;Bitpsition des TOVO-Flags laden
out  TIFR,temp1        ;TOVO-Flag rücksetzen


ByteFalse:
ldi  SchlZaY,0x00
ldi  temp1,0x00


sendSTX02:
sbis USR,UDRE        ;Warten bis UDR für das nächste Byte bereit ist
rjmp sendSTX02

ldi temp1,0x02       ;STX (Start of Text)
out UDR, temp1       ;Daten in den Puffer schreiben und damit senden

inc SchlZaY


sendFalse11:
sbis USR,UDRE        ;Warten bis UDR für das nächste Byte bereit ist
rjmp sendFalse11

ldi temp1,0x11      ;lade 0x11 für den Timeout/ Empfangsfehler
out UDR, temp1
inc SchlZaY


sendETX03:
sbis USR,UDRE     ;Warten bis UDR für das nächste Byte bereit ist
rjmp sendETX03

ldi temp1,0x03     ;ETX (End of Text)
out UDR, temp1
inc SchlZaY        ;inkrementiere schlZaY
cpi SchlZaY,3      ;vergleiche schlZaY mit3
brne ByteFalse     ;springen, wenn SchlaZaY nicht gleich 3

ldi Position,0x01

out SREG,STAT    ;Statusregister wiederherstellen
reti             ;Rücksprung in das Hauptprogramm

von Manuete (Gast)


Lesenswert?

Testkommunikation:

19.09.2007 10:22:20,863; 021F315E2843C2FB3B1903   11Bytes werden 
gesendet
19.09.2007 10:22:21,375: 02FF03                    3Bytes als Antwort
19.09.2007 10:22:21,394; 02CC22E2712D9FD70A1203
19.09.2007 10:22:21,905: 021103-------------------->
19.09.2007 10:22:21,915; 02A91E043CAC173A383603
19.09.2007 10:22:22,425: 02FF03
.......
.......
.......
19.09.2007 10:24:36,151: 02FF03
19.09.2007 10:24:36,160; 0274020BF0BBE639A34A03
19.09.2007 10:24:36,671: 021103-------------------->
19.09.2007 10:24:36,680; 0242ADC4456EAA656FA003
19.09.2007 10:24:37,191: 02FF03

von Gast (Gast)


Lesenswert?

Hallo Manuete,
ändere

LabView:
rcall  CheckSumOK

in

LabView:
rjmp   CheckSumOK

und probier das Programm noch mal aus.
Wenn es läuft erkläre ich Dir, warum.

Vor "Loop5" würde ich gerne
ldi SchlZaY,0x00
sehen.

Viel Glück!

von Manuete (Gast)


Lesenswert?

woooooooooooooooow,

ich glaube es scheint zu funktionieren, lasse es gerade laufen... meinen 
Test...

und bis jetzt sind keine Timeouts aufgetreten,ich glaub das einfach 
nicht....der hammer..

und das lag nur an diesem rcall und rjmp,
mit rcall, ruft man doch ein Unterprogramm auf und
mit rjmp springt man auf eine Stelle im Programm

viiiiiiiiiiiiiiiiiiiiiiiiiiiiiielen Dank, du bist ein Engel;)

von Johannes M. (johnny-m)


Lesenswert?

Manuete wrote:
> mit rcall, ruft man doch ein Unterprogramm auf
Richtig, aber man braucht dann auch am Ende des Unterprogramms ein 
"ret", das die gespeicherte Rücksprungadresse wieder vom Stack holt, 
sonst läuft der Stack über. Da der 90S8515 512 Bytes SRAM hat (und 
dementsprechend auch eine maximale Stacktiefe von 512 Bytes) und eine 
Rücksprungadresse zwei Bytes benötigt, gibts alle 256 
Unterprogrammaufrufe einen Stack-Überlauf...

> und mit rjmp springt man auf eine Stelle im Programm
Genau. Und dabei gibts im Gegensatz zum (r)call keinen Rücksprung an die 
aufrufende Stelle...

von Manuete (Gast)


Lesenswert?

ooooooooohhh ja,stimmt

ret: Rücksprung aus einem Unterprogramm
ja beim Interrupt habe ich darauf geachtet und reti verwendet;)

ich weiss gar net warum ich rcall überhaupt verwendet habe...

okay,sehe aber was die Auswirkungen sind...

und was ist geschickter oder besser, rjmp oder rcall...

oder sind sie gleich??

von Gast (Gast)


Lesenswert?

Hallo Manuete,
Du hättest uns viel Zeit ersparen können, wenn das
Listing von Anfang an "auf dem Tisch" gelegen hätte. ;-)
Die Vermutung, dass es gar nicht am Timer0-Überlauf
liegt, sondern anderweitig im Programm, stand sehr
früh auf der Agenda.
Dein Programm enthält manche überflüssige Anweisung
und ließe sich auch sonst überschaubarer gestalten.
Das ist aber eine Sache der Routine und des Geschmacks.
Entscheidend ist, dass es läuft. Daran herumfeilen
kann man dann immer noch.
Zu überdenken wäre z.B., dass eine Zeitüberschreitung
an einer Stelle (oder gar mehreren - habe das in nicht
in die Tiefe untersucht) erst nach dem Eintreffen
von Daten über UART erkannt wird. Es braucht also einen
Anstoß von außen, damit das Programm eine Zeitüber-
schreitung signalisiert. Vielleicht ist das auch so
beabsichtigt.

von Manuete (Gast)


Lesenswert?

ich könnte doch das hier dann löschen beim TimerInterrupt,
oder ist das schon so ok??

TimerV0:         ;Auftritt bei Ablauf des Timer0

ldi  temp1,0x00
out  TIMSK,temp1
ldi   temp1,0b00000000
out   tccr0,temp1        ;Timer0 wird gestoppt
ldi  temp1,0x00
out  TCNT0, temp1       ;Zaehler wird auf 0x00 gesetzt
ldi  temp1,1<<TOV0     ;Bitpsition des TOVO-Flags laden
out  TIFR,temp1        ;TOVO-Flag rücksetzen

von Manuete (Gast)


Lesenswert?

ooohhh man, ich war der festen Überzeugung das ich anderweitig keine 
Fehler habe, bis ich alle Varianten des TimerÜberlaufs 100mal 
ausprobiert habe und ich meine Nerven ausgekostet habe;)

Aber vielen Dank für deine/eure Geduld und eure Hilfe...

Den Teil deiner Aussage habe ich leider nicht verstanden, was meinst du 
mit eine Zeitüberschreitung??

"Zu überdenken wäre z.B., dass eine Zeitüberschreitung
an einer Stelle (oder gar mehreren - habe das in nicht
in die Tiefe untersucht) erst nach dem Eintreffen
von Daten über UART erkannt wird. Es braucht also einen
Anstoß von außen, damit das Programm eine Zeitüber-
schreitung signalisiert. Vielleicht ist das auch so
beabsichtigt"

der Mikrocontroller reagiert auf z.B.:
02FF FFFF FFFF FFFF FF00 03
02FF03-------> für richtige Checksumme und schaltet Steuerung frei
02FF FFFF FFFF FFFF FFFF 03
020003-------> für falsche Checksumme und wartet auf neues Telegramm

020000
021103-------> für Timeout, zu wenige Bytes, und wartet auf neue 
Telegramm

nur hier
0200 0000 0000 0000 0003 0000
02FF03021103 --->hier antwortet er mit richtig und Timeout

von Manuete (Gast)


Lesenswert?

es geht eigentlich nur um die Übertragungszeit von LabView zum UART des 
Mikrocontrollers... das soll nur gewährleistet sein, und Fehler eben 
abzufangen... also ein sicheres Übertragungsprotokoll...

und ich denke das ist okay so;)

von Gast (Gast)


Lesenswert?

Ja, lass' uns das Buch schließen. ;-)

Auf zum nächsten Projekt!

von Manuete (Gast)


Lesenswert?

;)

okay

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.