Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] PID control with pic18f4550

Status
Not open for further replies.

Daivd-Lin

Newbie level 4
Newbie level 4
Joined
Aug 2, 2016
Messages
6
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
42
I want to design a PID controller with pic18f4550,but I don't know how to do,can anyone help me? or if you have already done the PID controller ,can you give me your code ,thank you~:thumbsup:
 

Implementing PID for what ? Heater Control ?
 

PID.h:
Code:
#ifndef PID_H
#define PID_H

typedef float FloatType;
//typedef double floatType;
#include <stdbool.h>

//Constants used in some of the functions below
typedef enum
{
  PID_Mode_Automatic = 1,
  PID_Mode_Manual    = 0
} PidModeType;

typedef enum
{
  PID_Direction_Direct  = 0,
  PID_Direction_Reverse = 1
} PidDirectionType;

typedef struct {
  FloatType dispKp; // * we'll hold on to the tuning parameters in user-entered
  FloatType dispKi; //   format for display purposes
  FloatType dispKd; //

  FloatType kp; // * (P)roportional Tuning Parameter
  FloatType ki; // * (I)ntegral Tuning Parameter
  FloatType kd; // * (D)erivative Tuning Parameter

  PidDirectionType controllerDirection;

  FloatType myInput; // * Pointers to the Input, Output, and Setpoint variables
  FloatType myOutput; //   This creates a hard link between the variables and the
  FloatType mySetpoint; //   PID, freeing the user from having to constantly tell us
                     //   what these values are.  with pointers we'll just know.

//  unsigned long lastTime;
  FloatType ITerm, lastInput;

  unsigned long SampleTime;
  FloatType outMin, outMax;
  bool inAuto;
} PidType;

//commonly used functions **************************************************************************

//  constructor.  links the PID to the Input, Output, and
//  Setpoint.  Initial tuning parameters are also set here
void PID_init(PidType* pid,
    FloatType kp,
    FloatType ki,
    FloatType kd,
    PidDirectionType controllerDirection);

// sets PID to either Manual (0) or Auto (non-0)
void PID_SetMode(PidType* pid, PidModeType mode);

// performs the PID calculation.  it should be
// called every time loop() cycles. ON/OFF and
// calculation frequency can be set using SetMode
// SetSampleTime respectively
bool PID_Compute(PidType* pid);

// clamps the output to a specific range. 0-255 by default, but
// it's likely the user will want to change this depending on
// the application
void PID_SetOutputLimits(PidType* pid, FloatType min, FloatType max);

//available but not commonly used functions ********************************************************

// While most users will set the tunings once in the
// constructor, this function gives the user the option
// of changing tunings during runtime for Adaptive control
void PID_SetTunings(PidType* pid, FloatType kp, FloatType ki, FloatType kd);

// Sets the Direction, or "Action" of the controller. DIRECT
// means the output will increase when error is positive. REVERSE
// means the opposite.  it's very unlikely that this will be needed
// once it is set in the constructor.
void PID_SetControllerDirection(PidType* pid, PidDirectionType Direction);

// sets the frequency, in Milliseconds, with which
// the PID calculation is performed.  default is 100
void PID_SetSampleTime(PidType* pid, int newSampleTime);

//Display functions ****************************************************************
// These functions query the pid for interal values.
//  they were created mainly for the pid front-end,
// where it's important to know what is actually
//  inside the PID.
FloatType PID_GetKp(PidType* pid);
FloatType PID_GetKi(PidType* pid);
FloatType PID_GetKd(PidType* pid);
PidModeType PID_GetMode(PidType* pid);
PidDirectionType PID_GetDirection(PidType* pid);

//void PID_Initialize(PidType* pid);
#endif



PID.c:
Code:
/**********************************************************************************************
 * C PID Library - Version 1.0.1
 * modified my Matthew Blythe <mblythester@gmail.com> mjblythe.com/hacks
 * originally by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
 *
 * This Library is licensed under a GPLv3 License
 **********************************************************************************************/

#include "pid.h"
void PID_Initialize(PidType* pid);

