PID Position Control Dspic 30f2020
PID Position Control Dspic 30f2020
PID Position Control Dspic 30f2020
h"
_FOSCSEL(PRIOSC_PLL) // This sets up the device for use with external
Crystal in PLL MODE
_FOSC(CSW_FSCM_OFF & HS) // This sets the Oscillator Source
_FWDT(FWDTEN_OFF) // This turns the Watchdog timer off
_FBS(BWRP_WRPROTECT_ON) // Boot Segment is Write Protected
_FGS(CODE_PROT_OFF) //Code Protection is OFF
// Define Global Variables
struct pidParameters
{
float Kp;
float Ki;
float Kd;
signed long int error;
signed long int output;
signed long int outmax;
signed long int ReferenceSetpoint;
signed long int feedback;
signed long int integral;
signed long int derivitive;
signed long int previous_error;
signed long int saturated;
} pidParams;
void setup(void) // Funtion definition for setup of the ADC AND Power PWM Module
.
{
PTCONbits.PTEN = 0; // This will disable the PWM Module
PTPER = 15992; // Sets the PWM Period to 16.667u secs (60KHz Switc
hing Frequency)Period Duration = (PTPER + 1) / 120MHz.
/* Initialize PWM Generator 1 */
IOCON1bits.PENH = 1; // PWM Module controls High output
IOCON1bits.PENL = 1; // PWM Module controls Low output
IOCON1bits.POLH = 0; // High Output Polarity is active Hi
gh
IOCON1bits.POLL = 0; // Low Output Polarity is active Hig
h
IOCON1bits.PMOD = 1; // Independant output mode
IOCON1bits.OVRENH = 0; // High Output Override disabled
IOCON1bits.OVRENL = 0; // Low Output Override disabled
PWMCON1bits.FLTSTAT = 0; // Clear Fault Interrupt flag
PWMCON1bits.CLSTAT = 0; // Clear Current Limit Interrupt flag
PWMCON1bits.TRGSTAT = 0; // Clear PWM Trigger Interrupt flag
PWMCON1bits.FLTIEN = 0; // Disable Fault Interrupt
PWMCON1bits.CLIEN = 0; // Disable Current Limit Interrupt
PWMCON1bits.TRGIEN = 0; // Disable Trigger Interrupt
PWMCON1bits.ITB = 0; // Time base is read from PTMR
PWMCON1bits.MDCS = 0; // Duty cycle is read from PDC
PWMCON1bits.DTC = 2; // No Dead Time
PWMCON1bits.XPRES = 0; // No extenal reset for PTMR
PWMCON1bits.IUE = 0; // Immediate update to PDC
TRGCON1bits.TRGDIV = 2; // Trigger on every 3rd event. That is 5
0 usecs between successive interations with the system
TRGCON1bits.TRGSTRT = 0; // Start the counting at the start
TRIG1 = 15992; // Trigger event at 16.667 usec from
// start of the PWM cycle
PDC1 = 0; // Initially start with with a 0% Duty C
ycle
PHASE1 = 0; // No staggering
FCLCON1 = 0x0003; // Fault Input is disabl
ed
PTCON = 0x8000; // Enable PWM Module
/* Initialize the ADC */
ADCONbits.ADSIDL = 0; // Operate in Idle Mode
ADCONbits.FORM = 0; // Output in Integer Format
ADCONbits.EIE = 1; // Enable Early Interrupt
ADCONbits.ORDER = 0; // Even channel first
ADCONbits.SEQSAMP = 1; // Sequential Sampling Enabled
ADCONbits.ADCS = 4; // Clock Divider is set up for Fadc/12
ADSTAT = 0; // Clear the ADSTAT register
ADPCFG = 0xFFC0; // AN0/AN1, AN2/AN3, AN4/AN5 are analog
inputs
ADCPC0bits.TRGSRC0 = 0x4; // Trigger conversion OF CHANNELS AN0/AN
1 on PWM#1 Trigger
ADCPC0bits.IRQEN0 = 1; // Enable the interrupt
ADCONbits.ADON = 1; // Start the ADC module
/* Set up the Interrupts */
IFS0bits.ADIF = 0; // Clear AD Interrupt Flag
IPC2bits.ADIP = 4; // ADC Interrupt Priority Is Set To 1
IEC0bits.ADIE = 1; // Enable the ADC Interrupt
}
int main(void)
{
pidParams.Kp = 0.4;
pidParams.Ki = 0;
pidParams.Kd = 0;
pidParams.outmax = 15992; // The Maximum PWM Value is a functio
n of frequency
//
Max Duty = (Percent * PTPER) / 100
pidParams.ReferenceSetpoint = 707; //Corresponds to 14.5Volts
pidParams.feedback = 0;
pidParams.integral = 0;
pidParams.derivitive = 0;
pidParams.previous_error = 0;
setup(); // Function Call to setup peripherals
while(1)
{
}
}
// Interrupt Service Routine will read ADC and Update the Duty Cycle Register
// The ISR is Invoked at a 20KHz rate. This 50usecs between updates of the Duty
Cycle Register.
void __attribute__((__interrupt__)) _ADCInterrupt()
{
TRISE = 0xffCf; //Set LED pins E4 and E5
as outputs
LATEbits.LATE5 = 1; // Turn LED ON
IFS0bits.ADIF = 0; // Clear ADC Interrupt Flag
ADSTATbits.P0RDY = 0; // Clear the ADSTAT bits
pidParams.feedback = ADCBUF0; // Get the conversion re
sult from channel AN0
pidParams.error = pidParams.ReferenceSetpoint - pidParams.feedback
; // This claculated the proportional error
if ((pidParams.error > 2) || (pidParams.error < -2)) // If error is with
in 2 then do not do the PID Calculation.
{
if (pidParams.saturated == 0) // If the PID controller is s
taurated the flag will be 1 and no
//
accumulation of integral/derivitive will be done.
{
if (pidParams.integral < 32000)
{
if (pidParams.integral > -32000)
pidParams.integral += pidParams.error;
}
pidParams.derivitive = pidParams.error - pidPara
ms.previous_error;
}
// NOW WE EXECUTE THE ACTUAL PID ALGORITHM
pidParams.output =(pidParams.Kp * pidParams.error
+ pidParams.Ki * pidParams.integral
+ pidParams.Kd * pidParams.derivitive);
// Perform boundary checks on the output of the PID algorithm. If the output lim
its are exceeded then, then set output to the limit
// and set flag.
if (pidParams.output > pidParams.outmax)
{
pidParams.saturated = 1;
pidParams.output = pidParams.outmax;
}
else if (pidParams.output < 0)
{
pidParams.saturated = 1;
pidParams.output = 0;
}
else
pidParams.saturated = 0;
pidParams.previous_error = pidParams.error;
PDC1 = pidParams.output; // Update the duty cycle re
gister
LATEbits.LATE5 = 0;// Turn the LED OFF
}
}