Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter fuer schwierige und grosse Gaerten im Test

(Ein- und Ausgabe)
(Kleine Änderungen)
 
(28 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
= Ohne Interrupts =
+
Hier werden zwei verschiedene Arten vorgestellt, wie die [[UART]] benutzt werden kann. Die erste Variante verzichtet auf [[Interrupt]]s und [[Interrupt#Unterschiede zum Polling|pollt]] permanent oder auf Wunsch nach eingehenden Daten. Diese Variante ist einfach, aber nicht effizient. Die zweite Variante arbeitet mit Interrupts und ermögliche eine nebenläufige Kommunikation.
  
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''".
+
Die Routinen sind erstellt worden für den USART des [[ATmega8]]. Für andere AVR &ndash; neuere Modelle und solche, die mehrer UART-Module besitzen &ndash; haben die Register teilweise andere Namen. Dann gibt es z.B. Register <tt>UBRR0L</tt>, <tt>UBRR1L</tt>, etc.
  
== C-Datei ==
+
= Variante 1: Ohne Interrupts =
  
Der USART wird Initialisiert als UART mit dem Datenformat 8N1 (8&nbsp;Datenbits, kein Parity, 1&nbsp;Stopbit).  
+
Hier eine der einfachsten Möglichkeiten, den Hardware-[[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''".
  
Teilweise haben die Register andere Namen, etwa wenn mehr als ein USART vorhanden ist. Dann Gibt es Register UBRR0L, UBRR1L, etc.
+
== C-Datei ==
<pre>
+
#include "uart.h"
+
  
#define BAUDRATE 38400
+
Der USART wird initialisiert als UART mit dem Datenformat 8N1 (8&nbsp;Datenbits, kein Parity, 1&nbsp;Stopbit).
  
void uart_init()
+
'''uart.c'''
{
+
#include "uart.h"
    uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1);
+
   
+
#define BAUDRATE 38400UL //Definition als unsigned long, sonst gibt es Fehler in der Berechnung
    UBRRH = (uint8_t) (ubrr>>8);
+
    UBRRL = (uint8_t) (ubrr);
+
void uart_init (void)
 
+
{
    // UART Receiver und Transmitter anschalten
+
    uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1);
    // Data mode 8N1, asynchron
+
 
    UCSRB = (1 << RXEN) | (1 << TXEN);
+
    UBRRH = (uint8_t) (ubrr>>8);
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
+
    UBRRL = (uint8_t) (ubrr);
 
+
 
    // Flush Receive-Buffer
+
    {{ccomment|UART Receiver und Transmitter anschalten }}
    do
+
    {{ccomment|Data mode 8N1, asynchron }}
    {
+
    UCSRB = (1 << RXEN) | (1 << TXEN);
        uint8_t dummy;
+
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
        (void) (dummy = UDR);
+
    }
+
    {{ccomment|Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte) }}
    while (UCSRA & (1 << RXC));
+
    do
}
+
    {
</pre>
+
        UDR;
 +
    }
 +
    while (UCSRA & (1 << RXC));
 +
}
  
 
== Benutzer-Schnittstelle und Header ==
 
== Benutzer-Schnittstelle und Header ==
  
 
Im Header <tt>uart.h</tt> werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung:
 
Im Header <tt>uart.h</tt> werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung:
;<tt>extern void uart_init()</tt>: Initialisiert den UART und aktiviert Receiver und Transmitter.
+
;<tt>extern void uart_init (void)</tt>: Initialisiert den UART und aktiviert Receiver und Transmitter.
 
;<tt>static inline int uart_putc (const uint8_t c)</tt>: Sendet das Zeichen <tt>c</tt> über den UART. Der return-Wert ist immer&nbsp;1.
 
;<tt>static inline int uart_putc (const uint8_t c)</tt>: Sendet das Zeichen <tt>c</tt> über den UART. Der return-Wert ist immer&nbsp;1.
;<tt>static inline uint8_t uart_getc_wait()</tt>: Wartet bis zum nächsten Empfang bzw. liefert das empfangene Zeichen.
+
;<tt>static inline uint8_t uart_getc_wait (void)</tt>: Wartet bis zum nächsten Empfang bzw. liefert das empfangene Zeichen.
;<tt>static inline 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 int uart_getc_nowait (void)</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.
 
Die Defines <tt>F_CPU</tt> und <tt>BAUDRATE</tt> geben die Taktrate des AVR sowie die Baudrate an. Dabei hat <tt>F_CPU</tt> nur rein informativen Character, es ''ändert'' die CPU-Frequenz nicht! Der Define für <tt>F_CPU</tt> kann man in die Quelle dazu schreiben, oder man gibt einen Wert per Kommandozeile/[[make|Makefile]] an mit <tt>-DF_CPU=...</tt>
 
Die Defines <tt>F_CPU</tt> und <tt>BAUDRATE</tt> geben die Taktrate des AVR sowie die Baudrate an. Dabei hat <tt>F_CPU</tt> nur rein informativen Character, es ''ändert'' die CPU-Frequenz nicht! Der Define für <tt>F_CPU</tt> kann man in die Quelle dazu schreiben, oder man gibt einen Wert per Kommandozeile/[[make|Makefile]] an mit <tt>-DF_CPU=...</tt>
<pre>
 
#ifndef _UART_H_
 
#define _UART_H_
 
  
#include <avr/io.h>
+
<tt>'''uart.h'''</tt>
 +
#ifndef UART_H
 +
#define UART_H
 +
 +
#include <avr/io.h>
 +
 +
extern void uart_init (void);
 +
 +
static inline int
 +
uart_putc (const uint8_t c)
 +
{
 +
    {{ccomment|Warten, bis UDR bereit ist für einen neuen Wert }}
 +
    while (!(UCSRA & (1 << UDRE)))
 +
        ;
 +
 +
    {{ccomment|UDR Schreiben startet die Übertragung }}
 +
    UDR = c;
 +
 +
    return 1;
 +
}
 +
 +
static inline uint8_t
 +
uart_getc_wait (void)
 +
{
 +
    {{ccomment|Warten, bis etwas empfangen wird }}
 +
    while (!(UCSRA & (1 << RXC)))
 +
        ;
 +
 +
    {{ccomment|Das empfangene Zeichen zurückliefern }}
 +
    return UDR;
 +
}
 +
 +
static inline int
 +
uart_getc_nowait (void)
 +
{
 +
    {{ccomment|Liefer das empfangene Zeichen, falls etwas empfangen wurde; -1 sonst }}
 +
    return (UCSRA & (1 << RXC)) ? (int) UDR : -1;
 +
}
 +
 +
#endif {{comment|UART_H }}
  
extern void uart_init();
+
= Variante 2: Mit Interrupts =
 
+
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_ */
+
</pre>
+
  
= Mit Interrupts =
+
Diese Interrupt-getriebene Variante benutzt FIFO-Methoden aus dem Artikel "[[FIFO mit avr-gcc]]" zur Zwischenspeicherung der zu sendenden und empfangenen Daten.
  
 
== Benutzer-Schnittstelle und Header ==
 
== Benutzer-Schnittstelle und Header ==
Zeile 91: Zeile 94:
 
Im Header <tt>uart.h</tt> werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung. Das Interface ist das gleiche wie beim Polling.
 
Im Header <tt>uart.h</tt> werden die Funktionen veröffentlicht und stehen in anderen Modulen zur Verfügung. Das Interface ist das gleiche wie beim Polling.
  
;<tt>extern void uart_init()</tt>: 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.
+
;<tt>extern void uart_init (void)</tt>: 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.
 
;<tt>extern int uart_putc (const uint8_t c)</tt>: Speichert das Zeichen <tt>c</tt> in der Ausgabe-Warteschlange. Der return-Wert ist&nbsp;1, falls das Zeichen in die FIFO eingetragen wurde, und&nbsp;0, falls die Ausgabe-FIFO voll ist. Der UART DATA-Interupt wird aktiviert, der die Zeichen aus der Ausgabe-FIFO über den UART verschickt.
 
;<tt>extern int uart_putc (const uint8_t c)</tt>: Speichert das Zeichen <tt>c</tt> in der Ausgabe-Warteschlange. Der return-Wert ist&nbsp;1, falls das Zeichen in die FIFO eingetragen wurde, und&nbsp;0, falls die Ausgabe-FIFO voll ist. Der UART DATA-Interupt wird aktiviert, der die Zeichen aus der Ausgabe-FIFO über den UART verschickt.
;<tt>extern uint8_t uart_getc_wait()</tt>: Liefert das nächste empfangene Zeichen aus der Eingabe-FIFO bzw. wartet darauf, falls die FIFO leer ist.
+
;<tt>extern uint8_t uart_getc_wait (void)</tt>: Liefert das nächste empfangene Zeichen aus der Eingabe-FIFO bzw. wartet darauf, falls die FIFO leer ist.
;<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 (void)</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 (void)</tt>: Wartet, bis die Übertragung fertig ist.
 +
 
 +
;<tt>uart.h</tt>
  
 
<pre>
 
<pre>
#ifndef _UART_H_
+
#ifndef UART_H
#define _UART_H_
+
#define UART_H
  
 
#include <avr/io.h>
 
#include <avr/io.h>
  
extern void uart_init();
+
extern void uart_init (void);
 
extern int uart_putc (const uint8_t);
 
extern int uart_putc (const uint8_t);
extern uint8_t uart_getc_wait();
+
extern uint8_t uart_getc_wait (void);
extern int     uart_getc_nowait();
+
extern int uart_getc_nowait (void);
  
static inline void uart_flush()
+
static inline void uart_flush (void)
 
{
 
{
 
while (UCSRB & (1 << UDRIE));
 
while (UCSRB & (1 << UDRIE));
 
}
 
}
  
#endif /* _UART_H_ */
+
#endif /* UART_H */
 
</pre>
 
</pre>
  
Zeile 119: Zeile 124:
  
 
===Dateikopf und Initialisierung ===
 
===Dateikopf und Initialisierung ===
<pre>
+
#include <avr/io.h>
#include <avr/io.h>
+
#include <avr/interrupt.h>
#include <avr/signal.h>
+
#include "uart.h"
#include <avr/interrupt.h>
+
#include "fifo.h" {{ccomment|erklärt im Artikel "[[FIFO mit avr-gcc]]"}}
#include "uart.h"
+
#include "fifo.h"
+
#define BAUDRATE 38400
 
+
#define BAUDRATE 38400
+
{{ccomment|FIFO-Objekte und Puffer für die Ein- und Ausgabe }}
 
+
// FIFO-Objekte und Puffer für die Ein- und Ausgabe
+
#define BUFSIZE_IN  0x40
 
+
uint8_t inbuf[BUFSIZE_IN];
#define BUFSIZE_IN  0x40
+
fifo_t infifo;
uint8_t inbuf[BUFSIZE_IN];
+
fifo_t infifo;
+
#define BUFSIZE_OUT 0x40
 
+
uint8_t outbuf[BUFSIZE_OUT];
#define BUFSIZE_OUT 0x40
+
fifo_t outfifo;
uint8_t outbuf[BUFSIZE_OUT];
+
fifo_t outfifo;
+
void uart_init (void)
 
+
{
void uart_init()
+
    uint8_t sreg = SREG;
{
+
    uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16UL*BAUDRATE) - 1);
    uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16*BAUDRATE) - 1);
