I'm using this motor with a hall-effect encoder attached. I've contacted Pololu support and in response to a request for a datasheet to help understand how the sensor output is achieved from an engineering perspective, they only have this to say:
My controller is an Arduino Nano. I'm powering it directly on the 5V pin, bypassing the regulator. This same 5V is powering the encoder.
I'm using pin change interrupts to detect and decode the rotary encoder signals. I have a Raspberry Pi requesting count values over serial at a rate of 0.01s.
I'm wondering if there are conflicts between ATMega328 pin change interrupts and the current implementation of Arduino's Serial library. Specifically anything that would cause the Serial RX/TX to break pin change ISRs.
This code is working, but I experience drift when running for about an hour.
// encoder variables
volatile long enc1Count = 0;
volatile long enc2Count = 0;
volatile int enc1LastState = 0;
volatile int enc2LastState = 0;
const int stateTable[] = { 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0 };
// serial read write variables
const uint8_t buffSize = 3;
char buff[buffSize];
char request = 0;
char device = 0;
//////////////////////////////////////////////////////////////////////////////
// Pin Change Interrupt ISRs
ISR(PCINT0_vect) {
// D8 = PINB0
// D9 = PINB1
volatile int enc1State = (PINB & 1) << 1 | (( PINB >> 1 ) & 1);
volatile int stateCode = (enc1LastState << 2) | enc1State;
volatile int encValue = stateTable[stateCode];
if (encValue) {
enc1LastState = enc1State;
enc1Count += encValue;
}
}
ISR(PCINT1_vect) {
// D14 / ADC0 = PINC0
// D15 / ADC1 = PINC1
volatile int enc2State = (PINC & 1) << 1 | (( PINC >> 1 ) & 1);
volatile int stateCode = (enc2LastState << 2) | enc2State;
volatile int encValue = stateTable[stateCode];
if (encValue) {
enc2LastState = enc2State;
enc2Count += encValue;
}
}
//////////////////////////////////////////////////////////////////////////////
// Serial Communication
void pollSerial() {
if (Serial.available() >= 3) {
uint8_t bytesRead = Serial.readBytesUntil('\n', buff, buffSize);
if (bytesRead == 2) {
request = buff[0];
device = buff[1];
if (request == 'g' && device == '0') {
Serial.print(enc1Count);
Serial.print('|');
Serial.println(enc2Count);
} else if (request == 'g' && device == '1') {
Serial.println(enc1Count);
} else if (request == 'g' && device == '2') {
Serial.println(enc2Count);
} else if (request == 'c' && device == '0') {
enc1Count = 0;
enc2Count = 0;
} else if (request == 'c' && device == '1') {
enc1Count = 0;
} else if (request == 'c' && device == '2') {
enc2Count = 0;
} else {
Serial.println("err!");
}
}
// clear the buffer;
for (int i = 0; i < buffSize; ++i) {
buff[i] = 0;
}
}
}
//////////////////////////////////////////////////////////////////////////////
// Setup
void setup() {
PCICR |= 0b00000011; // enable pin change interrupts 0 and 1
PCMSK0 = 0b00000011; // enable PCI00 (D8, PINB0) & PCI01 (D9, PINB1)
PCMSK1 = 0b00000011; // enable PCI10 (ADC0, PINC0) & PCI11 (ADC1, PINC1)
enc1LastState = (PINB & 1) << 1 | (( PINB >> 1 ) & 1); // PINB0 = D8, PINB1 = D9
enc2LastState = (PINC & 1) << 1 | (( PINC >> 1 ) & 1); // PINC0 = D14/ADC0, PINC1 = D15/ADC1
Serial.setTimeout(1);
Serial.begin(115200); // start serial for output for debugging
}
//////////////////////////////////////////////////////////////////////////////
// Loop
void loop() {
pollSerial();
}
Should I limit the frequency that the serial input is polled? I'm really at a loss. This tracks encoder counts well enough for me to be fooled, but overtime, drifts, leading me to believe that I'm missing something either in:
- the wiring of the encoder to the Arduino (something that might introduce interference)
- the timing of the ISRs in relation to reading values off the PIN registers
- the table based lookup used for detecting and responding to only valid encoder pin states
- the timing of the Serial
- any potential conflicts between the Serial library implementation and the pin change interrupt vectors I'm utilizing.
Any help would be wonderful. Thank you!