Timer problem with 9S08AW

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Timer problem with 9S08AW

2,310 Views
plaiming
Contributor I

I've run into a problem using timer triggers and Im not sure what Im doing wrong.

I am using MC9S08AW16 for a motor control application. MCU is running off an external clock and setup to use TPM1 to PWM a motor control chip. (Prescaler=2, Modulo=1025, 256.25uS period) 
TPM2  is setup at 10ms with overflow interupt enabled. I am using the overflow interrupt to
trigger execution of my main control loop so I can track time-based statistics.
 
// Loop continously, do not leave this for loop
  for(;:smileywink: {
   
    // Wait for 10ms timer flag from ISR
    while(!timer10ms) {}
   
     main routine....
   }

The issue I am having is that I need to calculate motor speed by  measuring time between 2 tachometer pulses. I do not have any more timers so I am trying to use TPM2 channel to capture the timer on a falling edge of the tachometer input signal,  save to a variable, capture the time on the next pulse, and calculate the time between the two. I am also trying to count overflows so I can add them back to the total.
My code is as follows:


 if(calculate_speed) {
    
        if(hall_switch < 2) {
        //if we didnt get the halls to switch then the motor isnt spinning
           motor_period = 0;        
        }
        else {
        // calculate the motor rpm                                                          
          motor_period = (hall_tpm2_overflows * tpm2_modulo) + hall_time0 - hall_time1;
        }
      
        hall_switch = 0;
        hall_time0 = 0;
        hall_time1 = 0;
        //enable timer
        TPM2C0SC = 0x44;       
     
       
    }


__interrupt void isrVtpm2ch0(void)
{
  /* Write your interrupt code here ... */
 
  if(hall_switch == 0) {
    // store timer value
    hall_time0 = TPM2C0V;
    // reset channel
    TPM2C0SC = TPM2C0SC & 0x7F;
    // clear overflow counter
    tpm2_overflows = 0;
    //increment hall switch counter
    hall_switch++;
  } else { 
    hall_time1 = TPM2C0V;
    hall_tpm2_overflows = tpm2_overflows;
    //disable ISR
    TPM2C0SC = 0x04;
    hall_switch++;
  }

}

What I am seeing is very inconsistent values for motor_period even when the motor is spinning at a constant speed.  Can you see anything blaringly wrong with my setup or approach?

Thanks,
Paul

 

MCUinit.c

Message Edited by t.dowe on 2009-09-04 02:09 PM
Labels (1)
0 Kudos
Reply
7 Replies

826 Views
bigmac
Specialist III
Hello Paul,

Can I assume that tpm2_overflows is incremented within the TPM2 overflow ISR?  An uncertainty is possible when the Hall timing edge is very close to the TPM overflow point.  The TPM overflow interrupt has lower priority than the TPM channel interrupts.

Consider that the input capture should occur slightly later than TPM overflow, but interrupts are currently disabled, perhaps due to servicing of another interrupt source (maybe TPM1).  When interrupts are eventually re-enabled, the channel ISR would be serviced first.  This would mean that the overflow count would not yet have been updated (here assuming the timing calculation is done within the channel ISR).

Additionally, since the overflow count is cleared within the channel ISR, when the overflow ISR is subsequently serviced, the count will be incremented, which will cause error for the next reading.

If the timing calculation is done within main(), there would be further uncertainty.should the input capture occur just prior to overflow.  By the time that the calculation is commenced, both ISRs would have been processed, and the overflow count would be too high in this case.

The solution will possibly be to apply a correction to the overflow count based on the current input capture value relative to the modulo value (whether the input capture value is low or high).  Maybe there is some advantage in doing the period calculation within the overflow ISR.

Regards,
Mac




Message Edited by bigmac on 2009-02-12 05:09 AM
0 Kudos
Reply

826 Views
plaiming
Contributor I
bigmac-
No, actually I was not incremmenting tpm2_overflows inside the ISR but after a blocking statement in the main loop waiting for the overflow ISR:

 // Loop continously, do not leave this for loop
  for(;:smileywink: {
  
  
    // Wait for 10ms timer flag from ISR
    while(!timer10ms) {}
    // reset 10ms timer flag
    timer10ms= 0;
    tpm2_overflows++;
    .....
   }

The ISR is as follows:

__interrupt void isrVtpm2ovf(void)
{
  /* Write your interrupt code here ... */

  TPM2SC_TOF = 0x4E; /* ACK channel interrupt */
                  /* Read flag, then write a 0 to the bit. */
  timer10ms = 1;

}



0 Kudos
Reply

826 Views
bigmac
Specialist III
Hello,

Firstly, the hall_switch variable might be used as a simple flag to signal to the main loop that an input capture event has occurred.  Otherwise I would use the code snippet that Bob presented, where the current input capture value and the previous capture value are updated on each input capture event, so that a period calculation is possible after each capture event.

Your main loop seems to wait until your 10ms overflow flag is set before doing anything, including always incrementing the overflow counter - you do not appear to also test for an input capture event whilst waiting.  It is therefore possible that the counter has at least one count too many because the input capture event will have occurred some time prior to overflow.  This may account for some of the errors you are observing. 

For instance, if the  motor speed is sufficiently high that two consecutive capture events occur within the 10ms overflow period, your current coding would suggest that the overflow counter value would be either 1 or 2, depending on whether an overflow condition occurred between the two events, or not.  For the purpose of the period calculation, the number of intervening overflow periods should be zero in this case.

Yes, the counting of KBI events could be an alternative method, but keep in mind that speed resolution will be limited at low speeds.  Conversely, the period measurement method provides good speed resolution at low speeds, with reduced resolution as the speed increases.  It will depend on the specific requirements of your application.

Regards,
Mac

0 Kudos
Reply

826 Views
plaiming
Contributor I




>For instance, if the  motor speed is sufficiently high that two consecutive capture events occur
> within the 10ms overflow period, your current coding would suggest that the overflow counter
> value would be either 1or 2, depending on whether an overflow condition occurred between
> the two events, or not.  For the  purpose of the period calculation, the number of intervening
> overflow periods should be zero in this case.

The way the current code is structured (and intended to work), if two motor edges come during 10ms, the overflow count would be 0.  This is because the ISR will record tmr on first edge, increment hall_switch, and reset tpm_overflow counter. On the next edge, the ISR will record new timer value, store the tpm_overflow counter (which in this case should be 0), and disable the ISR for the channel.

At the next 10ms overflow, the main loop will see that the hall switch had been triggered and calculate the period.  I am not understanding what I am missing.

-paul

0 Kudos
Reply

826 Views
plaiming
Contributor I
After struggling in vain, I have attempted a completely new solution. In this method, we have a motor hall input also connected to a KBI pin.  What I am doing is simply enabling the KBI and counting the number of interupts during a fixed time period. (namely 1s) This allows for an easy RPM motor calculation. So far this method seems to be giving reliably consistent results (although not yet accurate), more testing/debug is needed.

Just another idea for future readers...

0 Kudos
Reply

826 Views
TurboBob
Contributor IV
to capture 2 falling edges you need something like
 
  if(hall_switch == 0) {
    hall_time1 = hall_time0;
    // store timer value
    hall_time0 = TPM2C0V;
    // reset channel
 
so you save the previous capture value before reading the current one..
 
Make sure you disable interupts when calculating the motor period.  so the values don't change in the middle of the calculation.  Or at least,  disable interrupts, copy the vaules to temp variables, then reenable interrupts, then perform the math.
 
Hope this helps.
 
Bob
0 Kudos
Reply

826 Views
plaiming
Contributor I
Thanks for the response TurboBob.
I think both of your concerns are covered. In my code, hall_switch is being used as a counter
to determine which edge has been triggered. The main loop starts the sequence by resetting the
hall switch counter and enables the ISR. At the first ISR, the timer value stored in the channel register
is written to hall_time0. The channel interupt is reset for the next edge, the tpm2_overflow counter is cleared so that I can count how many overflows may occur before the next edge, and the hall_switch counter is incremented so that the ISR routine knows the next edge is edge #2.


  if(hall_switch == 0) {
    // store timer value
    hall_time0 = TPM2C0V;
    // reset channel
    TPM2C0SC = TPM2C0SC & 0x7F;
    // clear overflow counter
    tpm2_overflows = 0;
    //increment hall switch counter
    hall_switch++;
  }


On the second edge, hall_switch should now be 1, so the ISR will execute the other branch to store the timer channel value into hall_time1. It also stores away the tpm2_overflows into a temporay variable and then disables the channel interupt so that the hall_time variables are not overwritten.

else { 
    hall_time1 = TPM2C0V;
    hall_tpm2_overflows = tpm2_overflows;
    //disable ISR
    TPM2C0SC = 0x04;
    hall_switch++;
  }


Back in the main loop, the period is calculated using the hall_time0 and hall_time1 variables before re-enabling the channel interrupt. The timing is such that the main loop should not run until after the
halls have had 2 edges, but just in case, the code verifies that hall_switch was incremented twice.


-paul
0 Kudos
Reply