OpenSerialPort(), In/Out-Buffer und SerialPortError()

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
TheCube
Beiträge: 169
Registriert: 20.07.2010 23:59
Computerausstattung: Risen 3400G 16MB Win10-64Bit
Wohnort: NRW

OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von TheCube »

Hallo, ich muss mal kurz die alten Sachen hervorkramen ...
Unten habe ich ein simples Testprogramm und die (zusammengefasste, kommentierte) Debugausgabe dazu angehängt.
Kurz: Mit Hilfe einer externen Datenquelle lasse ich den COM-Input-Puffer volllaufen.

[1] Die Angabe der Puffergrössen in OpenSerialPort() (hier 1024) scheint nur einen PB-Internen Nutzen zu haben ?

[2] Der reale Inputpuffer (Windows?) ist bei 65536 Bytes voll, AvailableSerialPortInput( ) stagniert. Gut.
Aber SerialPortError() gibt immer nur 28 zurück ... ist der Befehl auch nur auf die PB-internen 1024er-Puffer bezogen ?

[3] Was wäre die eleganteste/schnellste Puffer-Lösch-Methode für unrelevante Alt-Daten im Eingangspuffer?
(In PB bleibt wohl nur ReadSerialPortData() bis AvailableSerialPortInput()=0, aber vielleicht hilft Windows ?)

Code: Alles auswählen

zeichen$=Space(1)

If OpenSerialPort(0, "COM28", 500000, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
  Debug "COM Success"
  Repeat
    Debug (AvailableSerialPortInput(0)) ; COM Eingang empfängt ca. 1000 Bytes/sek. 
    ReadSerialPortData(0, @zeichen$, 1) ; COM Eingang aber nur sehr langsam absaugen ..
    Delay (100)
    Debug (SerialPortError(0)) ; 
  ForEver  
Else
  Debug "COM Failed"
EndIf
COM Success
0
28
417
28
758
28
871
28
870
28
869
28
868
28
867
28
..
..
.. ........ COM Daten laufen weiter auf ...
..
..
64578
28
64957
28
65336
28
65536 <- Inputbuffer full
28 <- kein #PB_SerialPort_RxOver o.ä. ??
65536
28
65536
28
65536
28
..
..
..
..
.. Hier wird Input auf COM (extern) beendet
65535
28
65534
28
65533
28 .......... COM Daten "tröpfeln" wieder heraus ...
65532
28
65531
28
..
..
..
DePe
Beiträge: 194
Registriert: 26.11.2017 16:17

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von DePe »

Windows 7 x64, PB v6.11b1 x64

Zu Punkt [2]:
Ich habe mit einem echten RS232-Port und einem USB-RS232-Adapter getestet. Der Rückgabewert von SerialPortError() ändert sich von 28 auf 29 wenn der Puffer voll ist, hier sind es 4.096 Bytes.

Peter

Code: Alles auswählen

[Debug] 3920
[Debug] 28
[Debug] 4008
[Debug] 28
[Debug] 4096
[Debug] 29
[Debug] 4096
[Debug] 29
DePe
Beiträge: 194
Registriert: 26.11.2017 16:17

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von DePe »

Zu Punkt [3]:
Den Buffer zu löschen funktioniert bei mir mit IOCTL. Ich habe mir das aus der Microsoft Hilfe und den SDK-Headerdateien zusammengebastelt.

Aber der Rückgabewerte von SerialPortError() ist wahrscheinlich falsch, es gibt keine Break-, Frame- oder RXParity-Fehler wie ausgegeben.

Peter

Code: Alles auswählen

#SERIAL_PURGE_RXCLEAR = $00000008

#FILE_DEVICE_SERIAL_PORT = $1b
#METHOD_BUFFERED = $0
#FILE_ANY_ACCESS = $0

#IOCTL_SERIAL_PURGE = (#FILE_DEVICE_SERIAL_PORT << 16) | ( #FILE_ANY_ACCESS << 14) | (19 << 2) | #METHOD_BUFFERED

Define iError.i, iInBuffer.l, iBytesReturned.i

If OpenSerialPort(0, "COM1", 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
  Debug "COM Success"
  Repeat
    Debug (AvailableSerialPortInput(0)) ; COM Eingang empfängt ca. 1000 Bytes/sek. 
    ReadSerialPortData(0, @zeichen$, 1) ; COM Eingang aber nur sehr langsam absaugen ..
    Delay (100)
    iError =SerialPortError(0)
    Debug iError

    If iError = 29
    	Debug "Clear Buffer"
    	iInBuffer = #SERIAL_PURGE_RXCLEAR
    	DeviceIoControl_(SerialPortID(0), #IOCTL_SERIAL_PURGE, @iInBuffer, SizeOf(Long), #Null, #Null, @iBytesReturned, #Null)
    	iError = GetLastError_()
    	Debug "" + iError + " - " + iBytesReturned
	EndIf

  ForEver  
Else
  Debug "COM Failed"
EndIf

Code: Alles auswählen

[19:22:41] [Debug] 3836
[19:22:41] [Debug] 28
[19:22:41] [Debug] 3920
[19:22:41] [Debug] 28
[19:22:41] [Debug] 4004
[19:22:41] [Debug] 29
[19:22:41] [Debug] Clear Buffer
[19:22:41] [Debug] 0 - 0
[19:22:41] [Debug] 0
[19:22:41] [Debug] 28
[19:22:41] [Debug] 84
[19:22:41] [Debug] 28
[19:22:41] [Debug] 168
[19:22:41] [Debug] 28
Benutzeravatar
TheCube
Beiträge: 169
Registriert: 20.07.2010 23:59
Computerausstattung: Risen 3400G 16MB Win10-64Bit
Wohnort: NRW

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von TheCube »

PB6.04 64Bit
Vielen Dank für deine Tests, dann sieht es jetzt erstmal so aus:

1] Die Angabe der Puffergrössen in OpenSerialPort() (hier 1024) scheint nur einen PB-Internen Nutzen zu haben. Mit dem real genutzten
Serial-Buffer hat es nichts zu tun. (Bei mir Win10 64Bit 64K, bei dir Win10 64Bit 4K)

[2] Der Nutzen oder sinnvolle Einsatz von SerialPortError() bleibt z.T. fraglich.

[3] Deine Puffer-Lösch-Methode per IOCTL ist interessant, aber dann vielleicht doch mehr Aufwand als (von mir) erhofft.
Wenn da nicht noch jemand mit einer direkt verwendbaren Windowsfunktion kommt (etwas wie "FlushCOMbuffer_()" bleibts
da wohl besser bei einer Lösung über PB selbst.
Simpel aber uneffektiv :

Code: Alles auswählen

While (AvailableSerialPortInput(0)) :  ReadSerialPortData(0, @zeichen$, 1) : Wend
Oder so, ist ggf. etwas schneller:

Code: Alles auswählen

  AnzBytes=AvailableSerialPortInput(0)
  *Puffer = AllocateMemory(AnzBytes, #PB_Memory_NoClear)
  ReadSerialPortData(0, *Puffer, AnzBytes)
  FreeMemory(*Puffer)
Wenn man seinen (ausreichend großen) Müll-Buffer nur einmal im Code generiert und
zur Laufzeit stehen lässt, reduziert es sich auf:

Code: Alles auswählen

ReadSerialPortData(0, *Puffer, AvailableSerialPortInput(0))
Benutzeravatar
H.Brill
Beiträge: 496
Registriert: 15.10.2004 17:42
Wohnort: 66557 Neunkirchen

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von H.Brill »

PurgeComm gibt es ja auch als API.
---- kernel32.lib -----

BOOL PurgeComm(
[in] HANDLE hFile,
[in] DWORD dwFlags
);

Parameters
[in] hFile

A handle to the communications resource. The CreateFile function returns this handle.

[in] dwFlags

This parameter can be one or more of the following values.

Value Meaning
PURGE_RXABORT
0x0002
Terminates all outstanding overlapped read operations and returns immediately, even if the read operations
have not been completed.
PURGE_RXCLEAR
0x0008
Clears the input buffer (if the device driver has one).
PURGE_TXABORT
0x0001
Terminates all outstanding overlapped write operations and returns immediately, even if the write operations
have not been completed.
PURGE_TXCLEAR
0x0004
Clears the output buffer (if the device driver has one).
Ein Profaner hatte damals gute Erfahrung damit gemacht.

Code: Alles auswählen

declare comi1&,erfolg%,comwert$,SSuccess&
DEF @PurgeComm(2) !KERNEL32,PurgeComm
cls
comi1&=opencom(Com1:,4096,4096)
erfolg%=SetCom(COM1:9600,N,8,1)

proc lesen

    comwert$=@ReadCom$(comi1&,1)
    @ComError(comi1&)

    whilenot comwert$=

        comwert$=@ReadCom$(comi1&,1)
        @ComError(comi1&)

    wend

    closecom(comi1&)
    comi1&=opencom(Com1:,4096,4096)
    erfolg%=SetCom(COM1:9600,N,8,1)
    LET SSuccess& = @PurgeComm(comi1&,8)
    drawtext 20,50,Erfolg : +str$(ssuccess&)

endproc

whilenot 0

    lesen

wend

closecom(comi1&)
Ist ja leicht für PB umzusetzen. Relevant sind als 2. Parameter die 8 für RX und die 4 für TX.
Probiere mal das.
PB 6.10
DePe
Beiträge: 194
Registriert: 26.11.2017 16:17

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von DePe »

Ich wollte gerade den Code mit PurgeComm_() posten, da hat schon H.Brill alles erledigt.

Trotzdem hier der Code nochmals.

Peter

Code: Alles auswählen

#PURGE_TXABORT = 1
#PURGE_RXABORT = 2
#PURGE_TXCLEAR = 4
#PURGE_RXCLEAR = 8

Define iError.l

If OpenSerialPort(0, "COM1", 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
  Debug "COM Success"
  Repeat
    Debug (AvailableSerialPortInput(0)) ; COM Eingang empfängt ca. 1000 Bytes/sek. 
    ReadSerialPortData(0, @zeichen$, 1) ; COM Eingang aber nur sehr langsam absaugen ..
    Delay (100)
    iError =SerialPortError(0)
    Debug iError

    If iError = 29
    	Debug "PURGE BUFFER"
    	If Not PurgeComm_(SerialPortID(0), #PURGE_RXABORT | #PURGE_RXCLEAR)
    		iError = GetLastError_()
    		Debug "ERROR: " + iError
		EndIf
	EndIf

  ForEver  
Else
  Debug "COM Failed"
EndIf

Code: Alles auswählen

[08:37:32] [Debug] 3920
[08:37:32] [Debug] 28
[08:37:32] [Debug] 4004
[08:37:32] [Debug] 28
[08:37:32] [Debug] 4088
[08:37:32] [Debug] 29
[08:37:32] [Debug] PURGE BUFFER
[08:37:32] [Debug] 0
[08:37:32] [Debug] 28
[08:37:32] [Debug] 84
[08:37:32] [Debug] 28
[08:37:32] [Debug] 168
Benutzeravatar
TheCube
Beiträge: 169
Registriert: 20.07.2010 23:59
Computerausstattung: Risen 3400G 16MB Win10-64Bit
Wohnort: NRW

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von TheCube »

Danke euch beiden, so kann es nützlich sein. :allright:
@DePe: An deinem Code (z.B. nach define) noch 'zeichen$=Space(1)' o.ä. hinzufügen dann klappt auch das Lesen wieder.

Die Entscheidung zu "Purgen" wenn SerialPortError(0)=29 benutze ich nicht, weil dies mit der gerade genutzten "Silicon Labs CP210x USB to UART Bridge"
(auf einem µC-Evalboard) nunmal nicht geht. Mag an der Konfiguration des CP210x liegen ... und keiner weiss welche Überraschungen die anderen
USB-COM-Brdges (CH341SER, FTDIxxx, etc.) da bereithalten.

P.S.: Bei mir funktionieren scheinbar als Purge-Parameter "#PURGE_RXABORT | #PURGE_RXCLEAR" als auch nur "#PURGE_RXCLEAR" gleich gut.
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von HeX0R »

Also ich habe ja schon jede Menge Applikationen für die COM erstellt, bin aber noch nie in die Verlegenheit gekommen den Puffer leeren zu müssen.
Eigentlich ist es so, wie bei einer Netzwerkanwendung, man holt möglichst schnell (also sicherlich nicht byteweise) ständig alles ab was da so gesendet wurde und was man nicht benötigt schmeißt man weg.
Dann kann dieses Szenario eigentlich nie auftreten.
Benutzeravatar
TheCube
Beiträge: 169
Registriert: 20.07.2010 23:59
Computerausstattung: Risen 3400G 16MB Win10-64Bit
Wohnort: NRW

Re: OpenSerialPort(), In/Out-Buffer und SerialPortError()

Beitrag von TheCube »

Klar, man kriegt es auch ohne extra Puffer leeren hin.
Auch ich hole mir zur Laufzeit fast immer den COM-Puffer in einem Rutsch, und suche meine CMD-Kennungen etc. im Speicher
und verwerfe ggf. den Rest.
Aber wenn ab OpenSerialPort() mit 500000 Baud der Puffer alle X ms mit einigen dutzend Bytes gefluted wird
(dazu können µC-Resetbootmeldungen vorkommen, teilw. kryptisch wg. falscher Baudrate) DANN ist es für mich nützlich.

Wenn ich dann eine (erste) CMD-Kennung zum µC-schicke möchte ich zum Start einen möglichst leeren Empfangspuffer:
- Ich muss nicht X Kilobytes an Müll durchsuchen nach der einen ms später eingegangenen Antwort-Kennung vom µC, sondern nur z.B. <200 Bytes
- Ich kann sicher sein das die Antwort-Kennung überhaupt Platz im Buffer findet.
- Kein Risiko von "false-positives" an alten/anderen obsoleten Kennungen im vollen Eingangspuffer

Kommt halt immer drauf an ... /:-> :lol:
Antworten