At 07:01 PM 9/15/2008, you wrote: Looks to me like you have the P gain inverted so you get locking up at 0% or 100% (positive feedback rather than negative). You are going to have to do more scaling of the I and D to get reasonable control IMHO, but one thing at a time.. >Hi guys, > I've just cooked up a PID loop based on a few Microchip appnotes, and for >some reason or other I can't get the thing to work. > > The hardware is a P-channel MOSFET on CCP1out (aka "P1A"), with the ECCP >set to invert P1A (because the PMOS switches on when its gate is >low). This is >used as a high-side driver for a small brushed DC motor, which also has a >1N4001 from 0V to motor +ve ('4001 cathode to motor +ve). > > The motor's positive supply is also connected to AN2. > > Every PWM cycle, the loop waits for the PWM output to go idle (high), and >then waits for a fixed period of time (625us) for the diode to stop >conducting >and the output to settle. The A/D is started, and the output value is stored >in adc_value. > > To start with, only Proportional control is enabled (the "P" in > "PID"). The >integral and derivative gains are both set to zero. > > If PID_P_GAIN is below 10, then the motor will not start. More precisely, >there's a brief surge, then after roughly 0.5secs the PWM output drops to >nothing and the motor stops. The PID loop makes no further attempts >to restart >the motor unless the PIC is reset. At no point does it ever run at the >setpoint (which is ~3200RPM). > > If I increase PID_P_GAIN to 10, the PWM duty cycle immediately > hits PWM_MAX >and stays there. > > Can some kind soul please take a look at my code and tell me where I'm >going wrong? I've been scratching my head over this for two days, >and the only >conclusion I've come to is that I may well need a PIC16F616 ICD pod after all. > > Here's my code, in Hitech PIC C: >-------- >#include > >/** > * Device configuration: > * - Internal clock with I/O > * - 8MHz internal oscillator > * - Unprotected code memory > * - MCLR enabled > * - Powerup timer disabled > * - Watchdog timer disabled > * - Brownout Reset off > */ >__CONFIG(INTIO & OSC_8MHZ & UNPROTECT & MCLREN & PWRTDIS & WDTDIS & BORDIS); > >// Set _XTAL_FREQ to current clock frequency (so we can use the >delay functions) >#define _XTAL_FREQ 8000000 > >// pindefs >#define PIN_PWM_OUT RC5 >#define PIN_DEBUG RC4 > >void main(void) >{ > // Clear port registers > PORTA = 0; > PORTC = 0; > > // Set up TRIS registers > TRISA = 0b00000100; > TRISC = 0b00000000; > > // Set up A/D converter > ANSEL = 0b00000100; // AN2 analog, rest > digital. TODO: Vref external! > ADCON1 = 0b00110000; // Clock A/D from Frc (~500kHz) > ADCON0 = 0b11001001; // Right justified, ref=Vcc > (TODO: Vref external!) > > // Set up CCP for PWM > T2CON = 0b00000110; // TMR2 on, 1:16 prescale, > no postscale > PR2 = 0xFF; // PR2 set > for max resolution. Fpwm=488.28Hz with 8MHz and >1:16 prescaler > CCPR1L = 64; // Set PWM initial duty cycle > CCP1CON = 0b00001110; // Set PWM mode -- P1A > modulated (rest I/O), active low > >// minimum value permitted for PWM value >#define MIN_PWM 0 >// maximum value permitted for PWM value >#define MAX_PWM 640 >// ADC target value -- 0.5Vcc >#define PID_TARGET 320 > >// PID factors >#define PID_P_GAIN 8 >#define PID_I_GAIN 0 >#define PID_D_GAIN 0 > > // Main processing loop > for (;;) { > static unsigned int cur_pwm = MAX_PWM; > unsigned int adc_value; > signed int deltaV = 0, last_deltaV; > > signed long pid_p; > signed long pid_i = 0; > signed long pid_d; > > signed long pid_value; > >//////////// > > // Wait for PWM output to go high, then low. > while (PIN_PWM_OUT); > while (!PIN_PWM_OUT); > > // Delay for Back-EMF to settle -- 625us > __delay_us(625); > > // Start A/D conversion >PIN_DEBUG=1; > GODONE = 1; > > // Wait for conversion to complete > while (GODONE); >PIN_DEBUG=0; > > // get the adc value > adc_value = (((unsigned int)ADRESH) << 8) + > ((unsigned int)ADRESL); > > // calculate error (delta-V) and update last_deltaV > last_deltaV = deltaV; > deltaV = ((signed int)PID_TARGET) - ((signed int)adc_value); > > // calculate proportional error > pid_p = ((signed long)PID_P_GAIN) * ((signed long)deltaV); > > // calculate integral error > pid_i = pid_i + (((signed long)PID_I_GAIN) * > ((signed long)deltaV)); > > // calculate derivative error > pid_d = ((signed long)PID_D_GAIN) * ((signed long) > (deltaV - last_deltaV)); > > pid_value = (pid_p + pid_i + pid_d); > > // re-scale the pid value for the PWM > if (pid_value < MIN_PWM) { > cur_pwm = MIN_PWM; > } else if (pid_value > MAX_PWM) { > cur_pwm = MAX_PWM; > } else { > cur_pwm = pid_value; > } > > // Send new PWM duty cycle value to the converter > CCP1CON = (CCP1CON & 0b11001111) | ((cur_pwm & 0x03) << 4); > CCPR1L = (cur_pwm >> 2); > } >} >-------- > >If Thunderbird has borked the wrapping, it's also available from >. I'm 99% sure the >initialisation is fine, it's the main processing loop I'm not sure about. > >Thanks, >-- >Phil. >piclist@philpem.me.uk >http://www.philpem.me.uk/ >-- >http://www.piclist.com PIC/SX FAQ & list archive >View/change your membership options at >http://mailman.mit.edu/mailman/listinfo/piclist Spehro Pefhany --"it's the network..." "The Journey is the reward" speff@interlog.com Info for manufacturers: http://www.trexon.com Embedded software/hardware/analog Info for designers: http://www.speff.com -- http://www.piclist.com PIC/SX FAQ & list archive View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist