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
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;
}
}
}
an.
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.
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;
}
}
Wenn du jetzt eine Variable PID_A definierst, würde der Aufruf so aussehen:
Code:
struct PIDstruct PID_A;
void eine_stop_funktion(void) {
StopPIDcontrol( &PID_A );
}
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.
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:
Code:
void *(*function)(void *arg);
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 *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;
/* ... */
}
so brauchst du auch diese Routine nur einmal, denn bei der Thread Erzeugung übergibst du den Zeiger auf die Variable z.B.:
Code:
pthread_create(&PID_A.tid, 0, pid_calc, &PID_A);
ich hoffe das es jetzt klarer ist, warum ich Zeiger verwenden würde?
Noch ein Wort zu den Variablen für die einzelnen PID Controler, du definierst die so:
Code:
struct PIDcontrol PID_A;
struct PIDcontrol PID_B;
struct PIDcontrol PID_C;
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:
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] );
}
find ich bequemer, ist aber Geschmackssache.
Gruss
botty
Lesezeichen