Frage zum Com Port

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
H.Brill
Beiträge: 496
Registriert: 15.10.2004 17:42
Wohnort: 66557 Neunkirchen

Frage zum Com Port

Beitrag von H.Brill »

Ich empfange von einem Mikrocontroller
alle 500 Ms einen 17 Zeichen langen String,
also alle Sekunde 2 Stück, die ich anschließend
etwas gekürzt ("1. Sensor : Temperatur")
in die ersten 2 Spalten eines ListIcons setze.

Grundgerüst steht mit zus. Button Empfangen.

Wie bekomme ich das am besten mit dem Timing
hin ?
So stürzt es irgendwie immer beim Empfang ab :

Code: Alles auswählen

Procedure Empfangen()
Protected bytes.l, sp1.s, sp2.s
If IsSerialPort(1)
    While AvailableSerialPortInput(1) <> 0
      bytes = ReadSerialPortData(1, *Puffer, 17) 
      sp1 = PeekS(*Puffer, 17)
      sp1 = Right(sp1, 6)
      Delay(500)
      bytes = ReadSerialPortData(1, *Puffer, 17) 
      sp2 = PeekS(*Puffer, 17)
      sp2 = Right(sp1, 6)
      AddGadgetItem(10, -1, sp1  + Chr(10) + sp2 + Chr(10) + "") 
      Delay(500)
    Wend
EndIf
EndProcedure
PB 6.10
Benutzeravatar
Falko
Admin
Beiträge: 3535
Registriert: 29.08.2004 11:27
Computerausstattung: PC: MSI-Z590-GC; 32GB-DDR4, ICore9; 2TB M2 + 2x3TB-SATA2 HDD; Intel ICore9 @ 3600MHZ (Win11 Pro. 64-Bit),
Acer Aspire E15 (Win11 Home X64). Purebasic LTS 6.11b1
HP255G8 Notebook @AMD Ryzen 5 5500U with Radeon Graphics 2.10 GHz 3.4GHz, 32GB_RAM, 3TB_SSD (Win11 Pro 64-Bit)
Kontaktdaten:

Re: Frage zum Com Port

Beitrag von Falko »

Ich habe das nicht ausprobiert,
aber hier könntest du folgendes ändern, damit auch nur Daten eingelesen werden, wenn auch
tatsächlich welche vorhanden sind:

von:

Code: Alles auswählen

While AvailableSerialPortInput(1) <> 0
nach:

Code: Alles auswählen

While AvailableSerialPortInput(1) > 0
Gruß Falko
Bild
Win11 Pro 64-Bit, PB_6.11b1
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Frage zum Com Port

Beitrag von ts-soft »

So wie ich das sehe, sind dort 2 While Wend nötig!
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
HeX0R
Beiträge: 3042
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: Frage zum Com Port

Beitrag von HeX0R »

Die Herangehensweise in einer Windowsumgebung ist ziemlich falsch.

Du solltest nicht auf die 500ms warten, sondern viel eher auf 17 empfangenen Zeichen.
Dann ist es auch wurscht, wann die ankommen, daher hier im Beispiel mal einen 50ms Timer eingestellt.
Das bedeutet dann, dass du die Werte mit einer maximalen Verzögerung von 50ms angezeigt bekommst.

Damit sollte man leben können.
(Nein ich starte keinen Wettbewerb "Wie viele Sätze kann ich hintereinander mit D beginnen lassen")

Der Code:

Code: Alles auswählen

Enumeration 
	#Main_Window
EndEnumeration

Enumeration
	#My_ComPort
EndEnumeration

Enumeration
	#My_WindowTimer
EndEnumeration

Enumeration
	#Frame3D
	#Text_ComPort
	#String_ComPort
	#Text_Baudrate
	#ComboBox_BaudRate
	#Button_Start
	#Text_Sensor_1
	#Text_Sensor_2
	;-Keep the following two constants in a row
	#String_Sensor_1
	#String_Sensor_2
EndEnumeration

Procedure main()
	Protected Bytes, CheckedSensor, String.s

	OpenWindow(#Main_Window, 0, 0, 320, 155, "Com Test", $C8001)
	Frame3DGadget(#Frame3D, 5, 5, 310, 50, "Comport Settings")
	TextGadget(#Text_ComPort, 15, 30, 50, 20, "Comport:")
	StringGadget(#String_ComPort, 70, 28, 40, 20, "1", #PB_Text_Right | #PB_String_Numeric)
	TextGadget(#Text_Baudrate, 120, 30, 50, 20, "Baudrate:")
	ComboBoxGadget(#ComboBox_BaudRate, 175, 28, 80, 20)
	ButtonGadget(#Button_Start, 260, 28, 45, 20, "Start")
	TextGadget(#Text_Sensor_1, 50, 72, 70, 20, "Sensor #1:")
	StringGadget(#String_Sensor_1, 130, 68, 100, 22, "0.0", #PB_String_ReadOnly | #PB_Text_Right)
	TextGadget(#Text_Sensor_2, 50, 102, 70, 20, "Sensor #2:")
	StringGadget(#String_Sensor_2, 130, 98, 100, 22, "0.0", #PB_String_ReadOnly | #PB_Text_Right)
	AddGadgetItem(#ComboBox_BaudRate, -1, "1200")
	AddGadgetItem(#ComboBox_BaudRate, -1, "2400")
	AddGadgetItem(#ComboBox_BaudRate, -1, "4800")
	AddGadgetItem(#ComboBox_BaudRate, -1, "9600")
	AddGadgetItem(#ComboBox_BaudRate, -1, "19200")
	;....
	SetGadgetState(#ComboBox_BaudRate, 0)

	;Init our Stringbuffer
	String = Space(17)
	
	Repeat
		Select WaitWindowEvent()
			Case #PB_Event_CloseWindow
				Break
			Case #PB_Event_Gadget
				Select EventGadget()
					Case #Button_Start
						If IsSerialPort(#My_ComPort)
							CloseSerialPort(#My_ComPort)
							RemoveWindowTimer(#Main_Window, #My_WindowTimer)
							SetGadgetText(#Button_Start, "Start")
						ElseIf OpenSerialPort(#My_ComPort, "COM" + GetGadgetText(#String_ComPort), Val(GetGadgetText(#ComboBox_BaudRate)), #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
							;Now add a Timer, which will fire any 50 ms
							AddWindowTimer(#Main_Window, #My_WindowTimer, 50)
							SetGadgetText(#Button_Start, "Stop")
							CheckedSensor = 0
						EndIf
				EndSelect
			Case #PB_Event_Timer
				If IsSerialPort(#My_ComPort)
					Bytes = AvailableSerialPortInput(#My_ComPort)
					If Bytes >= 17
						;O.k., here we go, read this one
						ReadSerialPortData(#My_ComPort, @String, 17)
						SetGadgetText(#String_Sensor_1 + CheckedSensor, Right(String, 6))
						CheckedSensor ! 1 ;<- will switch from 0 -> 1 and from 1 -> 0
					EndIf
				EndIf
						
		EndSelect
	ForEver
	
EndProcedure

main()
Benutzeravatar
H.Brill
Beiträge: 496
Registriert: 15.10.2004 17:42
Wohnort: 66557 Neunkirchen

Re: Frage zum Com Port

Beitrag von H.Brill »

Danke, klappt fast perfekt.
Bekomme für den ersten Sensor
bei jedem 2. Wert nur ein Fragment.
Hab unten in meinem Programm einfach
die Länge (= 7) abgefragt.
Da scheint es zu gehen.

Scheint aber eher bei meinem Bascom
Programm im Atmel Prozessor zu liegen.

Habe mittlerweile auch eins mit Thread
gemacht. Läuft problemlos. Da ich noch
nie was mit Thread gemacht habe, kannste
mal schauen, ob die Herangehensweise so
richtig ist.

Code: Alles auswählen

Global Quit.l, send.b, serialflag.l, com.s, *Puffer.l, EventID.l, ComThread.l, ThreadFlag.l

#Window = 1
serialflag = 0
ThreadFlag = 0

*Puffer = AllocateMemory(16)

Procedure Empfangen(*nada)
  Protected bytes.l, input.s{16}, sp1.s, sp2.s, file.s, i.l, cmd.s, buffer.b
If IsSerialPort(1)
     ;CreateFile(1, "R:\Sensor.dat")
     While AvailableSerialPortInput(1) 
       FillMemory(*Puffer, 16, 0, #PB_Byte)
       ;bytes = ReadSerialPortData(1, @buffer, 1)
       bytes = ReadSerialPortData(1, *Puffer, 16)
       ;input + chr(buffer)
       input = PeekS(*Puffer,16,#PB_Ascii)
       ;If buffer = 10
       If Right(input, 1) = Chr(10)
          cmd = Left(input, Len(input))
          sp1 = StringField(cmd, 1, "|")
          sp2 = StringField(cmd, 2, "|")
          If Len(sp1) = 7 
             SetGadgetText(5, sp1)
             SetGadgetText(7, sp2)
             AddGadgetItem(11, -1, sp1  + Chr(10) + sp2 + Chr(10) + "") 
             SetActiveGadget(11)
             SetGadgetItemState(11, CountGadgetItems(11) -1, 1)
             ;WriteStringN(1, input)   
          EndIf
         ; FillMemory(*Puffer, 17, 0, #PB_Byte)
       EndIf 
       Delay(200)
     Wend  
    ;CloseFile(1)
EndIf
EndProcedure

If OpenWindow(#Window, 10, 10, 670, 450, "DS1820 Lesen", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
       ButtonGadget(1,5, 10,100, 20, "Ende")
       ButtonGadget(2,120, 10, 100, 20, "Start")
       ButtonGadget(3,230, 10, 100, 20, "Open Port")
       TextGadget(4, 5, 50, 80, 20, "Sensor 1")
       StringGadget(5, 100, 50, 100, 20, "")
       TextGadget(6, 250, 50,80, 20, "Sensor 2")
       Stringgadget(7,350, 50, 100, 20, "") 
       ButtonGadget(8,340, 10, 100, 20, "Stop")
       ButtonGadget(9, 450, 10, 100, 20, "Pause")
       ButtonGadget(10, 560, 10, 100, 20, "Resume")
       
       ListIconGadget(11, 10, 200, 350, 200, "Sensor 1", 100, #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect)
       AddGadgetColumn(11, 1, "Sensor 2", 120)
       AddGadgetColumn(11, 2, "Spalte 3", 100)
       
       Quit = 0
   Repeat     
   ; Hier kommt die Event - Schleife
    EventID = WaitWindowEvent()
    ; Event abwarten und auswerten
    Select EventID
       Case #PB_Event_CloseWindow
          ; X im rechts oben für Schließen gedrückt.
          Quit = 1
       Case #PB_Event_Gadget ; Event für Gadgets
         Select EventGadget()    
            Case 1
               ; Ende - Button gedrückt
               Quit = 1
             Case 2
               If serialflag = 1
                  ComThread = CreateThread(@Empfangen(), 0)
                  If ComThread : ThreadFlag = 1 : EndIf
                  ClearGadgetItems(11)
               EndIf   
            Case 3
               ; Port öffnen 
               com = InputRequester("COM Öffnen", "COMPORT:", "COM1")
               If com > ""
                  If OpenSerialPort(1, com, 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1, 16)
                    serialflag = 1
                     MessageRequester("Fehler !", "Schnittstelle geöffnet !", #PB_MessageRequester_Ok)
                  Else
                    serialflag = 0
                    MessageRequester("Fehler !", "Konnte Schnittstelle nicht öffnen !", #PB_MessageRequester_Ok)
                  EndIf
                EndIf
              Case 8
                If ThreadFlag = 1
                    KillThread(ComThread)
                    ThreadFlag = 0
                    CloseSerialPort(1)
                    serialflag = 0
                    MessageRequester("Info !", "Schnittstelle neu öffnen !", #PB_MessageRequester_Ok)
                  EndIf 
                Case 9
                  ; Pause -> Thread
                  If ThreadFlag = 1
                    PauseThread(Comthread)
                  EndIf
                Case 10
                  ; Resume -> Thread
                  If ThreadFlag = 1
                    ResumeThread(ComThread)
                  EndIf  
            EndSelect
    EndSelect
   Until Quit = 1 Or EventID = #PB_Event_CloseWindow
EndIf
 
If serialflag = 1 : CloseSerialPort(1) : EndIf
FreeMemory(*Puffer)   
End
PB 6.10
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Frage zum Com Port

Beitrag von ts-soft »

killthread ist schon mal ganz verkehrt, das ist nur die notkeule.
Setze ein Flag an dieser Stelle und Frage das Flag im Thread ab.
Dann warte bis der Thread sich beendet hat, bzw. nach einem Timeout
kannste dann auch KillThread nutzen.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
HeX0R
Beiträge: 3042
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: Frage zum Com Port

Beitrag von HeX0R »

??
Da sieht ja alles anders aus, als in deinem Beispiel?
Du liest nun 16 anstatt 17 Zeichen aus, und beide Sensorwerte verstecken sich in diesem einen String?

Kein Wunder, dass mein Code oben nicht funktioniert...

Warum denn ein Thread? Was missfällt dir denn an meiner vorgeschlagenen Methode?
Benutzeravatar
mk-soft
Beiträge: 3855
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Frage zum Com Port

Beitrag von mk-soft »

@Hexor,

nichts gegen dein Code. Halte aber Threads für Untergrundbearbeitung wie Datenempfang oder Senden für den richtigen Weg.
Selber arbeite in ähnlichen Fällen immer mit Threads und teilweise sogar mit Watchdog-Thread die andere Threads überwachen und bei Ausfall oder Laufzeitüberschreitung neu initialisieren.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Frage zum Com Port

Beitrag von ts-soft »

Da sollte man doch erstmal abwägen was passiert. Es werden ca. alle 500 ms 17 bytes gelesen,
desweiteren werden events erzeugt deren endgültige Abarbeitung sowieso im Mainthread geschieht,
ansonsten wird über 99% der Zeit nur heiße Luft produziert.
Threads, die also keine Jobs sind, sondern ständig im hintergrund laufen, sind in diesem Falle
IMHO absolut überflüssig und ohne jeglichen Vorteil. Ob die CPU jetzt einen Thread, der sowieso
nur pausiert auf die Kerne besser verteilen kann oder nicht sollte zumindest in diesem Beispiel
keinerlei Vorteile bringen, zumal die Hauptarbeit, die Anzeige des Ergebnisses, im endeffekt sowieso
im Mainthread passiert, der Thread selber ist nur der Auslöser.

Solche Threads sind nur selten Sinnvoll, meist sind Threads von Vorteil, die nur einen Job erledigen
und dabei sogut wie keine Events erzeugen.

Ich finde HeX0r seine Lösung schon besser zur Aufgabe passend.

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
H.Brill
Beiträge: 496
Registriert: 15.10.2004 17:42
Wohnort: 66557 Neunkirchen

Re: Frage zum Com Port

Beitrag von H.Brill »

Das mit dem Thread hatte ich kurz vor
Hexor's Posting gemacht. Der Windows-
Timer gefällt mir auch besser. Geht ja
nur darum, daß die Oberfläche bedienbar
bleibt.
Jetzt zum Problem mit den Fragmenten :
In BASCOM (Basic für den Atmel Prozessor)
lese ich über 1wire (Ein - Draht - Bus) zwei
Sensoren hintereinander aus. Damit ich in
PB besser mit dem String hantieren kann,
habe ich die 2 Temperaturen zusammen
in einen String kopiert.

1. Temperatur :

Code: Alles auswählen

T3 = Format(t2 , "+00.0") + " C" + "|"
2. Temperatur :

Code: Alles auswählen

T3 = T3 + Format(t2 , "+00.0") + " C" + Chr(10) 
Um das nachher besser auseinander zu klabustern,
ist zwischen den Temperaturen ein Pipe-zeichen und
am Ende ein Chr(10).
Ergibt also je Temp. 7 Zeichen und zwei extra Zeichen
(| und chr(10)) zusammen = 16.

PS: Hab dein Code mal abgeändert (String = Space(16),
und ReadSerialPortData() auf 16, Right(..., 7, usw.).
Jetzt kommt an beiden nur noch Gemüse an.
PB 6.10
Antworten