Working with STM32 and PID Controller Part 2: Code implementation

In the previous guide (here), the PID controller has been introduced and discussed. This part, we shall use the equation introduced in part 1 to implement the code.

In this guide, we shall cover the following:

  • Header file implementation.
  • Source file implementation.
  • Main implementation.

1. Header File Implementation:

To start off, create a new header file with name of pid.h.

Inside this header file, we shall use header guard as following:

#ifndef PID_H_
#define PID_H_


#endif /* PID_H_ */

Then we create a data structure with the following parameters:

  • Kp
  • Ki
  • Kd
  • Ts
  • Set_point
  • Anti_windup_error
  • Out_max
  • Out_min
  • Anit_windup

the structure shall be like this:

typedef struct
	{
	float Kp;
	float Ki;
	float Kd;
	float Ts;
	float Set_point;
	float Anti_windup_error;
	float Outmin;
	float Outmax;
	int Anti_windup;

	}PID_Param_t;

Also the header file shall contain the initialization function and compute function as following:

void PID_init(PID_Param_t *par);
float PID_Calculation(float input);

Thats all for the header file.

2. Source File Implementation:

To start off, create a new source file with name of pid.c and include the pid.h file as following:

#include "pid.h"

We need some global variable as following:

float Kp,Ki,Kd,Ts,Outmin,Outmax,set_point,antiwinduperror;
int windup;
float error,prev_input,error_sum;

Those variable are only accessed in the source file

For the initialization function, the declared variable are set according to the values stored in the structure as following:

void PID_init(PID_Param_t *par)
	{

	;
	Kp=par->Kp;
	Ki=par->Ki;
	Kd=par->Kd;
	Ts=par->Ts;
	set_point=par->Set_point;
	antiwinduperror=par->Anti_windup_error;
	Outmin=par->Outmin;
	Outmax=par->Outmax;
	windup=par->Anti_windup;

	if(0==par->Anti_windup_error){antiwinduperror=10;}
	}

Now, for the calculation:

float PID_Calculation(float input)
	{
	error=(set_point-input);

	float out;
	if(Anti_windup_enabled==windup)
		{

		if(antiwinduperror<abs(error))
			{
			out=Kp*(error)+Kd*(input-prev_input)/Ts;
			}
		else
			{
			out=(Kp*(error)) +( Ki*(Ki_sum)*Ts) -( Kd*(input-prev_input)/Ts);
			}

		}

	else
		{
		out=Kp*(error) + Ki*(Ki_sum)*Ts - Kd*(input-prev_input)/Ts;
		}
	Ki_sum=Ki_sum+(Ki_sum);
	if(out>Outmax){out=Outmax;}
	if(out<Outmin){out=Outmin;}
	prev_input=input;
	return out;
	}

First, the error calculation:

error=input-set_point;

Accumulate the error for the integrator part:

error_sum+=error;

Check if the error is bigger than the output max and less than output minimum as following:

	if(error_sum<Outmin){error_sum=Outmin;}
	if(error_sum>Outmax){error_sum=Outmax;}

For the calculation, we have two paths, first one if the anti windup is enabled:

This will have two condition:

If the error bigger than what the anti windup set point, the calculation shall not include the integrator part:

if(antiwinduperror<abs(error))
			{
			*output=Kp*(error)+Kd*(input-prev_input)/Ts;
			}

If the error is less, calculate with integrator part :

else
			{
			*output=Kp*(error) + Ki*(error_sum)*Ts - Kd*(input-prev_input)/Ts;
			}

If the ani windup is not active, calculate the PID out as usual:

else
		{
		*output=Kp*(error) + Ki*(error_sum)*Ts - Kd*(input-prev_input)/Ts;
		}

Also, store the previous input for the differential part:

	prev_input=input;

3. Main Implementation:

For the main source file, we shall first include the header file following:

#include "pid.h"

Then declare the structure of the PID parameters as following:

PID_Param_t PIDParam;

Set the PID parameters as following:

	PIDParam.Kp=15.2;
	PIDParam.Kd=0.3;
	PIDParam.Ki=0.001;
	PIDParam.Outmin=0;
	PIDParam.Outmax=1000;
	PIDParam.Ts=200;
	PIDParam.Set_point=200;
	PIDParam.Anti_windup=Anti_windup_enabled;
	PIDParam.Anti_windup_error=10;

Note: those are not actual parameters, just for demonstration only .

Then pass the address of the structure to the PID_init function as following:

PID_init(&PIDParam);

The compute_pid function shall be called according the sample time stated in the structure:

if(millis()-previous>PIDParam->Ts)
				{
				PID_Calculation(input, &output);
				previous=millis();
				}

In part three, we shall see how to use PID in simple application

Happy coding 🙂

2 Comments

  • Test Posted May 26, 2022 11:01 am

    Where is part 3?

    • Husamuldeen Posted May 27, 2022 1:20 pm

      We shall post part three soon.

Add Comment

Your email address will not be published. Required fields are marked *