Seite 1 von 1

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

Verfasst: 20.04.2024 14:40
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
..
..
..

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

Verfasst: 20.04.2024 17:46
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

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

Verfasst: 20.04.2024 19:37
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

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

Verfasst: 20.04.2024 21:36
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))

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

Verfasst: 21.04.2024 08:29
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.

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

Verfasst: 21.04.2024 08:43
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

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

Verfasst: 21.04.2024 13:46
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.

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

Verfasst: 22.04.2024 09:15
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.

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

Verfasst: 22.04.2024 10:54
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: