??Prozedur neben dem Programmablauf aufrufen(20ms)??

Hardware- und Elektronikbasteleien, Ansteuerung von Schnittstellen und Peripherie.
Fragen zu "Consumer"-Problemen kommen in Offtopic.
Benutzeravatar
chrizl
Beiträge: 60
Registriert: 31.08.2004 15:03
Wohnort: Pbg.
Kontaktdaten:

??Prozedur neben dem Programmablauf aufrufen(20ms)??

Beitrag von chrizl »

Hi,

war nun ja lange nicht mehr hier um nen Beitrag zu schreiben, aber das muss nun sein, da ich unbedingt hilfe brauche!

Habe nun schon vieles ausprobiert, mit dem PBOSL-Timer usw. aber es klappt nicht richtig.


Also zum Thema:

Ich habe nun ein Schulprojekt, in dem es darum geht, ein RC-Fahrzeug fern zu steuern.

Der Sender ist in diesem Falle aber dann nicht meine Pistolen-Fernsteuerung, sondern mein PC/Laptop.


Da ich im Abstand von 20ms 4 Signale senden muss, um alle Kanäle des Empfängers anzusprechen, brauche ich irgendwie eine Möglichkeit, die Prozedur genau in diesen 20ms pausenlos aufzurufen ohne das der PC abgebremst wird, bzw. das Programm stockt.


Zur Frequenz mal ein Bild, wie diese gesendet wird:

Bild

Bei diesem Beispiel werden mehrer Teilabschnitte mit etwa 0,9 - 2,1 ms gesendet.

Nunja, wie diese Frequenz im nachhinein per TxD(RS232) gesendet wird, haben mein Vater und ich schon fertig, nun liegt es daran, evtl. mit einem "Thread" diese Prozedur halt 50 mal die Sekunde aufzurufen.


Hoffe, ihr könnt mir da auch noch ein bisschen weiterhelfen.

M. f. G.


chrizL
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Du erstellst einen Thread, welcher in einer Repeat : Forever Schleife das
senden ausführst.

Nun machste in die Schleife eine weitere Schleife, welche so lange nicht
verlassen wird, bis 20 ms vorbei sind. 20ms sind allerdings schon sehr
kurz, wo du besser kein ElapsedMilliseconds() verwendest ... da brauchste
was besseres. Was du hier findest:
http://www.purebasic.fr/german/viewtopi ... 16&start=0

Richtig angewand sollte dein Problem nun überwunden sein. :D

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
chrizl
Beiträge: 60
Registriert: 31.08.2004 15:03
Wohnort: Pbg.
Kontaktdaten:

Beitrag von chrizl »

PMV hat geschrieben:Du erstellst einen Thread, welcher in einer Repeat : Forever Schleife das
senden ausführst.
Also erstmal hi PMV,

ja das könnte ich mir wohl vorstellen... aber unter Thread erstellen ist nicht gemeint, dass ich CreatThread() anwenden soll, sondern die Daten aufrufe oder wie?

weil ich mich mit dem Thema CreateThread() nicht so richtig verstanden habe.
Habe es vorhin ausprobiert, aber irgendwie ging es nicht so, wie ich sollte.... Das Programm hat meinen Rechner eher ausgebremst als sonstwas.


Zu dem Codeaufruf mit der Zeit habe ich das so gemacht:

Code: Alles auswählen

Procedure DELAYUS(t.l)
  Structure DLong
    lowlong.l
    hilong.l
  EndStructure
  DLong=0
  hitimefreq.DLong
  QueryPerformanceFrequency_(@hitimefreq)
  hitimecount1.DLong
  hitimecount2.DLong
  QueryPerformanceCounter_(@hitimecount1)
  Repeat
    QueryPerformanceCounter_(@hitimecount2)
  Until Int((hitimecount2\lowlong - hitimecount1\lowlong)/(hitimefreq\lowlong/1000000.0))>=t
EndProcedure 

a=1500
b=1500
c=2000

MyCom.s = "Com1: baud=300 parity=N data=8 stop=1"
HCom.l

HCom = ComOpen(MyCom,0,255,255) ; Die Buffer 255 könnte man evt. auf 0 setzen, da ja keine Bytes gesendet werden,
                                ; sondern nur TXD auf High oder Low.

