- 3D-Druck Einstieg und Tipps         
Ergebnis 1 bis 10 von 45

Thema: pthread-Task als Methode einer C++ Klasse?

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    HaWe
    Gast
    ok, noch immer ein paar Verständnisprobleme:

    zu pid_calc....:

    bisher verwende ich ja diese Task-Funktion hier (in 3 Varianten, ab jetzt natürlich einheitlich 1x für alle):
    (ich HASSE ES, dass hier nie Codetags im Antwort-Editor dabei sind!!! )

    Code:
    task task_PID_A() {
      float aspeed, damp, PWMpwr, readold, errorold, tprop;
      long  readstart, cmax, cmin;                         // for monitoring
      long  starttime, runtime, clock, dtime;              // timer
      char  regloop;
    
      PID_A.runstate = 0x10;                               // reg state: RAMPUP
      PID_A.read     = (MotorRotationCount(OUT_A));        // get current encoder reading
      PID_A.err      = PID_A.target - PID_A.read;          // error to target
    
      readstart      = PID_A.read;
      regloop        = 1;
    
    
    
    #ifdef debug_PID_A
      //............................................................................
      // init variables for graph output
      ClearScreen();
      DisplayMask();
    
    
      int timex, oldtx, oldy=15, pwm0=15;           // values for graphic screen
    
      float scrXratio, scrYratio;
    
      scrXratio=abs(PID_A.err)/8;
      if(PID_A.target<50) scrXratio=abs(PID_A.err)/4;
    
      scrYratio=abs(PID_A.err)/40;
      //............................................................................
    #endif
    
      damp=0;                                 // damp the integral memory
    
      starttime= CurrentTick();
    
    
      // appoach target
      _Astart:
      PID_A.runstate = 0x10;                  // run state: RUNNING
    
      do {
    
        dtime    = CurrentTick() - clock;
        clock    = CurrentTick();
        runtime  = clock - starttime;
        tprop    = dtime/20.0;
    
        if ((PID_A.err==errorold)&& (abs(PID_A.err)>PID_A.precis)) damp=1;    // stalling
        else
        damp=PID_A.damp;
    
        PID_A.integr = (damp * PID_A.integr) + PID_A.err;
    
        if((PID_A.integr) > 3*PID_A.maxout) PID_A.integr = 3*PID_A.maxout; // cut away
        else
        if((PID_A.integr) <-3*PID_A.maxout) PID_A.integr =-3*PID_A.maxout;
    
        PWMpwr= (PID_A.P*PID_A.err) + (PID_A.I*PID_A.integr)*tprop + (PID_A.D*(PID_A.err-errorold))/tprop;
    
    
        if(PWMpwr >  PID_A.maxout) PWMpwr=  PID_A.maxout;   // forward maxout
        else
        if(PWMpwr < -PID_A.maxout) PWMpwr= -PID_A.maxout;   // reverse maxout
    
    
        PID_A.speed= (PID_A.read-readold)*100/dtime;  // rotat speed [degrees/100ms]
    
        aspeed = abs(PID_A.speed) ;
    
        if (aspeed > PID_A.tarpwm)  {
            PWMpwr = sign(PWMpwr)*PID_A.tarpwm;
        }
    
        PID_A.outp = round(PWMpwr);
    
    #ifdef debug_PID_A
        //..........................................................................
        //  for graph output
        timex= runtime/scrXratio;
        PointOut(timex,(PID_A.read-readstart)/scrYratio);
        LineOut(oldtx, oldy, timex, pwm0+PID_A.speed*0.3);
    
        oldtx=timex; oldy=pwm0+PID_A.speed*0.3;
        //..........................................................................
    #endif
    
    
        //**************************************************************************
                                                       // PID regulation !
        OnFwd(OUT_A, (PID_A.outp));                         // action !
        Wait(PID_A.regtime);                                // wait regulation time
    
        //**************************************************************************
    
        readold     = PID_A.read;                           // save old sensor
        errorold    = PID_A.err;                            // save old error
    
        PID_A.read  = (MotorRotationCount(OUT_A));          // get new encoder value
        PID_A.err   = PID_A.target-PID_A.read;              // new error to target
    
        if (PID_A.read>cmax) cmax=PID_A.read;               // monitor overshooting
        else
        if (PID_A.read<cmin) cmin=PID_A.read;               // monitor overshooting
    
        if ((PID_A.cont)&& (abs(PID_A.err)<=PID_A.precis)) PID_A.runstate = 0x60;
        else PID_A.runstate = 0x20;
    
    #ifdef debug_PID_A
        //..........................................................................
        printf1(68, 48,"%-5d"   , cmax);
        printf1(68, 40,"%-5d"   , cmin);
        printf1(68, 32,"%-5d"   , PID_A.read);
        //..........................................................................
    #endif
    
        if (PID_A.cont) continue;
        if (abs(PID_A.err)<=PID_A.precis) { regloop +=1 ; PID_A.runstate = 0x40; }
    
      } while ((abs(PID_A.err)>=PID_A.precis) && (regloop<=5));  // target reached
    
      Off(OUT_A);                                      // finished - brake motor
      PID_A.runstate = 0x40;                           // run state: RAMPDOWN
      PID_A.outp=0;
    
      Wait(50);
      PID_A.read = MotorRotationCount(OUT_A);
      regloop=1;
    
      if (PID_A.read>cmax) cmax=PID_A.read;            // detect overshooting
      if (PID_A.read<cmin) cmin=PID_A.read;            // detect overshooting
      PID_A.err = PID_A.target-PID_A.read;
    
    
    #ifdef debug_PID_A
      //............................................................................
      printf1(68, 56,"%-5d"    , PID_A.target);
      printf1(68, 48,"%-5d"    , cmax);
      printf1(68, 40,"%-5d"    , cmin);
      printf1(68, 32,"%-5d"    , PID_A.read);
      printf1(56, 24,"P %5.2f" , PID_A.P);
      printf1(56, 16,"I %5.2f" , PID_A.I);
      printf1(56,  8,"D %5.2f" , PID_A.D);
      //............................................................................
    #endif
    
    
      if ((abs(PID_A.err)>PID_A.precis))  {goto _Astart;}
    
    
    
    #ifdef debug_PID_A
      //............................................................................
      PointOut(timex,PID_A.read/scrYratio);
      LineOut(oldtx, oldy, timex, pwm0);
      LineOut(timex+2,PID_A.target/scrYratio, timex+10, PID_A.target/scrYratio);
      LineOut(timex+2, pwm0, timex+10, pwm0);
      //............................................................................
    #endif
    
      PID_A.runstate=0;
      Wait(1);                                //runstate = IDLE
    
    }
    
    //==============================================================================

    a) statt
    task task_PID_A()
    stünde dann hier also
    void * pid_calc( void *)


    und der Rest bliebe gleich?
    (die ..._A am Ende fallen ntl dann auch überall weg)


    und b)
    auch wenn ich jetzt kurz hinterenander mehrfach diesen Task für mehrere Motoren starte, wobei die Laufzeiten sich mehrfach überlappen
    RotatePID(0, 3000, 15, false);
    RotatePID(2, -4000, 90, false);
    delay(1);
    RotatePID(8, 10000, 50, true );
    delay(10);
    RotatePID(1, -5000, 60, false);
    RotatePID(3, -3000, 20, true);

    und also in diesem Falle 5 verschiedene Threads parallel asynchron laufen müssten, mit Starten eines neuen Tasks ohne auf die Beendigung des vorherigen warten zu müssen, die sich dann alle auch unabhängig zu verschiedenen Zeiten beenden oder (in 2 Fällen) "unendlich" weiter laufen -
    das funktioniert dann
    Geändert von HaWe (28.09.2016 um 20:01 Uhr)

  2. #2
    Benutzer Stammmitglied
    Registriert seit
    19.05.2015
    Beiträge
    69
    zu a)

    ja, task_PID_[ABC] wird durch die allgemeine form pid_calc ersetzt.

    Sinngemäss wie du auf die Elemente des Arrays zugreifst sähe so aus (ohne das ich jetzt verstehe was dein Algo macht):
    Code:
    void *pid_calc(void *arg) {
      float aspeed, damp, PWMpwr, readold, errorold, tprop;
      long  readstart, cmax, cmin;                         // for monitoring
      long  starttime, runtime, clock, dtime;              // timer
      char  regloop;
      
      // arg nach index casten
      unsigned port = (unsigned)arg;
    
      motor[port].runstate = 0x10;                               // reg state: RAMPUP
      motor[port].read     = (MotorRotationCount(port));        // get current encoder reading
      motor[port].err      = motor[port].target - motor[port].read;          // error to target
    
      readstart      = motor[port].read;
      regloop        = 1;
    
      damp=0;                                 // damp the integral memory
    
      starttime= CurrentTick();
    
    
      // appoach target
      _Astart:
      motor[port].runstate = 0x10;                  // run state: RUNNING
    
      do {
        pthread_testcancel();
    
        dtime    = CurrentTick() - clock;
        clock    = CurrentTick();
        runtime  = clock - starttime;
        tprop    = dtime/20.0;
    
        if ((motor[port].err==errorold)&& (abs(motor[port].err)>motor[port].precis))
          damp=1; // stalling
        else
          damp=motor[port].damp;
    
        motor[port].integr = (damp * motor[port].integr) + motor[port].err;
    
        if((motor[port].integr) > 3*motor[port].maxout) 
          motor[port].integr = 3*motor[port].maxout; // cut away
        else if((motor[port].integr) <-3*motor[port].maxout)
          motor[port].integr =-3*motor[port].maxout;
    
        PWMpwr= (motor[port].P*motor[port].err) + (motor[port].I*motor[port].integr)*tprop + (motor[port].D*(motor[port].err-errorold))/tprop;
    
    
        if(PWMpwr >  motor[port].maxout)
          PWMpwr=  motor[port].maxout;   // forward maxout
        else if(PWMpwr < -motor[port].maxout)
          PWMpwr= -motor[port].maxout;   // reverse maxout
    
        motor[port].speed= (motor[port].read-readold)*100/dtime;  // rotat speed [degrees/100ms]
    
        aspeed = abs(motor[port].speed) ;
    
        if (aspeed > motor[port].tarpwm)  {
            PWMpwr = sign(PWMpwr)*motor[port].tarpwm;
        }
    
        motor[port].outp = round(PWMpwr);
    
         //**************************************************************************
                                                       // PID regulation !
        OnFwd(port, (motor[port].outp));                         // action !
        Wait(motor[port].regtime);                                // wait regulation time
    
        //**************************************************************************
    
        readold     = motor[port].read;                           // save old sensor
        errorold    = motor[port].err;                            // save old error
    
        motor[port].read  = (MotorRotationCount(port));          // get new encoder value
        motor[port].err   = motor[port].target-motor[port].read;              // new error to target
    
        if (motor[port].read>cmax) 
          cmax=motor[port].read;               // monitor overshooting
        else if (motor[port].read<cmin)
          cmin=motor[port].read;               // monitor overshooting
    
        if ((motor[port].cont)&& (abs(motor[port].err)<=motor[port].precis))
          motor[port].runstate = 0x60;
        else 
          motor[port].runstate = 0x20;
    
        if (motor[port].cont) 
          continue;
        if (abs(motor[port].err)<=motor[port].precis) {
          regloop +=1 ; 
          motor[port].runstate = 0x40;
        }
    
      } while ((abs(motor[port].err)>=motor[port].precis) && (regloop<=5));  // target reached
    
      Off(port);                                      // finished - brake motor
      motor[port].runstate = 0x40;                           // run state: RAMPDOWN
      motor[port].outp=0;
    
      Wait(50);
      motor[port].read = MotorRotationCount(port);
      regloop=1;
    
      if (motor[port].read>cmax) cmax=motor[port].read;            // detect overshooting
      if (motor[port].read<cmin) cmin=motor[port].read;            // detect overshooting
      motor[port].err = motor[port].target-motor[port].read;
    
      if ((abs(motor[port].err)>motor[port].precis)) {
        goto _Astart;
      }
    
      motor[port].runstate=0;
      Wait(1);                                //runstate = IDLE
    
    }
    zu b)

    klar geht das, in deinem Beispiel legst du einfach fünf Threads an, die vom OS ausgeführt werden.
    Was du nicht machen darfst ist zweimal ein RotatePID mit der identischen Portnummer aufzurufen ohne vorher ein StopPIDcontrol aufzurufen. Würdest du das machen hättest du zwei Threads die dann doch wieder auf einem PID rumrechnen.
    Ausserdem würde ich statt Nummern für die Ports eine enum erstellen, Namen sind einfacher zu lesen, als mit 0,4,7 einen Motor zu assoziieren.

  3. #3
    HaWe
    Gast
    toll, danke, auch für deine Mühe mit dem ganzen Code

    das war auch bisher der Punkt, den ich nicht verstanden hatte mit der Vielzahl der pthread-Tasks:
    Ich dachte immer, um 2 (3,4,5) tasks unabhängig zu starten, müsse man ihnen außer eigene pthreadIDs auch 2 (3,4,5) verschiedene eindeutige unterscheidbare Namen geben
    - dass es ausreicht, einen identischen Namen zu verwenden und zusätzlich zur eindeutigen pthreadID nur noch ein 4.Parameter damit sie verschiedene Variablen ansprechen, war mir nicht klar.
    Es scheint also vor allem der eindeutige Handle (die pthread-ID im 1. Parameter) zu sein, die den task auf einer eigenen unabhängigen Timeslice starten lässt.
    Perfekt.
    Wenn ich das jetzt richtig verstanden habe, dann wird es funktionieren.
    Tausend Dank, ich setz mich dran!


    ps,
    das mit den Namen werde ich später auch so machen, wschl allerdings als #defines:
    #define LEFTW 0
    #define RIGHTW 1
    #define TURNTABLE 2
    #define SHOULDER 3
    oder so ähnlich...
    Geändert von HaWe (28.09.2016 um 22:34 Uhr)

  4. #4
    HaWe
    Gast
    update:

    hab meinen ursprünglichen NXC Code jetzt soweit fü gpp portiert und entlaust, dass er (bis auf ein paar Warnings) fehlerfrei compiliert.
    Ich bn mir noch unklar darüber, wo das pthread_join hinmuss, wenn man es den Tasks überlässt, dass sie bis zur Selbstterminierung laufen, um hier keine Karteileichen im Scheduler zu hinterlassen.
    Ist das richtig: direkt hinter dem Aufruf von pthread im RotatePID Wrapper?

    Code:
    void RotatePID(uint8_t port, long Target, float RotatSpeed, int8_t cont) {
        
        motor[port].runstate=1;                // set runstate: PID active
        // custom init PID [port]    
        motor[port].target =Target;                   // assign target
        motor[port].tarpwm =RotatSpeed;               // assign rotation speed
        motor[port].cont=cont;                        // cont vs. hit once
        // Reset PID control defaults
        motor[port].outp    =0;                // PID control output value
        motor[port].maxout  =100;              // absolute max possible output (max pwr)
        motor[port].read    =0;                // current reading
        motor[port].err     =0;                // current error
        motor[port].integr  =0;                // integral of errors
        motor[port].speed   =0;                // current speed
    
        pthread_create( & motor[port].tid,  // id speichern in dem dazugehörigen Array-Element
                        0,                  // Default Attribute (threads cancelbar)
                        PID_calc,           // Name der PID-Kalkulationsroutinge
                        (void*)port);       // der Index des Array-Elements für eine PID Struktur, 
                                            // die mit dem Motorindex gleich ist.     
     
        
        pthread_join(motor[port].tid, NULL);  //  <<<<<<<<<<<<<<<<<< ????
        
    
                                        
    }
    oder muss hier evtl auch noch eine zusätzliche while Abfrage dazwischen, denn der Task soll ja nicht unmittelbar nachdem er gestartet wurde schon wieder beendt/gejoint werden?
    z.B.:

    while(motor[port].tid);


  5. #5
    Benutzer Stammmitglied
    Registriert seit
    19.05.2015
    Beiträge
    69
    Nein, das join kommt woanders hin.

    Aber erstmal musst du zwei Typen von Threads unterscheiden:

    1) Ein Thread auf den du nicht warten willst und der einfach durchlaufen soll ist ein "Detached" Thread. Ist dieser durch seine Routine gelaufen, dann räumt das OS von alleine auf. Damit ein Thread "Detached" markiert ist, rufst du nach dem pthread_create mit der ThreadId pthread_detach auf:

    Code:
    #include <stdbool.h>
    
    void RotatePID(uint8_t port, long Target, float RotatSpeed, int8_t cont, bool detach) {
        
        motor[port].runstate=1;                // set runstate: PID active
        // custom init PID [port]    
        motor[port].target =Target;                   // assign target
        motor[port].tarpwm =RotatSpeed;               // assign rotation speed
        motor[port].cont=cont;                        // cont vs. hit once
        // Reset PID control defaults
        motor[port].outp    =0;                // PID control output value
        motor[port].maxout  =100;              // absolute max possible output (max pwr)
        motor[port].read    =0;                // current reading
        motor[port].err     =0;                // current error
        motor[port].integr  =0;                // integral of errors
        motor[port].speed   =0;                // current speed
    
        pthread_create( & motor[port].tid,  // id speichern in dem dazugehörigen Array-Element
                        0,                  // Default Attribute (threads cancelbar)
                        PID_calc,           // Name der PID-Kalkulationsroutinge
                        (void*)port);       // der Index des Array-Elements für eine PID Struktur, 
                                            // die mit dem Motorindex gleich ist.     
    
       // Zur Unterscheidung Parameterliste von RotatePID und High-Level-Funktionen erweitern.
       if(detach)
          pthread_detach(& motor[port].tid);
    }
    Zu beachten gilt hier noch, dass du so einen Thread nach dem detach nie wieder joinen kannst. Sinvollerweise markierst du das Ende des Threads in der Routine zum Schluss im runstate und setzt die tid wieder auf 0.

    2) Bei den anderen Threads die du joinen möchtest mußt du unterscheiden ob du auf das natürliche Ende warten willst oder ob du sie abbrechen (pthread_cancel) möchtest.
    In deinem API hattest du eine StopPIDcontroler Funktion, in diese würde ich das optionale cancel und das join implementieren. In etwa so:

    Code:
    #include <stdbool.h>
    
    void StopPIDcontrol(char port, bool cancel) {
    
      if(cancel)
        pthread_cancel(motor[port].tid)
    
      pthread_join(motor[port].tid, 0);
    
      motor[port].tid = 0;
      motor[port].runstate = 0;
    
      // und andere Variablen nach dem Ende des Threads setzen
    }
    Dann hast du eine klare Aufteilung: StopPIDcontrol hält joinable Threads (gewaltsam) an und alle anderen High-Level-Routinen starten einen Thread.

    Gruss
    botty

    P.S. Sinnvollerweise guckst du dir in den man Pages die Rückgabewerte der pthread_ Funktionen an und behandelst noch die möglichen Fehler, das habe ich hier weg gelassen.
    Geändert von botty (29.09.2016 um 12:22 Uhr)

  6. #6
    HaWe
    Gast
    meine PID threads müssen sowohl durchlaufen können (sich selbst überlassen) als auch von außen beendet werden können, falls nötig.
    Wie das gehen soll, ist im Prinzip auch "Verhandlungssache" (gewaltsam oder über einen Semaphor, s.u.).
    Wer dann hinterher aufräumt, ist mir wurscht, am praktischsten ntl, wenn es automatisch geht
    A-Bär: wenn er einmal beendet ist, muss er (bzw. sein Nachfolger mit derselben port-Nr) ntl jederzeit wieder neu gestartet werden können, für die nächste PID-Annäherung. Ob er dann vom OS eine neue pthread-ID zugewiesen bekommt, ist aber ntl auch wieder egal.

    Was das Joinen bewirkt, ist mir nicht ganz klar, ich hatte es so verstanden, dass ihre Timeslice wieder mit der von main() vereinigt wird.
    Alle PID-Tasks sollen so gleichartig behandelt werden, während meine Tasks, die von main() am Anfang gestartet werden, so bleiben können wie bisher:
    hier muss ich nichts unbedingt gewaltsam beenden, und wenn, dann funktioniert das dort über eine globale Variable (Semaphor) _TASKS_ACTIVE_ :
    sobald die iwo auf false gesetzt wird, beenden sich alle Haupt-Threads dann selbsttätig und werden dann in main() gejoint:


    Code:
    // ***SNIP***
    
    
    void* thread3Go (void* ) {       //  highest priority:  100µs encoder timer
       while(_TASKS_ACTIVE_) {   
          updateEncoders();
          usleep(100);
       }
       return NULL;
    }
    
    
    
    void* thread4Go (void* ) {       //  medium  priority: UART
        while(_TASKS_ACTIVE_) {   
          UARTcomm();
       }
        return NULL;
    }
    
    
    // ***SNIP***
    
    
    
    int main() {
        char  sbuf[128];
        int err;
     
        // wiringPi   
        err = wiringPiSetup();   
        if( err == -1 ) return 1;     
           
        // UART Serial com port
        fserialRemote = serialOpen (usb0, UARTclock);            //  UART baud rate     
           
        initarrays();       
        setupDpins();
        setupI2C();
       
        pthread_t thread0, thread1, thread2, thread3, thread4, thread5;
        struct  sched_param  param;
    
        pthread_create(&thread0, NULL, thread0Go, NULL);     // lowest priority task: screen output
        param.sched_priority = 10;
        pthread_setschedparam(thread0, SCHED_RR, &param);
       
        pthread_create(&thread1, NULL, thread1Go, NULL);     // low priority: keyboard monitoring (stop program)
        param.sched_priority = 20;
        pthread_setschedparam(thread1, SCHED_RR, &param);
       
        pthread_create(&thread2, NULL, thread2Go, NULL);     // medium  priority: motor control
        param.sched_priority = 40;
        pthread_setschedparam(thread2, SCHED_RR, &param);
       
        pthread_create(&thread3, NULL, thread3Go, NULL);     // highest priority: encoder reading     
        param.sched_priority = 80;
        pthread_setschedparam(thread3, SCHED_FIFO, &param);
       
        pthread_create(&thread4, NULL, thread4Go, NULL);     // medium priority:  UART comm   <<< test !!
        param.sched_priority = 40;
        pthread_setschedparam(thread4, SCHED_FIFO, &param);
       
        pthread_create(&thread5, NULL, thread5Go, NULL);     // medium priority: navigation
        param.sched_priority = 40;
        pthread_setschedparam(thread5, SCHED_FIFO, &param);
       
    
    
        while(_TASKS_ACTIVE_) { delay(1); }
         
        // wait for threads to join before exiting
        pthread_join(thread0, NULL);
        pthread_join(thread1, NULL);
        pthread_join(thread2, NULL);
        pthread_join(thread3, NULL);
        pthread_join(thread4, NULL);
        pthread_join(thread5, NULL);
       
        serialClose( fserialRemote);
       
        exit(0);
    }

    falls es das irgendwie einfacher macht, könnte man auch die PID-threads auf diese Weise mit einer zusätzlichen Abbruchbedingung stoppen!

  7. #7
    HaWe
    Gast
    Der Standardfall wäre sicher, dass der PID Task gestartet wird und man überlässt ihn sich selber.
    trotzdem kann es sein
    (z.B.: bei ursprünglich geplante "Annäherung um 9000 Grad vorwärts drehen"
    oder bei "Ziel annähern und kontinuierlich, dauerhaft gegen (passive) Verstellung halten")

    , dass er vorzeitig gestoppt werden muss , wenn es die Situation erfordert,
    und dann nach Abbruch eventuell sofort eine neue Annäherung erfolgen muss
    (auf z.B. -1000 Grad per rückwärts drehen oder was auch immer) .

    Wie wäre dann der einfachste Weg, das Stoppen zu bewerkstelligen?

    Idee zum vorzeitigen Abbruch der PID-Regulation-Loop:

    evtl das Stoppen über Implementierung einer weiteren PID-Strukturvariable
    char StopPIDcontrol
    die standardmässig anfangs auf Null gesetzt wird,
    und wenn sie extern zum Beenden auf 1 gesetzt wird, wird nach interner Prüfung in der pthread PID loop der pthread beendet:

    Code:
    if(motor[port].StopPIDcontrol) {
      motorCoast(port);
      motor[port].runstate=0;
      motor[port].speed   =0;
      motor[port].outp    =0; 
      motor[port].cont    =0;
      return NULL;
    }
    wie und wo kämen dann weitere Aufräumarbeiten hin, und wie oder wo muss dann etwas gejoint werden oder nicht?
    Geändert von HaWe (29.09.2016 um 19:19 Uhr)

Ähnliche Themen

  1. [ERLEDIGT] Abgeleitete Klasse = konkrete Klasse?
    Von vixo im Forum Software, Algorithmen und KI
    Antworten: 4
    Letzter Beitrag: 15.09.2016, 16:02
  2. Antworten: 4
    Letzter Beitrag: 02.04.2016, 14:23
  3. Task motionControl() mit der M32
    Von inka im Forum Robby RP6
    Antworten: 8
    Letzter Beitrag: 10.04.2013, 06:40
  4. Gegen-EMK-Methode
    Von MatlStg im Forum Motoren
    Antworten: 7
    Letzter Beitrag: 11.02.2008, 17:07
  5. Was ist die besser Methode? (ADC auswerten)
    Von quantum im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 3
    Letzter Beitrag: 28.01.2007, 12:57

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen