Hallo Leute, ich bin hier zur Zeit in Australien und arbeite an einem Projekt, bei dem wir eine 1kHz Datenstream per VB auslesen sollen. Ein kleiner Sensor liefert einen 9-Bit Hex Datenstrom, den es gilt per VB aufzunehmen und weiterzuverarbeiten. Soweit ist das ja kein Problem, Daten per RS232 zu empfangen, jedoch scheiter ich nun daran, das der Sensor permanent Daten im Format FFFFFFFF+CRlf liefert und mir beim Einlesen in eine Variable diese bis oben hin vollballert und mit Daten überschüttet. Bisher habe ich mit serialPort.ReadExisting experimentiert und auch die Daten erhalten. Ich bräuchte aber eine Funktion, die am Datenstrom lauscht und mir jede HEX Zahl in die variable schreibt. Kommt dann die neue HEX Zahl soll der alte Wert in der Variablen überschrieben werden, usw... Ja, da hänge ich im Moment ein wenig, hab mich aber auch erst in die Materie der RS232 eingearbeitet. Evtl. ist die Lösung ja ganz einfach und ReadExisting ist einfach nicht die richtige Methode. Evtl. ist aber auch VB nicht die richtige Sprache, aber das ist das was sie hier bisher nutzen und die Sensorabfrage darin integrieren möchten. Danke für eure Ideen dazu. Viele Grüße aus dem sommerlichen Brisbane bei 38º Paul PS: wundert euch nicht, das ich Nachts schreibe, es ist hellichter Tag!
9-Bit-Daten kann die serielle Schnittstelle des PCs nicht verarbeiten, das geht nur mit üblen Tricks (Umschalten zwischen 8o1 und 8e1 je nach berechneter Parität des zu sendenden Bytes, Auswerten der Paritätsfehlerinformation bei jedem empfangenen Byte und Abgleich mit der momentan aktiven Übertragungsart) - und das wird aus einem Usermode-Programm heraus sehr langsam. Was magst Du mit einem "1kHz-Datenstrom" meinen? Serielle Schnittstellen arbeiten mit gewissen standardisierten Baudraten, die (bei der klassischen seriellen Schnittstelle) ganzzahlige Teiler von 115200 sind, also 57600, 38400, 28800, 19200, 14400, 9600 ... Und das ist die Bruttobitrate, bei einer Datenlänge von 9 Bit kommen noch zwei Bits "Verpackung" dazu, so daß die Nutzdatenrate in Datenwörtern/Sekunde ein elftel der Baudrate beträgt. Andere Werte lassen sich allenfalls mit USB-RS232-Wandlern oder Steckkarten mit eigener UART-Hardware erzielen, diese bieten zusätzlich zu obiger Baudratenreihe noch andere, vornehmlich höhere Werte an.
Paul A. schrieb: > Hallo Leute, > > ich bin hier zur Zeit in Australien und arbeite an einem Projekt, bei > dem wir eine 1kHz Datenstream per VB auslesen sollen. Ein kleiner Sensor > liefert einen 9-Bit Hex Datenstrom, den es gilt per VB aufzunehmen und > weiterzuverarbeiten. > Also das mit den 9-Bit wird wohl so sein wie Rufus es schreibt, es sei denn du meinst mit dem 9ten Bit das Stopbit das wäre dann etwas anderes. Sind es wirklich 9 Bit wirds aufwendiger, da bietet es sich an einen Mikrocontroller zwischen Sensor und PC zu schalten, der entweder A) die 9 Bit als Bitstream verpackt (in 8-Bit Paketen) oder B) der Einfachheit halber immer 9-Bits in 2 Bytes verpackt, also 8 Bit im ersten Byte + 1 Bit im zweiten Byte. > Soweit ist das ja kein Problem, Daten per RS232 zu empfangen, jedoch > scheiter ich nun daran, das der Sensor permanent Daten im Format > FFFFFFFF+CRlf liefert und mir beim Einlesen in eine Variable diese bis > oben hin vollballert und mit Daten überschüttet. Bisher habe ich mit > serialPort.ReadExisting experimentiert und auch die Daten erhalten. serialPort.ReadExisting sagt mir jetzt gerade nichts, aber ich habe bereits unter VB6 mit dem seriellen Port gearbeitet, da gabs das "MSComm Control" und das hatte selbstverständlich auch Events, also irgendwas mit ... On Comm ... oder so, sprich es wurde ein Event ausgelöst sobald ein neues Datenbyte am seriellen Port anlag, das machte die Sache natürlich relativ einfach, weil man dadurch den seriellen Port nur dann kurz auslesen musste wenn tatsächlich ein neues Byte eingetroffen war und nicht permanent, würde mich wundern, wenn das unter VB.Net nicht auch so geht. > Ich bräuchte aber eine Funktion, die am Datenstrom lauscht und mir jede > HEX Zahl in die variable schreibt. Kommt dann die neue HEX Zahl soll der > alte Wert in der Variablen überschrieben werden, usw... > > Ja, da hänge ich im Moment ein wenig, hab mich aber auch erst in die > Materie der RS232 eingearbeitet. Evtl. ist die Lösung ja ganz einfach > und ReadExisting ist einfach nicht die richtige Methode. Evtl. ist aber > auch VB nicht die richtige Sprache, aber das ist das was sie hier bisher > nutzen und die Sensorabfrage darin integrieren möchten. Also wenn das Comm Control unter .Net wenigstens das selbe kann wie unter VB6 ist das überhaupt kein Problem, du musst dann aber auch auf jeden Fall die Events des Comm Controls verwenden sonst verlierst du permanent irgendwelche Bytes und dein Programm wird ziemlich prozessorlastig. > > Danke für eure Ideen dazu. > > Viele Grüße aus dem sommerlichen Brisbane bei 38º > Paul > > PS: wundert euch nicht, das ich Nachts schreibe, es ist hellichter Tag!
arrrrgggh, da hab ich mich wohl missverständlich ausgedrückt: Wir nennen ihn nur den 9-Bit Sensor. Er ist über eine RS422 Schnittstelle über ein USB-Interface am VCP COM3 angeschlossen. Wenn man ihn mit '1' triggert, dann liefert er einen dauernden Datenstream im Format FFFFFF+CRlf, bis er die '0' gesendet bekommt, also z.B. ffffff 000000 000001 000002 ... 0001fa usw und das im 1kHz Takt. Es handelt sich um einen Drehgeber, der in einer Maschine eingaut ist und nun sollen je nach Drehwinkel bestimmte Funktionen ausgelößt werden. Wenn sich die Maschine nicht dreht bekommt man trotzdem Daten, eben dann ständig den gleichen Wert. Er feuert quasi ununterbrochen und gibt den aktuellen Stand des Drehwinkels durch. Weiße ich disem Datenstrom nun eine Variable zu, so ist diese gefüllt mit haufenweiße Werten und sieht dann so aus: 000004 000004 000005 000006 000007 ... usw. Was ich nun erreichen möchte ist folgendes: es sollte immer nur ein Wert der Variablen zugeordnet werden, als nur bis zum CRlf. Kommt der nächste Wert soll die Variable mit diesem neuen Wert überschrieben werden. Es soll also nicht der ganze Stream (oder ein Teil davon) in der Variablen stehen, sondern nur 000004 kommt der nächste Wert soll eben 000005 darin stehen, usw. Mein Problem ist also, diesen andauernden Datenstrom zur Laufzeit hin aufzuspalten und jeden einzelnen Wert der Variablen X zuzuordnen. Hilft euch das weiter? Herzlichen Dank und viele Grüße aus Brisbane Paul
Die Werte sind offensichtlich durch Zeilenvorschübe getrennt. Speicher die empfangenen Bytes in einer (zusätzlichen) Stringvariablen zwischen, die Du auswertest, wenn der Zeilenvorschub empfangen wird. Den Wert weist Du erst in diesem Moment der eigentlichen Variablen zu, und Du setzt den Inhalt der Stringvariablen zurück, so daß das nächste empfangene Zeichen wieder an ihrem Anfang eingetragen wird.
Rufus t. Firefly schrieb: > Speicher > die empfangenen Bytes in einer (zusätzlichen) Stringvariablen zwischen Alles klar, nur das diese empfangenen Werte niemals enden. Es ist ein andauernder Datenstrom, der kommt. Wann mache also einen Schnitt? wenn ich a = serialPort1.ReadExisting mache, dann ist mein Variable a voller Werte (mit Zeilenvorschub getrennt), die dem Datenstrom gleichen. Weiß nicht genau wieviel Werte man in eine Variable reinschreiben kann, aber auch dies wird endlich sein. Spätestens, wenn diese überläuft, fehlen mir die Daten, die danach gesendet werden. Gibt es nicht eine Möglichkeit/Methode ReadExisting zu lauschen und die Daten in Echtzeit auzusplitten und weiter zu verarbeiten. Etwa so -> lies den Datenstrom und wenn ein CRlf kommt speichere den bisher erhaltenen Datenstrom in einer anderen Variablen. -> dann überschreibe den alten Wert mit dem neuen Wert der gleich nach dem letzten CRlf kommt... Idealerweiße hätte a immer nur den aktuellen Wert Danke und viele Grüße Paul
Das Stichwort heißt nach wie vor "Events"! Hab das gerade mal unter VS2008 überflogen. Da gibt es das "DataReceived"-Event und die "BytesToRead"-Property, das sollte genügen um das Problem zu lösen.
Ist es möglich, dass das neunte Bit ein simples Paritätsbit ist?
> wenn ich > > a = serialPort1.ReadExisting Dann mach das doch nicht, sondern lies Zeichen für Zeichen. Dann kannst Du problemlos CR/CRLF auswerten.
Für sowas verwende ich gerne einen Ringpuffer (FIFO), dann kann man das auch schön asynchron auswerten. Das .net-FX ist mir nicht mehr so geläufig, aber ich meine, der StringBuilder kann sowas auch. Du müsstest also im DataReceived-Event jeweils alle Daten vom SerialPort lesen und an den Puffer anhängen. In einem anderen Thread, also beispielsweise in einem Timer-Event liest du jetzt einfach alle Zeichen bis zum ersten CrLf aus dem Puffer und entfernst sie daraus. Das wiederholst du so lange, bis du beim letzten CrLf angekommen bist. Jetzt kannst du die als letztes empfangenen Daten parsen (Integer.Parse() kommt soweit ich weiss auch mit Hex klar) und deiner Variable zuweisen. Wenn du das jetzt z.B. alle 100ms ausführst, dann hast du schön vorhersehbare Updates deiner Variable und die Prozessorlast hält sich in Grenzen. In VB6 könnte man es (ohne auf die Effizienz zu achten) auch irgendwie so lösen:
1 | Private buffer As String |
2 | Private value As Long |
3 | |
4 | Private Sub mscomm1_OnComm(...) |
5 | 'Das ist dann auch sowas wie ein FIFO |
6 | buffer = mscomm1.Input & buffer |
7 | End Sub |
8 | |
9 | Private Sub Timer1_Tick() |
10 | Dim nums() As String |
11 | 'Auftrennen bei CrLf |
12 | nums = Split(buffer, vbCrLf) |
13 | If UBound(nums) > 0 Then |
14 | 'Zweitletzte Zahl nehmen (Der letzte Eintrag ist möglw. nicht vollständig |
15 | value = Val(nums(UBound(nums) - 1)) |
16 | End If |
17 | 'Unbehandelten Rest wieder in den Puffer schreiben |
18 | buffer = nums(UBound(nums)) |
19 | End Sub |
Hallo Leute, danke für eure Tipps. Leider war ich bishher nicht erfolgreich und konnte die Daten nicht auslesen. Im Datareceived event erhalte ich um die 440 Werte/s, die in den Puffer geschrieben werden. Das event läuft also in einer Art Schleife, solange Daten vom Port empfangen werden, richtig? Ist das dann schnell genug, um die Daten weiterzuverarbeiten? Das Aufteilen mit Split hat auch nicht so recht funktioniert und die Daten sind dann nicht vollständig vorhanden, d.h. es kommen nicht alle 8 digits des Hexstreams an, sondern manchmal nur 2, 3 oder 7. Nur ab und an ist der Wert vollständig. Bisher konnte ich keinen Beispielcode finden um den Puffer vernünftig auszulesen, so dass keine Daten verloren gehen... Es funktioniert einwandfrei, wenn die Daten endlich sind und keine neuen mehr nachkommen. Dieses Interface sendet aber ständig Daten, also ein Stream der nicht beendet wird. Kann mir jemand mal einen Beispielcode geben, der die Daten parallel zum Stream auslesen und aufteilen kann (als beim vbCr trennen)? Ich brauche nur eine Variable mit dem aktuellen Wert aus dem Stream, die alle 8digits des Hex beinhaltet und sich sofort mit ändert, wenn die Daten im Stream sich ändern. Hat jemand sowas schon mal gemacht und kann ein Beispiel posten? Danke euch nochmal Paul
Paul A. schrieb: > Hallo Leute, > > danke für eure Tipps. Leider war ich bishher nicht erfolgreich und > konnte die Daten nicht auslesen. > > Im Datareceived event erhalte ich um die 440 Werte/s, die in den Puffer > geschrieben werden. Das event läuft also in einer Art Schleife, solange > Daten vom Port empfangen werden, richtig? Jain, das zum "Datareceived event" zugehörige "Unterprogramm" wird nur jedesmal aufgerufen, wenn die die serielle Schnittstelle des PCs neue Daten empfangen hat, das kann im besten Fall, (wenn man es so voreingestellt hat mit "Me.SerialPort.ReceivedBytesThreshold = 1", (in deinem Fall je nach Geschwindigkeit aber nicht zwangsläufig sinnvoll)), bereits nach einem Byte passieren, muss aber nicht, es können auch mehrere Bytes sein. Der Trick besteht nun darin, jedesmal, wenn das Programm im "Datareceived event" ist, alle bis dahin angekommenen Daten auszulesen, (das geht dann z.B. auch mit .ReadExisting), und weiterzuverarbeiten, (das sind ja dann hoffentlich immer nicht allzuviele). Die Daten in DataReceived direkt auszuwerten macht aber bei dir u.U. nicht so viel Sinn, da du ja nicht davon ausgehen kannst, dass immer ein kompletter Datensatz in Form von "FFFFFFFF+CRlf" ankommt, mal kann es "CRlf+FFFFF..." sein, mal "FFFF" ein anderes mal nur "CR" oder auch einfach "lf+fffff" je nachdem zu welchen Zeitpunkt dein Programm startet oder ob es z.B. Übertragungsfehler oder Verzögerungen beim Auslösen des DataReceived-Events gibt. Du solltest also evt. aus dem DataReceived-Event heraus, die angekommenen Daten ersteinmal zwischenspeichern, also in einem String (oder einem 1-dimensionalen ByteArray), aneinanderhängen, bis du genügend Daten zum auswerten zusammenhast, (also irgendwas in der Form "FFFF+CRlf+FFFFFFFF+CRlf+FFFFFF"). "Ringbuffer" ist hier schon mal ein gutes Stichwort. Also ein String der nicht unendlich größer wird weil du permanent weitere Daten dranhängst, sondern einer mit einer festen Länge, der von Anfang bis Ende beschrieben wird und wenn das Ende erreicht ist wieder am Anfang beschrieben wird. Diesen Ringbuffer kannst dann mit einer zeitgesteuerten Routine, (Timer), regelmäßig abarbeiten. Das Konzept des Ringbuffers lebt davon, dass du die Daten im Ringbuffer immer etwas schneller abarbeitest als sie vom DataReceived-Event nachgeliefert werden. Natürlich musst du dann auch noch ein paar Variablen einführen, die dir sagen, bis zu welcher Stelle der Ringbuffer zuletzt beschrieben wurde und bis zu welcher Stelle du ihn zuletzt abgearbeitet hast, (also erkennen, dass z.B. gerade gar nichts abgearbeitet werden muss weil keine oder nicht genügend neue Daten vorliegen). Es sind aber auch Lösungen denkbar in denen du alles im DataReceived-Event erledigst, z.B. mit zwei kurzen Stringvariablen (so lange wie ein kompletter Datensatz), eine Stringvariable wird bei jedem DataReceived-Event sukzessive mit Daten gefüllt, (Startsignal ist ein ausgelesenes CRlf), ist die Variable "voll" wird sie in die zweite kopiert, das ist dann der aktuelle Wert. Hört sich jetzt einfacher an als es wohl in der praktischen Umsetzung sein dürfte, denn auch hier brauchst du wieder ein paar Variablen für Pointer und Flags die dir sagen wie der Zustand vor dem letzten Aufruf des letzten DataReceived-Events war und an denen das restliche Programm sehen kann, dass jetzt ein gültiger Wert vorliegt. Oder du kannst natürlich auch, wenn es die Anwendung erlaubt, "bescheißen". Der Sensor liefert permanent Daten, wenn du jetzt "Me.SerialPort.ReceivedBytesThreshold" z.B. auf "20" stellst, hast du bei jedem Aufruf des DataReceived-Events auf jedenfall genügend Daten um wenigstens einen Datensatz komplett auszuwerten. Also mit .ReadExisting in einen String lesen, String zerteilen anhand von CRlf, jeder Teilstring mit der richtigen Länge ist nun ein gültiger Datensatz, in eine globale Variable kopieren fertig ... Noch allgemein zum seriellen Port unter VB2005/2008 in diesem Thread: Beitrag "Re: SerialPort abfragen (VB2008)" Das activevb-Beispiel fand ich ganz gut. > ... > Ist das dann schnell genug, um > die Daten weiterzuverarbeiten? Das kann ich aus dem Stehgreif so leider auch nicht beantworten, werde so was aber bestimmt mal im Laufe des Jahres ausprobieren ... Wenn der Rechner schnell genug ist vermutlich schon. > > ...
Hallo Albrecht, Danke für Deine ausführliche Mail und die Infos bezüglich des DataReceived Events. Da hatte ich wohl was falsch verstanden. Jetzt scheint es für mich klarer zu sein und ich werde deine aufgezeigten Möglichkeiten versuchen umzusetzen. Und du hast recht, die Daten kommen im DataReceived Event nicht komplett an, wenn ich sie dort direkt auswerte und nur manchmal ist ein Datensatz vollständug abrufbar .Bisher war mir nicht klar warum. Nach deinen Erläuterungen weiß ich nun warum. Danke nochmal und viele Grüße aus Brisbane Paul
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.