• Pic® Basic


  • Fast Integer PID routine

    A Proportional–Integral–Derivative (PID) controller is a generic control loop feedback mechanism widely used in industrial control systems. A PID is the most commonly used feedback controller, and calculates an error value as the difference between a measured process variable and a desired setpoint. The controller attempts to minimise the error by adjusting the process control inputs.

    This implemetation uses integer only calculations, as opposed to the more usual floating point calculations, therefore it has the advantage of speed when used on an 8-bit device, but does sacrifice some accuracy.

    The actual PID include file code is listed below
    Code:
    '
    ' Amicus18 16-bit integer only PID routine
    ' Written by Les Johnson
    '
    ' PID variables
    '
        Dim PID_wInput As Word                      ' Input to the PID routine
        Dim PID_wRequired As Word                   ' Optimum value to achieve
        Dim PID_wOutput As Word                     ' Output of the PID
    
        Dim PID_wCorrectionMax As Word              ' Maximum correction to be applied
        Dim PID_wOutputMax As Word                  ' The maximum output value allowed
        Dim PID_wOutputMin As Word                  ' The minimum output value allowed
        Dim PID_wPgain As Word                      ' Response factor for P
        Dim PID_wIgain As Word                      ' Response factor for I
        Dim PID_wDgain As Word                      ' Response factor for D
    
        Dim PID_wPreviousInput As Word              ' Previous input of the PID
        Dim PID_wError As Word                      ' Error observed
        Dim PID_wCorrection As Word                 ' Internal correction to be applied
        Dim PID_wErrorSum As Word                   ' Sum of errors observed
        Dim PID_wErrorMax As Word                   ' Max error sum allowed
        Dim PID_wTemp As Word                       ' Temporary word variable
    
        Dim tSign As PRODL.0                        ' Temporarily holds the sign of a value
    
    '------------------------------------------------------------------------
        GoTo _PID_Main                              ' Jump over the subroutines
    '------------------------------------------------------------------------
    ' Integer PID routine
    ' Input     : PID_wInput holds the measurement from a sensor (-32768 to +32767)
    '           : PID_wRequired holds the optimum value to acheive
    '           : PID_wCorrectionMax holds the maximum correction to apply
    '           : PID_wErrorMax holds the maximum error allowed
    '           : PID_wPgain holds the Proportional gain (-32768 to +32767)
    '           : PID_wIgain holds the Integral gain (-32768 To +32767)
    '           : PID_wDgain holds the Derivative gain (-32768 To +32767)
    '           : PID_wOutputMax holds the maximum output allowed
    '           : PID_wOutputMin holds the minimum output allowed
    ' Output    : PID_wOutput holds the result of the PID calculation
    ' Notes     : Uses a pre-processor macro for ease of use
    '
    $define PID_Compute(pInput, pRequired, pResult) '
        PID_wInput = pInput         '
        PID_wRequired = pRequired   '
        GoSub _PID                  '
        pResult = PID_wOutput
    
    
    _PID:
        PID_wError = PID_wInput - PID_wRequired                             ' Find the error between what's required and what's just been input
        PID_wCorrection = PID_wError * PID_wPgain                           ' P factor
        PID_wErrorSum = PID_wErrorSum + PID_wError                          ' Sum the error value
        tSign = PID_wErrorSum.15                                            ' Save the sign of PID_wErrorSum
        PID_wErrorSum = Abs PID_wErrorSum                                   ' Make it a positive value for testing
        If PID_wErrorSum > PID_wErrorMax Then                               ' Is the accumulative error greater than that allowed?
            PID_wErrorSum = PID_wErrorMax                                   ' Yes. So make it maximum
        EndIf
        If tSign = 1 Then                                                   ' Was PID_wErrorSum originally a negative value?
            PID_wErrorSum = -PID_wErrorSum                                  ' Yes. So make it negative
        EndIf
        PID_wCorrection = PID_wCorrection + (PID_wErrorSum * PID_wIgain)    ' I factor
        PID_wTemp = ((PID_wPreviousInput - PID_wInput) * PID_wDgain)        ' \
        PID_wCorrection = PID_wCorrection + PID_wTemp                       ' / D factor
        PID_wPreviousInput = PID_wInput                                     ' Save the current measurement
        tSign = PID_wCorrection.15                                          ' Save the sign of PID_wCorrection
        PID_wCorrection = Abs PID_wCorrection                               ' Make it a positive value for testing
        PID_wCorrection = PID_wCorrection / 64                              ' Scale the value to a more manageable size
        If PID_wCorrection > PID_wCorrectionMax Then                        ' Is the correction greater than that allowed?
            PID_wCorrection = PID_wCorrectionMax                            ' Yes. So make it maximum
        EndIf
        If tSign = 1 Then                                                   ' Was PID_wCorrection originally a negative value?
            PID_wCorrection = -PID_wCorrection                              ' Yes. So make it negative
        EndIf
        PID_wOutput = PID_wOutput - PID_wCorrection                         ' Subtract the correction from the output result
        If PID_wOutput > PID_wOutputMax Then                                ' Trap maximum output
            PID_wOutput = PID_wOutputMax
        ElseIf PID_wOutput <= PID_wOutputMin Then                           ' Trap minimum output
            PID_wOutput = PID_wOutputMin
        EndIf
        Return
    
    '------------------------------------------------------------------------
    ' Initialise the PID routine
    ' Input     : PID_wOutputMax = Maximum output value
    '           : PID_wOutputMin = Minimum output value
    '           : PID_wCorrectionMax = Maximum correction to be applied
    '           : PID_wErrorMax = Maximum error sum allowed
    '           : PID_wPgain = Response factor for P
    '           : PID_wIgain = Response factor for I
    '           : PID_wDgain = Response factor for D
    ' Output    : None
    ' Notes     : Must be used at least once before the PID_Compute macro is used
    '           : Uses a pre-processor macro for ease of use
    '
    $define PID_Setup(pKP, pKI, pKD, pMaxCorrection, pMaxError, pMinOutput, pMaxOutput) '
        PID_wOutputMax = pMaxOutput          '
        PID_wOutputMin = pMinOutput          '
        PID_wCorrectionMax = pMaxCorrection  '
        PID_wErrorMax = pMaxError            '
        PID_wPgain = pKP                     '
        PID_wIgain = pKI                     '
        PID_wDgain = pKD                     '
        PID_wPreviousInput = 0               '
        PID_wErrorSum = 0
    
    '------------------------------------------------------------------------
    _PID_Main:
    A demonstration program for using the PID.inc file is listed below
    Code:
    '
    ' Amicus18 integer only PID routine Demonstration
    ' Written by Les Johnson
    '
    ' Connect a potentiometer to AN0 to give the required value to reach(0 to 1023)
    ' Connect the output from CCP1 to AN1 through a low pass filter
    '
    ' Turning the pot connected to AN0, the output of the PWM should match it
    ' Note that the PID routine will require tuning by adjusting P gain, I gain and D gain
    '
        Include "Amicus18.inc"                      ' Configure the compiler to use a PIC18F25K20 at 64MHz. i.e. An Amicus18 board
    
        Include "Amicus18_ADC.inc"                  ' Load the Amicus18 ADC macros into the program
        Include "Amicus18_HPWM10.inc"               ' Load the Amicus18 10-bit HPWM macros into the program
        Include "PID.inc"                           ' Load the PID routine into the program
    '
    ' Create some user variables
    '
        Dim MyInput As Word                         ' Input to the PID, which is the output from the sensor
        Dim MyRequired As Word                      ' The value required by the PID
        Dim MyOutput As Word                        ' The output from the PID
    
    '------------------------------------------------------------------------
    ' The main program loop starts here
    '
    Main:
    '
    ' Open the ADC for 10-bit reading of AN0 and AN1
    '
        OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_2_TAD, ADC_REF_VDD_VSS, ADC_2ANA)
    '
    ' Open the CCP1 channel for 10-bit PWM output
    '
        OpenAnalog1()
    '
    ' Setup the PID parameters
    ' (pKP, pKI, pKD, pMaxCorrection, pMaxError, pMinOutput, pMaxOutput)
    '
        PID_Setup(23, 1, 0, 9, 1, 0, 1023)
    '
    ' Create a loop to constantly adjust the PWM (CCP1) output depending on the ADC (AN1) input
    '
        While 
            MyRequired = ReadADC(ADC_CH0)               ' Read the value required by the PID from AN0
            MyInput = ReadADC(ADC_CH1)                  ' Read AN1 for the value to place into the PID
            PID_Compute(MyInput, MyRequired, MyOutput)  ' Compute PID
            WriteAnalog1(MyOutput)                      ' Place the output of the PID onto the HPWM
            DelayMS 3                                   ' A small delay
        Wend                                            ' Do it forever