Procedure PPM(a,b,c)
 Pause=300
 PauseG=1200
 Delta=20000-PauseG-a-b-c
 ComSetTXD(HCOM,1)
 ComSetTXD(HCOM,0)
 DELAYUS(Pause)
 ComSetTXD(HCOM,1)
 DELAYUS(a-Pause)
 ;Debug a
 ComSetTXD(HCOM,0)
 DELAYUS(Pause)
 ComSetTXD(HCOM,1)
 DELAYUS(b-Pause)
 ;Debug b
 ComSetTXD(HCOM,0)
 DELAYUS(Pause)
 ComSetTXD(HCOM,1)
 DELAYUS(c-Pause)
 ;Debug c
 ComSetTXD(HCOM,0)
 DELAYUS(Pause)
 ComSetTXD(HCOM,1)
 DELAYUS(Delta)
 ;Debug "test"
EndProcedure
Wobei a,b,c im Programm als Global vorgegeben werden und für die einzelnen Kanäle(Channel 1-3) stehen.

Die Werte, die dort stehen wären

1000 = Servo links
1500 = Servo Mittelstellung
2000 = Servo rechts

und halt viele viele zwischenpositionen.

Die Werte sind also in diesem Fall die Millisekunden.
Solange muss der Positionswert gesendet werden, damit der Empfänger das Sendersignal auswerten kann.

Wäre super, wenn du mir ein Beispiel geben könntest, wie deine Idee dabei aussehen könnte.

Bzw. wenn du auf Danilos PBOSL(StartTimer()) ansprechen möchtest, wie ich das vernünftig aufrufen muss.

Hatte mir einfach gedacht, dass ich dann

StartTimer(#Timer1, 20, @PPM())

ausführe und der das dann aufruft, bin mir halt mit dem extra Thread nicht richtig sicher :(
Mit freundlichen Grüßen,
chrizl
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Einsetzen musst dus schon selber ... das heißt, es auch verstehen :wink:
Hier also ein Beispielprogramm:

Code: Alles auswählen

;PB V4.0 Beta 11
Global RunThread.l ;Variable für den zu erstellenden Thread

;Timer by Danilo
Procedure InitGameTimer() 
  Shared _GT_DevCaps.TIMECAPS 
  SetPriorityClass_(GetCurrentProcess_(),#HIGH_PRIORITY_CLASS) 
  timeGetDevCaps_(_GT_DevCaps,SizeOf(TIMECAPS)) 
  timeBeginPeriod_(_GT_DevCaps\wPeriodMin) 
EndProcedure 

Procedure StopGameTimer() 
  Shared _GT_DevCaps.TIMECAPS 
  timeEndPeriod_(_GT_DevCaps\wPeriodMin) 
EndProcedure 
; ------------------

;dein Thread erstellen
Procedure Thread(Time.l)
  ;Variable deklarieren
  Protected Timer.l
  
  ;wartet, bis Thread laufen darf
  While Not RunThread : Delay(1) : Wend
  
  ;speichert die Startzeit
  Timer = timeGetTime_()
  
  Repeat
    ;das was ausgeführt werden soll, hier die vergangende Zeit anzeigen
    Debug Str(Time)+"ms"  
    
    ;wartet, bis X Millisekunden vorbei sind
    Repeat : Delay(1) : Until Timer + Time <= timeGetTime_()
    Timer + Time
    
  ;so lange vortführen, bis Thread nicht mehr laufen soll
  Until Not RunThread
EndProcedure
; ------------------

;Timerpriorität ändern
InitGameTimer() 

;Thread starten, Parameter ist die Zeit in ms
ThreadID = CreateThread(@Thread(), 1000)
If Not ThreadID ;wenn Thread nicht gestartet werden konnte
  MessageRequester("Error", "Konnte Thread nicht erstellen!")
  End
EndIf

;Thread erfolgreich gestartet, Thread darf laufen
RunThread = #True

;einfachste möglichkeit, um in dem Beispiel das Programm jederzeit
;beendbar zu machen ;-)
MessageRequester("", "Drücke 'OK' um Programm zu beenden")

;Thread soll sich selber beenden,
RunThread = #False   ;dafür die Variable auf False setzten und
WaitThread(ThreadID) ;warten, bis der Thread wirklich beendet wurde

;Timereinstellungen rückgängig machen
StopGameTimer()
Ansonnsten noch dem, was du gepostet hattest kommt die möglichkeit
auf, das die a-b-c Parameter deiner Prozedur gleich den Globalen a-b-c
Variablen sein sollen ... wenns so nicht ist, ignoriere den nächste
Beispielcode und zugehörigen Kommentar :D
Ansonnsten ein Beispielcode:

Code: Alles auswählen

Global a,b,c

Procedure Test(a,b,c)
  Debug a
  Debug b
  Debug c
EndProcedure

a = 10
b= 20
c = 30
Test(100, 100, 100)
Debug a
Debug b
Debug c
die Ausgaben sind nicht gleich ... das bedeutet, Variablen, die als
Parameter arbeiten sind "Protected", also geschützt und nur in der
Prozedur gültig, selbst wenn es Globale Variablen gibt, die genau so
heißen.

Edit: ich seh grad, du hast deine Timerfunktion auch im Forum gefunden,
die scheint wenn sie wirklich funktioniert, noch was genauer zu sein ...
wenn also mit timeGettime_() methode nicht ausreichend genau ist, sag
bescheid, oder vielleicht kannste die andere Methode jetzt selber einbaue
:D

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Soweit ich weiß ist es mit Windows nicht möglich, in diesen kurzen Zeitabständen zuverlässig zu arbeiten. Ein "Timeslice"/"Zeitscheibe" ist dort die kleinste Einheit (je nach Windows-Version 10-15ms) und bezeichnet die Dauer, die vom OS einem Prozess zugewiesen wird, während der er maximal den Prozessor für sich haben darf. Mischt sich aber mal ein anderer Prozess dazwischen, so erhält der andere ebenfalls Timeslices dieser Größe und verhindert damit zuverlässiges Timing. Selbst wenn der andere unterbrochen werden sollte, muss dann die Prozessumschaltzeit, die ebenfalls im ms-Bereich liegt, mitberücksichtigt werden. Es könnte helfen, den Prozess mit #REALTIME_PRIORITY_CLASS aufzurufen, dann erhält kein anderer Prozess mehr Rechenzeit, außer vielleicht (?) manchmal noch die Systemprozesse für z.B. I/O. Läuft aber ein Prozess mit Realtime-Priorität, reagiert z.B. nichteinmal mehr die Maus.

Manche Boards unterstützen zwar HighRes-Timer, die zur genaueren Zeitmessung geeignet sind, aber ich denke, dass sie dadurch einfach aufgrund der Prozessumschaltung nicht besser geeignet sind, für solch schnelle Timer.

Da aber 20ms immernoch länger als eine Zeitscheibe sind, und somit zwischen zwei loszuschickenden Signalen ruhig eine Zeitscheibe für einen anderen Prozess zur Verfügung steht, könnte das Ganze vielleicht sogar noch mit einem high-priority-Timer funktionieren.

Der Rückgabewert von ElapsedMilliseconds(), bzw. GetTickCount_() bzw. timeGetTime_() ist jedoch ungeeignet, da er nur nach einer Zeitscheibe geupdatet wird.

Code: Alles auswählen

; dasselbe für timeGetTime_() und GetTickCount_() anstelle ElapsedMilliseconds()
MessageRequester("", "OK drücken zum Beginnen der Ermittlung der Zeitscheibenlänge. Dauer: 3s")

For i=1 to 3
  sum = 0
  count = 0
  time = ElapsedMilliseconds()
  start = time
  Repeat
    If time <> ElapsedMilliseconds()
      sum + (ElapsedMilliseconds() - time)
      count+1
      time = ElapsedMilliseconds()
    EndIf
  Until time - start > 1000
  
  str.s + Str(sum/count) + "   "
Next

MessageRequester("Zeitscheibenlängen", str)
Du könntest für eine genauere Timer-Auflösung auch den ASM-Befehl !RDTSC nutzen, der dir den aktuellen CPU-Zyklus wiedergibt und daher genügend schnell geupdatet wird. Wenn du den mit der Prozessorgeschwindigkeit verrechnest, solltest du genügend feine Auflösung erhalten, die aber wiederum nur funkioniert, solange dem Prozess nicht mehrere ganze Zeitscheiben in Folge entzogen werden.

Linux ändert da übrigens auch nichts dran, da es ebenfalls mit einem solchen Zeitscheiben-Konzept arbeitet (was auch viele Vorteile hat).
!UD2
Benutzeravatar
chrizl
Beiträge: 60
Registriert: 31.08.2004 15:03
Wohnort: Pbg.
Kontaktdaten:

Beitrag von chrizl »

Jua,

jetzt bin ich platt...


also das hab ich so noch nicht gehört, ist aber interessant, mal was neues dazu zu lernen....


naja, mein vater und ich haben da jetzt ein bisschen was ausgetüftelt...

bzw. er hat da jetzt versucht, die timer richtig zu reihen, weil ich irgendwie die ganze zeit nen fehler hatte und die hilfe brauch(t)e


auf jeden fall läuft jetzt eine version vorerst so... muss nur noch die hardware umsetzen und dann ab an den oszi damit zum messen....

also das mit den ausgabezeiten sieht auf jeden fall schon einmal richtig aus.... beim debuggen

aber ich schreibe auf jeden fall noch einmal hier, sobald ich weiss ob es klappt oder nicht.. kann aber evtl. bis donerstag andauern

und dann muss ich noch aufs "große C" warten, dass die mir endlich mein Ersatzgerät zuschicken, damit ich alles umsetzen und ausprobieren kann, sonst kann ich alles vergessen :(
Mit freundlichen Grüßen,
chrizl
Benutzeravatar
chrizl
Beiträge: 60
Registriert: 31.08.2004 15:03
Wohnort: Pbg.
Kontaktdaten:

Beitrag von chrizl »

Nunja..

also wie es nun ja immer so ist, es funktioniert nicht so, wie es soll.

Ich bräuchte einen Microcontroller, mit dem ich dann die Signale an den Sender schicken kann.

Der PC, also die RS232 ist unter Windows viel zu langsam dazu.

Ich komme da mit den Signalen noch nicht einmal auf die 0,3ms, die ich senden muss, damit der Empfänger das Signal richtig aufnimmt.

Mit dem Oszi kann man das ganze dann schön verfolgen, dabei sieht man dann die großen Zeitunterschiede, zwischen dem RC-Sender und dem PC.

Jetzt machen wir das mit digitalen Potis. Und gehen direkt mit Umschalter auf die Lenk- und Gas-Potianschlüsse.

Dabei dürfte es dann wohl keine Probleme mehr geben...

Grüße und danke für eure Hilfe :allright: soweit.

chrizL
Mit freundlichen Grüßen,
chrizl
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

mal so ins blaue...

ich bin in hardware ein n00b, is schon 20 jahre her, dass ich mit meinen cosmos-e-labor gepozzelt hab...

der PC ist nur deshalb so "langsam" weil er eben auf multitasking ausgelegt ist und der ganze kram...

vielleicht könnt ihr ne kleine hardwareeinheit, die ne zuverlässige steuerung übernimmt zusammenlöten.
die kann dan von PC angesteuert werden, aber die millisekunden-kritischen vorgänge laufen in der einheit ab, also pc-extern...

mal einfach so spontan um meinen senf zu zugeben, vielleicht regts euch ja an, in ner richtung zu denken, wo ihr die lösung findet...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
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:

Beitrag von Falko »

@Kaeru Gaman

Das hast du gut gesagt :lol: .

Ich hatte in der Richtung schon was unternommen und zwar bei Pollin.de ein
Atmel Evaluations-Board-Bausatz für Atmega 16/32 und Atmega8535
µC-Controller bestellt und heute erhalten, welche wir dann auch erst einmal
zusammenlöten mussten. Nun darf mein Sohnemann sich mal daran
ausüben. Damit ist keine Zeitabhängigkeit mehr vorhanden, vorrausgesetzt er kriegt das Progen dieser Controller hin :lol: .
So langsam fängt mir das auch an, Spass zu machen. Vorallem wenn man
sich so ein Programmer selbst und dazu noch sehr günstig zusammen bauen kann :)
Bild
Win11 Pro 64-Bit, PB_6.11b1
Georg
Beiträge: 29
Registriert: 17.06.2005 19:04

Beitrag von Georg »

Hallo,

falls noch Interesse besteht.
Du könntest durch Ändern der Bautrate die Impulszeit von 1ms bis 2ms
einstellen. Die 20ms machtst Du dann mit einem Timer. Die Genauigkeit
ist hier nicht so Kritisch. Wichtig ist nur, das die Impulszeiten stimmen,
sodas die Servos nicht flattern.
Die Bautrate stellst Du mit direktem Zugriff auf die Register des UART ein.
Unter Win98 geht das auch mit ASM.
Hier ein Beispiel mit der IO.dll. Ist Freeware
Sollte auch mit WinXP gehen.

Code: Alles auswählen

; ------------------------------------------------------------
;
Global hWndMain, idWndMain
Global Btn1 , Btn2 , Tbg1 , Tbg2
Global *IsDriverInstalled , *PortOut , *PortIn
Global = Teiler1 , Teiler2 
#Port = 760 ;Com2

 
 If OpenLibrary(0, "Io.DLL")
    *IsDriverInstalled = GetFunction(0, "IsDriverInstalled")
    If *IsDriverInstalled = 0
       MessageRequester("Fehler","Hoppla, Funktion >IsDriverInstalled< nicht vorhanden" )
    EndIf  
        *PortOut = GetFunction(0, "PortOut")
    If *PortOut = 0
       MessageRequester("Fehler","Hoppla, Funktion >PortOut< nicht vorhanden" )
    EndIf   
        *PortIn = GetFunction(0, "PortIn")
    If *PortIn = 0
       MessageRequester("Fehler","Hoppla, Funktion >PortIn< nicht vorhanden" )
    EndIf  
 Else
    MessageRequester("Fehler","Hoppla, IO.dll nicht gefunden!",#PB_MessageRequester_Ok ) 
 EndIf
 
 Procedure ImpulseSenden()
 ;Erster Imuls
  ;Bautrate einstellen
      CallFunctionFast(*PortOut,#Port+3,128) ;Register umschalten
      CallFunctionFast(*PortOut,#Port,Teiler1);LowByte
      CallFunctionFast(*PortOut,#Port+1,0);HighByte
      CallFunctionFast(*PortOut,#Port+3,0);5N1
   ;Byte senden   
    CallFunctionFast(*PortOut,#Port,0) 

;Warten bis Byte gesendet
Repeat
Until  CallFunctionFast(*PortIn,#Port + 5) & 64 = 64;Line Status Register Bit 6
;Zweiter Imuls
  ;Bautrate einstellen
      CallFunctionFast(*PortOut,#Port+3,128) ;Register umschalten
      CallFunctionFast(*PortOut,#Port,Teiler2);LowByte
      CallFunctionFast(*PortOut,#Port+1,0);HighByte
      CallFunctionFast(*PortOut,#Port+3,0);5N1
;   ;Byte senden 
      CallFunctionFast(*PortOut,#Port,0)  
 EndProcedure

idWndMain =OpenWindow(#PB_Any,100,80,400,350,"Zwei Servo Test",#PB_Window_SystemMenu|#PB_Window_WindowCentered)
hWndMain = WindowID(idWndMain)
 CreateGadgetList(hWndMain)
  Btn1 = ButtonGadget(#PB_Any, 10,10,80,24,"Start")
  Btn2 = ButtonGadget(#PB_Any, 10,40,80,24,"Stop")

  Tbg1  = TrackBarGadget(#PB_Any, 10, 100, 300, 20, 20, 40) 
  Tbg2  = TrackBarGadget(#PB_Any, 10, 140, 300, 20, 20, 40) 

     
 If CallFunctionFast(*IsDriverInstalled) = 0
     MessageRequester("Fehler","Hoppla, Treiber ist nicht installiert")
     End
 EndIf  
       
Teiler1 = GetGadgetState(Tbg1) 
Teiler2 = GetGadgetState(Tbg2) 

Repeat
  Event = WaitWindowEvent()
        Select Event 
	
            Case #PB_Event_CloseWindow
               If EventWindow() = idWndMain 
                  CloseLibrary(0) 
                     End
                  EndIf  

            Case #PB_Event_Gadget
                Select EventGadget()
                     Case Btn1
                           SetTimer_(hWndMain, 0,20,@ImpulseSenden()) 
                     Case Btn2
                           KillTimer_(hWndMain, 0)
                     Case Tbg1
                           Teiler1 = GetGadgetState(Tbg1)  
                     Case Tbg2
                           Teiler2 = GetGadgetState(Tbg2)      
                EndSelect
                
       EndSelect 
 
ForEver
End
Georg
Antworten