cancel
Showing results for 
Search instead for 
Did you mean: 

Variable overflows inside timer irq handler - stm32f427

Bogdan
Senior
Posted on April 21, 2018 at 04:24

Hello,

i am using a stm32f427 trough C with IAR compiler, and i am facing a strange situation.

To have a overview, the functionality of the app,  is relaying on 3 ISR's.

Timer1 IRQ handler - runs at 25khz where  some comutation, and update dutycycle registers is performed

Timer2 IRQ handler - is used to calculate the rpm of the motor, via 3 hall sensors events, the rate of this isr can vary, depending on the speed of the hall signals.

Timer4 IRQ handler - runs at a fixed 1000hz rate, where i compute some PID algorithm

The problem is in timer4 irq handler,    if the value of pid is less than 0, the returned value of DutyCycle is somewhere like 65123,

For example :

 DutyCycle = (uint16_t)50 - (int)(-2) , i would expect to get 48.

If the pid_out is <=0, the dutycycle value is ok.

Why is this behaviour?

I have posted bellow the code from the irq handlers, as well as the variables declared globaly

uint16_t DutyCycle = 50;

float iTerm = 0.0;

float iTerm_last = 0.0;

float dTerm = 0.0;

float p_out = 0.0;

float KP = 0.2;

float KI = 0;

float KD = 0;

float SampleTime = 0.001;

int16_t pid_out =0;

void TIM4_IRQHandler(){

    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)

     {   TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

 

p_SetPoint = 200; // set desired speed for motor at 200rpm

p_error = p_SetPoint - mrpm; // calculate the error

iTerm = iTerm_last + ((float)p_error * SampleTime);

iTerm_last = iTerm; // save current iTerm for the next cycle

dTerm = (p_error - p_error_last)/SampleTime;

pid_out = (KP * p_error) + (KI*iTerm) + (KD*dTerm);

p_error_last = p_error;

if(pid_out > 400)

     DutyCycle= 400; // this works ok

else if(pid_out < 0)

    DutyCycle = DutyCycle + pid_out; // this is overflowing

else

    DutyCycle = pid_out;

}

}

void TIM1_CC_IRQHandler()

{

   if (TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET)

       {    TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);

            if(DutyCycle < 400)

             { // safe guard not to exceed 400 dutycycle

                 TIM1->CCR1=DutyCycle; // update Ch1 duty cycle

                 TIM1->CCR2=DutyCycle; 

// update Ch1 duty cycle

                 TIM1->CCR3=DutyCycle; 

// update Ch1 duty cycle

             }

}

void TIM2_IRQHandler()

{   uint8_t nSample = 6;

    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)

     {   TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

   

       t_sum = (uint16_t)TIM2->CCR1;

       t = (uint32_t)(t_sum *14);

       t= t * 18;

       t = t/1000;

       t= 60000/t;

t_buf=t_buf+t; // store in the buffer for average

t_cnt++;

if(t_cnt==nSample)

   {

       mrpm = t_buf/t_cnt;

       t_cnt=0;

      t_buf=0;

    }

  }

}

7 REPLIES 7
T J
Lead
Posted on April 21, 2018 at 07:06

I think that DutyCycle should be an int16_t  too...

henry.dick
Senior II
Posted on April 21, 2018 at 14:08

'

if the value of pid is less than 0, the returned value of DutyCycle is somewhere like 65123,'

unlikely due to overflow - if true, throw out the compiler - as it is wrong.

likely due to the DutyCycle going negative through successively adding a negative number to it.

'For example :

 DutyCycle = (uint16_t)50 - (int)(-2) , i would expect to get 48.'

in that case, throw out your expectation - as it is wrong.

AvaTar
Lead
Posted on April 21, 2018 at 17:56

As the others suggested.

Data type promotion in C is sometimes a bit peculiar.

It is ALWAYS a good idea to test and validate algorithms (like your PID controller) on a PC environment, with easy and convenient debugging + logging.

Posted on April 21, 2018 at 17:34

>>likely due to the DutyCycle going negative through successively adding a negative number to it.

Would suspect that too, would suggest instrumenting rather than probing in the debugger.

I'd probably range check it rather than blindly apply.

Would also stay in signed 32 bit variables instead of doing a lot of unnecessary casting back and forth. 16-bit math isn't any more efficient in this context. And TIM2 is 32-bit wide

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
T J
Lead
Posted on April 22, 2018 at 02:04

I would expect,

 DutyCycle = (uint16_t)50 - (int)(-2) , i would expect to get 52

Bogdan
Senior
Posted on April 22, 2018 at 02:45

In the end i did not find the root cause for this behaviour,   but i used a diferent aproach, trying limit the integrator (anti-windup).

After the calculations are made,  i only update DutyCycle = pid result, this seems to work pretty well after tuning the pid parameters

Sorry for the mistake,   its basic math....  +(-)  results -,   yes 

DutyCycle = (uint16_t)50 + (int)(-2) should yield 48.

Posted on April 22, 2018 at 03:37

don't try to fix your problem: you have a problem because there is a logic flaw in your approach. once you fix the logic, the problem will just go away.