+
   
+
    UBRRH = (uint8_t) (ubrr>>8);
    UBRRH = (uint8_t) (ubrr>>8);
+
    UBRRL = (uint8_t) (ubrr);
    UBRRL = (uint8_t) (ubrr);
+
 
+
    {{ccomment|Interrupts kurz deaktivieren }}
    // Rücksetzen von Receive und Transmit Complete-Flags
+
    cli();
    // UART Receiver und Transmitter anschalten, Receive-Interrupt aktivieren
+
    // Data mode 8N1, asynchron
+
    {{ccomment|UART Receiver und Transmitter anschalten, Receive-Interrupt aktivieren }}
    UCSRA = (1 << RXC) | (1 << TXC);
+
    {{ccomment|Data mode 8N1, asynchron }}
    UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
+
    UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
+
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
 
+
    // Flush Receive-Buffer
+
    {{ccomment|Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte) }}
    do
+
    do
    {
+
    {
        uint8_t dummy;
+
        {{ccomment|UDR auslesen (Wert wird nicht verwendet) }}
        (void) (dummy = UDR);
+
        UDR;
    }
+
    }
    while (UCSRA & (1 << RXC));
+
    while (UCSRA & (1 << RXC));
 
+
    // FIFOs für Ein- und Ausgabe initialisieren
+
    {{ccomment|Rücksetzen von Receive und Transmit Complete-Flags }}
    fifo_init (&infifo,  inbuf, BUFSIZE_IN);
+
    UCSRA = (1 << RXC) | (1 << TXC);
    fifo_init (&outfifo, outbuf, BUFSIZE_OUT);
+
}
+
    {{ccomment|Global Interrupt-Flag wieder herstellen }}
</pre>
+
    SREG = sreg;
 +
 +
    {{ccomment|FIFOs für Ein- und Ausgabe initialisieren }}
 +
    fifo_init (&infifo,  inbuf, BUFSIZE_IN);
 +
    fifo_init (&outfifo, outbuf, BUFSIZE_OUT);
 +
}
  
 
===Interrupt-Routinen===
 
===Interrupt-Routinen===
  
<pre>
+
{{ccomment|Empfangene Zeichen werden in die Eingabgs-FIFO gespeichert und warten dort }}
// Empfangene Zeichen werden in die Eingabgs-FIFO gespeichert und warten dort
+
ISR (USART_RXC_vect)
SIGNAL (SIG_UART_RECV)
+
{
{
+
    _inline_fifo_put (&infifo, UDR);
    _inline_fifo_put (&infifo, UDR);
+
}
}
+
 
+
{{ccomment|Ein Zeichen aus der Ausgabe-FIFO lesen und ausgeben }}
// Ein Zeichen aus der Ausgabe-FIFO lesen und ausgeben
+
{{ccomment|Ist das Zeichen fertig ausgegeben, wird ein neuer SIG_UART_DATA-IRQ getriggert }}
// Ist das Zeichen fertig ausgegeben, wird ein neuer SIG_UART_DATA-IRQ getriggert
+
{{ccomment|Ist die FIFO leer, deaktiviert die ISR ihren eigenen IRQ. }}
// Ist die FIFO leer, deaktiviert die ISR ihren eigenen IRQ.
+
ISR (USART_UDRE_vect)
SIGNAL (SIG_UART_DATA)
+
{
{
+
    if (outfifo.count > 0)
    if (outfifo.count > 0)
+
        UDR = _inline_fifo_get (&outfifo);
      UDR = _inline_fifo_get (&outfifo);
+
    else
    else
+
        UCSRB &= ~(1 << UDRIE);
        UCSRB &= ~(1 << UDRIE);
+
}
}
+
</pre>
+
  
 
=== Ein- und Ausgabe ===
 
