element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Achievement Levels
    • Benefits of Membership
    • Feedback and Support
    • Members Area
    • Personal Blogs
    • What's New on element14
  • Learn
    Learn
    • eBooks
    • Learning Center
    • Learning Groups
    • STEM Academy
    • Webinars, Training and Events
  • Technologies
    Technologies
    • 3D Printing
    • Experts & Guidance
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Arduino Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Project Groups
    • Raspberry Pi Projects
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Or choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
Personal Blogs
  • Members
  • More
Personal Blogs
Legacy Personal Blogs An arduino controlled brushless ESC
  • Blog
  • Documents
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: strb
  • Date Created: 2 May 2018 10:02 AM Date Created
  • Views 878 views
  • Likes 5 likes
  • Comments 2 comments
  • esc
  • ardexpert
  • brushless motor
  • openarduinoch
Related
Recommended

An arduino controlled brushless ESC

strb
strb
2 May 2018
image

Open Arduino

Enter Your Project for a chance to win a grand prize for the most innovative use of Arduino or a $200 shopping cart! The Birthday Special: Arduino Projects for Arduino Day!

Back to The Project14 homepage image

Project14 Home
Monthly Themes
Monthly Theme Poll

 

The goal:
The goal of this project is to make an ESC (electronic speed controller) using an arduino as brain and a self made board as output stage, I also decided to not use any commercial available motor driver because I wanted to learn how a brushless motor works and how I can control it (without using sensors).

The learning process:
Internet is full of video/application notes about this topic, but for the very basic understanding on how a ESC works I found these two video very helpful:
https://www.youtube.com/watch?v=W9IHEqlGG1s
https://www.youtube.com/watch?v=8LXPcJD6hEA
This application notes was very helpful too: http://ww1.microchip.com/downloads/en/AppNotes/00857B.pdf
At the end, I made a bit of reverse engineering with an old ESC.

