Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Bascom UART Input

An sich hat Bascom eine ganze Reihe von recht praktischen Befehle für die Daten Ein- u. Ausgabe über den UART. Solange sich Sender und Empfänger immer darüber einig sind, ob gerade ein Byte oder ein Single transferiert wird, ist man hier gut bedient.

Wenn man aber wegen anderer Beschäftigungen des Kontrollers den Empfang asynchron gestalten will, zeigen sich ein paar Widrigkeiten. Denn der Uart empfängt seine Daten immer als eine Reihe von einzelnen Bytes und es müssen daraus erst wieder die richtigen Felder zusammengesetzt werden. Verschärft wird das, wenn auch noch mehrere, womöglich verschiedenartige Felder zu empfangen sind, die erst zusammen einen Sinn ergeben, also zum Beispiel strukturierte "Messages"

Mehrere Felder (Message)

Das ist gewissermaßen die Aufgabenstellung:Bascinput1.png

DIM Msgbyte1 as Byte
DIM Msgword as Word
DIM Msgbyte2 as Byte
DIM Msglong  as Long
DIM Msgbyte3 as Byte

All diese Felder sollen hintereinander empfangen werden. Die Reihenfolge wäre nicht so schlimm, aber wie macht man z.B. aus 4 einzelnen Bytes ein "Long" ?

OVERLAY

Ein sehr häufige Anwendung von OVERLAY ist das Redefinieren von mehrbytigen Feld-Typen wie WORD, LONG, SINGLE, etc. für serielle Input/Output-Zwecke

DIM Redef_word  AS WORD
DIM Redef_int  AS INTEGER AT Redef_word OVERLAY
DIM Redef_long  AS LONG AT Redef_word OVERLAY
DIM Redef_single  AS SINGLE AT Redef_word OVERLAY
DIM Redef_array(4) AS Byte AT Redef_word OVERLAY

Die verschiedenen Typen stehen alle an der gleichen physischen Addresse. Verwendung, z.B.

   Redef_single = 3.44
   print hex(Redef_array(1))   ' LSB von LSW von Redef_single
   print hex(Redef_array(2))   ' MSB von LSW von Redef_single
   print hex(Redef_array(3))   ' LSB von MSW von Redef_single
   print hex(Redef_array(4))   ' MSB von MSW von Redef_single


Ein weiter Möglichkeit ist es, einen Speicherbereich, den 5 Felder insgesamt einnehmen, einfach ein zweites mal zu definieren, diesmal aber als einfaches Byte-Array.

DIM Msgbyte1 as Byte
DIM Msgword as Word
DIM Msgbyte2 as Byte
DIM Msglong  as Long
DIM Msgbyte3 as Byte

DIM Buffer(9) as Byte AT Msgbyte1 OVERLAY

Dadurch entsteht: Bascinput2.png Zu dem Buffer-Array wird natürlich auch ein Index mit dem Anfangswert 1 benötigt

DIM Bufferindex as Byte
Bufferindex = 1

Wenn nun zu einem x-beliebigen Zeitpunkt ein Byte empfangen wird, wird es in den Buffer geschrieben

   IF IsCharWaiting() <> 0 then 
      Buffer(Bufferindex) = INKEY()
      incr Bufferindex 
      IF Bufferindex > 9 then
'------------- Alle Datenfelder komplett, jetzt verarbeiten oder signalisieren ----------------
         Bufferindex = 1        ' Index zurücksetzen
      End If
   End If

Es muß vor Inkey() sichergestellt sein, daß auch tatsächlich ein Zeichen eingelangt ist, denn Inkey() würde, wenn nichts da ist, 0x00 zurückgeben. Dieses 0x00 können wir aber von einem "echten" 0x00, das ja möglich wäre, nicht unterscheiden. Daher vorher "IsCharWaiting()". In einer Receive-ISR ist das natürlich nicht erforderlich, denn da ist immer ein gültiges Zeichen da.

Message-Header und Trailer

Das gezeigte Prinzip hat natürlich für eine praktische Anwendung einen gravierenden Nachteil: Wenn wegen Überlastung oder sonstigen Gründen der Empfänger auch nur ein Byte versäumt, kommt er eigentlich nie oder nur zufällig wieder zu den richtigen Daten.

  • Bei ASCII-Daten kann natürlich ein Steuerzeichen (<CR>) als Kennzeichen verwendet werden, daß als nächstes wieder eine neue Message beginnt.
  • Haben wir es aber mit "transparenten" Daten zu tun, also Bytes mit 0-255, haben wir keine solchen reservierbare Kennzeichen.

