CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Could somebody check the code for me? PWM,PI control. thanks

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
gunking88



Joined: 18 Jun 2004
Posts: 11
Location: WV

View user's profile Send private message Send e-mail

Could somebody check the code for me? PWM,PI control. thanks
PostPosted: Tue Jun 22, 2004 2:18 pm     Reply with quote

I am doing one project for DC/DC converter.40kHz.the following code is a test code for the function feedback and set PWM dutycycle. but I always can't get the good result .could somebody check it for me at his/her convenienece? thanks a lot! have a good day

#include <18F458.h>
#device ADC=10
#use delay(clock=40000000)
#fuses HS,NOWDT
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X1F

void main()
{
long int temp;
long int vref;
long int error;
long int Sum_error;
float Ti;
float Tp;
float Ki;
float Kp;
int duty_time;
int duty_temp;
int i;
int PWM_MAX=202;
int PWM_MIN=20;

temp=0;
//vreference voltage 3.331v-->100V output
vref=686;
error=0;
Sum_error=0;
Ki=0.125;
Kp=1/64;;
setup_adc_ports(all_analog);
setup_adc(adc_clock_div_16);
//set up 40kHz PWM
setup_timer_2(T2_DIV_by_1,62,1);
setup_ccp1(CCP_PWM);
set_adc_channel(0);
//ADCON0|=0X4;
//soft start
for(i=0;i<=20;i=i+5)
{ duty_time=i;
set_pwm1_duty(duty_time);
//delay_ms(10);
}
while(1)
{
ADCON0|=0X4;
//read output voltage
temp=read_adc();
error=vref-temp;
//integrate term
Sum_error=Sum_error+error;
Ti=Ki*Sum_error;
Tp=error*Kp;
duty_time=(int)(Ti+Tp);
if( duty_time>PWM_MAX)
{duty_time=PWM_MAX;}
if (duty_time<PWM_MIN)
{duty_time=PWM_MIN;}
set_pwm1_duty(duty_temp);
}
}
Ttelmah
Guest







Re: Could somebody check the code for me? PWM,PI control. th
PostPosted: Tue Jun 22, 2004 2:51 pm     Reply with quote

gunking88 wrote:
I am doing one project for DC/DC converter.40kHz.the following code is a test code for the function feedback and set PWM dutycycle. but I always can't get the good result .could somebody check it for me at his/her convenienece? thanks a lot! have a good day

#include <18F458.h>
#device ADC=10
#use delay(clock=40000000)
#fuses HS,NOWDT
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X1F

void main()
{
long int temp;
long int vref;
long int error;
long int Sum_error;
float Ti;
float Tp;
float Ki;
float Kp;
int duty_time;
int duty_temp;
int i;
int PWM_MAX=202;
int PWM_MIN=20;

temp=0;
//vreference voltage 3.331v-->100V output
vref=686;
error=0;
Sum_error=0;
Ki=0.125;
Kp=1/64;;
setup_adc_ports(all_analog);
setup_adc(adc_clock_div_16);
//set up 40kHz PWM
setup_timer_2(T2_DIV_by_1,62,1);
setup_ccp1(CCP_PWM);
set_adc_channel(0);
//ADCON0|=0X4;
//soft start
for(i=0;i<=20;i=i+5)
{ duty_time=i;
set_pwm1_duty(duty_time);
//delay_ms(10);
}
while(1)
{
ADCON0|=0X4;
//read output voltage
temp=read_adc();
error=vref-temp;
//integrate term
Sum_error=Sum_error+error;
Ti=Ki*Sum_error;
Tp=error*Kp;
duty_time=(int)(Ti+Tp);
if( duty_time>PWM_MAX)
{duty_time=PWM_MAX;}
if (duty_time<PWM_MIN)
{duty_time=PWM_MIN;}
set_pwm1_duty(duty_temp);
}
}

As written, the code won't work at all (you are setting the duty cycle to 'duty_temp', but not actually setting this anywhere).
This is presumably just a typing error though.
I'd suggest synchronising both the outside loops (the initial one that ramps the duty cycle, and the second one that tries to perform the PID), to timer2. At present, the value will be changing at random times round the loop, and possibly (depending on the relative speeds of the loop), re-evaluating the PID loop several times for one actual clock cycle of the external PWM.
I'd suggest enabling an interrupt on timer2, and (since no other interrupt is in use), just writing a global handler, to clear a single flag, and return using retfie 1, which will then only take perhaps 10 machine instructions. Then in the main, wait till this flag changes, and set it again. Take the reading from the ADC, using 'read_adc(ADC_READ_ONLY)' at the start of the loop, calculate your PID values, then as the last operation before looping back to the 'wait', perform 'read_adc(ADC_START_ONLY)'. This way, the actual adc 'reading' will be taken while the system is waiting, and the loop will occur once for each pulse.
This should be relatively easy (I have performed this sequence at 42KHz, on a 40MHz processor, changing a bi-phase signal, with each half duty cycle set seperately). Timing was 'tight', but OK.

Best Wishes
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Jun 22, 2004 3:41 pm     Reply with quote