/*Constructor (...)*********************************************************
 *    The parameters specified here are those for for which we can't set up 
 *    reliable defaults, so we need to have the user set them.
 ***************************************************************************/
void PID_init(PidType* pid, FloatType Kp, FloatType Ki, FloatType Kd,
    PidDirectionType ControllerDirection) {
  pid->myInput = 0;
  pid->myOutput = 0;
  pid->mySetpoint = 0;
  pid->ITerm = 0;
  pid->lastInput = 0;
  pid->inAuto = false;

  PID_SetOutputLimits(pid, 0, 0xffff);

  //default Controller Sample Time is 1 seconds
  pid->SampleTime = 1000;

  PID_SetControllerDirection(pid, ControllerDirection);
  PID_SetTunings(pid, Kp, Ki, Kd);

//  pid->lastTime = millis() - pid->SampleTime;
}
 
 
/* Compute() **********************************************************************
 *     This, as they say, is where the magic happens.  this function should be called
 *   every time "void loop()" executes.  the function will decide for itself whether a new
 *   pid Output needs to be computed.  returns true when the output is computed,
 *   false when nothing has been done.
 **********************************************************************************/ 
bool PID_Compute(PidType* pid) {
  if (!pid->inAuto) {
    return false;
  }
//  unsigned long now = millis();
//  unsigned long timeChange = (now - pid->lastTime);
//  if (timeChange >= pid->SampleTime) {
    /*Compute all the working error variables*/
    FloatType input = pid->myInput;
    FloatType error = pid->mySetpoint - input;
    pid->ITerm += (pid->ki * error);
    if (pid->ITerm > pid->outMax)
      pid->ITerm = pid->outMax;
    else if (pid->ITerm < pid->outMin)
      pid->ITerm = pid->outMin;
    FloatType dInput = (input - pid->lastInput);

    /*Compute PID Output*/
    FloatType output = pid->kp * error + pid->ITerm - pid->kd * dInput;

    if (output > pid->outMax)
      output = pid->outMax;
    else if (output < pid->outMin)
      output = pid->outMin;
    pid->myOutput = output;

    /*Remember some variables for next time*/
    pid->lastInput = input;
//    pid->lastTime = now;
    return true;
//  } else {
//    return false;
//  }
}


/* SetTunings(...)*************************************************************
 * This function allows the controller's dynamic performance to be adjusted. 
 * it's called automatically from the constructor, but tunings can also
 * be adjusted on the fly during normal operation
 ******************************************************************************/ 

void PID_SetTunings(PidType* pid, FloatType Kp, FloatType Ki, FloatType Kd) {
  if (Kp < 0 || Ki < 0 || Kd < 0){
    return;
  }

  pid->dispKp = Kp;
  pid->dispKi = Ki;
  pid->dispKd = Kd;

  FloatType SampleTimeInSec = ((FloatType) pid->SampleTime) / 1000;
  pid->kp = Kp;
  pid->ki = Ki * SampleTimeInSec;
  pid->kd = Kd / SampleTimeInSec;

  if (pid->controllerDirection == PID_Direction_Reverse) {
    pid->kp = (0 - pid->kp);
    pid->ki = (0 - pid->ki);
    pid->kd = (0 - pid->kd);
  }
}
  
/* SetSampleTime(...) *********************************************************
 * sets the period, in Milliseconds, at which the calculation is performed
 ******************************************************************************/
void PID_SetSampleTime(PidType* pid, int NewSampleTime) {
  if (NewSampleTime > 0) {
    FloatType ratio = (FloatType) NewSampleTime / (FloatType) pid->SampleTime;
    pid->ki *= ratio;
    pid->kd /= ratio;
    pid->SampleTime = (unsigned long) NewSampleTime;
  }
}
 
/* SetOutputLimits(...)****************************************************
 *     This function will be used far more often than SetInputLimits.  while
 *  the input to the controller will generally be in the 0-1023 range (which is
 *  the default already,)  the output will be a little different.  maybe they'll
 *  be doing a time window and will need 0-8000 or something.  or maybe they'll
 *  want to clamp it from 0-125.  who knows.  at any rate, that can all be done
 *  here.
 **************************************************************************/
