Homemade PI problems

Hi

We have a problem with a homemade PI controller. We are not sure what the problem is, but by using the program we can never reach our setpoint.
And our duty cycle is swithing sometimes form 20 - 80 %.. but over 100 % as it was an overflow. but we can't see where this should be.
some that can help ?

The P and I parameteres are found according to so simulating in simulink they should be right according to our motor.
The external analog potential applied is 3 V and the range og the input is 0-2.8

/*
 * Interrupt template http://popdevelop.com/2010/04/mastering-timer-interrupts-on-the-arduino/
 *
 * This program is sourpose to functions as a PI controller. witch takes the an analog reading 
 * controled by e 3us interrupt. each 30us it shoud take the avg of theres readings
 * and do the PI calculations. The results is converted to 8 bit, for analog output (PWM). 
 * anti windup is implementet in the calculations. 
 *
 * for every 30us a "flag" is raised from the interrupt to flip a port i the main (loop)
 *
 * Made by: Christian Winum and Mikkel Callesen
 * SDU Sønderborg  education: mechatronic
 */
   
unsigned int tcnt2;      
int toggle = 0;  
static boolean adcready = false;
static int adcvalue=0 , i , adcindex=0;
static unsigned int adc[10];
static unsigned char output8=0;
int inputPin= A1;
int outputPin= 5;

unsigned int output, input, setpoint, b=1, c=1;
unsigned int e_last, e, dedt, A; 
float Kp, Ki,Ts;

void setup() {  
  pinMode(2, OUTPUT);                      // seting pinmode
     
  TIMSK2 &= ~(1<<TOIE2);                   // First disable the timer overflow interrupt while we're configuring
   
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));    // Configure timer2 in normal mode (pure counting, no PWM etc.)
  TCCR2B &= ~(1<<WGM22);  
   
  ASSR &= ~(1<<AS2);                       // Select clock source: internal I/O clock
   
  TIMSK2 &= ~(1<<OCIE2A);                  // Disable Compare Match A interrupt enable (only want overflow)
 
                                           // Configure the prescaler to CPU clock divided by 256
  TCCR2B |= (1<<CS22)  | (1<<CS21);        // Set bits  
  TCCR2B &= ~(1<<CS20);                    // Clear bit  
   
  tcnt2 = 69;                              // (CPU clock)/(precaler)/(tcnt2=256-69) = 3us interrupt
   
  TCNT2 = tcnt2;                           // load and enable timer
  TIMSK2 |= (1<<TOIE2);  
  analogReference(EXTERNAL);
  Serial.begin(9600);
  Kp=1.5832;                             // Load P values
  Ki=5.4107;                             // Load P values
  output=0;
  e_last = 0, A=0, e=0, Ts=0.030;
}  
    
ISR(TIMER2_OVF_vect) {                     // Interrupt Service Routine at timer2 overflow
      
  TCNT2 = tcnt2;                         // Reload timer
    
  adc[adcindex++]=analogRead(inputPin);
  if(adcindex ==10){
    adcvalue=0;
    adcindex=0;
    for(i=0;i<10;i++){
      adcvalue=adcvalue+adc[i];
      
    }
    adcvalue=adcvalue/10;                 // avg of 10 meas
    
    e=setpoint-adcvalue;                  // Error on motor dependent on stepoint
    c=(output<=1024);                     // Windup parameters  
    b=(output>=0);
    
    A=A+0.5*Ts*(e+e_last)*b*c;            // Integration part with windup
    
    e_last=e;                             // Set current error to last error  
    output = e*Kp+A*Ki;                   // PI output
    output=output/4;                  // 10 bit int value to 8 bit int value
    output8=(unsigned char)output;    // 8 bit int value to 8 bit unsigned char value.
    
    
    analogWrite(outputPin, output8);
 
    adcready=true;
  }
}  
    
void loop() {  
  
  setpoint=500;                                // 10 bit value between 10 - 90 %
  if(adcready==true){
    digitalWrite(2, toggle == 0 ? HIGH : LOW); // test code for confermation of program runtime
    toggle = ~toggle; 
     Serial.print("output 8  ");
    Serial.println(output8, DEC);
    
    
   adcready=false; 
  }  
}

Before anyone jumps into your program, let me ask you this: is the sensor wire running along the controlled power output? Say if you have one cable with multiple cores, one is thermistor sensor and another is a heater power voltage, you will answer yes.

I've run into cross talk problems in the past and either separating the two or shielding your signal will solve the problem.

Can the Arduino do an analogRead() in three microseconds?

In the ATmega168 the internal A/D clock runs at 50 to 200 KHz (between 20 and 5 microseconds per cycle) divided down from the CPU clock. It takes 1.5 cycles to do each conversion once the first conversion is done in 13.5 cycles. That's 67.5 to 270 microseconds for the first conversion and 7.5 to 30 microseconds for subsequent conversions if you don't switch pins.

I think your math might be right for 3 microsecond intervals but you are missing a LOT of interrupts.

Try doing an analogRead(pin in a loop and timing how long it takes to do 1000 of them. That will give you an idea of a reasonable sample rate.

liudr:
Before anyone jumps into your program, let me ask you this: is the sensor wire running along the controlled power output? Say if you have one cable with multiple cores, one is thermistor sensor and another is a heater power voltage, you will answer yes.

I've run into cross talk problems in the past and either separating the two or shielding your signal will solve the problem.

Cross talk | LiuDr Electronic Solutions LLC Official Blog

I'm not sure that I get your question. but there should not be any interference from wires running beside each others.. and we don't have have cables with multiple cores.

johnwasser:
Can the Arduino do an analogRead() in three microseconds?

In the ATmega168 the internal A/D clock runs at 50 to 200 KHz (between 20 and 5 microseconds per cycle) divided down from the CPU clock. It takes 1.5 cycles to do each conversion once the first conversion is done in 13.5 cycles. That's 67.5 to 270 microseconds for the first conversion and 7.5 to 30 microseconds for subsequent conversions if you don't switch pins.

I think your math might be right for 3 microsecond intervals but you are missing a LOT of interrupts.

Try doing an analogRead(pin in a loop and timing how long it takes to do 1000 of them. That will give you an idea of a reasonable sample rate.

we made some testing to the analogRead() that showed that we could make the reading and calculations in much less than 3us. we flip a pin when ever we get to the end of the ISR() and monitored that with a scope. we also tried to raise a flag and move the flipping part to the main loop. and all of that could also easily be in the space between the 3us interrupt.

We think that the error is somewhere where we go over the size of a variable and therefore get something completely wrong. We have tried to do all the calculations by hand, and there was no problem, but we cant see where the problems else should be..

we made some testing to the analogRead() that showed that we could make the reading and calculations in much less than 3us

I'd take a very careful look at your results and test method, if I were you.

AWOL:

we made some testing to the analogRead() that showed that we could make the reading and calculations in much less than 3us

I'd take a very careful look at your results and test method, if I were you.

It takes about 100 microseconds (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.

From: analogRead() - Arduino Reference

But I have made a fail, it is not every 3 microsecond but every 3 millisecond... then it should be possible. Sorry for the mistake.

it is not every 3 microsecond but every 3 millisecond.

Only three orders of magnitude out.

Ever considered a career in banking? :smiley: