chipKIT® Development Platform

Inspired by Arduino™

how to read two rotary encoders with the max32

Created Fri, 01 Mar 2013 13:48:53 +0000 by alphamike


alphamike

Fri, 01 Mar 2013 13:48:53 +0000

I have a max32 that I would like to use to read two quadrature encoders (1024 p/r) to relay that information to my robot operation system. Is there any libs that I can use in mpide?


Jacob Christ

Sun, 03 Mar 2013 06:40:33 +0000

The pontech uav100 code can rwad a single encoder and it should be easily expandable to two.

Search for it on github

Jacob


alphamike

Wed, 06 Mar 2013 23:50:36 +0000

I looked through the uav100 code but I could not find the part that read an encoder. I have pieced together this code from ones I have found online. The issue I am having is that only one encoder will count up and down. the other will only count up or down, regardless of direction. I know this cod is very unrefined. I am not at all good at writing code. What I am trying to do is read two Quadrature encoders (1024 p/r) and relay that info to the Robot operating system running on my computer. I would be thankful of any help. [code][// Quadrature encoders // Left encoder #define c_LeftEncoderInterrupt 2 #define c_LeftEncoderPinA 20 #define c_LeftEncoderPinB 6 #define LeftEncoderIsReversed volatile bool _LeftEncoderBSet; volatile long _LeftEncoderTicks = 0;

// Right encoder #define c_RightEncoderInterrupt 3 #define c_RightEncoderPinA 21 #define c_RightEncoderPinB 7 volatile bool _RightEncoderBSet; volatile long _RightEncoderTicks = 0;

void setup() { Serial.begin(115200); // Quadrature encoders // Left encoder pinMode(c_LeftEncoderPinA, INPUT); // sets pin A as input pinMode(c_LeftEncoderPinB, INPUT); // sets pin B as input attachInterrupt(c_LeftEncoderInterrupt, HandleLeftMotorInterruptA, RISING);

// Right encoder pinMode(c_RightEncoderPinA, INPUT); // sets pin A as input pinMode(c_RightEncoderPinB, INPUT); // sets pin B as input attachInterrupt(c_RightEncoderInterrupt, HandleRightMotorInterruptA, RISING);

}

void loop() { Serial.print(_LeftEncoderTicks); Serial.print("\t"); Serial.print(_RightEncoderTicks); Serial.print("\n"); delay(20); }

// Interrupt service routines for the left motor's quadrature encoder void HandleLeftMotorInterruptA() { // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A _LeftEncoderBSet = digitalRead(c_LeftEncoderPinB); // read the input pin

// and adjust counter + if A leads B #ifdef LeftEncoderIsReversed _LeftEncoderTicks -= _LeftEncoderBSet ? -1 : +1; #else _LeftEncoderTicks += _LeftEncoderBSet ? -1 : +1; #endif }

// Interrupt service routines for the right motor's quadrature encoder void HandleRightMotorInterruptA() { // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A _RightEncoderBSet = digitalRead(c_RightEncoderPinB); // read the input pin

// and adjust counter + if A leads B #ifdef RightEncoderIsReversed _RightEncoderTicks -= _RightEncoderBSet ? -1 : +1; #else _RightEncoderTicks += _RightEncoderBSet ? -1 : +1; #endif }/code]


mikes

Thu, 07 Mar 2013 03:53:25 +0000

This code will read one encoder using a pair of interrupts it should be easy to make it read another with a different pair of interrupts. (I could use change Notice pins but that would require direct register addressing like was done on the UAV100)

volatile signed long pos = 0;
volatile char changed = 0;
void setup()
{
  Serial.begin(115200);
  pinMode(PIN_INT0, INPUT);
  pinMode(PIN_INT1, INPUT);
  if(digitalRead(PIN_INT0))
    attachInterrupt(0, Fall0, FALLING);
  else
    attachInterrupt(0, Rise0, RISING);
  if(digitalRead(PIN_INT1))
    attachInterrupt(1, Fall1, FALLING);
  else
    attachInterrupt(1, Rise1, RISING);
}

void loop()
{
  if (changed==1)
  {
    changed=0;
    Serial.println(pos);
  }
}
void Rise0()
{
  if(digitalRead(PIN_INT1))
    pos++;
  else
    pos--;
  changed=1;
  attachInterrupt(0, Fall0, FALLING);
}
void Fall0()
{
  if(digitalRead(PIN_INT1))
    pos--;
  else
    pos++;
  changed=1;
 attachInterrupt(0, Rise0, RISING);
}
void Rise1()
{
  if(digitalRead(PIN_INT0))
    pos--;
  else
    pos++;
  changed=1;
  attachInterrupt(1, Fall1, FALLING);
}
void Fall1()
{
  if(digitalRead(PIN_INT0))
    pos++;
  else
    pos--;
  changed=1;
 attachInterrupt(1, Rise1, RISING);
}

Note: You could use only one interrupt and a digital input but then you have half the resolution. You must have both Rising and Falling to keep a accurate count of position unless the encoder only travels in one direction.


alphamike

Fri, 08 Mar 2013 15:15:21 +0000

So I changed the code to

volatile signed long posleft = 0;
volatile char changedleft = 0;
volatile signed long posright = 0;
volatile char changedright = 0;
void setup()
{
  Serial.begin(115200);

  pinMode(PIN_INT0, INPUT); //2
  pinMode(PIN_INT1, INPUT); //3
  pinMode(PIN_INT4, INPUT); //20
  pinMode(PIN_INT3, INPUT); //21

  // left encoder
  if(digitalRead(PIN_INT0))
    attachInterrupt(0, Fall0, FALLING);
  else
    attachInterrupt(0, Rise0, RISING);
  if(digitalRead(PIN_INT1))
    attachInterrupt(1, Fall1, FALLING);
  else
    attachInterrupt(1, Rise1, RISING);
  // right encoder
  if(digitalRead(PIN_INT3))
    attachInterrupt(3, Fall3, FALLING);
  else
    attachInterrupt(3, Rise3, RISING);
  if(digitalRead(PIN_INT4))
    attachInterrupt(4, Fall4, FALLING);
  else
    attachInterrupt(4, Rise4, RISING);

}

void loop()
{
  // left encoder
  if (changedleft==1)
  {
    changedleft=0;
    Serial.print("left  ");
    Serial.println(posleft);
  }
  // right encoder
  if (changedright==1)
  {
    changedright=0;
    Serial.print("right ");
    Serial.println(posright);
  }

}
// left encoder
void Rise0()
{
  if(digitalRead(PIN_INT1))
    posleft++;
  else
    posleft--;
  changedleft=1;
  attachInterrupt(0, Fall0, FALLING);
}
// right encoder
void Rise3()
{
  if(digitalRead(PIN_INT4))
    posright++;
  else
    posright--;
  changedright=1;
  attachInterrupt(3, Fall3, FALLING);
}
// left encoder
void Fall0()
{
  if(digitalRead(PIN_INT1))
    posleft--;
  else
    posleft++;
  changedleft=1;
  attachInterrupt(0, Rise0, RISING);
}
// right encoder
void Fall3()
{
  if(digitalRead(PIN_INT4))
    posright--;
  else
    posright++;
  changedright=1;
  attachInterrupt(3, Rise3, RISING);
}
// left encoder
void Rise1()
{
  if(digitalRead(PIN_INT0))
    posleft--;
  else
    posleft++;
  changedleft=1;
  attachInterrupt(1, Fall1, FALLING);
}

// right encoder
void Rise4()
{
  if(digitalRead(PIN_INT3))
    posright--;
  else
    posright++;
  changedright=1;
  attachInterrupt(4, Fall4, FALLING);
}

// left encoder
void Fall1()
{
  if(digitalRead(PIN_INT0))
    posleft++;
  else
    posleft--;
  changedleft=1;
  attachInterrupt(1, Rise1, RISING);
}

// right encoder
void Fall4()
{
  if(digitalRead(PIN_INT3))
    posright++;
  else
    posright--;
  changedright=1;
  attachInterrupt(4, Rise4, RISING);
}

The code will read both my encoders. when i start spinning the motors faster, the serialprint readout starts to skip numbers. it still count up or down correctly, but may skip 10 or 100 counts. Is this because the max 32 cant detect the pulses or is the serial is too slow to update every pulse?


mikes

Fri, 08 Mar 2013 16:00:34 +0000

It is the Serial that is slow. You may be having multiple interrupts during the send. This may cause errors in the sent data if the number changes in the middle of sending it (I have not looked at the source code for Serial.print) try copying the value to a different variable before sending it so the value won't change in the middle.


alphamike

Tue, 12 Mar 2013 01:15:52 +0000

Okay that makes sense. What could I add to the code to output a speed? Like rpm?


mikes

Tue, 12 Mar 2013 04:09:33 +0000

The easiest way would be to read one of the timers ReadCoreTimer() returns a 32 bit tick timer value increases at a rate of 40MHz for a 80MHz part. micros(); Returns the number of microseconds since the board began running the current program. In the interrupt average several values together and use that value with the number of counts in one rotation to calculate the velocity.

Don't forget to account for rollover when the values exceed the capacity of the variable and go back to zero.

Note: You need #include <plib.h> to use ReadCoreTimer()