Seite 1 von 2

Frage zum Com Port

Verfasst: 04.04.2010 16:05
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

Re: Frage zum Com Port

Verfasst: 06.04.2010 19:45
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

Re: Frage zum Com Port

Verfasst: 06.04.2010 19:56
von ts-soft
So wie ich das sehe, sind dort 2 While Wend nötig!

Re: Frage zum Com Port

Verfasst: 06.04.2010 20:12
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()

Re: Frage zum Com Port

Verfasst: 06.04.2010 20:27
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

Re: Frage zum Com Port

Verfasst: 06.04.2010 20:33
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.

Re: Frage zum Com Port

Verfasst: 06.04.2010 20:35
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?

Re: Frage zum Com Port

Verfasst: 07.04.2010 00:18
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.

Re: Frage zum Com Port

Verfasst: 07.04.2010 03:14
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

Re: Frage zum Com Port

Verfasst: 07.04.2010 17:41
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.