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.
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...
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...
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.
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...
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
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
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?
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
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
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?
>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.
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
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
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...
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...
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..."
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.
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
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!
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;)
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...
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??
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.
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
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
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;)