; ************************************************************** ; * Library: UART8 für AVR Prozessoren * ; * Autor: Oliver Dahlmann * ; * Datum: 29.12.2002 * ; * Beschreibung: Bibliothek mit verschiedenen Unterprogrammen * ; * zur Benutzung der seriellen Schnittstelle im * ; * 8Bit-Modus * ; ************************************************************** ; ; void UART8_Init(Baud:r16) bekommt in r16 einen Wert für die Baudrate übergeben, ; der UART wird auf 8Bit eingestellt, TX und RX Interrupts werden erlaubt ; und die Ringpuffer werden initialisiert (Zeiger) ; ; r16 Baud Abweichung% ; ------------------------------ f_clock ; 207 2.400 0,2% Baud = ------------------ ; 103 4.800 0,2% 16 * ( r16 + 1 ) ; 51 9.600 0,2% ; 34 14.400 0,8% ; 25 19.200 0,2% ; 16 28.800 2.1% !! ; 15 31.250 0,0% (MIDI) ; 12 38.400 0,2% ; ; void UART8_SendByte(Byte:r16) schiebt das in r16 übergebene Byte in den Sende-Ringpuffer und startet, falls ; nötig, den Sendevorgang. Befinden sich noch Bytes im Puffer, werden diese ; interruptgesteuert gesendet (UART8_TXC_Handler wird automatisch aufgerufen). ; Ist der Puffer n Bytes gross, können maximal n Bytes 'am Stück' versandt werden ; (also direkt nacheinander). ; ; void UART8_GetByte(*Callback:Z) prüft, ob ein neues (noch nicht verarbeitetes) Byte im Ringpuffer auf seine ; r16 wird verändert Verarbeitung wartet. In Z ist die Adresse einer Funktion zu übergeben, die ; die eigentliche Verarbeitung des Bytes sicherstellt. ; UART8_GetByte ändert keine Register ausser r16, immer im Hinterkopf behalten ; dass die CallBack-Routine vielleicht Register verändert! ; ; Beispiel für Aufruf im Hauptprogramm: ; ; MAINLOOP: ; ... ; mache irgendwas anderes... ; ldi zl,LOW(VERARBEITUNG) ; ldi zh,HIGH(VERARBEITUNG) ; rcall UART8_GetByte ; ... ; rjmp MAINLOOP ; VERARBEITUNG: ; ... ; mache was mit dem Inhalt von r17 ; ret ; zurück hinter den Aufruf von UART8_GetByte ; ; void UART8_SendPGMString(*Char:Z) sendet einen im ProgramMemory befindlichen null-terminierten String ; Z ist ein Zeiger auf die Anfangs-Adresse des Strings ; ; void UART8_SendRAMString(*Char:Z) sendet einen im RAM befindlichen null-terminierten String ; Z ist ein Zeiger auf die Anfangs-Adresse des Strings .CSEG UART8_GetByte: ; ** Diese Routine holt ein Byte aus dem als Ringpuffer implementierten Empfangspuffer der seriellen Schnittstelle ; ** und springt, falls es ein noch nicht verarbeitetes Byte im Puffer gab, an die in Z übergebene Adresse. ; ** Das zu verarbeitende Byte befindet sich zu diesem Zeitpunkt in r17 und darf verändert werden. Ebenso darf Z ; ** in der CallBack-Routine verändert werden. ; ** Das RET der in Z übergebenen Callback-Routine führt zu einem Rücksprung hinter den Aufruf von UART8_GetByte ; ** r16 wird verändert !!! cli lds r17,UART8_RB_ReadPos lds r16,UART8_RB_WritePos cp r17,r16 breq UART8_GetByte_NoNewData push zl ; Adresse für CallBack sichern push zh ldi zl,LOW(UART8_RB_START) ldi zh,HIGH(UART8_RB_START) ; Basisadresse des ReceiveBuffers lds r16,UART8_RB_ReadPos ; Der Lesezeiger innerhalb des ReceiveBuffers add zl,r16 clr r16 adc zh,r16 ld r17,z ; in r17 steht das empfangene Byte rcall UART8_Inc_RB_ReadPos ; Lesezeiger erhöhen sei ; ** Hier irgendwas mit dem empfangenen Byte anstellen ... ; ** Also Einsprung in eine Routine, die das Byte in r17 verarbeitet pop zh ; Adresse für CallBack wiederherstellen pop zl ijmp ; springt an die Adresse in Z (r31:r30), von dort wird direkt hinter den Aufruf UART8_GetByte zurückgesprungen UART8_GetByte_NoNewData: sei ret UART8_SendByte: ;** Diese Routine schreibt das in r17 übergebene Byte in den Sendepuffer (Ringpuffer) ;** der über den Interrupt automatisch gesendet wird ;** r16 wird verändert !!! push zl push zh cli ; *** Critical Section, Interrupts sperren *** ldi zl,LOW(UART8_SB_START) ldi zh,HIGH(UART8_SB_START) lds r16,UART8_SB_WritePos add zl,r16 clr r16 adc zh,r16 ; Z := Z + SB_WritePos st z,r17 ; Dann das Byte im Ringpuffer speichern rcall UART8_Inc_SB_WritePos lds r16,UART8_SB_WritePos push r17 lds r17,UART8_SB_ReadPos cp r16,r17 pop r17 brne UART8_BufferNotFull UART8_SB_BufferFull: rcall UART8_Dec_SB_WritePos lds r16,UART8_SB_ReadPos sei ; *** Ende Critical Section Weg1, Interrupts freigeben *** push r17 UART8_SB_WaitBufferFree: lds r17,UART8_SB_ReadPos cp r16,r17 breq UART8_SB_WaitBufferFree pop r17 rcall UART8_Inc_SB_WritePos ; Erhöhe Schreibzeiger um ein Byte UART8_BufferNotFull: sei ; *** Ende Critical Section Weg2, Interrupts freigeben *** lds r16,UART8_SB_Empty cpi r16,1 ; Wenn 1 in SB_Empty, ist der SB leer, es werden keine Daten gesendet, also TX_Interrupt auslösen brne UART8_SendevorgangLaeuft clr r16 sts UART8_SB_Empty,r16 ; SB_Empty auf 0 setzen out UDR,r17 ; Zeichen senden (Das erste Zeichen hier manuell, Rest folgt per IRQ) rcall UART8_Inc_SB_ReadPos ; Erhöhe Lesezeiger um ein Byte UART8_SendevorgangLaeuft: pop zh pop zl ret UART8_Inc_SB_WritePos: lds r16,UART8_SB_WritePos inc r16 cpi r16,(UART8_SB_END-UART8_SB_START) ; Ist der Schreibzeiger auf 16, dann setze ihn auf 0 (Ringpuffer-Prinzip) brne UART8_SB_NeuerRingzeigerWrite clr r16 UART8_SB_NeuerRingzeigerWrite: sts UART8_SB_WritePos,r16 ret UART8_Dec_SB_WritePos: lds r16,UART8_SB_WritePos dec r16 cpi r16,-1 ; Ist der Schreibzeiger auf -1, dann setze ihn auf 16 (Ringpuffer-Prinzip) brne UART8_Dec_SB_NeuerRingzWrite ldi r16,((UART8_SB_END-UART8_SB_START)-1) UART8_Dec_SB_NeuerRingzWrite: sts UART8_SB_WritePos,r16 ret UART8_Inc_SB_ReadPos: lds r16,UART8_SB_ReadPos inc r16 cpi r16,(UART8_SB_END-UART8_SB_START) ; Ist der Lesezeiger auf 16, dann setze ihn auf 0 (Ringpuffer-Prinzip) brne UART8_SB_NeuerRingzeigerRead clr r16 UART8_SB_NeuerRingzeigerRead: sts UART8_SB_ReadPos,r16 ret UART8_Inc_RB_WritePos: lds r16,UART8_RB_WritePos inc r16 cpi r16,(UART8_RB_END-UART8_RB_START) ; Ist der Schreibzeiger auf 16, dann setze ihn auf 0 (Ringpuffer-Prinzip) brne UART8_RB_NeuerRingzeigerWrite clr r16 UART8_RB_NeuerRingzeigerWrite: sts UART8_RB_WritePos,r16 ret UART8_Inc_RB_ReadPos: lds r16,UART8_RB_ReadPos inc r16 cpi r16,(UART8_RB_END-UART8_RB_START) ; Ist der Lesezeiger auf 16, dann setze ihn auf 0 (Ringpuffer-Prinzip) brne UART8_RB_NeuerRingzeigerRead clr r16 UART8_RB_NeuerRingzeigerRead: sts UART8_RB_ReadPos,r16 ret UART8_SendPGMString: ; Z: Zeiger auf String im ProgramMemory push r0 push r17 push zl push zh UART8_SP_Kopiere: lpm ; liest aus (Z) -> r0 mov r17,r0 cpi r17,0 ; Null Terminierung gefunden? breq UART8_SP_NullGefunden rcall UART8_SendByte ; Das Byte in r16 wird gesendet (UART) adiw zh:zl,1 ; inc(Z) rjmp UART8_SP_Kopiere UART8_SP_NullGefunden: pop zh pop zl pop r17 pop r0 ret UART8_SendRAMString: ; Z: Zeiger auf String im RAM push r0 push r17 push zl push zh UART8_SR_Kopiere: ld r17,z+ ; liest aus (Z) -> r16 cpi r17,0 ; Null Terminierung gefunden? breq UART8_SR_NullGefunden rcall UART8_SendByte ; Das Byte in r16 wird gesendet (UART) rjmp UART8_SR_Kopiere UART8_SR_NullGefunden: pop zh pop zl pop r17 pop r0 ret UART8_Init: out UBRR,r16 ; Baudrate wird in r16 übergeben (25 ergibt 19.200 Baud bei 8 MHz) ldi r16,(1< Der Sendepuffer ist leer ret UART8_RXC_Handler: ; aufzurufen vom UART_RXC_Interrupt ; ** ein Byte wurde von der seriellen Schnittstelle empfangen und hier in den Ringpuffer geschrieben push zh push zl push r16 in r16,SREG push r16 ldi zl,LOW(UART8_RB_START) ldi zh,HIGH(UART8_RB_START) ; Basisadresse des ReceiveBuffers lds r16,UART8_RB_WritePos ; Der Schreibzeiger innerhalb des ReceiveBuffers add zl,r16 clr r16 adc zh,r16 in r16,UDR st z,r16 ; Das Byte in den Empfangspuffer schreiben rcall UART8_Inc_RB_WritePos ; Schreibzeiger für den Empfangspuffer erhöhen pop r16 out SREG,r16 pop r16 pop zl pop zh ret UART8_TXC_Handler: ;** Wenn noch Bytes im Ringpuffer sind (SB_WritePos <> SB_ReadPos), wird das nächste gesendet push r16 in r16,SREG push r16 push r17 push zl push zh lds r16,UART8_SB_WritePos lds r17,UART8_SB_ReadPos cp r16,r17 breq UART8_AlleBytesGesendet ldi zl,LOW(UART8_SB_START) ldi zh,HIGH(UART8_SB_START) lds r16,UART8_SB_ReadPos add zl,r16 clr r16 adc zh,r16 ld r16,z ; Hole Zeichen aus Ringpuffer out UDR,r16 ; Und senden rcall UART8_Inc_SB_ReadPos rjmp UART8_TXC_Ende ; Springe zum Ende des IRQ's. Ein neuer IRQ wird ausgelöst, wenn das Byte gesendet wurde UART8_AlleBytesGesendet: ldi r16,1 sts UART8_SB_Empty,r16 ; SB_Empty auf 1 setzen -> Sendepuffer ist leer UART8_TXC_Ende: pop zh pop zl pop r17 pop r16 out SREG,r16 pop r16 ret .DSEG UART8_SB_START: .BYTE 16 ; Sendepuffer UART8_SB_END: UART8_RB_START: .BYTE 2 ; Empfangspuffer (Größe frei veränderbar, max. 256) UART8_RB_END: UART8_RB_WritePos: .BYTE 1 UART8_RB_ReadPos: .BYTE 1 UART8_SB_WritePos: .BYTE 1 UART8_SB_ReadPos: .BYTE 1 UART8_SB_Empty: .BYTE 1 ; ** RAM Bedarf: 23 Bytes.