Homemade motor encoder works great on Mega, not so well on Teensy3.1

Status
Not open for further replies.

bicycleguy

Well-known member
I have a motor encoder using a single magnet stuck axially on the end of a motor shaft with two properly located MLX92211 hall sensors reading the magfield change to make a very clean debounced quadrature signal. The Arduino mega version of my controller never looses the motor position. The Teensy looses it in a quisi random way, quisi because I don't know why :) Sometimes works for minutes without an error, sometimes hours.

Because of the hardware required to test, I'm not sure posting the code is going to help much.
I have the interrupt related code here:
Code:
#define MYVERSION_STUFF "EncoderTeensy 6/14/2016"

//Names for i/o pins used:
//Interrupt inputs for motors, speed and cadence  switches
const int NCDR_R1 = 17;  //rear Hall MLX92211 requires mag field reversal to switch
const int NCDR_R2 = 16;  //rear "

//hallswitch  motor encoder stuff
volatile int vrencodercnt = 0;

void setup() {
  pinMode(NCDR_R1, INPUT_PULLUP);  //pull up the interrupt pin.  Hall sensor rear motor
  pinMode(NCDR_R2, INPUT_PULLUP);  //pull up the interrupt pin.  Hall sensor rear motor

  attachInterrupt(digitalPinToInterrupt(NCDR_R1), ncdr_R1Int, CHANGE);
  attachInterrupt(digitalPinToInterrupt(NCDR_R2), ncdr_R2Int, CHANGE);
  
  Serial.begin(9600);
  delay(1000);
  
  Serial.println(MYVERSION_STUFF);  //CHANGELOG
  
}

void loop() {
  static int lastcount=-999;
  int thecount=rCount();
  
  if(thecount!=lastcount){  //only print on change
    lastcount=thecount;
    Serial.println(lastcount);
  }
}

void ncdr_R1Int(){
    vrencodercnt+=digitalRead(NCDR_R1)==digitalRead(NCDR_R2) ? -1 : 1;
}

void ncdr_R2Int(){
    vrencodercnt+=digitalRead(NCDR_R1)==digitalRead(NCDR_R2) ? 1 : -1;
}

int rCount(){
    int r;
    // disable interrupts while we read volatile
    noInterrupts();
    r=vrencodercnt;
    interrupts();
    return r;
}

With this code if I spin the motor the encoder will print out. Of course I haven't been able to make this fail :mad: even spinning with a drill.

The complete code does stuff on every pin, here's some of the functions:
Code:
//Names for i/o pins used:
const int GREENBUTTON= 22; // Digital input for button to go down, uses internal pull-up, also outputs for led
const int YELLOWBUTTON = 21;  // Digital input for button to go up, uses internal pull-up, also outputs for led
const int STOPBUTTON = 20;   // Digital input for button to go stop, uses internal pull-up, also outputs for led
const int BUTTON1 = 2;
const int BUTTON5 = 23;
const int MYSCL = 19;  // SCL for TWI/I2C
const int MYSDA = 18;  // SDA for TWI/I2C

//Interrupt inputs for motors, speed and cadence  switches
const int NCDR_R1 = 17;  //rear Hall MLX92211 requires mag field reversal to switch and is twice as sensitve
const int NCDR_R2 = 16;  //rear "
const int NCDR_F1 = 12;  //front "
const int NCDR_F2 = 5;  //front "
const int SPEEDPN = 15;  //reed switch N.O. switched to gnd calls speedInt()
const int CADENCEPIN = 14;  //Hall MLX92212 rqrs a south pole .3 inch. N pole no effect. calls cadenceInt

//motor control outputs
const int OUT1=11;   //phase 1,rear motor
const int OUT2=8;   //phase 2,rear motor 
const int PWM_R=4;  //PWM output, enable rear motor
const int PWM_F=3;  //PWM output, enable front motor
const int OUT3=6;   //phase 1,front motor 
const int OUT4=7;   //phase 2,front motor
const int LCDdisplay=1; //TX1 for Serial1 LCD display
const int RASPBERRYTX = 9;  //for logging to pi
const int RASPBERRYRX = 10;
const int AVAILABLEPIN = 13;  // on board led

Would using pwm, 2 serials, 4 other interrupts and I2C at the same time cause issues with my count?
 
It could cause issues yes.

A good step would be to boost your interrupts so they have the highest priority:
Code:
NVIC_SetPriority(PIOA_IRQn, 0);
NVIC_SetPriority(PIOB_IRQn, 0); //<- Pins 16/17 are on this port
NVIC_SetPriority(PIOC_IRQn, 0);
NVIC_SetPriority(PIOD_IRQn, 0);
NVIC_SetPriority(PIOE_IRQn, 0);
I'm not sure what ports your interrupt triggers are on. So one/two of those are relevant

Then reduce the systick priority from 0 to 32
Code:
SCB_SHPR3 = 0x20200000;  // Systick = priority 32 (defaults to zero)

Now your interrupts should always fire as fast as possible. There is some overhead to determine which pin fired the interrupt but your test code proves this isn't an issue
 
Last edited:
Would using pwm, 2 serials, 4 other interrupts and I2C at the same time cause issues with my count?
If all that stuff is well written and uses hardware devices, there shouldn't be problems. But there is a lot of Arduino stuff that uses bit banging/polling and potentially disables interrupts - which could screw up your code.
A good step would be to boost your interrupts so they have the highest priority:
That will help if interrupts are not disabled for a long time. On Arm Cortex higher priority interrupts can interrupt lower priority ones. It won't help if the cause of the problem is that interrupts are disabled for a long time.
 
Something else to consider is Teensy 3.1 runs *much* faster than Arduino Mega. If your signals have some chatter or noise, you might have to add something like delayMicroseconds(10) in those interrupt functions, before you read the pins to get similar results as you see on Arduino Mega.

If you're only using the internal pullup resistors, perhaps also try adding some real lower value resistors. This is less likely to matter since Teensy and Mega have similar pullups, but lower resistor values can help with noise issues.
 
Solved.

Solved.
Thanks for all for the suggestings. I added some code to one of the interrupts to trigger a scope to investigate:
Code:
void ncdr_R2Int(){
    int r=vrencodercnt;
    
    //delayMicroseconds(5);
    digitalWrite(AVAILABLEPIN, HIGH); //mdr20160620 test
    digitalWrite(AVAILABLEPIN, LOW);
    r+=digitalRead(NCDR_R1)==digitalRead(NCDR_R2) ? 1 : -1;
    
    vrencodercnt=r;
}
P1000721.jpg
Upper trace is the trigger (AVAILABLEPIN), lower is the Hall sensor interrupt. So the voltage is at least 2.6V which should be ok. Adding delay didn't help. The falling edge, not shown, is very steep. Changing the interrupt priority didn't help but may when I enable some of the other stuff. The count did seem to get messed up worse with higher motor accelerations and speed, even though the traces I captured looked the same.

The problem turned out to be messy wiring on my prototype setup. Twisting the digital power, gnd and two hall wires together and separately twisting the motor wires together solved the problem. Duh The final version will also have shielded wires.
 
Status
Not open for further replies.
Back
Top