A simple service routine called between other loop functions like this could also work well.

Code:

if(Timer_2_Interupt_Flag)
{  Timer_2_Interupt_Flag=0;
   Solve new Pulse period
}
falleaf



Joined: 23 May 2004
Posts: 48

View user's profile Send private message

PostPosted: Tue Jun 22, 2004 11:58 pm     Reply with quote

Quote:
A simple service routine called between other loop functions like this could also work well.

Code:

if(Timer_2_Interupt_Flag)
{ Timer_2_Interupt_Flag=0;
Solve new Pulse period
}


I think we shouldn't do this, because while we are computing the PI calculation, an interrupt for encoder counter may occur. That is, you will lost encoders.

We may solve new PI calculation (solve new pulse period = duty cycle) in the main routine. And check if timer2_flag is set, do PI calculation.

With high resolution encoder, lost encoder counter may happen, especially when we use gear box. The motor may run at about 6000 to 9000 rpm at 500 pulse per second resolution. That is about 50000 pulse per second with 4pulse reading per period, we have 200,000 pulse per second, and if we use 4MHz osc ext we have 1,000,000 instruction per second. Higher resolution encoder may cause problems. In some special application, we may use upto 2000 pulse per second encoders. We get problems here.

The C code generate longer code than ASM.!

However, if we take it into the main routine, it may solve the problems.

This is my PID control code:

Code:
////////////////////////////////
// Chuong trinh tinh toan PID //
////////////////////////////////

signed long velo_error_perfect;                  // dung de tinh dao ham bac 2
signed long velo_error_past;                     // tinh dao ham bac nhat
signed long velo_error_present;                  // gia tri sai so hien tai

#define Kp  1000
#define Ki  100
#define Kd  400


void PID_calculation (signed long velo_current, signed long velo_target)
{
   float daoham1;
   float daoham2;
   float Proportion;
   float Integration;
   float Derivation;
   signed long delta_duty;
   signed long duty_temp;


   velo_error_present = velo_target - velo_current;
   daoham1 = velo_error_present - velo_error_past;
   daoham2 = velo_error_present - 2*velo_error_past + velo_error_perfect;

   Proportion = Kp * daoham1;
   Integration = Ki * velo_error_present;
   Derivation = Kd * daoham2;


   delta_duty = (signed long) (Proportion + Integration + Derivation);      // ep kieu

   if (delta_duty > 1023)                                  // han che gia toc dieu khien
   {
      delta_duty = 1023;
   }
   if (delta_duty < -1023)
   {
      delta_duty = -1023;
   }

   duty_temp = pwm_duty + delta_duty;        // dung bien tam de tranh bi sai

   if (duty_temp > 1023)
   {
      pwm_duty = 1023;
   }
   if (duty_temp <-1023)
   {
      pwm_duty = -1023;
   }

   if (duty_temp <0)                          // kiem tra dau cua pwm_duty
   {
      pwm_direction = 0;                      // gan vao pwm_direction la bien toan cuc
      pwm_duty = - duty_temp;                 // gan vao pwm_duty la bien toan cuc
   }
   else
   {
      pwm_direction = 1;
      pwm_duty = duty_temp;
   }
}
Guest








Re: Could somebody check the code for me? PWM,PI control. th
PostPosted: Wed Jun 23, 2004 7:00 am     Reply with quote

Thanks, you are right, should be duty_time, which is what I did first. I will try your methods. another question,

Sum_error=Sum_error+error;
Ti=Ki*Sum_error;
Tp=error*Kp;
duty_time=(int)(Ti+Tp);
these four steps take long time to calculate. is there any ways to reduce the calculation time? thanks again, have a good day.


Ttelmah wrote:
gunking88 wrote:
I am doing one project for DC/DC converter.40kHz.the following code is a test code for the function feedback and set PWM dutycycle. but I always can't get the good result .could somebody check it for me at his/her convenienece? thanks a lot! have a good day

#include <18F458.h>
#device ADC=10
#use delay(clock=40000000)
#fuses HS,NOWDT
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X1F

void main()
{
long int temp;
long int vref;
long int error;
long int Sum_error;
float Ti;
float Tp;
float Ki;
float Kp;
int duty_time;
int duty_temp;
int i;
int PWM_MAX=202;
int PWM_MIN=20;

temp=0;
//vreference voltage 3.331v-->100V output
vref=686;
error=0;
Sum_error=0;
Ki=0.125;
Kp=1/64;;
setup_adc_ports(all_analog);
setup_adc(adc_clock_div_16);
//set up 40kHz PWM
setup_timer_2(T2_DIV_by_1,62,1);
setup_ccp1(CCP_PWM);
set_adc_channel(0);
//ADCON0|=0X4;
//soft start
for(i=0;i<=20;i=i+5)
{ duty_time=i;
set_pwm1_duty(duty_time);
//delay_ms(10);
}
while(1)
{
ADCON0|=0X4;
//read output voltage
temp=read_adc();
error=vref-temp;
//integrate term
Sum_error=Sum_error+error;
Ti=Ki*Sum_error;
Tp=error*Kp;
duty_time=(int)(Ti+Tp);
if( duty_time>PWM_MAX)
{duty_time=PWM_MAX;}
if (duty_time<PWM_MIN)
{duty_time=PWM_MIN;}
set_pwm1_duty(duty_temp);
}
}

