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

how can I make the table lookup faster? 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

how can I make the table lookup faster? thanks
PostPosted: Tue Aug 03, 2004 12:35 pm     Reply with quote

I have been trying to make a controller for DC/DC converter. and I first try the switching frequency at 40kHz, 25us. I would like the duty cycle can be updated at same frequency, which means only 25us to do the A/D and PID calculation. is it possible? even I use the table look up, it looks like still take about 18us to calculate the new duty. Could somebody check my code and give me some suggestions? of I have to update the duty at 50us or 40us, which is enough to do the job. thanks ahead.

have a good day

#include <18F458.h>
#device ADC=8
#use delay(clock=40000000)
#fuses HS,NOWDT
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X1F
///digital PID table a=kp*error,b=kb*error,c=kc*error
signed int16 a[11]={-618,-494,-371,-247,-123,0,124,248,372,495,619};
signed int16 b[11]={-1215,-972,-729,-486,-243,0,243,486,729,972,1215};
signed int16 c[11]={-600,-480,-360,-240,-120,0,120,240,360,480,600};
int PWM_MAX=180;
int PWM_MIN=100;
signed int error=0;
signed int error_1=0;
signed int error_2=0;
int u=0;
int u_1=0;
int temp;
int vref=51; //51 in 6 bits for 4V, output at 12V
//interupt, once newduty start, start to read output voltage and calculate next duty
#int_TIMER2
void TIMER2_irs(void)
{
temp=(read_adc()+2)>>2; //just need 6 bit( 5v--64)
error=vref-temp;//difference between ref and output
if (error>5){error=5;}
if (error<-5){error=-5;}
error=error+5;
// implement digital PID by reading table.
u=u_1+a[error]-b[error]+c[error];
if( u>PWM_MAX)
{u=PWM_MAX;}
if (u<PWM_MIN)
{u=PWM_MIN;}
set_pwm1_duty(u);
u_1=u;
error_1=error;
error_2=error_1;
}


void main()
{
setup_adc_ports(all_analog);
setup_adc(adc_clock_div_64);
setup_timer_2(T2_DIV_by_1,249,1);
setup_ccp1(CCP_PWM);
set_adc_channel(0);
ADCON0|=0X4;
enable_interrupts(INT_TIMER2);
enable_interrupts(global);
while(1);
}
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Aug 03, 2004 2:23 pm     Reply with quote

Quote:
u=u_1+a[error]-b[error]+c[error];
Is this your real code or a simplified version of it? Now the index into the three arrays is the same, so you can simplify the calculation by using only a single array instead.

One other thing that takes a lot of time is that the CCS compiler has a generic interrupt dispatcher, taking care of all possible registers you might change in your interrupt routine. This comes at the price of about 100 instructions of overhead on a PIC18, or 10us when running 40MHz. The PIC18 is equiped with two interrupt levels, using the high interrupt will bypass the long CCS code. Just add the keyword FAST to your ISR and add some code yourself for saving and restoring only the registers you use. This will save you at least 5us.

Unrolling the tables is another option for improving speed. This will make your program larger but faster to execute, example for unrolling a single table:
Code:

// a[11]={-618,-494,-371,-247,-123,0,124,248,372,495,619}
// Using a binary sort like algorithm for finding a[error].
if (error < 0)
{
  if (error < -2)
  {
    if (error < -4)
       a = -618;    // -5
    else
    {
      if (error > -4)
         a = -494;   // -3
      else
         a = -371;   // -4
    }
  }
  else
  {
    if (error < -1)
       a = -247;    // -2
    else
       a = -123;   // -1
  }
}
else
{
  if (error < 2)
  {
    if (error < 1)
       a = 0;    // 0
    else
       a = 124;    // 1
  }
  else if (error < 4)
  {
    if (error < 3)
       a = 248;   // 2
    else
       a = 372;   // 3
  }
  else if (error == 4)
       a = 495;   // 4
    else
       a = 619;   // 5
}
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Aug 03, 2004 3:15 pm     Reply with quote

I would recomend not running this with interupts.

This is what I would do.

Code:
While(!PWM_Timer_Interupt_Flag_Set)
{  PWM_Timer_Interupt_Flag_Set=0;     //Clear the interupt flag
   GO_Done=1;                         //Start an analog reading
   u=u+A[Error]-B[Error]+C[Error];
   set_pwm1_duty(u);
   temp=(read_adc(read_Only)+2)>>2;           //just need 6 bit( 5v--64)
   error=vref-temp;                  //difference between ref and output
   if (error>5){error=5;}
   if (error<-5){error=-5;}
   error=error+5;
}
gunking88



