Hello,
I'm using an Arduino Mega to control stepper motors and close a PID loop.
I'm using timer 1 for the PID control. It sets of the ISR every 0.1 second, which in turn calculates the required PWM
for a DC motor. The setpoint is the turning frequency and the measurement is the encoder count. The control works well. As stated, this ISR runs every 0.1 second.
I'm using timer 3 to vary the PWM frequency to control the speed of a stepper motor. An external measurement relates directly to TCNT3, which changes the frequency of the PWM signal, which in turn changes the stepper motor speed. This also works well. The ISR is usually run every ~600-2000 microseconds.
However, when I try to use both of these applications together, the PID loop is affected. In order for the DC motor to respond at all I need to change my control constants (Kp, Ki, Kd) to extremely high, which makes my control very jittery and un-reliable.
Does anyone have any ideas what could be causing this? When I comment out the initialization of Timer3 the PID loop (with Timer1) works fine. Do these timers have a dependency I'm unaware of? This is my first project with timers so I'm probably missing something elementary.
//--------------Parameters-------------
double foam_freq = 3;//Hz, foaming frequency
int foaming_time = 10; //seconds, foaming time
int foam_volume = 130; //ml
double foam_draw_time = 10; //seconds, into syringes
double foam_push_time = 20; //seconds, from syringes
int drug_volume = 30; //ml
double drug_admin_time = 2; //seconds
int delay_foam2drug = 50; //ms before drug administration starts
int delay_drug2foam = 50; //ms of foam after drug administration starts
int air_volume = 250; //ml
double air_admin_time = 6; //seconds
//--------------End parameters-------------
String mySt = "";
char myChar;
boolean stringComplete = false; // whether the string is complete
//Define pins
#define pwmOutput 5 //PWM for DC motor control
#define in1 6 //H-bridge transistor 1
#define in2 7 //H-bridge transistor 2
#define drug_direction 8 //
#define drug_step 9 //
#define foam_direction 10 //Stepper motor 1 dir
#define foam_step 11 //Stepper motor 1 pin
int push_mm=0, draw_mm=0;
double setPoint = 0;
//Volatile memory for changing in ISR
int encoder = 0;
double pv_speed = 0;
double e_speed = 0; //error of speed = setPoint - pv_speed
double e_speed_pre = 0; //last error of speed
double e_speed_sum = 0; //sum error of speed
double pwm_pulse = 0; //this value is 0~255
double kp = 3;
double ki = 5;
double kd = 1;
int timer1_counter; //for timer
boolean motor_start = true;
volatile int steps;
volatile byte home_flag=false;
volatile byte end_flag=false;
volatile int pressure, pt;
byte pressure_control = false, plot_flag=false;
int prev_time=0;
double f_target= 1667;
double PSC = 1024;
volatile long int timer3_counter = 65536-15625/f_target;
volatile int step_count=1000000;
volatile byte drug_flag = false, foam_flag = false;
byte set_time_flag = false;
void setup() {
noInterrupts();
//--------------------------timer1 setup
TCCR1A = 0;
TCCR1B = 0;
timer1_counter = 59286; // 65536-16MHz/256/10Hz (59286 for 0.1sec)
TCNT1 = timer1_counter; // preload timer
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
//--------------------------timer3 setup
TCCR3A = 0;
TCCR3B = 0;
TCNT3 = timer3_counter; // preload timer
TCCR3B |= (1 << CS12); // 256 prescaler, CS = source clock select
TCCR3B |= (1 << CS10);
TIMSK3 |= (1 << TOIE3); // enable timer overflow interrupt
//--------------------------end time setup
interrupts();
Serial.begin(9600);
pinMode(A0,INPUT); //Potentiometer (for pressure loop simulation)
pinMode(2,INPUT); // Encoder interrupt
attachInterrupt(digitalPinToInterrupt(2),detect_pulse,RISING);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(drug_direction,OUTPUT); //Stepper direction
pinMode(drug_step,OUTPUT); //Stepper step
pinMode(foam_direction,OUTPUT); //Stepper direction
pinMode(foam_step,OUTPUT); //Stepper step
pinMode(pwmOutput,OUTPUT); //DC motor pwm (foamer)
// Set initial rotation direction
digitalWrite(in1, LOW); //Having both transistors on LOW means no current
digitalWrite(in2, LOW); //This is a fsafety precaution to turn off foamer
analogWrite(pwmOutput,0); //Another precaution. PWM=0 ---> Voltage=0.
}
void loop() {
foam_freq=setPoint;
}
void detect_pulse() {
encoder+=1; //increasing encoder at new pulse
}
ISR(TIMER1_OVF_vect) { // interrupt service routine - tick every 0.1sec
TCNT1 = timer1_counter; // set timer
pressure = map(analogRead(A0),0,1023,0,80); //Read sensor
if (pressure_control == true){
timer3_counter = 65536-15626*(6+pow(1.07,pressure))/10000;
}
else {
timer3_counter = 65536-15625/f_target;
}
pv_speed = (encoder/20.0)/0.1; //calculate motor speed
encoder=0;
//PID program
if (motor_start){
e_speed = setPoint - pv_speed;
pwm_pulse = e_speed*kp + e_speed_sum*ki + (e_speed - e_speed_pre)*kd;
e_speed_pre = e_speed; //save last (previous) error
e_speed_sum += e_speed; //sum of error
if (e_speed_sum >30) e_speed_sum = 30;
if (e_speed_sum <-30) e_speed_sum = -30;
}
else{
e_speed = 0;
e_speed_pre = 0;
e_speed_sum = 0;
pwm_pulse = 0;
}
//update new speed
if (pwm_pulse <255 & pwm_pulse >0){
analogWrite(pwmOutput,pwm_pulse); //set motor speed
}
else{
if (pwm_pulse>255){
analogWrite(pwmOutput,255);
}
else{
analogWrite(pwmOutput,0);
}
}
}
ISR(TIMER3_OVF_vect){
if (step_count<steps){
if (drug_flag==true){
digitalWrite(drug_step,!digitalRead(drug_step));
step_count++;
}
}
else {
steps = 0;
}
TCNT3 = timer3_counter; // preload timer
}