As written, the code won't work at all (you are setting the duty cycle to 'duty_temp', but not actually setting this anywhere).
This is presumably just a typing error though.
I'd suggest synchronising both the outside loops (the initial one that ramps the duty cycle, and the second one that tries to perform the PID), to timer2. At present, the value will be changing at random times round the loop, and possibly (depending on the relative speeds of the loop), re-evaluating the PID loop several times for one actual clock cycle of the external PWM.
I'd suggest enabling an interrupt on timer2, and (since no other interrupt is in use), just writing a global handler, to clear a single flag, and return using retfie 1, which will then only take perhaps 10 machine instructions. Then in the main, wait till this flag changes, and set it again. Take the reading from the ADC, using 'read_adc(ADC_READ_ONLY)' at the start of the loop, calculate your PID values, then as the last operation before looping back to the 'wait', perform 'read_adc(ADC_START_ONLY)'. This way, the actual adc 'reading' will be taken while the system is waiting, and the loop will occur once for each pulse.
This should be relatively easy (I have performed this sequence at 42KHz, on a 40MHz processor, changing a bi-phase signal, with each half duty cycle set seperately). Timing was 'tight', but OK.

Best Wishes
Ttelmah
Guest







PostPosted: Wed Jun 23, 2004 7:25 am     Reply with quote

Yes.
Have a look at the MicroChip application notes, where a full PID servo system is done using integer arithmetic. I hadn't realised you were using FP arithmetic, and the time difference involved in this is massive.

Best Wishes
gunking88



Joined: 18 Jun 2004
Posts: 11
Location: WV

View user's profile Send private message Send e-mail

PostPosted: Wed Jun 23, 2004 11:36 am     Reply with quote

thank you so much for your massage here.it is quite helpful,and I am checking it now.
since the project you mentioned is quite similar with mine. is it possible for you to send me the source code? or just the specific PID part? anyway, thanks again.

have a nice day.

Ttelmah wrote:
Yes.
Have a look at the MicroChip application notes, where a full PID servo system is done using integer arithmetic. I hadn't realised you were using FP arithmetic, and the time difference involved in this is massive.

Best Wishes
Ttelmah
Guest







PostPosted: Wed Jun 23, 2004 3:30 pm     Reply with quote

gunking88 wrote:
thank you so much for your massage here.it is quite helpful,and I am checking it now.
since the project you mentioned is quite similar with mine. is it possible for you to send me the source code? or just the specific PID part? anyway, thanks again.

have a nice day.

Ttelmah wrote:
Yes.
Have a look at the MicroChip application notes, where a full PID servo system is done using integer arithmetic. I hadn't realised you were using FP arithmetic, and the time difference involved in this is massive.

Best Wishes

That particular code is commercial, but I do have the core calculation 'loop' of the Microchip code that I can post:
Code:


/* This union is used internally to allow access to the various parts of each
32bit 'address' as bytes, 16bit integers etc. */
union lblock {
    unsigned int16 ui[2];
    signed int16 i[2];
    int8 b[4];
    signed int32 word;
};

   union lblock ypid, error;
   int1 sat;
   int8 ms16;

//main PID code

   error.word=next-temp;

   //error is limited to 16 bit
   error.word=trim16(error.word);

   //Here have to handle the servo arithmetic
   //This is the 'P' term
   ypid.word=error.word*factor[0];

   //This is the 'I' term. To prevent integrator 'lock up', I do not add
   //anything if the output is saturated.
   if (!sat) {
       integral=integral+error.word;
   }

   //Now the 'D' term.
   ypid.word=ypid.word+((error.word-lasterror)*factor[2]);

   //Finally add in the integral term.
   ypid.word=ypid.word+(integral*factor[1]);

   lasterror=error.word;

   if (ypid.b[3] & 0x80) {
   //Here motor needs to drive backwards.
   if (move_in_progress && servo_tick==0) rsputc("I");
       DIRECTION=FORWARD;
   ypid.word=-ypid.word;
   }
   else if (ypid.word!=0L) {
   //Here motor needs to drive forwards.
       DIRECTION=BACKWARD;
   }

   //Force to 8 bits, allready +ve only, and turn of the sat flag
   sat=false;
   if (ypid.ui[1]!=0l) {
       ms16=255;
       sat=true;
   }
   else ms16=ypid.b[1];
   //set pulse width the ms16 here, and loop



//This is the 'trim' subroutine.
//Trims an incoming 32bit value to a 16bit value
signed int32 trim16(union lblock val) {
   if (val.b[3] & 0x80) {
   //Here -ve
        if ((val.ui[1] != 0xFFFFl) || !(val.b[2] & 0x80)) val.word=0xffff8000l;
   }
   else {
   if ((val.ui[1]) || (val.b[2] & 0x80)) val.word=0x7FFFl;
   }
   return(val.word);
}


Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group