Difference between revisions of "AVR Embedded Tutorial - UART/de"
Line 201: | Line 201: | ||
Dies ist praktisch, wen man einen AVR hat, welcher keinen/zuwenig Hardware-UART hat.<br> | Dies ist praktisch, wen man einen AVR hat, welcher keinen/zuwenig Hardware-UART hat.<br> | ||
<br> | <br> | ||
− | '''Hinweis:''' Dieser Code funktioniert nur mit 16MHz und | + | '''Hinweis:''' Dieser Code funktioniert nur mit '''16MHz''' CPU-Takt und '''9600Baud'''. Einfach '''Baud''' und '''CPU_Clock''' anzupassen reicht nicht, Weil die Formel noche einen Fehler hat.<br> |
+ | '''Wichtig:''' Während der Übertragung, müssen Interrupts gesperrt werden.<br> | ||
Gleichzeigtes lesen und schreiben funktioniert nicht, da kein Puffer vorhanden ist.<br> | Gleichzeigtes lesen und schreiben funktioniert nicht, da kein Puffer vorhanden ist.<br> | ||
Im Idealfall ist dies gut verwendbar, um Fehler auf ein UART-Terminal auszugeben. | Im Idealfall ist dies gut verwendbar, um Fehler auf ein UART-Terminal auszugeben. | ||
Line 249: | Line 250: | ||
Data := (c shl 1) or $FE00; | Data := (c shl 1) or $FE00; | ||
for j := 1 to 10 do begin // Bit ausgeben, inklusiv Stop-Bits. | for j := 1 to 10 do begin // Bit ausgeben, inklusiv Stop-Bits. | ||
− | if (Data and 1) = 1 then | + | if (Data and 1) = 1 then |
PORTD := PORTD or TXpin; | PORTD := PORTD or TXpin; | ||
− | + | else | |
PORTD := PORTD and not TXpin; | PORTD := PORTD and not TXpin; | ||
− | |||
Data := Data shr 1; | Data := Data shr 1; | ||
for i := 0 to SoftTeiler do; // Ein Takt Pause | for i := 0 to SoftTeiler do; // Ein Takt Pause | ||
Line 276: | Line 276: | ||
for i := 0 to SoftTeiler do; // Ein Takt Pause | for i := 0 to SoftTeiler do; // Ein Takt Pause | ||
Result := Result shr 1; | Result := Result shr 1; | ||
− | if PIND and RXpin <> 0 then | + | if PIND and RXpin <> 0 then |
Inc(Result, $80); | Inc(Result, $80); | ||
− | |||
end; | end; | ||
for i := 0 to SoftTeiler; // Ein Takt Pause | for i := 0 to SoftTeiler; // Ein Takt Pause |
Revision as of 17:53, 12 May 2018
UART Polling
ATmega328p (Arduino uno/nano)
Für die serielle Ausgabe wird ein Terminal mit folgenden Einstellungen gebraucht:
Baud Rate | 9600 |
Bits | 8 |
Stopbits | 1 |
Parity | none |
Beim Betätigen von Space im Terminal-Programm, sollte ein "Hello World !" zurück kommen.
Konstanten für Inizialisierung
const
CPU_Clock = 16000000; // Taktfrequenz Arduino, default 16MHz.
Baud = 9600; // Baudrate
Teiler = CPU_Clock div (16 * Baud) - 1;
Inizialisieren
Die UART-Schnittstelle konfigurierieren.
procedure UARTInit;
begin
UBRR0 := Teiler; // Baud
UCSR0A := 0 // Normale Geschwindigkeit
UCSR0B := (1 shl TXEN0) or (1 shl RXEN0); // Empfangen und Senden
UCSR0C := (%011 shl UCSZ0); // 8-Bit Übertragung, 1 Stop-Bits.
end;
Zeichen empfangen
function UARTReadChar: char;
begin
while UCSR0A and (1 shl RXC0) = 0 do begin // Warten, bis Zeichen ankommt.
end;
Result := char(UDR0); // Zeichen einlesen.
end;
Zeichen senden
procedure UARTSendChar(c: char);
begin
while UCSR0A and (1 shl UDRE0) = 0 do begin // Warten, bis letztes Zeichen gesendet.
end;
UDR0 := byte(c); // Zeichen senden.
end;
String senden
procedure UARTSendString(s: ShortString);
var
i: integer;
begin
for i := 1 to length(s) do begin
UARTSendChar(s[i]); // Zeichen einzeln senden.
end;
end;
Hauptschleife
Hier wird gewartet, bis Space ankommt, anschliessend wir "Hello World !" gesendet.
program Project1;
begin
UARTInit;
repeat
if UARTReadChar = #32 then begin // #32 = space
UARTSendString('Hello World !'#13#10);
end;
until 1 = 2;
end.
ATtiny2313
Beim ATtiny2313 haben die Register eine andere Bezeichnung als beim ATmega328p.
- Die 0 entfällt in der Bezeichnung.
- UBRR0 muss man in UBRRH und UBRRL zerlegen.
Das sieht dann so aus:
UBRRH := Teiler shr 8;
UBRRL := Byte(Teiler);
UCSRB := (1 shl TXEN) or (1 shl RXEN);
UCSRC := (%011 shl UCSZ);
(Der Code wurde nicht getestet)
Interrupt
Beim Polling kann es passieren, das Zeichen verschluckt werden, dies passiert, wen ein Zeichen ankommt und man dieses nicht abholt. Aus diesem Grund empfiehlt es sich, den Zeichenempfang Interrupt gesteuert zum machen.
Bei höheren Baudraten, ist dies zwingend.
Es wir ein einfacher Ringpuffer mit 16 Zeichen verwendet.
ATmega328p (Arduino uno/nano)
Deklaration
Zeichenbuffer deklarieren, hier wir ein Puffer von 16 Zeichen angelegt.
const
Crxlen = 16;
var
RxBuf: array[0..Crxlen - 1] of byte;
rxread: byte = 0;
rxwrite: byte = 0;
Inzialisieren
Dies ist ähnlich des Polling verfahren, der Unterschied, man teilt mit RXCIE0 mit, das beim Zeichenempfang ein Interrupt ausgelöst wird.
procedure UARTInit;
begin
UBRR0 := teiler;
UCSR0A := (0 shl U2X0);
UCSR0B := (1 shl TXEN0) or (1 shl RXEN0) or (1 shl RXCIE0);
UCSR0C := %011 shl UCSZ0;
end;
Interrupt
Bei auslösen des Interrupts, wird das Zeichen vom UART in Zeichenbuffer kopiert.
procedure UART_RX_Empfang; public Name 'USART__RX_ISR'; interrupt;
var
ch: byte;
begin
// Zeichen einlesen
tchr := UDR0;
// Zeichen in Puffer schreiben.
if ch <> 0 then begin
RxBuf[rxwrite] := ch;
Inc(rxwrite);
if rxwrite >= Crxlen then begin
rxwrite := 0;
end;
end;
end;
Hauptschleife
In der Hauptschleife wird geprüft, ob sich etwas im Empfangspuffer, wen ja, wird ein Zeichen davon ausgelesen. In diesem Beispiel werden, die empfangenen Zeichen in Grossbuchstaben umgewandelt und anschiessend wieder ausgegeben.
begin
// UART inizialisieren, die Interrupt gesteuert.
UARTInit;
// Interrupt aktivieren.
asm sei end;
// Hauptschleife.
repeat
// Ist ein Zeichen im Puffer ?
while rxwrite <> rxread do begin
// Interrupt sperren.
asm cli end;
// Zeichen aus Puffer lesen
ch := RxBuf[rxread];
Inc(rxread);
if rxread >= Crxlen then begin
rxread := 0;
end;
// Interrupt aktivieren.
asm sei end;
// Wen kleiner Buchstaben, diesen in einen grossen umwandeln.
if ch in [$61..$7A] then begin
Dec(ch, 32);
end;
// Zeichen ausgeben.
UARTSendChar(char(ch));
end;
until 1 = 2;
Software - UART / UART - Emulation
Es ist auch möglich eine UART-Schnittstelle per Software zu emulieren.
Dies ist praktisch, wen man einen AVR hat, welcher keinen/zuwenig Hardware-UART hat.
Hinweis: Dieser Code funktioniert nur mit 16MHz CPU-Takt und 9600Baud. Einfach Baud und CPU_Clock anzupassen reicht nicht, Weil die Formel noche einen Fehler hat.
Wichtig: Während der Übertragung, müssen Interrupts gesperrt werden.
Gleichzeigtes lesen und schreiben funktioniert nicht, da kein Puffer vorhanden ist.
Im Idealfall ist dies gut verwendbar, um Fehler auf ein UART-Terminal auszugeben.
Konstanten
In diesem Beispiele werden die Pin 2 & 3 von PortD verwendet.
const
CPU_Clock = 16000000; // Taktfrequenz Arduino, default 16MHz.
Baud = 9600; // Baudrate
TXpin = (1 shl 3); // Pin 3 ( Empfangen )
RXpin = (1 shl 2); // Pin 2 ( Senden )
SoftTeiler = CPU_Clock div (Baud * 22); // Bitzeitzähler
Den Empfangs-Pin auf Input stellen.
begin
DDRD := DDRD or TXpin;
...
// Loop
...
end.
Hinweis: Will man die gleichen Pins wie bei der Hardware-UART verwenden, muss beim Arduino der Pin 0 & 1 frei gegeben werden.
begin
UCSR0B := 0; // Blockierter Pin 0 & 1 frei geben
...
// Loop
...
end.
Zeichen senden
procedure UARTSoftSendByte(c: byte);
const
Baud = 9600;
var
i, Data: int16;
j: Int8;
begin
asm Cli end; // Interrupt sperren
Data := (c shl 1) or $FE00;
for j := 1 to 10 do begin // Bit ausgeben, inklusiv Stop-Bits.
if (Data and 1) = 1 then
PORTD := PORTD or TXpin;
else
PORTD := PORTD and not TXpin;
Data := Data shr 1;
for i := 0 to SoftTeiler do; // Ein Takt Pause
end;
asm Sei end; // Interrupt erlauben
end;
Zeichen empfangen
Hinweis: Wen kein Zeichen im Empfang kommt, hängt die Schleife im Endlosen.
function UARTSoftReadByte: byte;
var
i: Int16;
j: Int8;
begin
asm Cli end; // Interrupt sperren
Result := 0;
while PIND and RXpin > 0 do; // Warten bis erste fallende Flanke
for i := 0 to SoftTeiler div 2 do; // Ein halber Takt Pause
for j := 0 to 7 do begin
for i := 0 to SoftTeiler do; // Ein Takt Pause
Result := Result shr 1;
if PIND and RXpin <> 0 then
Inc(Result, $80);
end;
for i := 0 to SoftTeiler; // Ein Takt Pause
asm Sei end; // Interrupt erlauben
end;
Siehe auch
- Übersichtseite AVR Embedded Tutorial/de
Autor: Mathias