Encoder Pin IO_E2 on A Star 32U4 Board?

Hello,
I’m trying to build a larger version of the Balboa 32U4 balancer by reusing some of the Balboa Balancer code on a Pololu 32U4 A Star board. I have the following question:

The Balboa32U4Encoders.cpp library makes reference to a pin called IO_E2. I can’t seem to find this one on the schematic, so I’m at a loss for how to hook up my encoders to the board I ordered. Can you help please? Here’s the code segment from the library:

#define LEFT_XOR   8
#define LEFT_B     IO_E2
#define RIGHT_XOR  7
#define RIGHT_B    23

I’ve already got the Balboa Kit up and running with an Adafruit Bluetooth LE card so I can drive it around with my phone. There’s a short video of the project on my instagram account. Now I’m on the hook for making a big one with larger motors and big wheels.

Any help would be appreciated, Thank you in advance.

Hello @timalot,

IO_E2 corresponds to AVR pin PE2 on the A-Star 32U4. You can find this information on the README of the pololu/fastgpio-arduino library under the “Specifying a pin” section. A copy of this library is included in the pololu/a-star-32u4-arduino-library. I believe this AVR pin is not exposed on an arduino 32U4 (micro/Leonardo) hence it doesn’t have an Arduino “digital pin” number.

Note that the Pololu boards for the Zumo, Romi and Balboa use XOR (exclusive OR) gates which combine the encoders pins A and B to use only one interrupt per side. I you plan on using the A-Star micro controller, you will either have to use XOR gates or use two interrupts for each side (pins A & B). See this forum thread for more details.

I made my own encoder library for my Romi based robot which uses the A-Star 32U4 Robot Controller with Raspberry Pi Bridge. You can find the latest encoder class files in the src folder of my DrGFreeman/RasPiBot202V2 repository. It should work fine as-is for your project if you can free pins 0, 7, 8 and 16 for the encoders. Feel free to try it and ask questions if you have any.

Hope this helps and good luck with your project!

1 Like

Wow, Thanks!

There’s a lot of good information here and I appreciate the thoroughness of your response. I’ve ordered an A-Star 32U4 which does have the PE2 pin, however the XOR issue makes it a bit more difficult. I’ll give your library a try once my board arrives.

Thanks again for the assistance. I’ll post back with the results so that others may benefit as well.
–Tim

1 Like

Hello, Dr Freeman,
I’ve had some time over the last week to test all the subsystems (Acc, Gyro, SPI interface, Motor Controller, and Encoders) Now I’m doing the integration and running into problems moving the pins around in the AStarEncoders.cpp library. I needed to free up the SPI pins so I modified the code as follows. I get zero’s for both Left and Right counts when running the example sketch. Would you mind having a look to see if I’m missing anything obvious please?
Thank you for the time.
–Tim

#include <AStarEncoders.h>

// Encoder pins
// !!! Update ISRs and constructor below if changing these pins !!!
// Left A: INT1, PD1, digital pin 2, PIND & 0b00000010 (0X02)
const byte LEFT_A = 2;

// Left B: INT0, PD0, digital pin 3, PIND & 0b00000001 (0x01)
const byte LEFT_B = 3;

// Right A: INT6, PE6, digital pin 7, PINE & 0b01000000 (0x40)
const byte RIGHT_A = 7;

// Right B: PCINT4, PB4, digital pin 8, PINB & 0b00010000 (0x10)
const byte RIGHT_B = 8;

// Last encoder readings
static volatile bool lastLeftA;
static volatile bool lastLeftB;
static volatile bool lastRightA;
static volatile bool lastRightB;

volatile uint16_t leftCount;
volatile uint16_t rightCount;

// ISRs
// ISR for left encoder pins A & B (INT1, INT0)
ISR(PCINT0_vect)
{
  bool newLeftA = PIND & 0x02; // PIND bit 1
  bool newLeftB = PIND & 0x01; // PIND bit 0

  leftCount += (newLeftA ^ lastLeftB) - (lastLeftA ^ newLeftB);

  lastLeftA = newLeftA;
  lastLeftB = newLeftB;
}

//ISR for right encoder pins A & B (PCINT7, PCINT4)
static void rightISR()
{
  bool newRightA = PINE & 0x40; // PINE bit 6
  bool newRightB = PINB & 0x10; // PINB bit 4

  rightCount += (newRightA ^ lastRightB) - (lastRightA ^ newRightB);

  lastRightA = newRightA;
  lastRightB = newRightB;
}

