|
|
View previous topic :: View next topic |
Author |
Message |
gunking88
Joined: 18 Jun 2004 Posts: 11 Location: WV
|
Could somebody check the code for me? PWM,PI control. thanks |
Posted: Tue Jun 22, 2004 2:18 pm |
|
|
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 |
Posted: Tue Jun 22, 2004 2:51 pm |
|
|
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
|
|
Posted: Tue Jun 22, 2004 3:41 pm |
|
|
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
|
|
Posted: Tue Jun 22, 2004 11:58 pm |
|
|
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 |
Posted: Wed Jun 23, 2004 7:00 am |
|
|
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
|
|
Posted: Wed Jun 23, 2004 7:25 am |
|
|
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
|
|
Posted: Wed Jun 23, 2004 11:36 am |
|
|
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
|
|
Posted: Wed Jun 23, 2004 3:30 pm |
|
|
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 |
|
|
|
|
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
|