=== 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.
 
<pre>
 
<pre>
 
int uart_putc (const uint8_t c)
 
int uart_putc (const uint8_t c)
Zeile 199: Zeile 207:
 
}
 
}
  
int uart_getc_nowait ()
+
int uart_getc_nowait (void)
 
{
 
{
 
     return fifo_get_nowait (&infifo);
 
     return fifo_get_nowait (&infifo);
 
}
 
}
  
uint8_t uart_getc_wait ()
+
uint8_t uart_getc_wait (void)
 
{
 
{
 
     return fifo_get_wait (&infifo);
 
     return fifo_get_wait (&infifo);
Zeile 214: Zeile 222:
  
 
Übergeben wird die Start-Adresse des Strings. Die Zeichenkette wird solange durchlaufen und die Zeichen ausgegeben, bis eine <tt>'\0'</tt> (Stringende-Marke) gelesen wird.
 
Übergeben wird die Start-Adresse des Strings. Die Zeichenkette wird solange durchlaufen und die Zeichen ausgegeben, bis eine <tt>'\0'</tt> (Stringende-Marke) gelesen wird.
<pre>
+
#include <avr/interrupt.h>  {{ccomment|Wird nur gebraucht bei der Interrupt-Version }}
#include <avr/interrupt.h>  // Wird nur gebraucht bei der Interrupt-Version
+
#include "uart.h"
#include "uart.h"
+
 
+
{{ccomment|Einen 0-terminierten String übertragen. }}
void uart_puts (const char *s)
+
void uart_puts (const char *s)
{
+
{
    do
+
    do
    {
+
    {
        uart_putc (*s);
+
        uart_putc (*s);
    }
+
    }
    while (*s++);
+
    while (*s++);
}
+
}
 
+
// Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will
+
{{ccomment|Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will }}
// Winwows: "\r\n"
+
{{ccomment|Windows: "rn" }}
// Linux  : "\n"
+
{{ccomment|Linux  : "n" }}
// MacOS  : "\r"
+
{{ccomment|MacOS  : "r" }}
#define CR "\r\n"
+
#define CR "\r\n"
 
+
char text[] = "Hallo Welt." CR;
+
char text[] = "Hallo Welt." CR;
 
+
int main()
+
int main()
{
+
{
    uart_init();
+
    uart_init();
    sei();  // Wird nur gebraucht bei der Interrupt-Version
+
    sei();  {{ccomment|Wird nur gebraucht bei der Interrupt-Version }}
 
+
    uart_puts (text);
+
    uart_puts (text);
    uart_puts ("Hallo Welt!" CR);
+
    uart_puts ("Hallo Welt!" CR);
 
+
    return 0;
+
    return 0;
}
+
}
</pre>
+
  
 
== Einen konstanten String senden ==
 
== 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).
 
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).
<pre>
 
