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.
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
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