Reading BLDC Hall Sensors using interrupts

Status
Not open for further replies.

jensa

Well-known member
Hi all,
It's been a while since I've posted on this forum, but I need some advice on a Teensy project I'm doing. I have a Brushless motor with 3 Hall sensors. The motor has 7 pole pairs that each are being read by the Hall sensors at 5000 rpm. Controlling the motor is done using a separate motor controller, but I need to convert the Hall Sensor input into an A/B quadrature Encoder signal for another device. My first idea was using the Encoder library for Teensy, but that only supports two inputs. I then figured that I'll do a simplistic approach using Interrupts and I came up with the code below.

It works quite well. I can get direction and position out, but even at moderate speeds, I'm loosing some "steps" so the position on the motor controller will differ from the one on the Teensy. I could of course throw a Teensy 4.0 at it and hope that the extra speed solves the issue, but I can't help but think that there are others that have done similar things and come up with a better solution? I'd love to hear how others have solved similar problems.

Code:
#define HALLPIN1 10
#define HALLPIN2 11
#define HALLPIN3 12

volatile unsigned int lastPin = 0;
volatile signed int dir = 0;
signed int dirDisplay = 0;
volatile signed long pos = 0;
volatile signed long lastChange = 0;
signed long prevPos = 0;

void setup() {
  pinMode(HALLPIN1, INPUT);
  pinMode(HALLPIN2, INPUT);
  pinMode(HALLPIN3, INPUT);
  
  attachInterrupt(digitalPinToInterrupt( HALLPIN1 ), hall1, CHANGE);
  attachInterrupt(digitalPinToInterrupt( HALLPIN2 ), hall2, CHANGE);
  attachInterrupt(digitalPinToInterrupt( HALLPIN3 ), hall3, CHANGE);
}

void loop() {
  // nada
}

void hall1() {
  if( lastPin == HALLPIN3 )
  {
    dir = -1; // backward
  }
  else if( lastPin == HALLPIN2 )
  {
    dir = 1; // forward
  }
  pos += dir;
  lastPin = HALLPIN1;
}

void hall2() {
  if( lastPin == HALLPIN1 )
  {
    dir = -1;
  }
  else if( lastPin == HALLPIN3 )
  {
    dir = 1;
  }
  pos += dir;
  lastPin = HALLPIN2;
}

void hall3() {
  if( lastPin == HALLPIN2 )
  {
    dir = -1;
  }
  else if( lastPin == HALLPIN1 )
  {
    dir = 1;
  }
  pos += dir;
  lastPin = HALLPIN3;
}
 
Last edited:
Your sample code doesn't show anything using the dir and pos values. I think it is that missing code that is your problem. The default priority for attachInterrupt is 128, in the middle of the priority range. Other things such as the systick, USB, SDIO and timer drivers may have higher priority and be blocking your pin interrupts long enough to cause problems. At 2400 RPM (40 RPS) , you are getting 3 interrupts every 25 milliseconds. If equally spaced, that is an interrupt each 8.3mSec.

You can search the forum for "attachInterrupt priority" and find some information on how to increase the priority of your pin interrupts. That might solve your problem with missed pulses.
 
Thanks @mbogerson! Those posts was quite a dense read, but from what I gathered it boils down to adding this one line to the setup-method (given that the pins I use are on the C-port:

Code:
NVIC_SET_PRIORITY(IRQ_PORTC, 0);

Using 0 gave the better results, but it was not perfect still. I initially tried using priority 48 on my Teensy 3.2 and that was practically no difference. A friend suggested using the timers, so I'll look into that next.
 
The solution became to use the standard Encoder class for Teensy and just ignore one of the three phases in the Hall sensor signal. This looses precision, but solves the problem elegantly as it produces the A&B signals I need. If the result is multiplied with 1.5, I'll get the same count as the Epos4 motor driver also and it's 100% identical to the Teensy :)
 
Status
Not open for further replies.
Back
Top