#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)
+
#include <avr/interrupt.h>  {{ccomment|Wird nur gebraucht bei der Interrupt-Version }}
{
+
#include <avr/pgmspace.h>
    while (1)
+
#include "uart.h"
    {
+
        unsigned char c = pgm_read_byte (s);
+
{{ccomment|Einen 0-terminierten String senden, der im Flash steht. }}
        s++;
+
void uart_puts_P (const char *s)
        if ('\0' == c)
+
{
            break;
+
    while (1)
        uart_putc (c);
+
    {
    }
+
        unsigned char c = pgm_read_byte (s);
}
+
        s++;
 
+
        if ('\0' == c)
// Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will
+
            break;
// Winwows: "\r\n"
+
        uart_putc (c);
// Linux  : "\n"
+
    }
// MacOS  : "\r"
+
}
#define CR "\r\n"
+
 
+
{{ccomment|Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will }}
const prog_char text_p[] = "Hallo Welt." CR;
+
{{ccomment|Windows: "rn" }}
 
+
{{ccomment|Linux  : "n" }}
int main()
+
{{ccomment|MacOS  : "r" }}
{
+
#define CR "\r\n"
    uart_init();
+
    sei();  // Wird nur gebraucht bei der Interrupt-Version
+
const char text_p[] PROGMEM = "Hallo Welt." CR;
 
+
    uart_puts_P (text_p);
+
int main()
    uart_puts_P (PSTR("Hallo Welt!" CR));
+
{
 
+
    uart_init();
    return 0;
+
    sei();  {{ccomment|Wird nur gebraucht bei der Interrupt-Version }}
}
+
</pre>
+
    uart_puts_P (text_p);
 +
    uart_puts_P (PSTR("Hallo Welt!" CR));
 +
 +
    return 0;
 +
}
  
 
= Siehe auch =
 
= Siehe auch =
Zeile 295: Zeile 302:
  
 
==WebLinks==
 
