(→Dateikopf und Initialisierung) |
K (→C-Datei) |
||
| Zeile 27: | Zeile 27: | ||
UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0); | UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0); | ||
| − | // Flush Receive-Buffer | + | // Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte) |
do | do | ||
{ | { | ||
Version vom 23. Juni 2006, 07:25 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.
uart.c
#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 (entfernen evtl. vorhandener ungültiger Werte)
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=...
uart.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.
- uart.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()
{
uint8_t sreg = SREG;
uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1);
UBRRH = (uint8_t) (ubrr>>8);
UBRRL = (uint8_t) (ubrr);
// Interrupts kurz deaktivieren
cli();
// UART Receiver und Transmitter anschalten, Receive-Interrupt aktivieren
// Data mode 8N1, asynchron
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
// Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte)
do
{
uint8_t dummy;
(void) (dummy = UDR);
}
while (UCSRA & (1 << RXC));
// Rücksetzen von Receive und Transmit Complete-Flags
UCSRA = (1 << RXC) | (1 << TXC);
// Global Interrupt-Flag wieder herstellen
SREG = sreg;
// 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
Die Ein- und Ausgebefunktionen machen nichts weiter, als die empfangenen/zusendenden Zeichen aus der FIFO zu lesen/in die FIFO zu schreiben.
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;
}
Siehe auch
WebLinks