(→Benutzer-Schnittstelle und Header) |
(→Benutzer-Schnittstelle und Header) |
||
Zeile 98: | Zeile 98: | ||
;<tt>extern int uart_getc_nowait()</tt>: Schaut nach, ob ein Zeichen empfangen wurde und liefert dieses gegebenenfalls als <tt>int</tt> zurück (Wertebereich ist 0...255). Wurde nichts empfangen, wird <tt>-1</tt> geliefert. | ;<tt>extern int uart_getc_nowait()</tt>: Schaut nach, ob ein Zeichen empfangen wurde und liefert dieses gegebenenfalls als <tt>int</tt> zurück (Wertebereich ist 0...255). Wurde nichts empfangen, wird <tt>-1</tt> geliefert. | ||
;<tt>static inline void uart_flush()</tt>: Wartet, bis die Übertragung fertig ist. | ;<tt>static inline void uart_flush()</tt>: Wartet, bis die Übertragung fertig ist. | ||
+ | |||
+ | ;<tt>fifo.h</tt> | ||
<pre> | <pre> |
Version vom 3. März 2006, 14:37 Uhr
Inhaltsverzeichnis
Ohne Interrupts
Hier eine der einfachsten Möglichkeiten, den Harware-UART von AVR zu nutzen. Die Empfangs- und Sendefunktionen sind so kurz, daß sie am besten als inline-Funktionen definiert werden. Auf Zeichen bzw. Ereignisse zu warten bezeichnet man auch als "polling".
C-Datei
Der USART wird Initialisiert als UART mit dem Datenformat 8N1 (8 Datenbits, kein Parity, 1 Stopbit).
Teilweise haben die Register andere Namen, etwa wenn mehr als ein USART vorhanden ist. Dann Gibt es Register UBRR0L, UBRR1L, etc.
#include "uart.h" #define BAUDRATE 38400 void uart_init() { uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1); UBRRH = (uint8_t) (ubrr>>8); UBRRL = (uint8_t) (ubrr); // UART Receiver und Transmitter anschalten // Data mode 8N1, asynchron UCSRB = (1 << RXEN) | (1 << TXEN); UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0); // Flush Receive-Buffer do { uint8_t dummy; (void) (dummy = UDR); } while (UCSRA & (1 << RXC)); }
Benutzer-Schnittstelle und Header
Im Header uart.h werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung:
- extern void uart_init()
- Initialisiert den UART und aktiviert Receiver und Transmitter.
- static inline int uart_putc (const uint8_t c)
- Sendet das Zeichen c über den UART. Der return-Wert ist immer 1.
- static inline uint8_t uart_getc_wait()
- Wartet bis zum nächsten Empfang bzw. liefert das empfangene Zeichen.
- static inline int uart_getc_nowait()
- Schaut nach, ob ein Zeichen empfangen wurde und liefert dieses gegebenenfalls als int zurück (Wertebereich ist 0...255). Wurde nichts empfangen, wird -1 geliefert.
Die Defines F_CPU und BAUDRATE geben die Taktrate des AVR sowie die Baudrate an. Dabei hat F_CPU nur rein informativen Character, es ändert die CPU-Frequenz nicht! Der Define für F_CPU kann man in die Quelle dazu schreiben, oder man gibt einen Wert per Kommandozeile/Makefile an mit -DF_CPU=...
fifo.h
#ifndef _UART_H_ #define _UART_H_ #include <avr/io.h> extern void uart_init(); static inline int uart_putc (const uint8_t c) { // Warten, bis UDR bereit ist für einen neuen Wert while (!(UCSRA & (1 << UDRE))) ; // UDR schreiben startet die Übertragung UDR = c; return 1; } static inline uint8_t uart_getc_wait() { // Warten, bis etwas empfangen wird while (!(UCSRA & (1 << RXC))) ; // Das empfangene Zeichen zurückliefern return UDR; } static inline int uart_getc_nowait() { // Liefer das empfangene Zeichen, falls etwas empfangen wurde; -1 sonst return (UCSRA & (1 << RXC)) ? (int) UDR : -1; } #endif /* _UART_H_ */
Mit Interrupts
Benutzer-Schnittstelle und Header
Im Header uart.h werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung. Das Interface ist das gleiche wie beim Polling.
- extern void uart_init()
- Initialisiert den UART und aktiviert Receiver und Transmitter sowie den Receive-Interrupt. Die Ein- und Ausgebe-FIFO werden initialisiert. Das globale Interrupt-Enable-Flag (I-Bit in SREG) wird nicht verändert.
- extern int uart_putc (const uint8_t c)
- Speichert das Zeichen c in der Ausgabe-Warteschlange. Der return-Wert ist 1, falls das Zeichen in die FIFO eingetragen wurde, und 0, falls die Ausgabe-FIFO voll ist. Der UART DATA-Interupt wird aktiviert, der die Zeichen aus der Ausgabe-FIFO über den UART verschickt.
- extern uint8_t uart_getc_wait()
- Liefert das nächste empfangene Zeichen aus der Eingabe-FIFO bzw. wartet darauf, falls die FIFO leer ist.
- extern int uart_getc_nowait()
- Schaut nach, ob ein Zeichen empfangen wurde und liefert dieses gegebenenfalls als int zurück (Wertebereich ist 0...255). Wurde nichts empfangen, wird -1 geliefert.
- static inline void uart_flush()
- Wartet, bis die Übertragung fertig ist.
- fifo.h
#ifndef _UART_H_ #define _UART_H_ #include <avr/io.h> extern void uart_init(); extern int uart_putc (const uint8_t); extern uint8_t uart_getc_wait(); extern int uart_getc_nowait(); static inline void uart_flush() { while (UCSRB & (1 << UDRIE)); } #endif /* _UART_H_ */
C-Datei
Dateikopf und Initialisierung
#include <avr/io.h> #include <avr/signal.h> #include <avr/interrupt.h> #include "uart.h" #include "fifo.h" #define BAUDRATE 38400 // FIFO-Objekte und Puffer für die Ein- und Ausgabe #define BUFSIZE_IN 0x40 uint8_t inbuf[BUFSIZE_IN]; fifo_t infifo; #define BUFSIZE_OUT 0x40 uint8_t outbuf[BUFSIZE_OUT]; fifo_t outfifo; void uart_init() { uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1); UBRRH = (uint8_t) (ubrr>>8); UBRRL = (uint8_t) (ubrr); // Rücksetzen von Receive und Transmit Complete-Flags // UART Receiver und Transmitter anschalten, Receive-Interrupt aktivieren // Data mode 8N1, asynchron UCSRA = (1 << RXC) | (1 << TXC); UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE); UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0); // Flush Receive-Buffer do { uint8_t dummy; (void) (dummy = UDR); } while (UCSRA & (1 << RXC)); // FIFOs für Ein- und Ausgabe initialisieren fifo_init (&infifo, inbuf, BUFSIZE_IN); fifo_init (&outfifo, outbuf, BUFSIZE_OUT); }
Interrupt-Routinen
// Empfangene Zeichen werden in die Eingabgs-FIFO gespeichert und warten dort SIGNAL (SIG_UART_RECV) { _inline_fifo_put (&infifo, UDR); } // Ein Zeichen aus der Ausgabe-FIFO lesen und ausgeben // Ist das Zeichen fertig ausgegeben, wird ein neuer SIG_UART_DATA-IRQ getriggert // Ist die FIFO leer, deaktiviert die ISR ihren eigenen IRQ. SIGNAL (SIG_UART_DATA) { if (outfifo.count > 0) UDR = _inline_fifo_get (&outfifo); else UCSRB &= ~(1 << UDRIE); }
Ein- und Ausgabe
int uart_putc (const uint8_t c) { int ret = fifo_put (&outfifo, c); UCSRB |= (1 << UDRIE); return ret; } int uart_getc_nowait () { return fifo_get_nowait (&infifo); } uint8_t uart_getc_wait () { return fifo_get_wait (&infifo); }
Weitere Routinen
Einen String senden
Übergeben wird die Start-Adresse des Strings. Die Zeichenkette wird solange durchlaufen und die Zeichen ausgegeben, bis eine '\0' (Stringende-Marke) gelesen wird.
#include <avr/interrupt.h> // Wird nur gebraucht bei der Interrupt-Version #include "uart.h" void uart_puts (const char *s) { do { uart_putc (*s); } while (*s++); } // Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will // Winwows: "\r\n" // Linux : "\n" // MacOS : "\r" #define CR "\r\n" char text[] = "Hallo Welt." CR; int main() { uart_init(); sei(); // Wird nur gebraucht bei der Interrupt-Version uart_puts (text); uart_puts ("Hallo Welt!" CR); return 0; }
Einen konstanten String senden
Unveränderliche Strings brauchen kein Platz im SRAM zu verschwenden, man lässt sie im Flash (wo sie sonst ebenfalls stehen für die SRAM-Initialisierung).
#include <avr/interrupt.h> // Wird nur gebraucht bei der Interrupt-Version #include <avr/pgmspace.h> #include "uart.h" void uart_puts_P (PGM_P s) { while (1) { unsigned char c = pgm_read_byte (s); s++; if ('\0' == c) break; uart_putc (c); } } // Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will // Winwows: "\r\n" // Linux : "\n" // MacOS : "\r" #define CR "\r\n" const prog_char text_p[] = "Hallo Welt." CR; int main() { uart_init(); sei(); // Wird nur gebraucht bei der Interrupt-Version uart_puts_P (text_p); uart_puts_P (PSTR("Hallo Welt!" CR)); return 0; }