Checksum

Als erste Sicherung ist es mal gut, solche verstümmelten Messages als solche zu erkennen. Das löst zwar nocht nicht das genannte Problem, aber wenigstens ist die Gefahr falscher Daten abgewendet. Da gibt es viele Varianten, CRC, BCC und andere. Das sind Prüfziffern, die man hinten an die Message anhängt. Der Sender erstellt sie, der Empfänger prüft.

Byte-Stuffing

Das ist das Verfahren, doch irgendein Zeichen als Steuerzeichen verwenden zu können, indem der Sender gezielt Bytes in den Byte-Stream "reinstopft" (stuffing), die nur zur Kennzeichnung dienen, ob ein Byte "echt" ist oder Teil der Daten ist. Der Empfänger entfernt sie dann wieder.

Dabei gilt folgende Regel: Folgt diesem Steuerzeichen ein beliebiges anderes Zeichen, ist es 
auch wirklich ein Steuerzeichen. Wenn nicht, gehört dieses Folgebyte zu den Daten

Da jedes Zeichen recht ist, hier ein Beispiel mit dem Zeichen "A", das einfach zum Message-Beginn-Steuerzeichen erklärt worden ist.

  Die Zeichenfolge "Hello, World" ist zu übertragen. Der Sender schickt also einfach 
  "AHello, World" über die Leitung (A=Header, das andere eben irgendeine Nachricht)

Das war nun offensichtlich kein Problem, es ist kein Mißverständnis möglich
Aber nun:

  "Guten Abend"  Losgeschickt wird nun 
  "AGuten AAbend"
  Das erste "A" wird von einem "G" gefolgt, also gilt es auch als Header
  Dem zweiten "A" folgt aber ein weiteres "A", also fällt es weg und es bleibt nurmehr "Abend" übrig

Beispielscode:

Vielleicht klingt das kompliziert, ist es aber nicht

Der Sender

DIM Buffer(9) as Byte
DIM BufIdx as Byte
    PRINT chr("A");                   ' der Header wird gesendet
    for Bufidx = 1 to 9 
       if Buffer(BufIdx) = "A" then 
             PRINT chr("A");          ' ein "A" wird "reingestopft"
       End If
       PRINT Buffer(BufIdx);
    next 

Der Empfänger


Dim Outpt(24) As Byte At Anywhere Overlay
Dim Outpnt As Byte

Dim Stuff As Byte    ' das Zeichen, daß ein stuffing Byte da war
Dim Messg As Byte    ' das Zeichen, daß eine Message gestartet wurde

receive_one_char:
  if ischarwaiting() = 1 then 
         Temp = Inkey()
         If Stuff = 0 And Temp = "A" Then
               Stuff = 1
         Else
               If Stuff = 1 And Temp <> "A" Then
                  Stuff = 0               ' Message starten 
                  Messg = 1
                  Outpnt = 1

                  Outpt(outpnt) = Temp    ' Erstes Zeichen speichern
                  Incr Outpnt

               Else
                  If Messg = 1 Then

                     Outpt(outpnt) = Temp   ' weiter Zeichen 
                     Incr Outpnt            ' speichern 

                  End If
               End If
               Stuff = 0
         End If
  End if 

In der Store-Sequenz muß dann auch die Ende-Erkennung drinnen stecken, also z.B. ob 11 (netto) bytes von "Guten Abend" empfangen worden sind.

   Outpt(outpnt) = Temp
   Incr Outpnt
   if  Outpnt >= 11 Then 
    ' Message abarbeiten
        Messg = 0     ' reset
        Stuff = 0     ' reset
   End If

Recht geschickt ist es aber auch, im ersten Byte der Message gleich die Länge der Message zu senden, dann können auch variabel lange Nachrichten richtig transportiert werden. Das letzte Byte wiederum könnte die Check-Sum beinhalten.

Anmerkung:: Diese Beispiele mit Text-Messages dienen nur der Demonstration. Das Verfahren ist aber voll-transparent, kann also alle Bytewerte von 0 - 255 beinhalten.

Statt des (willkürlich) gewählten Zeichens "A" nimmt man in der Praxis besser eines, das statistisch selten vorkommt, wenn sich das überhaupt ermitteln läßt.

Autor

Siehe auch