Quadrature decoder

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I need to capture the position from an optical coder. No problem with that, I configured the quadratic decoder hardware on my SAME70 and it works well. It increments / decrements the counter and resets on index edge.

The problem I have is that the timers are 16bits (too bad on a 32b MCU !!) and I have 180 000 edges per rotation, which does not fit in a 16b counter :/

 

The "Solution" I did is to get the interrupt on overflow, and increment a variable (named diz). So combining diz and CV0 gives me the position.

I also use TC_QIER_IDX interruption to reset diz.

The following image shows that:

 

Until here, no problem.

What is problematic is if I turn in the other way. I can't detect the "underflow" on TC0 (going from 0x0000 to 0xFFFF), so I can't decrement diz. Here is what happens :

 

 

So 2 questions:

 - Do you think to a better solution than using interrupts? Some kind of counter configuration to automatically increment another counter on overflow

 - If no, do you see a solution for my the described problem when I decrement my counter?

 

Thanks in advanced

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hello Asterix86,

 

The standard solution is to read the hardware counter periodically and increment (or decrement) an internal 32/64 bit variable.  You must read the counter rapidly enough that the change in count is presumed to be less that 1/2 of the hardware counters maximum value.

In your case, reading the counter before it can change by 90,000 counts will be the minimum rate.  If the maximum possible rotation rate is 6000RPM (100 revolutions per second), you must read the counter at least 550 times per second to update the internal variable.  Normally this is not a problem as servo systems commonly read position and update the command output at > 1000 times per second.  Hope this helps...

 

Regards,

Dave

Dave

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

Thanks Dave four your suggestion. That means we have to look at the counter, compare with latest value, do some tests to know if need to increment / decrement a variable., and do that regulary (minimum 600 times a second as you mentioned). That should indeed work, but I'm sad that I cannot find a more hardware solution without interruption and CPU utilization. A 32bit coulter would have been the perfect solution :/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This was my solution to compare TC_CV at 0x8000 and increase/decrease based on direction, seems to report correctly no matter which direction motor moves, however we do lose one count:

volatile int32_t highCV = 0;
//setup QDEC BMR, CMR
//....
TC0->TC_CHANNEL[0].TC_CMR |= TC_CMR_CPCTRG; //internal counter cleared for each revolution
TC0->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;
TC0->TC_CHANNEL[0].TC_TC = 0x8000;

int32_t GetPosition()
{
    int32_t lastpos, position=0;
    do
    {  //there's no point disabling interrupt since TC_CV0 changes in hardware so we read until values are the same
       lastpos = position;  
       int32_t tmpCnt = (int16_t)TC0->TC_CHANNEL[0].TC_CV;
       position = highCV + tmpCnt;
    } while(lastpos != position);
    return position;
}
int32_t GetDirection()
{
    return (TC0->TC_QISR & TC_QISR_DIR) ? -0x8000 : +0x8000;
}
void TC0_Handler()
{
   if((TC0->TC_CHANNEL[0].TC_SR & TC0->TC_CHANNEL[0].TC_IMR) & TC_SR_CPCS)
   {
      highCV += GetDirection();
   }
}

Best regards,

David.