Joined: 18 Jun 2004
Posts: 11
Location: WV

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

yes, here is a mistake...
PostPosted: Tue Aug 03, 2004 3:16 pm     Reply with quote

should be u=u_1+a[error]-b[error_1]+c[error_2], thank you for finding this mistake for me. and I will try what you said.


ckielstra wrote:
Quote:
u=u_1+a[error]-b[error]+c[error];
Is this your real code or a simplified version of it? Now the index into the three arrays is the same, so you can simplify the calculation by using only a single array instead.

One other thing that takes a lot of time is that the CCS compiler has a generic interrupt dispatcher, taking care of all possible registers you might change in your interrupt routine. This comes at the price of about 100 instructions of overhead on a PIC18, or 10us when running 40MHz. The PIC18 is equiped with two interrupt levels, using the high interrupt will bypass the long CCS code. Just add the keyword FAST to your ISR and add some code yourself for saving and restoring only the registers you use. This will save you at least 5us.

Unrolling the tables is another option for improving speed. This will make your program larger but faster to execute, example for unrolling a single table:
Code:

// a[11]={-618,-494,-371,-247,-123,0,124,248,372,495,619}
// Using a binary sort like algorithm for finding a[error].
if (error < 0)
{
  if (error < -2)
  {
    if (error < -4)
       a = -618;    // -5
    else
    {
      if (error > -4)
         a = -494;   // -3
      else
         a = -371;   // -4
    }
  }
  else
  {
    if (error < -1)
       a = -247;    // -2
    else
       a = -123;   // -1
  }
}
else
{
  if (error < 2)
  {
    if (error < 1)
       a = 0;    // 0
    else
       a = 124;    // 1
  }
  else if (error < 4)
  {
    if (error < 3)
       a = 248;   // 2
    else
       a = 372;   // 3
  }
  else if (error == 4)
       a = 495;   // 4
    else
       a = 619;   // 5
}
gunking88



Joined: 18 Jun 2004
Posts: 11
Location: WV

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

table lookup+A/D take 40.3us to get a new duty...sigh...
PostPosted: Tue Aug 03, 2004 3:21 pm     Reply with quote

it looks like I have to run at 50us, 20khz.

Neutone wrote:
I would recomend not running this with interupts.

This is what I would do.

Code:
While(!PWM_Timer_Interupt_Flag_Set)
{  PWM_Timer_Interupt_Flag_Set=0;     //Clear the interupt flag
   GO_Done=1;                         //Start an analog reading
   u=u+A[Error]-B[Error]+C[Error];
   set_pwm1_duty(u);
   temp=(read_adc(read_Only)+2)>>2;           //just need 6 bit( 5v--64)
   error=vref-temp;                  //difference between ref and output
   if (error>5){error=5;}
   if (error<-5){error=-5;}
   error=error+5;
}
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Aug 03, 2004 4:54 pm     Reply with quote

Code:
error_1=error;
error_2=error_1;

This looks like an error too, most likely it should be:
Code:
error_2=error_1;
error_1=error;


I thought you gave us just a code snippet, but if this is your whole program than I'm sure you can easily run at 40kHz. Just forget about the interrupt routine this gives way too much overhead and working around it gives you a headache, just follow Neutone's hint.

code will then become:
Code:

#include <18F458.h>
#device ADC=8
#use delay(clock=40000000)
#fuses HS,NOWDT,NOLVP
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X0FC2
#BIT TMR2IF=0x0F9E.1
///digital PID table a=kp*error,b=kb*error,c=kc*error
signed int16 a[11]={-618,-494,-371,-247,-123,0,124,248,372,495,619};
signed int16 b[11]={-1215,-972,-729,-486,-243,0,243,486,729,972,1215};
signed int16 c[11]={-600,-480,-360,-240,-120,0,120,240,360,480,600};
int PWM_MAX=180;
int PWM_MIN=100;
signed int error=0;
signed int error_1=0;
signed int error_2=0;
int u=0;
int u_1=0;
int temp;
int vref=51; //51 in 6 bits for 4V, output at 12V