void PID_SetOutputLimits(PidType* pid, FloatType Min, FloatType Max) {
  if (Min >= Max) {
    return;
  }
  pid->outMin = Min;
  pid->outMax = Max;

  if (pid->inAuto) {
    if (pid->myOutput > pid->outMax) {
      pid->myOutput = pid->outMax;
    } else if (pid->myOutput < pid->outMin) {
      pid->myOutput = pid->outMin;
    }

    if (pid->ITerm > pid->outMax) {
      pid->ITerm = pid->outMax;
    } else if (pid->ITerm < pid->outMin) {
      pid->ITerm = pid->outMin;
    }
  }
}

/* SetMode(...)****************************************************************
 * Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
 * when the transition from manual to auto occurs, the controller is
 * automatically initialized
 ******************************************************************************/ 
void PID_SetMode(PidType* pid, PidModeType Mode)
{
    bool newAuto = (Mode == PID_Mode_Automatic);
    if(newAuto != pid->inAuto)
    {  /*we just went from manual to auto*/
        PID_Initialize(pid);
    }
    pid->inAuto = newAuto;
}
 
/* Initialize()****************************************************************
 *  does all the things that need to happen to ensure a bumpless transfer
 *  from manual to automatic mode.
 ******************************************************************************/ 
void PID_Initialize(PidType* pid) {
  pid->ITerm = pid->myOutput;
  pid->lastInput = pid->myInput;
  if (pid->ITerm > pid->outMax) {
    pid->ITerm = pid->outMax;
  } else if (pid->ITerm < pid->outMin) {
    pid->ITerm = pid->outMin;
  }
}

/* SetControllerDirection(...)*************************************************
 * The PID will either be connected to a DIRECT acting process (+Output leads 
 * to +Input) or a REVERSE acting process(+Output leads to -Input.)  we need to
 * know which one, because otherwise we may increase the output when we should
 * be decreasing.  This is called from the constructor.
 ******************************************************************************/
void PID_SetControllerDirection(PidType* pid, PidDirectionType Direction) {
  if (pid->inAuto && Direction != pid->controllerDirection) {
    pid->kp = (0 - pid->kp);
    pid->ki = (0 - pid->ki);
    pid->kd = (0 - pid->kd);
  }
  pid->controllerDirection = Direction;
}

/* Status Funcions*************************************************************
 * Just because you set the Kp=-1 doesn't mean it actually happened.  these
 * functions query the internal state of the PID.  they're here for display 
 * purposes.  this are the functions the PID Front-end uses for example
 ******************************************************************************/
FloatType PID_GetKp(PidType* pid) {
  return pid->dispKp;
}
FloatType PID_GetKi(PidType* pid) {
  return pid->dispKi;
}
FloatType PID_GetKd(PidType* pid) {
  return pid->dispKd;
}
PidModeType PID_GetMode(PidType* pid) {
  return pid->inAuto ? PID_Mode_Automatic : PID_Mode_Manual;
}
PidDirectionType PID_GetDirection(PidType* pid) {
  return pid->controllerDirection;
}

Example of use:
Code:
#include "PID.h"
Code:
PidType PID;
Code:
PID_init(&PID, 1, 0.05, 0.25, PID_Direction_Direct);
PID_SetMode(&PID, PID_Mode_Automatic);
PID_SetOutputLimits(&PID, PWM_Step, PWM_Period);

PID handling:
Code:
    PID.mySetpoint = SetTemp;    
    PID.myInput = RealTemp;
    PID_Compute(&PID);    
    SetPWM = PID.myOutput;
 
Speed control of DC motor using PWm or using other techniques for AC Induction motor ?
 

I need a output0 0V~5V to control motor speed.
 

Ok. EasyRider already gave code for DC motor speed control using PWM.
 

Hi,

The key to success is a known fixed timing. Interrupt controlled. Don't use software delays.
Always read in all necessary information at a fixed timing and send out all information at a fixed timing (PID I/Os).
This makes the PID function predictable.

Klaus
 

Sorry,your pid.h still include a <stdbool.h>,where is that??
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top