Properly handling timer overflow ?
Posted: Sun Feb 18, 2024 6:02 pm
Hi everyone!
I am/have been building a small router with dc servomotors based on blue pills and cheap h-bridges,
using arduino 1.8.18 and Roger's core
I already have excessive hair thinning caused by head scratching trying to find the cause for infrequent movement errors.
Last week I finally noticed that I have removed most electrical/RFI/EMI causes thus
leaving what has probably been the major cause whole time.
Something in my timer overflow isr is wrong but I'm somehow blind as I can't pinpoint the cause
I added debounce to my PID routine but to no avail
My somewhat poorly commented/structured code below
Would be grateful of any tips
Rgds
Koep
I am/have been building a small router with dc servomotors based on blue pills and cheap h-bridges,
using arduino 1.8.18 and Roger's core
I already have excessive hair thinning caused by head scratching trying to find the cause for infrequent movement errors.
Last week I finally noticed that I have removed most electrical/RFI/EMI causes thus
leaving what has probably been the major cause whole time.
Something in my timer overflow isr is wrong but I'm somehow blind as I can't pinpoint the cause
I added debounce to my PID routine but to no avail
My somewhat poorly commented/structured code below
Would be grateful of any tips
Rgds
Koep
Code: Select all
volatile long turns = 0; //this can count the number of turns the encoder gives.
volatile long steps = 0; //this can count the number of turns the encoder gives.
volatile double error = 0; //error of speed = set_speed - pv_speed
volatile double error_pre = 0; //last error of speed
volatile double error_sum = 0; //sum error of speed
volatile double Output = 0; //sum error of speed
unsigned long tprint = 0;
int EncDebounce = 0;
HardwareTimer Power = HardwareTimer(1);
HardwareTimer enc = HardwareTimer(2);
HardwareTimer PidTime = HardwareTimer(3);
HardwareTimer pulse = HardwareTimer(4);
void countTurns() {
PidTime.pause();
if (enc.getDirection())
turns -= 65449;
//turns -= settings.PPR;
//turns ++;
else
turns += 65449;
//turns += settings.PPR;
//turns--;
PidTime.resume();
}
void countSteps() {
PidTime.pause();
if (pulse.getDirection())
steps -= 65029;
// steps -= (settings.PPR - 3333);
//turns ++;
else
steps += 65029;
//steps -= (settings.PPR - 3333);
//turns--;
PidTime.resume();
}
void DoPid() {
error = (double)(steps + pulse.getCount() - (turns + enc.getCount()));
if (Lost){
}else if (error < -settings.MaxErr || error > settings.MaxErr){
EncDebounce++;
if (EncDebounce > 900) Lost = true;
}else{
error_sum += (error * settings.Ki * settings.Gain); //sum of error
if (error_sum > settings.ZeroS + settings.MaxKi) error_sum = settings.ZeroS + settings.MaxKi;
if (error_sum < -settings.ZeroS - settings.MaxKi) error_sum = -settings.ZeroS - settings.MaxKi;
Output = (error * settings.Kp * settings.Gain);
Output += error_sum - (((error - error_pre) * settings.Kd) * settings.Gain);
//max Ki and output;
if (Output > (settings.ZeroS-300)) Output = (settings.ZeroS-300);
if (Output < (-settings.ZeroS+300)) Output = (-settings.ZeroS+300);
error_pre = error; //save last (previous) error
//pwmWrite(PWM_OUT, (long)settings.ZeroS+Output);
bitSet(TIMER1->regs.adv->CR1, 1); // update disable
if (Output < 0) {
TIMER1->regs.adv->CCR1 = (int)((settings.ZeroS - settings.FFw) + Output);
} else if (Output > 0) {
TIMER1->regs.adv->CCR1 = (int)((settings.ZeroS + settings.FFw) + Output);
} else {
TIMER1->regs.adv->CCR1 = (int)(settings.ZeroS + Output);
}
bitClear(TIMER1->regs.adv->CR1, 1); // update enable
EncDebounce = 0;
}
}
SerialCommand sCmd; // The demo SerialCommand object
void setup() {
Power.pause();
Power.setPrescaleFactor(1);
//Power.setOverflow(((long)settings.ZeroS) * 2); //12,5kHz
Power.setOverflow(((long)settings.ZeroS + settings.FFw + 80) * 2); //12,5kHz
Power.refresh();
Power.resume();
pinMode(PWM_OUT, PWM); //PA8
pinMode(PWM_OUT_COMP, PWM);//PB13
bitClear(TIMER1->regs.adv->BDTR, 8); //
bitClear(TIMER1->regs.adv->BDTR, 9); //
bitClear(TIMER1->regs.adv->CCER, 0); //this should enable complimentary outputs
bitClear(TIMER1->regs.adv->CCER, 2);
bitSet(TIMER1->regs.adv->BDTR, 0); //dead time 1*125ns
//bitSet(TIMER1->regs.adv->BDTR, 1); //dead time 1*125ns
//bitSet(TIMER1->regs.adv->BDTR, 2); //dead time 2*125ns = tot 5*125ns
//bitSet(TIMER1->regs.adv->BDTR, 3); //dead time 1*125ns
afio_cfg_debug_ports(AFIO_DEBUG_NONE); // remap ENCODER pins TIM2
afio_remap(AFIO_REMAP_TIM2_PARTIAL_1);
enc.pause(); //stop...
enc.setMode(1, TIMER_ENCODER); //set mode, the channel is not used when in this mode.
enc.setOverflow(65449); //use this to match the number of pulse per revolution of the encoder. Most industrial use 1024 single channel steps.
//enc.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
enc.refresh();
enc.attachInterrupt(0, countTurns); //channel doesn't mean much here either.
enc.resume(); //start the encoder...
bitSet(TIMER2->regs.adv->CCMR1, 4);
bitSet(TIMER2->regs.adv->CCMR1, 5);
bitSet(TIMER2->regs.adv->CCMR1, 6);
bitSet(TIMER2->regs.adv->CCMR1, 7);
bitSet(TIMER2->regs.adv->CCMR1, 12);
bitSet(TIMER2->regs.adv->CCMR1, 13);
bitSet(TIMER2->regs.adv->CCMR1, 14);
bitSet(TIMER2->regs.adv->CCMR1, 15);
/* */
pulse.pause(); //stop...
pulse.setMode(2, TIMER_ENCODER); //set mode, the channel is not used when in this mode.
pulse.setOverflow(65029); //use this to match the number of pulse per revolution of the encoder. Most industrial use 1024 single channel steps.
pulse.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1); //or TIMER_SMCR_SMS_ENCODER1 or TIMER_SMCR_SMS_ENCODER2. This uses both channels to count and ascertain direction.
pulse.refresh();
pulse.attachInterrupt(0, countSteps); //channel doesn't mean much here either.
pulse.resume(); //start the encoder...
//bitClear(TIMER4->regs.adv->CR1, 4);
bitSet(TIMER4->regs.adv->CCMR1, 4);
bitSet(TIMER4->regs.adv->CCMR1, 5);
bitSet(TIMER4->regs.adv->CCMR1, 6);
bitSet(TIMER4->regs.adv->CCMR1, 7);
bitSet(TIMER4->regs.adv->CCMR1, 12);
bitSet(TIMER4->regs.adv->CCMR1, 13);
bitSet(TIMER4->regs.adv->CCMR1, 14);
bitSet(TIMER4->regs.adv->CCMR1, 15);
/* */
PidTime.pause();
PidTime.setPrescaleFactor(1);
PidTime.setOverflow(3049);
//PidTime.setOverflow(3000);
PidTime.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE);
PidTime.setCompare(TIMER_CH1, 1);
PidTime.attachInterrupt(TIMER_CH1, DoPid);
PidTime.refresh();
PidTime.resume();