void main()
{
  setup_adc_ports(all_analog);
  setup_adc(adc_clock_div_64);
  setup_timer_2(T2_DIV_by_1,249,1);
  setup_ccp1(CCP_PWM);
  set_adc_channel(0);
  ADCON0|=0X4;
  //enable_interrupts(INT_TIMER2); // disabled
  //enable_interrupts(global);  // disabled
  clear_interrupt(INT_TIMER2);

  while(1)
  {
    // Wait for PWM_Timer_interrupt_flag
    if (TMR2IF)
    {
      // Clear PWM_Timer_interrupt_flag
      clear_interrupt(INT_TIMER2);   // equal to:   TMR2IF=0;

      // read output voltage and calculate next duty
      temp=(read_adc()+2)>>2; //just need 6 bit( 5v--64)
      error=vref-temp;//difference between ref and output
      if (error>5){error=5;}
      if (error<-5){error=-5;}
      error=error+5;
      // implement digital PID by reading table.
      u=u_1+a[error]-b[error_1]+c[error_2];
      if (u>PWM_MAX)
      {u=PWM_MAX;}
      if (u<PWM_MIN)
      {u=PWM_MIN;}
      set_pwm1_duty(u);
      u_1=u;
      error_2=error_1;   // Switched these two lines
      error_1=error;     //
    }
  }
}


I also changed ADCON0 from 0x1F to 0x0FC2 and added the fuse NOLVP.
gunking88



Joined: 18 Jun 2004
Posts: 11
Location: WV

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

thanks for your reply and finding another mistake in side..
PostPosted: Tue Aug 03, 2004 5:39 pm     Reply with quote

but I checked with break point,
from
error=error+5;
// implement digital PID by reading table.
u=u_1+a[error]-b[error_1]+c[error_2];
if (u>PWM_MAX)
{u=PWM_MAX;}
if (u<PWM_MIN)
{u=PWM_MIN;}
set_pwm1_duty(u);
u_1=u;

it takes about 20us already. plus A/D 19us... seems impossible to run at
40Khz...anyway, thank a lot


ckielstra wrote:
Code:
error_1=error;
error_2=error_1;

This looks like an error too, most likely it should be:
Code:
error_2=error_1;
error_1=error;


I thought you gave us just a code snippet, but if this is your whole program than I'm sure you can easily run at 40kHz. Just forget about the interrupt routine this gives way too much overhead and working around it gives you a headache, just follow Neutone's hint.

code will then become:
Code:

#include <18F458.h>
#device ADC=8
#use delay(clock=40000000)
#fuses HS,NOWDT,NOLVP
#use fixed_io(c_outputs=PIN_C2)
#BYTE ADCON0=0X0FC2
#BIT TMR2IF=0x0F9E.1
///digital PID table a=kp*error,b=kb*error,c=kc*error
signed int16 a[11]={-618,-494,-371,-247,-123,0,124,248,372,495,619};
signed int16 b[11]={-1215,-972,-729,-486,-243,0,243,486,729,972,1215};
signed int16 c[11]={-600,-480,-360,-240,-120,0,120,240,360,480,600};
int PWM_MAX=180;
int PWM_MIN=100;
signed int error=0;
signed int error_1=0;
signed int error_2=0;
int u=0;
int u_1=0;
int temp;
int vref=51; //51 in 6 bits for 4V, output at 12V

void main()
{
  setup_adc_ports(all_analog);
  setup_adc(adc_clock_div_64);
  setup_timer_2(T2_DIV_by_1,249,1);
  setup_ccp1(CCP_PWM);
  set_adc_channel(0);
  ADCON0|=0X4;
  //enable_interrupts(INT_TIMER2); // disabled
  //enable_interrupts(global);  // disabled
  clear_interrupt(INT_TIMER2);

  while(1)
  {
    // Wait for PWM_Timer_interrupt_flag
    if (TMR2IF)
    {
      // Clear PWM_Timer_interrupt_flag
      clear_interrupt(INT_TIMER2);   // equal to:   TMR2IF=0;

      // read output voltage and calculate next duty
      temp=(read_adc()+2)>>2; //just need 6 bit( 5v--64)
      error=vref-temp;//difference between ref and output
      if (error>5){error=5;}
      if (error<-5){error=-5;}
      error=error+5;
      // implement digital PID by reading table.
      u=u_1+a[error]-b[error_1]+c[error_2];
      if (u>PWM_MAX)
      {u=PWM_MAX;}
      if (u<PWM_MIN)
      {u=PWM_MIN;}
      set_pwm1_duty(u);
      u_1=u;
      error_2=error_1;   // Switched these two lines
      error_1=error;     //
    }
  }
}


I also changed ADCON0 from 0x1F to 0x0FC2 and added the fuse NOLVP.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Aug 03, 2004 6:55 pm     Reply with quote

This will start the ADC measurment
Code:
GO_Done=1;

Then you can perform some math while aquiring a new reading and then read the new ADC reading.
Code:
read_adc(read_Only)
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