BLDC Motor Sensorless (BACK EMF)

I have been trying to run this 790kv BLDC motor using back EMF signals for commutation but I have been having issues after startup. It seems like I am getting some sort of misalignment between the actual motor position and where the arduino thinks the motor is. I used code from another forum post (shown below) but yet it seems to get stuck. Here is a video of it:

https://drive.google.com/drive/folders/1KteAuuL87zXT0W5_aSK2Taxkux9g-ZGE?usp=sharing

Here is the schematic it was inspired by:

The actual schematic: (note, some of the capacitor values listed in the schematic are not the same as the ones I actually used)

Code:

 #define PWM_MAX_DUTY      255
#define PWM_MIN_DUTY      50
#define PWM_START_DUTY    100
 
byte bldc_step = 0, motor_speed, pin_state;
 
void setup()
{
  DDRD  |= 0xE0;  // configure pins 5, 6 and 7 as outputs
  PORTD  = 0x00;
  DDRB  |= 0x0E;  // configure pins 9, 10 and 11 as outputs
  PORTB  = 0x31;
  // Timer1 module setting: set clock source to clkI/O / 1 (no prescaling)
  TCCR1A = 0;
  TCCR1B = 0x01;
  // Timer2 module setting: set clock source to clkI/O / 1 (no prescaling)
  TCCR2A = 0;
  TCCR2B = 0x01;
  // ADC module configuration
  ADMUX  = 0x60;   // configure ADC module and select channel 0
  ADCSRA = 0x84;   // enable ADC module with 16 division factor (ADC clock = 1MHz)
 
  PCICR  = EIMSK = 0;  // disable all external interrupts
 
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
}
 
// pin change interrupt 2 (PCINT2) ISR
ISR (PCINT2_vect)
{
  if( (PIND & PCMSK2) != pin_state )
    return;
  // BEMF debounce
  for(byte i = 0; i < 20; i++)
  {
    if(bldc_step & 1){
      if(PIND & PCMSK2)     i -= 1;
    }
    else {
      if(!(PIND & PCMSK2))  i -= 1;
    }
  }
 
  bldc_move();
  bldc_step++;
  bldc_step %= 6;
}
 
// BLDC motor commutation function
void bldc_move()
{
  switch(bldc_step)
  {
    case 0:
      AH_BL();
      BEMF_C_FALLING();
      break;
    case 1:
      AH_CL();
      BEMF_B_RISING();
      break;
    case 2:
      BH_CL();
      BEMF_A_FALLING();
      break;
    case 3:
      BH_AL();
      BEMF_C_RISING();
      break;
    case 4:
      CH_AL();
      BEMF_B_FALLING();
      break;
    case 5:
      CH_BL();
      BEMF_A_RISING();
  }
}
 
void loop()
{
  SET_PWM_DUTY(PWM_START_DUTY);  // setup starting PWM with duty cycle = PWM_START_DUTY
  int i = 5000;
 
  // motor start
  while(i > 100)
  {
    delayMicroseconds(i);
    bldc_move();
    bldc_step++;
    bldc_step %= 6;
    i = i - 20;
  }
 
  motor_speed = PWM_START_DUTY;
  
  PCICR  = 4;  // enable pin change interrupt for pins PCINT23..16 (Arduino 0 to 7)
  
  while(1)
  {
    ADCSRA |= 1 << ADSC;    // start conversion
    while(ADCSRA & 0x40);   // wait for conversion complete
    motor_speed = ADCH;     // read ADC data (8 bits only)
    if(motor_speed < PWM_MIN_DUTY)
      motor_speed = PWM_MIN_DUTY;
    SET_PWM_DUTY(motor_speed);
  }
}
 
void BEMF_A_RISING()
{
  PCMSK2 = 0x04;    // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
  pin_state = 0x04;
}
void BEMF_A_FALLING()
{
  PCMSK2 = 0x04;    // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
  pin_state = 0;
}
void BEMF_B_RISING()
{
  PCMSK2 = 0x08;    // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
  pin_state = 0x08;
}
void BEMF_B_FALLING()
{
  PCMSK2 = 0x08;    // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
  pin_state = 0;
}
void BEMF_C_RISING()
{
  PCMSK2 = 0x10;    // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
  pin_state = 0x10;
}
void BEMF_C_FALLING()
{
  PCMSK2 = 0x10;    // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
  pin_state = 0;
}
 
void AH_BL()
{
  PORTD &= ~0xA0;
  PORTD |=  0x40;
  TCCR1A =  0;      // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
  TCCR2A =  0x81;   //
}
void AH_CL()
{
  PORTD &= ~0xC0;
  PORTD |=  0x20;
  TCCR1A =  0;      // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
  TCCR2A =  0x81;   //
}
void BH_CL()
{
  PORTD &= ~0xC0;
  PORTD |=  0x20;
  TCCR2A =  0;       // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
  TCCR1A =  0x21;    //
}
void BH_AL()
{
  PORTD &= ~0x60;
  PORTD |=  0x80;
  TCCR2A =  0;      // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
  TCCR1A =  0x21;   //
}
void CH_AL()
{
  PORTD &= ~0x60;
  PORTD |=  0x80;
  TCCR2A =  0;       // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
  TCCR1A =  0x81;    //
}
void CH_BL()
{
  PORTD &= ~0xA0;
  PORTD |=  0x40;
  TCCR2A =  0;       // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
  TCCR1A =  0x81;    //
}
 
void SET_PWM_DUTY(byte duty)
{
  OCR1A  = duty;  // set pin 9  PWM duty cycle
  OCR1B  = duty;  // set pin 10 PWM duty cycle
  OCR2A  = duty;  // set pin 11 PWM duty cycle
}

It worked a couple times (out of around 20 attempts) but it is frustrating that it is not working consistently.

I read through all the code and thought this part was incorrect as when bldc_step is odd, the signal would be rising and should be only decrementing when its LOW and vice versa? Not sure though. When I changed this it still did not work (similar behavior as before).

if(bldc_step & 1){
      if ((PIND & PCMSK2))  i -= 1; // Isn't it supposed to be : if (!(PIND & PCMSK2))  i -= 1;
    }
    else {
      if (!(PIND & PCMSK2))  i -= 1; // Isn't it supposed to be : if ((PIND & PCMSK2))  i -= 1;
    }

If the reason is due to a lot of motor noise, what can I do to fix it on a hardware or firmware level? Or is there something else at hand.

I've been working on this for a long time and hope to solve this issue.

Thank you.

NOTE: I have checked if the comparator is damaged by disconnecting PVDD and turning the motor manually. The arduino does receive signals from all 3 phases.

Any special reason for doing all this yourself when ESCs are common and cheap, and have been well understood and reliable for years?
The good ones can give back power when braking, and measure all sorts of parameters.
You only have to feed it with PPM - a form of PWM.
Or you can use DSHOT and get all that data back too, including RPM.

No surprise there, even assuming that the original author had the necessary skills to design and implement the system correctly. Always post links to sources.

@patrick0840
Your schematic seems to be quite different from the original that works.
How do you know your design should even work?

The solution ended up being that there was a lot of noise on the back EMF signals. This was solved using a simple low pass filter.

I suggest commenters to be more encouraging rather than scolding future engineers for not knowing exactly how to do everything right from the get go.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.