Let's build the output stage:
Arduino has a current limit of about 20mA for each pin, so it can not power up a motor directly, hence I designed a small board to drive the motor. The board is very simple, and to make all even easier I decided to start with a small motor from a CD player and only 3.3V as power supply (so I didn't need to bootstrap the signal for the upper portion of the board and arduino can read it). The result was this:
image

Since a while ago I made a cnc engraver (with you guessed it, an arduino), I decided to make the board with this machine to test it, after a couple attempts I managed to get my board....


....and then I soldered all the components.


Time to code
This part was the most time consuming  so I will not explain all the iterations that I made to the code (I made 34 different versions of it), instead I will focus on the basic principle, which (in teory) is very simple: you have to power up motor's coils in a certain order so the rotor will follow the rotating magnetic field created by the windings, the problem is that without sensors you don't know the rotor angular position, so you have to estimate it using the bemf (back electromotive force) generated by a floating phase (if you don't know what I mean watch the video linked at the beginning of this post image ).

After a lot of troubleshooting I finally managed to obtain my first functioning code:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image


From this I continued to improve the code by setting up faster ADC readings (and optimizing when to do this readings), adding acceleration control and a crude brake and also by implementing the control using a standard RC remote controller, the code that I obtained is this:

imageimageimage

/*board pinout:
 *   bjt1   bjt3   bjt5
 *    4      6      8       R-R-R
 *    
 *   bjt2   bjt4   bjt6
 *    5      7      9
 * (leg 1) (leg 2) (leg 3)
 * phase sequence: b1b4 b1b6 b3b6 b3b2 b5b2 b5b4 ok
 * bemf reading: a1 a2 a3
 * motor virtual neutral  point: a4
 * interrupt pin 2, 3 for remote controller
 */

const byte mask=B01000000;//mask for start of conversion & state of conversion
const byte deadvalue=30;//if under this value deactivate all
const unsigned int minimum=950;//min on period for receiver signal (us)
const unsigned int maximum=2130;//max on period for receiver signal (us)
const unsigned int timelimit=10000;
const byte switchpoint=100;//39%
const byte addhigh=1;//increment after switch point
const byte addlow=15;//increment before switch point

unsigned long t;//time variable
unsigned int d1;//tempo impulso
byte dmax;
byte soglia;
byte fase=0;//phase state
volatile boolean input=0;//if 1->received a new pulse from the receiver
boolean brake=0;//brake flag
boolean high=0;//phase active flag
byte neutral=80;//neutral point value
byte emread=0;//floating phase reading
boolean delta=0;//1=negative, 0=positive, if the two deltas are different I've crossed the neutral point
boolean lastdelta=1;
byte datacomp=0;

void setup() {
  DDRD= (0<<ddd2)|(0<<ddd3)|(1<<ddd4)|(1<<ddd5)|(1<<ddd6)|(1<<ddd7); define="" pin="" 4...7="" as="" output,="" 2="" 3="" input="" interrupt<br="">  DDRB= (1<<ddb0)|(1<<ddb1); pin="" 8,9="" as="" output<br="">  EICRA|=(1<<isc01)|(1<<isc00); int0="" (pin2)="" rising<br="">  EICRA|=(1<<isc11); int1="" (pin3)="" falling<br="">  
  ADMUX=B01100000;//I'm using input A0,result is left adjusted
  DIDR0=0b00111111;//shut off digital input for A0...A5
  ADCSRA=B11000011;//I'm using 8 as division factor,with 4 I obtain quite noisy measurements
  //adc in free running
  setA0();
  startofconversion();//calibration conversion

  

  for(unsigned int i=0;i<300;i++){//let's make some sound with phase 6
    PORTD=(1<<pd7);
    PORTB=(1<<pb0);
    delayMicroseconds(150);
    PORTD=(0<<pd7);
    PORTB=(0<<pb0);
    delayMicroseconds(350);
  }
  delay(100);
  for(unsigned int i=0;i<300;i++){
    PORTD=(1<<pd7);
    PORTB=(1<<pb0);
    delayMicroseconds(150);
    PORTD=(0<<pd7);
    PORTB=(0<<pb0);
    delayMicroseconds(350);
  }
  delay(300);
  for(unsigned int i=0;i<400;i++){
    PORTD=(1<<pd7);
    PORTB=(1<<pb0);
    delayMicroseconds(100);
    PORTD=(0<<pd7);
    PORTB=(0<<pb0);
    delayMicroseconds(330);
  }
  PORTD=(0<<pd4)|(0<<pd5)|(0<<pd6)|(0<<pd7); all="" output="" to="" zero<br="">  PORTB=(0<<pb0)|(0<<pb1); <br="">
  //timer2 setup
  TCCR2A=(1<<wgm21)|(1<<wgm20); fast="" pwm<br="">  TCCR2B=(1<<cs21); divider="" a="" 8<br="">  TIMSK2=(1<<ocie2b)|(1<<toie2); enable="" flags="" for="" interrupt<br="">  OCR2B=0;//0%DC

  EIMSK|=(1<<int1)|(1<<int0); interrupt="" enable="" int0="" int1<br="">  //Serial.begin(250000);//debug
}

void loop() {
  if(input==1 & d1<1600){//assign DC (duty cicle)
    dmax=(1600-d1)/2.6;
    brake=0;
  }
  else{
    if(d1>1700){
      dmax=(d1-1700)/2;
      brake=1;
    }
  }
  //acceleration control
  soglia=OCR2B;
  if(sogliatimelimit){//da 0 a 255 in 2 secondi ca con timelimit 10000
    t=micros();
    if(soglia<switchpoint){
      OCR2B+=addlow;
    }
    else{
      OCR2B+=addhigh;
    }
  }
  else{
    if(dmax<soglia)
      OCR2B=dmax;//deceleration
  }
  if(input==1 & high){//check motor neutral point only every 20 ms (receiver frequency at 50Hz)
    setA4();
    startofconversion();
    neutral=waitandget();
    //Serial.println(neutral);//debug
    input=0;
  }

  if(high){//lset reading for bemf on the floating phase
    switch(fase){
      case 1:
        setA3();
        startofconversion();
        break;

      case 2:
        setA2();
        startofconversion();
        break;

      case 3:
        setA1();
        startofconversion();
        break;

      case 4:
        setA3();
        startofconversion();
        break;

      case 5:
        setA2();
        startofconversion();
        break;

      case 6:
        setA1();
        startofconversion();
        break;
    }//fine switch
    emread=waitandget();//get the data
    if(emread-neutral>0){
      delta=0;
    }
    else{
      delta=1;
    }
    //Serial.println(fase);//debug
  }//fine if high

  //cli();//disable all interrupts
  if(lastdelta==0 & delta==1){//check "zero cross"
      lastdelta=1;
      fase++;
    }

    if(lastdelta==1 & delta==0){//check "zero cross"
      lastdelta=0;
      fase++;
    }
    
    if(fase>6)
      fase=1;
    //sei();//enable all interrupts
}

//------------------------------ADC's functtions--------------
void startofconversion()
{
  ADCSRA=ADCSRA|mask;//let's start a conversion!
}

byte waitandget()
{
  while((ADCSRA&mask)==mask)//wait
  {
  }

  return ADCH;
}

void setA0(){
  ADMUX=0b01100000;
}

void setA1(){
  ADMUX=B01100001;
}

void setA2(){
  ADMUX=0b01100010;
}

void setA3(){
  ADMUX=0b01100011;
}

void setA4(){
  ADMUX=0b01100100;
}

//---------------------------ISR (interrupt service routine) for input signal-------------------------
ISR(INT0_vect){//void up(){
  t=micros();
}

ISR(INT1_vect){//void down(){
  t=micros()-t;
  d1=t;
  if(d1>minimum & d1<maximum){
    input=1;//set the flag, input signal received
  }
}

//-----------------------------ISR timer2 pwm-----------------------------
ISR(TIMER2_COMPB_vect){
  PORTD=(0<<pd4)|(0<<pd5)|(0<<pd6)|(0<<pd7); all="" output="" to="" zero<br="">  PORTB=(0<<pb0)|(0<<pb1); <br="">  high=0;//flag: all phases are shut off
 // Serial.println(OCR2B);
}

ISR(TIMER2_OVF_vect){//
  if(brake){//
    PORTD=(1<<pd5);
  }
  else{//accelerator routine
    if(OCR2B>deadvalue){
    switch(fase){
      case 1:
        PORTD=(1<<pd4)|(1<<pd7);
        break;

      case 2:
        PORTD=(1<<pd4);
        PORTB=(1<<pb1);
        break;

      case 3:
        PORTD=(1<<pd6);
        PORTB=(1<<pb1);
        break;

      case 4:
        PORTD=(1<<pd6)|(1<<pd5);
        break;

      case 5:
        PORTB=(1<<pb0);
        PORTD=(1<<pd5);
        break;

      case 6:
        PORTB=(1<<pb0);
        PORTD=(1<<pd7);
        break;
    }
    high=1;//flag: phases are active
  }//fine if deadvalue
  }
}



Further improvements:
Now that I have a working prototipe I can improve it, first of all I'm working on a beefier board that can handle more that 3.3V (and also more current than the previous board), I'm also working on the "position tracking" of the rotor, because one adc reading take up "a lot" of time, so the next iteration is to make a circuit that give to arduino a digital signal to read (much faster and reliable compared to an "analog read"), basically it is an array of three comparator with a level shift.
On the software side I want to add a better brake/acceleration control, the "fail safe" feature, the possibility to modify/ store the settings in the EEPROM and so on... basically I want to obtain a fully functioning ESC to test on myRC car image.

At the moment I haven't the time to complete the project as I should do (it's quite a time consuming project and I have to study), but the version 2 of the output stage is almost ready image



Have a nice day! imageimageimage

  • Sign in to reply
  • strb
    strb over 5 years ago in reply to moinocencio

    Hi,
    at the moment I've only tested the "first prototype" (the small circuit that you see in the first photo) and it has not any comparator, to generate the motor neutral point I've used 3 resistors in Y configuration, one resistor connected to every phase and the central point is your reference, and this worked just fine.
    If you are concerned about spurious commutation of the comparators this shouldn't be an issue because you can add a small hysteresis to the comparators (but be aware that this will modify the commutation point between two phases) or you can manage it by code (for example by reading only the correct comparator at the "correct time" and ignoring the rest->spurious commutation will not be received) .

    I hope this will help you, if there is something else that you haven't understand (or I explained it badly) just let me know, I will do my best to clarify any dubt.

    PS: the code that I posted doesn't seem to be displayed corrrectly to me, someone has the same problem?

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • moinocencio
    moinocencio over 5 years ago

    Great project!

     

    I'm currently making a similar project to present on a course unit, but instead of Arduino, we're using a Max32 ChipKit from Digilent.

     

    Have you had any problem generating the zero point for the comparators? That's the thing i'm most concerned about.

     

    Keep up the great work!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2023 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube