Hallo HaWe,
sorry das ich mich jetzt erst melde aber bei dem schönen Wetter konnte mich am WE nichts vor den Bildschirm locken.
Zu Deinen Fragen:
Meine "struct pid" sollte nur ein Auszug aus Deiner "struct PIDstruct" sein. Worauf es mir ankommt ist, dass du diese um zwei Felder erweiterst:
einmal um ein Element pthread_t und
zweitens, um ein Element worüber du deine Motoren identifizieren kannst.
Dadurch kannst du deinen Code wesentlich verkürzen, schau mal deine
an.Code:safecall void StopPIDcontrol (char port) { if (port==OUT_A) { if (PID_A.runstate!=0) { // stop PID task if already running stop task_PID_A; PlayTone(TONE_C7,100); Off(OUT_A); Wait(50); PID_A.cont=false; PID_A.runstate=0; Wait(1); return; } } else if (port==OUT_B) { if (PID_B.runstate!=0) { // stop PID task if already running stop task_PID_B; PlayTone(TONE_C7,100); Off(OUT_B); Wait(50); PID_B.cont=false; PID_B.runstate=0; Wait(1); return; } } else if (port==OUT_C) { if (PID_C.runstate!=0) { // stop PID task if already running stop task_PID_C; PlayTone(TONE_C7,100); Off(OUT_C); Wait(50); PID_C.cont=false; PID_C.runstate=0; Wait(1); return; } } }
Da steht dreimal quasi das Gleiche.
Wenn du jetzt deine Struktur erweiterst und statt dem Port einen Zeiger auf die Variable übergibst, läßt sich der Code um 2/3 kürzen.
Wenn du jetzt eine Variable PID_A definierst, würde der Aufruf so aussehen:Code:struct PIDstruct { /* Erweiterung */ pthread_t tid; unsigned short motor_id; // custom target values long target; // set target int tarpwm; // motor target speed // custom regulation parameters float P; // P: basic propotional to error float I; // I: integral: avoid perish float D; // D: derivative: avoid oscillating float precis; // error precision to target int regtime; // PID loop time float damp; // damp the integral memory char cont; // target: continue or hit once // internal control variables char runstate; // monitors runstate int outp; // PID control output value int maxout; // max output (max motor pwr) long read; // current sensor reading float err; // current error float integr; // integral of errors float speed; // current speed } ; void StopPIDcontrol (struct PIDstruct *pid) { if (pid->tid) { // stop PID task if already running pid_stop(pid, 1); // siehe botties zweites Beispiel Off(pid->motor_id); pid->cont=false; pid->runstate=0; return; } }
Du wählst somit nicht mehr über eine port/switch Kombination deinen PID Controler aus, sondern beim Aufruf der Funktionen über den Zeiger auf die Variable, welche alle Daten für einen Controler enthält.Code:struct PIDstruct PID_A; void eine_stop_funktion(void) { StopPIDcontrol( &PID_A ); }
D.h. bei allen Funktionen ersetzt du "char port" durch "struct PIDstruct* pid" und dereferenzierst die Elemente der Struktur nicht mehr über den "." Operator sondern über den "->" Operator.
Auch bei der Erezugung der Threads können wir jetzt den Zeiger auf die Variable als Parameter in die Thread-Funktion hineingeben, denn die Signatur dieser Funktion lautet:
Zwar ist arg vom Typ "void *" nur da wir immer nur Zeiger vom Typ "struct PIDcontrol *" hineingeben können wir am Anfang der Routine diesen Zeiger sicher auf den richtigen Typ casten:Code:void *(*function)(void *arg);
so brauchst du auch diese Routine nur einmal, denn bei der Thread Erzeugung übergibst du den Zeiger auf die Variable z.B.: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; /* das ist ein ziemlich sicherer cast */ struct PIDcontrol *pid = (struct PIDcontrol *) arg; pid->runstate = 0x10; // reg state: RAMPUP pid->read = (MotorRotationCount(pid->motor_id)); // get current encoder reading pid->err = pid->target - pid->read; // error to target readstart = pid->read; /* ... */ }
ich hoffe das es jetzt klarer ist, warum ich Zeiger verwenden würde?Code:pthread_create(&PID_A.tid, 0, pid_calc, &PID_A);
Noch ein Wort zu den Variablen für die einzelnen PID Controler, du definierst die so:
bei sowas sehe ich sofort eine Aufzählung und wenn ich dann auch noch Operationen über diese machen muss, wie z.B. ein warten auf das Beenden eines Threads, dann ordne ich sowas in einem Array an:Code:struct PIDcontrol PID_A; struct PIDcontrol PID_B; struct PIDcontrol PID_C;
find ich bequemer, ist aber Geschmackssache.Code:enum motoren { MOT_SCHULTER = 0, MOT_ELLE, MOT_HAND, MOT_MAX }; struct PIDcontrol pids [MOT_MAX]; /* Verwendung */ void ein_stop(void) { StopPIDcontrol( &pids[MOT_SCHULTER ); } void mehrere_stops(void) { int idx; for(idx = MOT_SCHULTER; idx < MOT_MAX; idx++) StopPIDcontrol( &pids[idx] ); }
Gruss
botty







Zitieren
Lesezeichen