Skip to main content
Bogdan
Senior
April 21, 2018
Question

Variable overflows inside timer irq handler - stm32f427

  • April 21, 2018
  • 7 replies
  • 1277 views
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;

    }

  }

}

    This topic has been closed for replies.

    7 replies

    T J
    Senior III
    April 21, 2018
    Posted on April 21, 2018 at 07:06

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

    henry.dick
    Associate II
    April 21, 2018
    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.

    Tesla DeLorean
    Guru
    April 21, 2018
    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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
    AvaTar
    Senior III
    April 21, 2018
    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.

    T J
    Senior III
    April 22, 2018
    Posted on April 22, 2018 at 02:04

    I would expect,

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

    Bogdan
    BogdanAuthor
    Senior
    April 22, 2018
    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.

    henry.dick
    Associate II
    April 22, 2018
    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.