cancel
Showing results for 
Search instead for 
Did you mean: 

Tracking an optical encoder overflow and underflow

Hrishikesh
Senior

I'm using an STM32F407 MCU with a 600 PPR incremental optical encoder. The encoder is coupled to a stepper motor that drives a 5mm pitch ballscrew. I'm trying to keep track of the encoder count to determine the absolute position of the spindle attached to the ballscrew. Also, using the encoder in quadrature mode so the actual count per 1 revolution of the encoder is 2400 counts. I wrote the following program only to test the logic (and not store the actual count) but this does not seem to work. Any idea what I'm missing?

I'm keeping track of when the encoder overflows and becomes zero. If its rotating CW, then the previous count is 2399 and in CCW it should be 1 right? Also I'd really appreciate it if anyone can recommend a better way to track overflow.

void encoderCount()
{
    currCount = __HAL_TIM_GET_COUNTER(&htim1); //get encoder count
 
    if (!__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1)) //Is the encoder timer counting up? (CW)
    {
        if (currCount == 0)
        {
            prevCount = 2399;
            indexCount++;
        }
    }
    else //then it must be counting down (CCW)
    {
        if (currCount == 0)
        {
            prevCount = 1;
            indexCount--;
        }
    }
}

10 REPLIES 10

I think you're going to have to observe the direction of the numbers, not a setting in the counter.

Might also suggest using a larger span for the counter (ARR), say 48000-1, which fits in 16-bit, and allows for modulo 2400 of the count (CNT)

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TDK
Guru

Look at the definition for __HAL_TIM_IS_TIM_COUNTING_DOWN. It just returns the DIR bit, which isn't what you want.

I don't believe there is a robust way to know if the counter just overflowed or underflowed, particularly since these can be triggered multiple times before you even handle the first one.

I would set up a callback every 5ms or whatever is required for the maximum speed you need to track to hold the previous value and handle any overflow/underflow. With a counter with ARR=65535, it holds a lot of revolutions before you need to track this event.

If you feel a post has answered your question, please click "Accept as Solution".

Thanks! I set the ARR to 2400 since that was one rev of the encoder. I thought I could keep track of the number of revs of the encoder to arrive at the total count. Essentially converting the incremental encoder to an absolute one. With the highest number in the 16-bit range (which is a modulo of 2400) I still will have to keep track of overflow or underflow. If you assume an axis length of 1000mm, I'll have a max count of 120,000 which exceeds the 16-bit length.

I'm still unclear on how the modulo would help? I tried reading about it but couldn't really understand it how it would apply in this context. Could you elaborate a little please?

Also found this code here. Not sure how this is keep track of the actual count though,

Uint16 Encoder :: getRPM(void)
{
    if(ENCODER_REGS.QFLG.bit.UTO==1)       // If unit timeout (one 10Hz period)
    {
        Uint32 current = ENCODER_REGS.QPOSLAT;
        Uint32 count = (current > previous) ? current - previous : previous - current;
 
        // deal with over/underflow
        if( count > _ENCODER_MAX_COUNT/2 ) {
            count = _ENCODER_MAX_COUNT - count; // just subtract from max value
        }
 
        rpm = count * 60 * RPM_CALC_RATE_HZ / ENCODER_RESOLUTION;
 
        previous = current;
        ENCODER_REGS.QCLR.bit.UTO=1;       // Clear interrupt flag
    }
 
    return rpm;
}

Interesting! As I mentioned above, even with an ARR of 65535 I'd still need to track overflow and underflow since the axis length may be longer than what fits in the max 16-bit value. Storing the previous value is where I'm stumped! Not sure how to track that and hence I used __HAL_TIM_IS_TIM_COUNTING_DOWN to keep track of the encoder direction (and ergo overflow or underflow).

As both TDK and Clive hinted, the key is to read out periodically, possible in another timer ISR, faster than half the overflow period at the maximum RPM. That's why it's better to leave ARR at max, so that you don't need to do this too often.

JW

TDK
Guru

You could also use TIM2 or TIM5 instead which are 32-bit timers. Won't have to worry about overflow there on this application. Probably a better solution than what I suggested above.

If you feel a post has answered your question, please click "Accept as Solution".

Yep true. That would limit me to two axes per chip. For a six axes machine the bom cost would go up.

How would that work? For example, assume that the axes has to move to a specific position. However if this position is reached when I’m not reading the count, the machine could over or undershoot since it’s relying on a dated count value.

You'd check the count with sufficient frequency that you could catch over/under limit zones. Alternative is to interrupt at each edge, or use limit switches.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..