;******************************************************************************** ;* Libary zur Kommunikation über die asynchrone serielle Schnittstelle (USART) * ;* eines ATmega-Kontrollers * ;* Die Libary enthält Unterprogramme zum Interrupt-gesteuerten Senden und zum * ;* Empfangen ganzer Datensätze ('Strings'). Zudem enthält es die auch die dazu * ;* passenden Interrupt-Behandlungsroutinen (ISRs). * ;* * ;* benötigte Variablen im SRAM: * ;* S_ptrL: (1) : Pointer auf Sendetabelle * ;* S_ptrH: (1) : * ;* S_sts : (1) : Status des Sendevorgangs * ;* : 0 = Übertragung beendet * ;* : 1 = Übertragung aus SRAM-Bereich * ;* : 2 = Übertragung aus Flashspeicher * ;* : (3 = Übertragung aus Ringbuffer) * ;* S_chr : (1) : Anzahl der noch zu sendenden Bytes * ;* S_str : (xx) : Sendetabelle (Sendestring) im SRAM; Größe kann der je- * ;* : weiligen Situation angepasst werden, sie muss aber kleiner* ;* : als 255 Zeichen (+ Länge des Strings) sein. * ;* : die Länge des Sendestrings steht immer an erster Position * ;* * ;* R_ptrL: (1) ; Schnittstelle * ;* R_ptrH: (1) ; Pointer auf Empfangsbuffer * ;* R_sts : (1) ; Status des seriellen Empfangsvorgangs * ;* R_err : (1) ; Status des letzten empfangenen Strings * ;* ; (=Status nach Timeout-ISR). * ;* ; 0 = kein Fehler aufgetreten * ;* ; 1 = es sind weniger als die zu erwartenden Zeichen * ;* ; empfangen worden * ;* ; 2 = es sind mehr als die zu erwartenden Zeichen * ;* ; empfangen worden * ;* * ;* R_chr : (1) ; Anzahl der schon empfangenen Zeichen * ;* R_exch: (1) ; Länge der zu erwartenden Übertragung * ;* R_buf : (xx) ; xx Byte Empfangsbuffer * ;* R_str : (xx+1) ; xx Byte Empfangstabelle (Empfangsstring) Der Empfangs- * ;* ; string ist um 1 Byte länger als der Empfangsbuffer, da * ;* ; das erste Byte des Strings noch zusätzlich zu den Daten * ;* ; seine Länge angibt (<256). * ;* * ;* ---------------------------------------------------------------------------- * ;* * ;* Last Update : 02/11/08 - Version 1.0 * ;* Autor : DRUI Raoul * ;* * ;* Prozessor : alle ATmega-Prozessoren mit zwei 8 Bit-Timer * ;* Quarzfrequenz: beliebig, Konfiguration des Timers 2 (für Timeout) und der * ;* seriellen Schnittstelle muss aber angepasst werden. * ;* * ;* ---------------------------------------------------------------------------- * ;* * ;* History: 15/10/08 erste Programmierung der Libary * ;* * ;* ---------------------------------------------------------------------------- * ;* * ;* Unterprogramme: sendstr: Senden eines 'Strings' d.h eines Datensatzes* ;* der im SRAM steht * ;* * ;* sendFstr: Senden eines 'Strings', der im Flashspeicher* ;* steht * ;* * ;* * ;* ISRs : isr_udre: ISR passend zu 'sendstr' und 'sendFstr' * ;* * ;* isr_rxc: ISR zum Empfang von Daten, ACHTUNG: es gibt * ;* mehrere Varianten - nur EINE darf gleich- * ;* zeitig benutzt werden. * ;* * ;* isr_cm2: ISR für Timeout (notwendig für einige Vari- * ;* anten der isr_rcx) * ;* * ;* ---------------------------------------------------------------------------- * ;* * ;* * ;* * ;* * ;******************************************************************************** ; !!! Folgende Zeilen müssen im Hauptprogramm eingefügt werden !!! ; ================================================================ ; Variablen für ser. Kommunikation (USART) ; ---------------------------------------- ;S_ptrL: .BYTE 1 ; Pointer auf Sendetabelle ;S_ptrH: .BYTE 1 ; ;S_sts: .BYTE 1 ; Status des Sendevorgangs ; 0 = Übertragung beendet ; 1 = Übertragung aus SRAM-Bereich ; 2 = Übertragung aus Flashspeicher ; (3 = Übertragung aus Ringbuffer) ;S_chr: .BYTE 1 ; Anzahl der noch zu sendenden Bytes ;S_str: .BYTE 65 ; Sendetabelle (Sendestring) Länge: 64+1 ; ;R_ptrL: .BYTE 1 ; Schnittstelle ;R_ptrH: .BYTE 1 ; Pointer auf Empfangsbuffer ;R_sts: .BYTE 1 ; Status des seriellen Empfangsvorgangs ;R_err: .BYTE 1 ; Status des letzten empfangenen Strings ;R_chr: .BYTE 1 ; Anzahl der schon empfangenen Zeichen ;R_buf: .BYTE 64 ; xx Byte Empfangsbuffer ;R_str: .BYTE 64+1 ; xx Byte Empfangstabelle (Empfangsstring) ; Der Empfangsstring ist um 1 Byte länger ; als der Empfangspuffer, da das erste Byte ; des Strings seine Länge angibt (<256). ;R_exch: .BYTE 1 ; Länge der zu erwartenden Übertragung ;R_cs: .BYTE 1 ; Prüfsumme ; Interrupt-Vektoren setzen ; .CSEG ; was ab hier folgt kommt in den ; ; FlashSpeicher ; .ORG 0x0000 ; Beginn des Programmspeichers ;reset: RJMP init ; springe nach 'init' ; --- USART --- ; .ORG OC2addr ; Compare Match von Timer 2 ; RJMP isr_cm2 ; zur ISR springen ; .ORG URXCaddr ; Empfangsdaten im UDR ; RJMP isr_rxc ; zur ISR springen ; .ORG UDREaddr ; Sende-Datenregister (UDR) ist leer ; RJMP isr_udre ; zur ISR springen ; ....... ; serielle Schnittstelle initialisieren ; ------------------------------------- ;Sender einschalten und Datenformat festlegen ; LDI temp, 0b10011000 ; Sender & Empfänger einschalten ; OUT UCSRB, temp ; RXC Interrupt einschalten ; UDRE Interrupt nocht NICHT einschalten ; LDI temp, 0x86 ; UCSRC = 1000 0110b ; OUT UCSRC, temp ; Wähle Asynchr. 8N1 ; B7 URSEL=1 UCSRC ausgewählt ; B6 UMSEL=0 Asynchron ; B4,5 UPM=00 keine Parität ; B3 USBS=0 1 Stoppbit ; B21 UCSZ=11 8 Datenbit ; B0 UCPOL=0 bei Asyn. ; Baudrate initialisieren ; LDI temp, 0x00 ; Teiler für Baudrate festlegen ; OUT UBRRH, temp ; Higbyte = 0x00 ; LDI temp, 0x67 ; [16MHz/(16*9600)]-1 = 103 ; OUT UBRRL, temp ; Lowbyte = 0x67 ; Senden ; CLR temp ; ; STS S_sts, temp ; Sendevorgang beendet = neuer ; Sendevorgang darf begonnen werden ; Empfangen ; STS R_sts, temp ; neuen Empfangsvorgang beginnen (0) ; STS R_chr, temp ; noch kein Zeichen empfangen (0) ; STS R_err, temp ; noch kein Fehler aufgetreten (0) ; LDI temp, 5 ; 16 Zeichen sollen empfangen werden ; STS R_exch, temp ; ; LDI XL, LOW (R_buf) ; Empfangszeiger auf Begin des ; LDI XH, HIGH(R_buf) ; Empfangspuffers setzen ; STS R_ptrL, XL ; ; STS R_ptrH, XH ; ; Timer 2 konfigurieren (Timeout der Schnittstelle) ; LDI temp, 0b00000000 ; Teilerfaktor = 0 -> kein Takt ; OUT TCCR2, temp ; TCCR2 ; OUT TCNT2, temp ; Timeout zurücksetzen ; LDI temp, 130 ; Vergleichswert (130 - für 9600 Baud) ; OUT OCR2, temp ; OCR2 ; IN i, TIMSK ; Status = 0 ; ORI i, 0x80 ; Timeout Interrupt einschalten ; Empfangen ; STS R_sts, temp ; neuen Empfangsvorgang beginnen (0) ; STS R_chr, temp ; noch kein Zeichen empfangen (0) ; STS R_err, temp ; noch kein Fehler aufgetreten (0) ; LDI temp, 5 ; 16 Zeichen sollen empfangen werden ; STS R_exch, temp ; ; LDI XL, LOW (R_buf) ; Empfangszeiger auf Begin des ; LDI XH, HIGH(R_buf) ; Empfangspuffers setzen ; STS R_ptrL, XL ; ; STS R_ptrH, XH ; ; Timer 2 konfigurieren (Timeout der Schnittstelle) ; LDI temp, 0b00000000 ; Teilerfaktor = 0 -> kein Takt ; OUT TCCR2, temp ; TCCR2 ; OUT TCNT2, temp ; Timeout zurücksetzen ; LDI temp, 130 ; Vergleichswert (130 - für 9600 Baud) ; OUT OCR2, temp ; OCR2 ; IN i, TIMSK ; Status = 0 ; ORI i, 0x80 ; Timeout Interrupt einschalten ;******************************************************************************** ;* Das Unterprogramm sendet Interrupt-gesteuert einen Datensatz, der sich im * ;* SRAM befindet über die serielle Schnittstelle. * ;* Dem UP wird der Anfang des Datensatzes über einen Pointer übergeben (Z). * ;* Das erste Byte des Datensatzes ist immer dessen Länge (= Anzahl der zu senden- ;* den Bytes) * ;* Das UP startet die Interrupt-gesteuerte Datenübertragung. * ;* * ;******************************************************************************** ;sendstr: PUSH ZL ; ; PUSH ZH ; ; LD temp, Z+ ; 1. Byte des Datensatzes (=Länge) lesen und ; Pointer inkrementieren ; STS S_ptrL, ZL ; Pointer in SRAM-Variable abspeichern ; STS S_ptrH, ZH ; ; STS S_chr, temp ; Zeichenzähler (SRAM) mit Wert laden ; LDI temp, 1 ; '1' = Datenübertragung aus SRAM ; STS S_sts, temp ; Status abspeichern ; SBI UCSRB, UDRIE ; Interrupt UDRIE freigeben ; POP ZH ; ; POP ZL ; ; RET ; ;******************************************************************************** ;* Das Unterprogramm sendet Interrupt-gesteuert einen Datensatz, der sich im * ;* Flashspeicher befindet über die serielle Schnittstelle. * ;* Dem UP wird der Anfang des Datensatzes über einen Pointer übergeben (Z). * ;* Das erste Byte des Datensatzes ist immer dessen Länge (= Anzahl der zu senden- ;* den Bytes) * ;* Das UP startet die Interrupt-gesteuerte Datenübertragung. * ;* * ;******************************************************************************** ;sendFstr: PUSH ZL ; ; PUSH ZH ; ; LPM temp, Z+ ; 1. Byte des Datensatzes (=Länge) lesen und ; Pointer inkrementieren ; STS S_ptrL, ZL ; Pointer in SRAM-Variable abspeichern ; STS S_ptrH, ZH ; ; STS S_chr, temp ; Zeichenzähler (SRAM) mit Wert laden ; LDI temp, 2 ; '2' = Datenübertragung aus Flashspeicher ; STS S_sts, temp ; Status abspeichern ; SBI UCSRB, UDRIE ; Interrupt UDRIE freigeben ; POP ZH ; ; POP ZL ; ; RET ; ;******************************************************************************** ;* ISR für UDRE des USART (= UDR ist leer ODER Senderegister ist leer) * ;* ---------------------- * ;* alle Parameter stehen in SRAM-Variablen * ;* S_chr : Anzahl der noch zu sendenden Zeichen (max. 255) * ;* S_sts : Status der Übertragung (0=keine, 1=aus SRAM, 2=aus Flash * ;* S_ptrL: Pointer auf Tabelle mit den zu sendenden Zeichen, Pointer * ;* S_ptrH: zeigt auf SRAM oder auf Flash (abhängig von S_sts) * ;* * ;******************************************************************************** ;isr_udre: PUSH temp ; benutzte Register retten ; IN temp, SREG ; ; PUSH temp ; ; PUSH ZL ; ; PUSH ZH ; ; LDS temp, S_chr ; Anzahl der noch zu übertragenen Zeichen ; CPI temp, 0 ; lesen // und mit 0 vergleichen ; BRNE iudre_4 ; wenn <> 0, geht's weiter ; STS S_sts, temp ; bei 0, gilt Übertragung als beendet ; CBI UCSRB, UDRIE ; Interrupt UDRIE wieder sperren ; RJMP iudre_e ; und ISR wird beendet ;iudre_4: DEC temp ; Zeichenzahl dekrementieren und ; STS S_chr, temp ; zurückschreiben ; LDS ZL, S_ptrL ; Pointer auf Sende-Tabelle laden ; LDS ZH, S_ptrH ; ; LDS temp, S_sts ; Status der seriellen Übertragung lesen ; CPI temp, 0 ; Übertragung abgeschaltet ; BRNE iudre_5 ; wenn NEIN, alles OK und weiter ; CLR temp ; wenn JA, ist dies zu diesem Zeitpunkt ; STS S_sts, temp ; nicht möglich und ist ein Fehler -> die ; CBI UCSRB, UDRIE ; Übertragung wird zwangsweise abgebrochen ; RJMP iudre_e ; ;iudre_5: CPI temp, 1 ; Tabelle steht im SRAM ; BREQ iudre_1 ; ; CPI temp, 2 ; Tabelle steht im Flashspeicher ; BREQ iudre_2 ; ; RJMP iudre_e ; keine der oberen Optionen -> Fehler ;iudre_1: LD temp, Z+ ; nächtes Byte aus SRAM-Tabelle lesen ; RJMP iudre_3 ; und Pointer inkrementieren ;iudre_2: LPM temp, Z+ ; nächtes Byte aus Flash-Tabelle lesen ; und Pointer inkrementieren ;iudre_3: OUT UDR, temp ; Byte aus Tabelle ins Senderegister ; STS S_ptrL, ZL ; akuellen Pointerstand wieder in ; STS S_ptrH, ZH ; SRAM-Variable zurückschreiben ;iudre_e: POP ZH ; ; POP ZL ; ; POP temp ; ; OUT SREG, temp ; ; POP temp ; ; RETI ; ;******************************************************************************** ;* ISR für RXC des USART (= es wurde ein Zeichen empfangen) * ;* --------------------- * ;* * ;* wichtiger Hinweis: alle nun folgenden Interrupt-Behandlungsroutinen sind * ;* ================== auskommentiert, da nur eine (!) gleichzeitig aktiv sein * ;* darf. * ;* * ;* Die detaillierte Beschreibung der Funktion steht in der Kopfzeile der jewei- * ;* ligen ISR, aber alle Varianten schreiben zunächst die empfangenen Daten in * ;* einen Puffer, der nach Beendigung der Datenübertragung in einen s.g. * ;* Empfangsstring (oder Empfangstabelle) kopiert wird. Dieser String kann dann * ;* ausgewertet werden. Während dieser Auswertung können bereits neue Daten emp- * ;* fangen werden. * ;* * ;* Die Vielfalt der aufgeführten Varianten kommt dadurch zustande, da es viele * ;* Möglichkeiten gibt, das Ende einer Übertragung zu definieren. * ;* * ;******************************************************************************** ;******************************************************************************** ;* ISR für RXC, Varainte 1: * ;* ----------------------- * ;* * ;* Die Anzahl der zu erwartenden Daten wird vor der Übertragung festgelegt. Die * ;* tatsächliche Anzahl der empfangenen Zeichen wird mit diesem Wert verglichen. * * ;* Ist der festgelegte Wert erreicht, so gilt die Übertragung als beendet und * ;* der Empfangspuffer wird in den Empfangsstring kopiert. Folgen weitere Zeichen* ;* so werden diese zu der folgenden Übertragung gezählt. * ;* Die Pause zwischen zwei Zeichen darf beliebig groß sein. * ;* Da das erste Byte des Empfangsstrings die Länge des Strings angibt, steht * ;* hier immer (!) die Anzahl der zu erwartenden Zeichen. * ;* * ;* Statusbyte (R_sts) * ;* ------------------ * ;* 0 = warten auf 1. Byte einer neuen Übertragung, d.h. die Anzahl der zu * ;* erwartenden Zeichen aus der vorhergehenden Übertragung wurde erreicht - * ;* eine neue Übertragung darf beginnen. * ;* * ;* 1 = Übertragung ist noch nicht abgeschlossen * ;* * ;* * ;* Nachteil: Ist die Anzahl der übertragenen Zeichen zu klein, erfolgt keine * ;* Beendigung der Übertragung. * ;* Ist die Anzahl hingegen zu groß, so werden die überschüssigen * ;* Zeichen zur folgenden Übertragung gezählt, dessen Dateninhalt, * ;* dann meistens nicht das gewünchte Format besitzt. Fehlüber- * ;* tragungen sind die Folge. * ;* * ;* Vorteil: einfache Realisierung * ;* * ;******************************************************************************** isr_rxc: PUSH temp ; benutzte Register retten IN temp, SREG ; PUSH temp ; PUSH i ; PUSH j ; PUSH ZL ; PUSH ZH ; PUSH XL ; PUSH XH ; LDS ZL, R_ptrL ; aktuellen Wert des Empfangspointers LDS ZH, R_ptrH ; laden LDS i, R_chr ; Anzahl der bereits empfangenen Zeichen LDS j, R_exch ; laden // Anzahl der zu erwartenden Zeichen ; laden IN temp, UDR ; empfangenes Zeichen aus Empfangsreg. ; lesen ST Z+, temp ; Zeichen in Empfangspuffer setzen INC i ; Anzahl empfangener Zeichen inkrementieren CP i, j ; ist max. Anzahl erreicht? BREQ irx_1 ; wenn JA, dann Sprung ; Anzahl ist noch nicht erreicht STS R_chr, i ; aktuelle Anzahl wieder zurückschreiben STS R_ptrL, ZL ; aktuelle Pointerposition zurückschreiben STS R_ptrH, ZH ; RJMP irx_e ; und fertig irx_1: CLR temp ; Anzahl ist erreicht STS R_chr, temp ; Anzahl empfangener Zeichen löschen STS R_sts, temp ; Empfangsstatus = 0 LDI XL, LOW (R_str+1) ; Zeiger2 auf Ende des Empfangsstrings LDI XH, HIGH(R_str+1) ; setzen ADD XL, j ; "+1" = Korrektur, weil an 1. Position ADC XH, temp ; die Stringlänge steht irx_2: LD temp, -Z ; Empfangsbuffer in Empfangsstring ST -X, temp ; kopieren DEC i ; solange in Schleife bleiben, bis Anzahl BRNE irx_2 ; der empfangenen Zeichen Null ist LDS temp, R_exch ; Anzahl der erwarteten Zeichen ST -X, temp ; Position im String setzen ;RCALL "Bearbeitung der Daten" ;* LDI ZL, LOW (R_str+1) ;* LDI ZH, HIGH(R_str+1) ;* LDI para1, 1 ;* ;RCALL PRowx ;* LD temp, -Z ;* OUT PORTA, temp ;* irx_e: POP XH ; POP XL ; POP ZH ; POP ZL ; POP j ; POP i ; POP temp ; OUT SREG, temp ; POP temp ; RETI ;******************************************************************************** ;* ISR für RXC, Variante 2: * ;* ----------------------- * ;* * ;* und * ;* * ;* ISR für OC2 (für Variante 2) * ;* ---------------------------- * ;* * ;* Die Anzahl der zu erwartenden Daten wird auch hier vor der Übertragung fest- * ;* gelegt. Die tatsächliche Anzahl der empfangenen Zeichen wird mit diesem Wert * ;* verglichen. * ;* Ist der festgelegte Wert erreicht, so gilt die Übertragung als beendet und * ;* der Empfangspuffer wird in den Empfangsstring kopiert. Folgen weitere Zeichen* ;* ohne Abstand zu den vorhergehen, so werden diese einfach ignoriert, bis eine * ;* Übertragungspause von mind. 2 Zeichen aufgetreten ist. Erst dann wird eine * ;* neue Übertragung begonnen, d.h. die empfangenen Zeichen werden gespeichert. * ;* * ;* Bricht die Übertragung vor Erreichen des eingestellten Wertes ab, so wird * ;* die Übertragungszeit von mind. 2 Zeichen abgewartet bis die Übertragung als * ;* beendet gilt; der Empfangspuffer wird dann kopiert. * ;* * ;* Ein für jede Übertragungsgeschwindigkeit eingestelltes Timeout wacht darüber * ;* ob die empfangenen Daten 'zusammenhängend' sind, also zu einer bestimmten * ;* Übertragung gehören. * ;* * ;* * ;* Statusbyte (R_sts) * ;* ------------------ * ;* 0 = warten auf 1. Byte einer neuen Übertragung, d.h. die Anzahl der zu * ;* erwartenden Zeichen aus der vorhergehenden Übertragung wurde erreicht - * ;* eine neue Übertragung darf beginnen. * ;* * ;* 1 = Übertragung ist noch nicht abgeschlossen, Timeout läuft * ;* * ;* 2 = Übertragung ist beendet, aber das Timeout ist noch nicht abgelaufen. * ;* Alle nun ankommenden Zeichen werden verworfen und setzen zudem das * ;* Timeout wieder zurück. * ;* * ;* * ;* Error-Byte (R_err) * ;* ------------------ * ;* 0 = kein Fehler (der Empfangsstring - R_str - hat die vorgegebene Länge. * ;* * ;* 1 = Fehler, Timeout während der Übertragung aufgetreten, es wurden weniger * ;* als die erwartete Anzahl an Zeichen übertragen. * ;* * ;* 2 = Fehler, es wurden mehr als die erwartete Anzahl an Zeichen übertragen. * ;* Die überschüssigen Zeichen folgten ohne Pause an die erwarteten Zeichen. * ;* Sie wurden verworfen. Im Empfangsstring befindet sich nur die Anzahl der * ;* erwarteten Zeichen. * ;* * ;* * ;* Anmerkung: * ;* ---------- * ;* Ist die Anzahl der zu empfangenden Zeichen unbekannt, so kann trotzdem diese * ;* ISR mit leichten Einschränkungen zur Anwendung kommen. * ;* Die Anzahl der zu erwarteneden Zeichen ist auf die Größe des Empfangspuufers * ;* zu setzen (auf jeden Fall aber kleiner als 256). * ;* Das Ende der Übertragung wird durch den Timeout erkannt (zwischen 2 Über- * ;* tragungen muss mind. eine Pause von 2 Zeichen erfolgen). Eine Fehlererkennung* ;* ist nun nicht mehr möglich, R_err wird einfach ignoriert. * ;* * ;* * ;* * ;* Timeout * ;* ------- * ;* Das Timeout hat im Prinzip eine Länge die der Übertragungszeit von 2 Zeichen * ;* entspricht, d.h. es variiert entsprechend der eingestellten Übertragungsge- * ;* schwindigkeit. Bei der nun folgenden Berechnung der Timeout-Zeiten wird vom * ;* Format 8N1 ausgegangen, d.h. pro Zeichen werden 10 Bit übertragen * ;* Dann gelten folgende Werte für die Wartezeit oder das Timeout: * ;* * ;* (0)* 300 Baud : 66,7 ms (2 x 33,3ms) ¦ (4) 9600 Baud : 2,1 ms * ;* (1)* 1200 Baud : 16,7 ms ¦ (5) 19200 Baud : 1,0 ms * ;* (2) 2400 Baud : 8,3 ms ¦ (6) 38400 Baud : 520 us * ;* (3) 4800 Baud : 4,2 ms ¦ (7) 76800 Baud : 260 us * ;* * ;* (8) 153.600 Baud : 130 us ¦ (11) 250 kBaud : 80 us * ;* (9) 307.200 Baud : 65 us ¦ (12) 500 kBaud : 40 us * ;* (10) 614.400 Baud : 33 us ¦ (13) 1 MBaud : 20 us * ;* -*- kann nicht mit einem 8 Bit-Zähler * ;* unterstützt werden * ;* * ;* Baudrate Teiler (16MHz) Teiler (14,...MHz) * ;* ------------------------------------------------------------------ * ;* 300 1024/255* (1042) * ;* 1200 1024/255* (260) * ;* 2400 1024/130 * ;* 4800 1024/ 65 * ;* 9600 256/130 * ;* 19200 128/130 * ;* 38400 64/130 * ;* 76800 32/130 * ;* 153500 32/ 65 * ;* 307200 8/130 * ;* 614400 8/ 65 * ;* * ;* 250000 8/160 * ;* 500000 8/ 80 * ;* 1000000 8/ 40 * ;* * ;******************************************************************************** ;isr_rxc: PUSH temp ; benutzte Register retten ; IN temp, SREG ; ; PUSH temp ; ; PUSH i ; ; PUSH j ; ; PUSH k ; ; PUSH ZL ; ; PUSH ZH ; ; CLR temp ; Timeout-Zeit ; OUT TCNT2, temp ; löschen ; IN k, UDR ; empfangenes Byte lesen ; LDS i, R_sts ; Empfangsstatus laden ; CPI i, 0x00 ; ist Status = 0 ? ; BRNE irx_1 ; Sprung, wenn <> 0 ; LDI temp, 0b00000110 ; Teilerfaktor = 256 (für 9600 Baud) ; OUT TCCR2, temp ; dadurch: Timeout einschalten ; LDI temp, 1 ; Status auf 1 setzen ; STS R_sts, temp ; ;irx_1: CPI i, 0x02 ; ist Status = 2 ? ; BRNE irx_2 ; wenn Nein, -> Sprung zu Status 1 ; LDS temp, R_chr ; Anzahl der bereits empfangenen Zeichen ; INC temp ; inkrementieren (aber Zeichen nicht ; STS R_chr, temp ; speichern, sie werden verworfen) ; RJMP irx_e ; -> fertig ;irx_2: ; Status = 1 ; LDS ZL, R_ptrL ; aktuellen Wert des Empfangspointers ; LDS ZH, R_ptrH ; laden ; LDS i, R_chr ; Anzahl der bereits empfangenen Zeichen ; LDS j, R_exch ; laden // Anzahl der zu erwartenden ; Zeichen laden ; ST Z+, k ; Zeichen in Empfangspuffer setzen ; INC i ; Anzahl empfangener Zeichen inkrementieren ; CP i, j ; ist max. Anzahl erreicht? ; BRNE irx_3 ; wenn NEIN, dann folgende Zeilen über- ; springen ; LDI temp, 2 ; STS R_sts, temp ; Empfangsstatus = 2 ; Anzahl ist noch nicht erreicht ;irx_3: STS R_chr, i ; aktuelle Anzahl wieder zurückschreiben ; STS R_ptrL, ZL ; aktuelle Pointerposition zurückschreiben ; STS R_ptrH, ZH ; ;irx_e: POP ZH ; ; POP ZL ; ; POP k ; ; POP j ; ; POP i ; ; POP temp ; ; OUT SREG, temp ; ; POP temp ; ; RETI ; ------------------------------------------------------------------- ;isr_cm2: PUSH temp ; benutzte Register retten ; IN temp, SREG ; ; PUSH temp ; ; PUSH i ; ; PUSH j ; ; PUSH ZL ; ; PUSH ZH ; ; PUSH XL ; ; PUSH XH ; ; LDI temp, 0b00000000 ; Teilerfaktor = 0 -> kein Takt ; OUT TCCR2, temp ; Timeout wieder abschalten ; OUT TCNT2, temp ; Timeout zurücksetzen ; LDS ZL, R_ptrL ; aktuellen Wert des Empfangspointers ; LDS ZH, R_ptrH ; laden ; LDI XL, LOW (R_str) ; Zeiger2 auf Ende des Empfangsstrings ; LDI XH, HIGH(R_str) ; setzen ; LDS i, R_chr ; ; LDS j, R_exch ; ; INC j ; Korrektur wegen nachfolgendem Test ; CP i, j ; ; BRLO icm2_1 ; wenn kleiner, alles OK, weiter ; LDS i, R_exch ; wenn größer oder gleich, wird die ; erwartete Maximalanzahl eingesetzt ;icm2_1: MOV j, i ; Inhalt von i retten ; INC i ; Korrektur für Schleifenzähler: +1 ; und für Pointer, da Länge (des Strings) ; ADD XL, i ; hinzugefügt werden muss ; ADC XH, temp ; ;icm2_2: LD temp, -Z ; Empfangsbuffer in Empfangsstring ; ST -X, temp ; kopieren ; DEC i ; solange in Schleife bleiben, bis Anzahl ; BRNE icm2_2 ; der empfangenen Zeichen Null ist ; Länge des Empfangsstrings an erste ; ST X, j ; Position im String setzen ; LDI ZL, LOW (R_buf) ; Empfangszeiger auf Begin des ; LDI ZH, HIGH(R_buf) ; Empfangspuffers setzen ; STS R_ptrL, ZL ; ; STS R_ptrH, ZH ; ; CLR temp ;Error = 0 (vorläufig) ; LDS i, R_chr ; ; LDS j, R_exch ; Anzahl der erwarteten Zeichen ; CP i, j ; Vergleich mit tatsächlicher Anzahl ; BREQ icm2_e ; wenn GLEICH, alles OK -> Ende ; BRLO icm2_3 ; wenn KLEINER, Sprung ; LDI temp, 2 ; wenn GRÖSSER ; RJMP icm2_e ; ;icm2_3: LDI temp, 1 ; ;icm2_e: STS R_err, temp ; ; CLR temp ; ; STS R_chr, temp ; Anzahl empfangener Zeichen löschen ; STS R_sts, temp ; Empfangsstatus wieder auf 0 ;RCALL "Bearbeitung der Daten" ;* ; LDI ZL, LOW (R_str+1) ;* +1, weil 1.Byte (=Stringlänge) über- ; LDI ZH, HIGH(R_str+1) ;* sprungen wird. ; LDI para1, 1 ;* ; RCALL PRowx ;* ; LD temp, -Z ;* ; OUT PORTA, temp ;* ; POP XH ; ; POP XL ; ; POP ZH ; ; POP ZL ; ; POP j ; ; POP i ; ; POP temp ; ; OUT SREG, temp ; ; POP temp ; ; RETI ;******************************************************************************** ;* ISR für RXC, Variante 3: * ;* ----------------------- * ;* * ;* und * ;* * ;* ISR für OC2 (für Variante 3) * ;* ---------------------------- * ;* * ;* Bei Variante 3 handelt es sich um ein einfaches Protokoll. * ;* Jede Übertragung wird durch 'STX' (=0x02) eingeleitet, anschließend wird die * ;* Anzahl der Datenbytes (der eigentlichen Payload) übertragen, dann folgen die * ;* Datenbytes, die Prüfsumme und die Endemarkierung der Übertragung, d.h. das * ;* Zeichen 'ETX' (=0x03). * ;* Die Prüfsumme wird ausschließlich über die Datenbytes gebildet. Es handelt * ;* um eine simple 8 Bit-Addition, dessen Überlauf nicht berücksichtigt wird. * ;* * ;* Alle empfangenen Zeichen werden in den Empfangspuffer geschrieben (R_buf), * ;* erst nach vollständiger fehlerfreier Übertragung des gesamten Frames werden * ;* auschließlich die Nutzdaten in den Empfangsstring (R_str) kopiert, in dessen * ;* erstem Byte die Länge der Nutzdaten kodiert ist. * ;* Ist ein Übertragungsfehler aufgetreten, so erfolgt nur eine Fehlermeldung (in* ;* R_err), es werden keine Daten kopiert (R_str bleibt dann unverändert). * ;* * ;* Die gesamte Übertragung (=Frame) muss 'in einem Zuge' erfolgen, d.h es dürfen* ;* keine Übertragungspausen zwischen den Zeichen entstehen (oder sie müssen * ;* kleiner als die Übertragunszeit von zwei Zeichen sein). Erst nach der Über- * ;* tragung des Frames darf eine Pause erfolgen. * ;* * ;* Folgen zwei Frames 'nathlos' (=ohne Pause) nacheinander und der erste Frame * ;* war fehlerfrei, so wird dieser kopiert und mit dem Empfang des nächsten be- * ;* gonnen. * ;* Ist bei der Übertragung eines Frames ein Fehler entstanden und die Anzahl der* ;* Zeichen ist zu klein, so gilt die Übertragung nach Ablauf des Timeouts als * ;* beendet (mit Fehlermeldung / kein Kopieren der Daten). * ;* Ist bei der Übertragung eines Frames ein Fehler entstanden und der folgende * ;* Frame folgt ohne(!) Pause an der vorhergehenden, so können beide Frames nicht* ;* korrekt unterschieden werden. Alle ankommenden Zeichen werden verworfen, es * ;* erfolgt eine Fehlermeldung für den ersten und u.U für alle folgenden Frames * ;* sofern diese nicht durch eine Pause getrennt sind. Nur eine Pause mit einer * ;* Länge von mind. 2 Zeichen garantiert, dass im Fehlerfall ein Timeout auftritt* ;* und auf den Anfang eines neuen Frames gewartet wird. * ;* * ;* Aufbau der ISR: * ;* --------------- * ;* Status 0: Es wird auf das Zeichen 'STX' gewartet. Werden andere Zeichen * ;* empfangen, so werden diese ignoriert. * ;* Wird ein STX-Zeichen empfangen, so gilt die Übertragung als be- * ;* gonnen, es wird zu Status 1 übergegangen. * ;* Ist das empfangene STX-Zeichen irgendein Datenbyte einer (ande- * ;* ren) Übertragung, so kann dieser Umstand nicht erkannt werden, * ;* der Empfang eines Frames wird trotzdem gestartet. Der Fehler * ;* kann nur später am Übertragungsende oder durch Ablauf des Time- * ;* outs festgestellt werden. Werden Pausen zwischen den Frames ein-* ;* gehalten, dürfte dieses Verhalten kein Problem darstellen. * ;* * ;* Status 1: Das nun empfangene Byte wird als Anzahl der Nutzdaten inter- * ;* pretiert. * ;* * ;* Status 2: Die in Status 1 definierte Anzahl an Nutzbytes wird empfangen * ;* und in den Puffer geschrieben. Zusätzlich erfolgt die Bildung * ;* einer Prüfsumme. * ;* * ;* Status 3: Das nun empfangene Byte wird als Prüfsumme interpretiert und mit* ;* der berechneten Prüfsumme (aus Status 2) verglichen. Nach dem * ;* Vergleich wird immer zu Status 4 übergegangen. Sind die Prüf- * ;* summen ungleich wird zusätzlich eine Fehlermeldung erzeugt (in * ;* R_err / Bit 2^0 setzen). * ;* * ;* Status 4: Es wird nun auf das letze Byte der Übertragung gewartet. Ist es * ;* 'ETX' und ist sonst kein Fehler aufgetreten, so wird die * ;* Timeout-Steuerung angehalten, die Datenbytes in den Empfangs- * ;* string kopiert und der Status auf 0 gesetzt, R_err = 0. * ;* Ist das empfangene Byte NICHT 'ETX' so wird Bit 2^1 in R_err ge-* ;* setzt. * ;* Auch wenn ein Fehler aufgetreten ist (in Status 3 oder 4) wird * ;* der Ablauf der Timeout-Zeit gestoppt und mit Status 0 wieder be-* ;* gonnen. Die Daten wurden aber nicht kopiert, und R_err enthält * ;* einen Fehlercode. * ;* * ;* Timeout : Wurde die Übertragung eines Frames unterbrochen und läuft die * ;* Timeout-Zeit ab, so wird Bit 2^2 in R_err gesetzt. Es werden * ;* keine Daten kopiert und wieder in Status 0 gesprungen. * ;* ;* * ;******************************************************************************** ;isr_rxc: PUSH temp ; benutzte Register retten ; IN temp, SREG ; ; PUSH temp ; ; PUSH i ; ; PUSH k ; ; PUSH XL ; ; PUSH XH ; ; PUSH ZL ; ; PUSH ZH ; ; CLR temp ; Timeout-Zeit ; OUT TCNT2, temp ; löschen ; IN k, UDR ; empfangenes Zeichen retten ; LDS i, R_sts ; Status laden ; -------------------- Status 0 -- (auf STX warten) ----------------- ; CPI i, 0 ; Status = 0? (auf STX warten) ; BRNE irx_1 ; wenn NEIN, dann weiter zum nächsten ; Status ; CPI k, STX ; wenn JA, Zeichen = STX? ; BRNE irx_0 ; wenn NEIN, dann fertig ; wenn JA, dann ... ; LDI temp, 0b00000110 ; Teilerfaktor = 256 (für 9600 Baud) ; OUT TCCR2, temp ; dadurch: Timeout einschalten ; LDI temp, 1 ; neuer Status = 1 ; STS R_sts, temp ; Status retten und.. ;irx_0: RJMP irx_e ; ..fertig! ; -------------------- Status 1 -- (Länge der Übertragung) ---------- ;irx_1: CPI i, 1 ; Status = 1? (auf Länge warten) ; BRNE irx_2 ; wenn NEIN, dann weiter ; LDI ZL, LOW (R_buf) ; Pointer auf Anfang des Empfangsbuffers ; LDI ZH, HIGH(R_buf) ; setzen ; ST Z+, k ; empfangene Länge in Buffer setzen ; und Pointer inkrementieren ; STS R_chr, k ; empfangenes Zeichen = Zählerstand ; STS R_ptrL, ZL ; Pointer in SRAM zurückschreiben ; STS R_ptrH, ZH ; ; CLR temp ; ; STS R_cs, temp ; Prüfsumme löschen ; LDI i, 2 ; neuer Status = 2 ; STS R_sts, i ; ; RJMP irx_e ; fertig! ; -------------------- Status 2 -- (Datenbytes empfangen) ----------- ;irx_2: CPI i, 2 ; Status = 2? (auf Datenbytes warten) ; BRNE irx_3 ; wenn NEIN, dann weiter ; LDS ZL, R_ptrL ; Pointer auf Empfangsbuffer laden ; LDS ZH, R_ptrH ; ; ST Z+, k ; empfangenes Zeichen in Buffer setzen ; und Pointer inkrementieren ; STS R_ptrL, ZL ; Pointer in SRAM zurückschreiben ; STS R_ptrH, ZH ; ; LDS temp, R_cs ; schon berechnete Prüfsumme laden ; ADD temp, k ; Datenbyte zur Prüfsumme addieren ; STS R_cs, temp ; Prüfsumme zurückschreiben ; LDS temp, R_chr ; Zeichenzähler laden, ; DEC temp ; dekrementieren und ; STS R_chr, temp ; zurückschreiben ; BREQ irx_2a ; ist Zähler schon Null? ; RJMP irx_e ; wenn NEIN, dann fertig! ;irx_2a: LDI temp, 3 ; neuer Status = 3 ; STS R_sts, temp ; ; RJMP irx_e ; fertig! ; -------------------- Status 3 -- (Prüfsumme empfangen) ------------ ;irx_3: CPI i, 3 ; Status = 3? (auf Prüfsumme warten) ; BRNE irx_4 ; wenn NEIN, dann weiter ; LDI temp, 4 ; neuer Status = 4 (egal, ob Prüfsumme ; STS R_sts, temp ; gleich ist oder nicht) ; LDS temp, R_cs ; berechnetete Prüfsumme laden und ; CP temp, k ; mit gesendeter Prüfsumme vergleichen ; BRNE irx_3a ; wenn ungleich, dann Sprung ; CLR temp ; ; STS R_err, temp ; Fehlerstatus löschen ; RJMP irx_e ; fertig! ;irx_3a: LDI temp, 0x01 ; ungleich: -> Fehler (Bit 2^0 setzen) ; STS R_err, temp ; Fehlerstatus ; RJMP irx_e ; fertig! ; -------------------- Status 4 -- (auf ETX warten) ----------------- ;irx_4: CPI i, 4 ; Status = 4? (auf 'ETX' warten) ; BRNE irx_e ; wenn NEIN, dann fertig (Fehler) ; LDI temp, 0b00000000 ; Teilerfaktor = 0 (für 9600 Baud) ; OUT TCCR2, temp ; dadurch: Timeout ausschalten ; CLR temp ; ; STS R_sts, temp ; in Status 0 gehen ; CPI k, ETX ; empf. Zeichen mit 'ETX' vergleichen ; BREQ irx_4a ; wenn JA, dann Sprung ; LDS temp, R_err ; Bit 2^1 in Fehlerstatus ; ORI temp, 0x02 ; setzen ; STS R_err, temp ; ; RJMP irx_e ; fertig! ;irx_4a: LDS temp, R_err ; Fehlerstatus laden ; CPI temp, 0x00 ; ist KEIN Fehler aufgetreten ; BRNE irx_e ; wenn Fehler, dann fertig ; -- Daten kopieren -- ; LDI ZL, LOW (R_buf) ; Zeiger1 auf Begin des ; LDI ZH, HIGH(R_buf) ; Empfangspuffers setzen ; LDI XL, LOW (R_str) ; Zeiger2 auf Begin des ; LDI XH, HIGH(R_str) ; Empfangsstrings setzen ; LD i, Z+ ; Länge des Strings lesen und in.. ; ST X+, i ; ..Empfangsstring setzen ;irx_5: LD temp, Z+ ; Datenbytes aus Puffer lesen und in.. ; ST X+, temp ; ..Empfangsstring setzen ; DEC i ; solange in Schleife bleiben, bis ; BRNE irx_5 ; Index (=restliche Länge) = 0 und.. ; fertig! ;RCALL "Bearbeitung der Daten" ;* ; LDI ZL, LOW (R_str+1) ;* +1, weil 1.Byte (=Stringlänge) über- ; LDI ZH, HIGH(R_str+1) ;* sprungen wird. ; LDI para1, 1 ;* ; RCALL PRowx ;* ;irx_e: LDS temp, R_err ;* ;LD temp, -Z ;* ;OUT PORTA, temp ;* ; POP ZL ; ; POP ZH ; ; POP XL ; ; POP XH ; ; POP k ; ; POP i ; ; POP temp ; benutzte Register wiederherstellen ; OUT SREG, temp ; ; POP temp ; ; RETI ; ; ------------------------------------------------------------------- ;isr_cm2: PUSH temp ; benutzte Register retten ; IN temp, SREG ; ; PUSH temp ; ; LDI temp, 0b00000000 ; Teilerfaktor = 0 -> kein Takt ; OUT TCCR2, temp ; = Timeout wieder abschalten ; OUT TCNT2, temp ; und Timeout zurücksetzen ; CLR temp ; ; STS R_sts, temp ; wieder auf Status 0 setzen ; LDI temp, 0x04 ; Bit 2^2 setzen ; STS R_err, temp ; ;OUT PORTA, temp ;* ; POP temp ; ; OUT SREG, temp ; ; POP temp ; ; RETI