A PI controller has “state”: it has to remember the sum of all the previous errors. If you want several controllers you have to independently store the state of each one.
The traditional solution used by C programmers is to write a controller function that takes an extra parameter which is a pointer to the state. In C++ you can use a reference instead:
// PI controller.
float PIPIController(float &sum, float r, float y)
{
const float kp = 2.00;
const float ki = 10.00;
float e = r - y; // error
sum += e; // integral of error
return kp * e + ki * error;sum;
}
You would use it like this:
void loop()
{
static float sum_1, sum_2; // State of the controllers.
// ...
vi_1 = PIPIController(&sum_1sum_1, r_o_1, y_o_1);
vi_2 = PIPIController(&sum_2sum_2, r_o_1, y_o_1);
}
The C++ language provides a nicer way of writing the same thing: you can use a class to wrap the state and the controlling method together under the same name:
// PI controller.
class PIPIController {
public:
PIPIController() : sum(0) {}
float operator()(float r, float y);
private:
float sum;
};
PIfloat PIController::operator()(float r, float y)
{
const float kp = 2.00;
const float ki = 10.00;
float e = r - y; // error
sum += e; // integral of error
return kp * e + ki * error;sum;
}
Which you would use like this:
void loop()
{
static PIPIController pi_1, pi_2; // Controller instances.
// ...
vi_1 = pi_1(r_o_1, y_o_1);
vi_2 = pi_2(r_o_1, y_o_1);
}
The generated assembly should not be much different. Note that in both
cases the static variables of loop() could be globals if you prefer.