Difference between revisions of "AVR Embedded Tutorial - UART/de"

From Lazarus wiki
Jump to navigationJump to search
Line 110: Line 110:
 
===Deklaration===
 
===Deklaration===
 
Zeichenbuffer deklarieren, hier wir ein Puffer von 16 Zeichen angelegt.
 
Zeichenbuffer deklarieren, hier wir ein Puffer von 16 Zeichen angelegt.
<syntaxhighlight lang="pascal">
+
<syntaxhighlight>
 
const
 
const
 
   Crxlen = 16;
 
   Crxlen = 16;
 
var
 
var
   RxBuf: array[0..Crxlen - 1] of byte;
+
   RxBuf:   array[0..Crxlen - 1] of byte;
   rxread: byte = 0;
+
   rxread: byte = 0;
 
   rxwrite: byte = 0;     
 
   rxwrite: byte = 0;     
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 121: Line 121:
 
===Inzialisieren===
 
===Inzialisieren===
 
Dies ist ähnlich des Polling verfahren, der Unterschied, man teilt mit '''RXCIE0''' mit, das beim Zeichenempfang ein Interrupt ausgelöst wird.
 
Dies ist ähnlich des Polling verfahren, der Unterschied, man teilt mit '''RXCIE0''' mit, das beim Zeichenempfang ein Interrupt ausgelöst wird.
<syntaxhighlight lang="pascal">
+
<syntaxhighlight>
 
   procedure UARTInit;
 
   procedure UARTInit;
 
   begin
 
   begin
Line 135: Line 135:
 
Bei auslösen des Interrupts, wird das Zeichen vom UART in Zeichenbuffer kopiert.
 
Bei auslösen des Interrupts, wird das Zeichen vom UART in Zeichenbuffer kopiert.
  
<syntaxhighlight lang="pascal">
+
<syntaxhighlight>
 
   procedure UART_RX_Empfang; public Name 'USART__RX_ISR'; interrupt;
 
   procedure UART_RX_Empfang; public Name 'USART__RX_ISR'; interrupt;
 
   var
 
   var
Line 159: Line 159:
 
In diesem Beispiel werden, die empfangenen Zeichen in Grossbuchstaben umgewandelt und anschiessend wieder ausgegeben.
 
In diesem Beispiel werden, die empfangenen Zeichen in Grossbuchstaben umgewandelt und anschiessend wieder ausgegeben.
  
<syntaxhighlight lang="pascal">
+
<syntaxhighlight>
 
begin
 
begin
 
   // UART inizialisieren, die Interrupt gesteuert.
 
   // UART inizialisieren, die Interrupt gesteuert.
Line 196: Line 196:
 
   until 1 = 2;
 
   until 1 = 2;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
= Software - UART / UART - Emulation =
 +
Es ist auch möglich eine UART-Schnittstelle per Software zu emulieren.<br>
 +
Dies ist praktisch, wen man einen AVR hat, welcher keinen/zuwenig Hardware-UART hat.<br>
 +
<br>
 +
'''Hinweis:''' Dieser Code funktioniert nur mit 16MHz und 9600 Baud. Einfach '''Baud''' und '''CPU_Clock''' anzupassen reicht nicht, Weil die Formel noche einen Fehler hat.<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.
 +
 +
== Konstanten ==
 +
In diesem Beispiele werden die gleichen Pins verwendet, wie bei dem Hardware-UART beim Atmege328.
 +
<syntaxhighlight>
 +
const
 +
  CPU_Clock = 16000000;  // Taktfrequenz Arduino, default 16MHz.
 +
  Baud      = 9600;      // Baudrate
 +
 +
  TXpin    = (1 shl 1);
 +
  RXpin    = (1 shl 0);
 +
 +
  SoftTeiler = CPU_Clock div (Baud * 22); // Bitzeitzähler
 +
</syntaxhighlight>
 +
 +
 +
== Zeichen senden ==
 +
<syntaxhighlight>
 +
  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 begin
 +
        PORTD := PORTD or TXpin;
 +
      end else begin
 +
        PORTD := PORTD and not TXpin;
 +
      end;
 +
      Data := Data shr 1;
 +
      for i := 0 to SoftTeiler do; // Ein Takt Pause
 +
    end;
 +
    asm Sei end; // Interrupt erlauben
 +
  end;
 +
</syntaxhighlight>
 +
 +
== Zeichen empfangen ==
 +
'''Hinweis:''' Wen kein Zeichen im Empfang kommt, hängt die Schleife im Endlosen.
 +
<syntaxhighlight>
 +
  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 begin
 +
        Inc(Result, $80);
 +
      end;
 +
    end;
 +
    for i := 0 to SoftTeiler;          // Ein Takt Pause
 +
    asm Sei end;                      // Interrupt erlauben
 +
  end;
 +
</syntaxhighlight>
 +
  
 
== Siehe auch ==
 
== Siehe auch ==

Revision as of 17:29, 12 May 2018

Template:Translate

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 und 9600 Baud. Einfach Baud und CPU_Clock anzupassen reicht nicht, Weil die Formel noche einen Fehler hat.
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 gleichen Pins verwendet, wie bei dem Hardware-UART beim Atmege328.

const
  CPU_Clock = 16000000;   // Taktfrequenz Arduino, default 16MHz.
  Baud      = 9600;       // Baudrate

  TXpin     = (1 shl 1);
  RXpin     = (1 shl 0);

  SoftTeiler = CPU_Clock div (Baud * 22); // Bitzeitzähler


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 begin
        PORTD := PORTD or TXpin;
      end else begin
        PORTD := PORTD and not TXpin;
      end;
      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 begin
        Inc(Result, $80);
      end;
    end;
    for i := 0 to SoftTeiler;          // Ein Takt Pause
    asm Sei end;                       // Interrupt erlauben
  end;


Siehe auch

Autor: Mathias