// Constructor
AStarEncoders::AStarEncoders()
{
  // Set encoder pins mode
  pinMode(LEFT_A, INPUT_PULLUP);
  pinMode(LEFT_B, INPUT_PULLUP);
  pinMode(RIGHT_A, INPUT_PULLUP);
  pinMode(RIGHT_B, INPUT_PULLUP);

  // Enable PCINT1 & PCINT2; set 1 in PCMSK0 register bits 1 & 2
  // PCMSK0 |= (1 << PCINT1);
  // PCMSK0 |= (1 << PCINT2);
  PCMSK0 |= (1 << INT1);
  PCMSK0 |= (1 << INT0);


  // Enable pin change interrupts; set 1 in PCICR register bit 1 (PCIE0)
  PCICR |= (1 << PCIE0);

  // Clear pin change interrupt flag; set 1 in PCIFR register bit 1 (PCIFR)
  PCIFR |= (1 << PCIF0);

  // Attach interrupts for right encoder
  attachInterrupt(digitalPinToInterrupt(RIGHT_A), rightISR, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RIGHT_B), rightISR, CHANGE);

  // Initialize encoders variables
  lastLeftA = PIND & 0x02;
  lastLeftB = PIND & 0x01;
  lastRightA = PINE & 0x40;
  lastRightB = PINB & 0x10;
  leftCount = 0;
  rightCount = 0;

  flipDirection(false, false);
}

// Flip count directions
void AStarEncoders::flipDirection(bool left, bool right)
{
  if (left)
  {
    // Flip left encoder directions
    _flipLeft = true;
  }
  else
  {
    _flipLeft = false;
  }
  if (right)
  {
    // Flip right encoder directions
    _flipRight = true;
  }
  else
  {
    _flipRight = false;
  }
}

// Get left counts
int AStarEncoders::getCountsLeft()
{
  cli();
  int counts = leftCount;
  sei();

  if (_flipLeft)
  {
    counts *= -1;
  }

  return counts;
}

// Get right counts
int AStarEncoders::getCountsRight()
{
  cli();
  int counts = rightCount;
  sei();

  if (_flipRight)
  {
    counts *= -1;
  }

  return counts;
}

// Get left counts and reset left counts
int AStarEncoders::getCountsLeftAndReset()
{
  cli();
  int counts = leftCount;
  leftCount = 0;
  sei();

  if (_flipLeft)
  {
    counts *= -1;
  }

  return counts;
}

// Get right counts and reset right counts
int AStarEncoders::getCountsRightAndReset()
{
  cli();
  int counts = rightCount;
  rightCount = 0;
  sei();

  if (_flipRight)
  {
    counts *= -1;
  }

  return counts;
}

Hi Tim,

Looks like you figured out the ATmega pin numbers and corresponding hex codes properly. The code you updated inside the Interrupt Service Routines (ISR) also looks OK. What is missing in your code is “attaching” the ISRs to the right* interrupts so the right* one fires when each of the encoder pins change. (* right here means “appropriate”, not the opposite of left…)

For your left encoder, you are now using external interrupts INT0 and INT1 so you cannot use ISR(PCINT0_vect) which fires when enabled pin change interupts (PCINT0-7) change state. Instead, you need to create a new ISR function:

static void leftISR() {
  // Code for left encoder interrupt handling here
}

and attach it to the pins corresponding to INT0 and INT1 in the constructor:

attachInterrupt(digitalPinToInterrupt(LEFT_A), leftISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(LEFT_B), leftISR, CHANGE);

You can also get rid of the two following lines in the constructor as the lines above already take care of it.

PCMSK0 |= (1 << INT1);
PCMSK0 |= (1 << INT0);

For your right encoder, it is a bit more complicated since you are using both an external interrupt (INT6) and a pin change interrupt (PCINT4). For this reason, you will need two ISRs to handle the different types of interrupts. Both will use the same code inside the ISR body.

To handle changes on INT6 (right pin A), you can keep the rightISR() function as-is. In the constructor, you can remove the following line as you will handle the changes on your right pin B differentlly:

attachInterrupt(digitalPinToInterrupt(RIGHT_B), rightISR, CHANGE);

To handle changes on PCINT4, you will need to use the ISR defined by ISR(PCINT0_vect) which was previously used to handle pin changes for the left encoder. Simply replace the code in this function by the same code that is in rightISR().

Finally, in the constructor you need to enable PCINT4 by setting the corresponding bit to 1 in the PCMSK0 register:

PCMSK0 |= (1 << PCINT4);

Hopefully that should get it working. I couldn’t try it out as I would have to re-wire my robot. If you still run into issues, let me know and I will try to help!

Looking forward to see the results of your work! Make sure to post in the “Share you project” section of the forum when you have it working.

Dr. Feeman,
Thank you for the prompt reply. As you were composing, I was searching and found a post alluding to the difference in the PCINT and the INT by itself. I rewired, and recoded as follows with a positive result. Thank you for the kind assistance.

// Encoder pins
// !!! Update ISRs and constructor below if changing these pins !!!
// Left A: PCINT4, PB4, digital pin 8,
// PINB & 0b00010000 (0x10)
const byte LEFT_A = 8;

// Left B: PCINT5, PB5, digital pin 9,
// PINB & 0b00100000 (0x20)
const byte LEFT_B = 9;

// Right A: INT1, PD1, digital pin 2,
// PIND & 0b00000010 (0x02)
const byte RIGHT_A = 2;

// Right B: INT0, PD0, digital pin 3,
// PIND & 0b00000001 (0x01)
const byte RIGHT_B = 3;

Now I’m on to completing the integration as all of the subsystems are functional now.

Thank you again for the assistance.

–Tim

1 Like