K (typos) |
|||
(5 dazwischenliegende Versionen von einem anderen Benutzer werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
==Bascom UART Input== | ==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:[[Bild:Bascinput1.png]] | ||
+ | <pre> | ||
+ | DIM Msgbyte1 as Byte | ||
+ | DIM Msgword as Word | ||
+ | DIM Msgbyte2 as Byte | ||
+ | DIM Msglong as Long | ||
+ | DIM Msgbyte3 as Byte | ||
+ | </pre> | ||
+ | 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 | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | Die verschiedenen Typen stehen alle an der gleichen physischen Addresse. | ||
+ | Verwendung, z.B. | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | 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. | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | Dadurch entsteht: | ||
+ | [[Bild: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 | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | 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<br> | ||
+ | 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 | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | Der Empfänger | ||
+ | <pre> | ||
+ | |||
+ | 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 | ||
+ | </pre> | ||
+ | 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. | ||
+ | <pre> | ||
+ | Outpt(outpnt) = Temp | ||
+ | Incr Outpnt | ||
+ | if Outpnt >= 11 Then | ||
+ | ' Message abarbeiten | ||
+ | Messg = 0 ' reset | ||
+ | Stuff = 0 ' reset | ||
+ | End If | ||
+ | </pre> | ||
+ | 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== | ==Autor== | ||
Zeile 14: | Zeile 165: | ||
[[Kategorie:Microcontroller]] | [[Kategorie:Microcontroller]] | ||
[[Kategorie:Software]] | [[Kategorie:Software]] | ||
+ | [[Kategorie:Praxis]] | ||
[[Kategorie:Quellcode Bascom]] | [[Kategorie:Quellcode Bascom]] |
Aktuelle Version vom 26. Februar 2007, 02:05 Uhr
Inhaltsverzeichnis
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:
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: 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.