==WebLinks==
* [http://people.freenet.de/gjl/helferlein/avr-uart-rechner.html AVR-Baudraten-Rechner (JavaScript)]
+
* [http://www.gjlay.de/helferlein/avr-uart-rechner.html AVR-Baudraten-Rechner (JavaScript)]
 +
* [http://jump.to/fleury Peter Fleurys Webseite mit UART Library (AVR-Software -> AVR-GCC libraries)]
  
 
[[Kategorie:Kommunikation]]
 
[[Kategorie:Kommunikation]]

Aktuelle Version vom 4. September 2011, 20:50 Uhr

Hier werden zwei verschiedene Arten vorgestellt, wie die UART benutzt werden kann. Die erste Variante verzichtet auf Interrupts und pollt permanent oder auf Wunsch nach eingehenden Daten. Diese Variante ist einfach, aber nicht effizient. Die zweite Variante arbeitet mit Interrupts und ermögliche eine nebenläufige Kommunikation.

Die Routinen sind erstellt worden für den USART des ATmega8. Für andere AVR – neuere Modelle und solche, die mehrer UART-Module besitzen – haben die Register teilweise andere Namen. Dann gibt es z.B. Register UBRR0L, UBRR1L, etc.

Variante 1: Ohne Interrupts

Hier eine der einfachsten Möglichkeiten, den Hardware-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).

uart.c

#include "uart.h"

#define BAUDRATE 38400UL //Definition als unsigned long, sonst gibt es Fehler in der Berechnung

void uart_init (void)
{
    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
    {
        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 (void)
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 (void)
Wartet bis zum nächsten Empfang bzw. liefert das empfangene Zeichen.
static inline int uart_getc_nowait (void)
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 (void);

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 (void)
{
    // Warten, bis etwas empfangen wird 
    while (!(UCSRA & (1 << RXC)))
        ;

    // Das empfangene Zeichen zurückliefern 
    return UDR;
}

static inline int
uart_getc_nowait (void)
{
    // Liefer das empfangene Zeichen, falls etwas empfangen wurde; -1 sonst 
    return (UCSRA & (1 << RXC)) ? (int) UDR : -1;
}

#endif /* UART_H  */

Variante 2: Mit Interrupts

Diese Interrupt-getriebene Variante benutzt FIFO-Methoden aus dem Artikel "FIFO mit avr-gcc" zur Zwischenspeicherung der zu sendenden und empfangenen Daten.

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 (void)
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 (void)
Liefert das nächste empfangene Zeichen aus der Eingabe-FIFO bzw. wartet darauf, falls die FIFO leer ist.
extern int uart_getc_nowait (void)
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 (void)
Wartet, bis die Übertragung fertig ist.
uart.h
#ifndef UART_H
#define UART_H

#include <avr/io.h>

extern void uart_init (void);
extern int uart_putc (const uint8_t);
extern uint8_t uart_getc_wait (void);
extern int uart_getc_nowait (void);

static inline void uart_flush (void)
{
	while (UCSRB & (1 << UDRIE));
}

#endif /* UART_H */

C-Datei

Dateikopf und Initialisierung

#include <avr/io.h>
#include <avr/interrupt.h>
#include "uart.h"
#include "fifo.h" // erklärt im Artikel "FIFO mit avr-gcc"

#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 (void)
{
    uint8_t sreg = SREG;
    uint16_t ubrr = (uint16_t) ((uint32_t) F_CPU/(16UL*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
    {
        // UDR auslesen (Wert wird nicht verwendet) 
        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 
ISR (USART_RXC_vect)
{
    _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. 
ISR (USART_UDRE_vect)
{
    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 (void)
{
    return fifo_get_nowait (&infifo);
}

uint8_t uart_getc_wait (void)
{
    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"

// Einen 0-terminierten String übertragen. 
void uart_puts (const char *s)
{
    do
    {
        uart_putc (*s);
    }
    while (*s++);
}

// Ein Zeilenumbruch, abhängig davon, was die Gegenstelle haben will 
// Windows: "rn" 
// 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"

// Einen 0-terminierten String senden, der im Flash steht. 
void uart_puts_P (const char *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 
// Windows: "rn" 
// Linux  : "n" 
// MacOS  : "r" 
#define CR "\r\n"

const char text_p[] PROGMEM = "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


LiFePO4 Speicher Test