0

Driving a BLDC (brushless DC motor from harddisk)
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Driving a BLDC (brushless DC motor from harddisk)

by funkycoder on Thu Jun 18, 2009 9:48 pm

Since some harddisks died on me I accumulated the BLDCs that drive the disks.

They have four connectors, without labels. Every pin seems to be connected to every other via a small resistance (some ohms), although people helped me identify the GND port by its slightly lower resistance to the other ports.

It seems the BLDC now needs three phase shifted sine waves to run smoothly.

How can I generate those with the motor shield I have in my hands?
Is there a way to use it simply via the D/A converters, so I can generate the waveforms manually and just have the motor shield boost the current?

And I need to know the current angle (of the driving current, not the actual motor), since I hope to avoid additional rotary encoders that way...

And, although off topic: since I only need to drive the BLDC, there should be free D/A pins available, right? How can I use them for other, non motor-shieldish purposes? (i.e. driving LEDs. Yes I'm building a POV hard-disk display)

thx, -- pascal
funkycoder
 
Posts: 2
Joined: Thu Jun 18, 2009 9:33 pm

Re: Driving a BLDC (brushless DC motor from harddisk)

by adafruit on Thu Jun 18, 2009 10:11 pm

er...i dont know that the motor shield can do what you want...i've never run those sorts of motors and didn't consider them for the shield
sorry!

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm
Location: nyc

Re: Driving a BLDC (brushless DC motor from harddisk)

by funkycoder on Thu Jun 18, 2009 10:51 pm

Ok, thanks for the quick answer. (Now I've got a nice first circuit project on my hands, hehe)

On http://en.wikipedia.org/wiki/Brushless_DC_electric_motor I saw that my motors come in the "wye winding" style (with the center connection being GND), but of course one can also use them in "delta mode" by just ignoring the center. Also, given good timing, driving them as a stepper should work (which will need a sensor though).

So new question: Does the motor shield support "3 port" steppers in addition to "4 port" as shown in the manual?

(and I will dig some into the arduino library -- is it possible to destroy the shield and/or arduino just by giving bad commands to the shield? without a connected motor of course, just a scope for debugging)
funkycoder
 
Posts: 2
Joined: Thu Jun 18, 2009 9:33 pm

Re: Driving a BLDC (brushless DC motor from harddisk)

by adafruit on Fri Jun 19, 2009 12:18 pm

do you have any information on what a 3 port stepper is
most steppers are either unipolar (5 or 6 wires) or bipolar (4 wires)

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm
Location: nyc

Re: Driving a BLDC (brushless DC motor from harddisk)

by karlgg on Fri Jun 19, 2009 1:05 pm

I was JUST going through one of my older microcontroller books (is 1982 considered old?) and looking at a description of using a three-phase stepper. Sorta creepy...

Basically (to paraphrase Caxton C. Foster) he shows energizing the second coil before turning off the first, then energizing the third coil before turning off the second. Etcetera and so forth... In general, it seems pretty simple. ABCABC will go forward, CBACBA will do for reverse. No H-bridges or anything.
I think I am, therefore I am... I think.

karlgg
 
Posts: 212
Joined: Sat Dec 27, 2008 2:41 pm
Location: Anthony, NM

Re: Driving a BLDC (brushless DC motor from harddisk)

by karlgg on Fri Jun 19, 2009 1:10 pm

It's Caxton C. Foster's "Real Time Programming - Neglected Topics" under the (model) Lunar Lander chapter, in case anybody else has (or wants) the book. Seemed rare, last time I looked for another copy.
I think I am, therefore I am... I think.

karlgg
 
Posts: 212
Joined: Sat Dec 27, 2008 2:41 pm
Location: Anthony, NM

Re: Driving a BLDC (brushless DC motor from harddisk)

by babblefish on Fri Aug 14, 2009 4:22 am

New member here...responding to an old post. :)

It would probably save you a lot of programming if you just use an ESC (Electronic Speed Controller) intended for use in RC (Radio Control) applications. It has the same 3-wire connector as a servo and accepts the same PWM signals. An ESC intended for aircraft use uses a 100ms pulse to stop the motor and 200ms is full speed. If you need the motor to go forward as well as reverse, then you'll need an ESC intended for car/boat use because most (but not all) of them have reverse. In this case, 150ms tells the motor to stop while 100ms causes the motor to spin in one direction and 200ms the other with fully proportional speed control going in either direction. The output from the ESC is three wires that connect directly to the wires coming from a brushless motor. All ESCs' are rated by how many output amps it can handle. This can range from 2A to well over 100A. Power for the motor (either a PS or battery) is plugged directly into the ESC. One thing to watch out for is that most ESCs' have a built-in BEC (Battery Eliminator Circuit) which is meant to supply 5 volts to an RC receiver. You can elect to keep this functional in which case the BEC will now supply +5V power to the motor shield (and the Arduino board?), but if you do, don't plug-in another power source or the two may conflict with each other. The BEC can be disabled by simply disconnecting the RED wire from the 3-pin connector (from the ESC). Hope this helps someone. :)
babblefish
 
Posts: 9
Joined: Fri Aug 14, 2009 3:53 am

Re: Driving a BLDC (brushless DC motor from harddisk)

by cbmalloch on Sat Dec 12, 2009 11:17 pm

There is quite a good tutorial on steppers which includes the page at http://www.cs.uiowa.edu/~jones/step/types.html. Scroll down to figure 1.5 in the Multiphase Motors section, where the center diagram shows a wye configuration (although of a 5-phase rather than a 3-phase motor).
Note that the unipolor motor looks a lot like a wye-configured 4-phase motor when run in SINGLE mode. The three-phase motor will have one center tap and three other "coil" wires. As I understand it, the three 120-degrees-out-of-phase sine waves applied at the three coil wires looks a lot like microstepping.
I just built the motor shield and read the Jones tutorial and got my first (unipolar) motor running. I also just took apart a Zip drive and found the pancake drive motor to be just such a wye-configured 3-phase motor. I'd bet we could drive these guys (although roughly and probably slowly) just using the equivalents of SINGLE (A,B,C,A,B,C) or INTERLEAVE (A,A+B,B,B+C,C,C+A,A...).
I think you could do that using the motor shield and four of the five connections on one side of the board. However, the motor shield is set up with latches, and you can't just directly drive the outputs. It will take an extension to AFMotor to do this properly.
For my own purposes, I doubt I'll go beyond hacking an extension without the microstepping, unless and until I get motivated enough to read through the documentation on the PWM subsystem.

cbm
Last edited by cbmalloch on Sun Dec 13, 2009 10:18 am, edited 1 time in total.

cbmalloch
 
Posts: 8
Joined: Sat Dec 12, 2009 10:52 pm

Re: Driving a BLDC (brushless DC motor from harddisk)

by cbmalloch on Sun Dec 13, 2009 9:40 pm

OK. I went ahead and did it. Started with AFMotor and ripped it apart and put it back together again to do a 3-phase motor. Seems to work OK at least with LEDs. Since each phase needs its own PWM control, I had to use two timers -- each timer can control two different duty cycles independently. But each timer controls one stepper block, so I had to use both stepper blocks - labeled 1, 2, and 4.
Here's the code for cbm_AFMotor.h

Code: Select all | TOGGLE FULL SIZE

// Adafruit Motor shield library
// copyright Adafruit Industries LLC, 2009
// this code is public domain, enjoy!

// with help from cbm

#ifndef _AFMotor_h_
#define _AFMotor_h_

#include <inttypes.h>
#include <avr/io.h>

// comment out this line to remove microstepping support
// for smaller library. Be sure to delete the old library objects!
#define MICROSTEPPING 1

#ifdef MICROSTEPPING
#define MICROSTEP 8
#endif

#define MOTOR12_64KHZ _BV(CS20)  // no prescale
#define MOTOR12_8KHZ _BV(CS21)   // divide by 8
#define MOTOR12_2KHZ _BV(CS21) | _BV(CS20) // divide by 32
#define MOTOR12_1KHZ _BV(CS22)  // divide by 64

#define MOTOR34_64KHZ _BV(CS00)  // no prescale
#define MOTOR34_8KHZ _BV(CS01)   // divide by 8
#define MOTOR34_1KHZ _BV(CS01) | _BV(CS00)  // divide by 64

#define MOTOR1_A 2
#define MOTOR1_B 3
#define MOTOR2_A 1
#define MOTOR2_B 4
#define MOTOR4_A 0
#define MOTOR4_B 6
#define MOTOR3_A 5
#define MOTOR3_B 7

#define FORWARD 1
#define BACKWARD 2
#define BRAKE 3
#define RELEASE 4

#define SINGLE 1
#define DOUBLE 2
#define INTERLEAVE 3


/*
#define LATCH 4
#define LATCH_DDR DDRB
#define LATCH_PORT PORTB

#define CLK_PORT PORTD
#define CLK_DDR DDRD
#define CLK 4

#define ENABLE_PORT PORTD
#define ENABLE_DDR DDRD
#define ENABLE 7

#define SER 0
#define SER_DDR DDRB
#define SER_PORT PORTB
*/

// Arduino pin names
#define MOTORLATCH 12
#define MOTORCLK 4
#define MOTORENABLE 7
#define MOTORDATA 8

#define MICROSTEPS3 16  // 8, 16 & 32 are popular
#define HALFSTEP MICROSTEPS3 / 2

class AFMotorController {
  public:
    AFMotorController(void);
    void enable(void);
    void latch_tx(void);
};

class cbm_3Stepper {
 public:
  cbm_3Stepper(uint16_t);
  void setSpeed(uint16_t);
  void release(void);
  void step(uint16_t steps, uint8_t dir,  uint8_t style = SINGLE);
  uint8_t onestep(uint8_t dir, uint8_t style);
   void bump(int *wstep, int *mstep, int msteps);
  void setLocation(uint8_t wstep, uint8_t mstep);

  uint16_t revsteps; // # steps per revolution
  uint32_t usperstep, steppingcounter;

  int wstep, mstep;         // whole step number (0 - 2), microstep number (0 - MICROSTEPS3-1)

  uint8_t cosineTable[MICROSTEPS3 + 1];
 private:
};

uint8_t getlatchstategetlatchstate(void);

#endif



and the code for cbm_AFMotor.cpp:

Code: Select all | TOGGLE FULL SIZE

// Adafruit Motor shield library
// copyright Adafruit Industries LLC, 2009
// this code is public domain, enjoy!


#include <avr/io.h>
#include "WProgram.h"
#include "cbm_AFMotor.h"

static uint8_t latch_state;

#define MOTORDEBUG 1

AFMotorController::AFMotorController(void) {
}

void AFMotorController::enable(void) {
  // setup the latch
  /*
  LATCH_DDR |= _BV(LATCH);
  ENABLE_DDR |= _BV(ENABLE);
  CLK_DDR |= _BV(CLK);
  SER_DDR |= _BV(SER);
  */
  pinMode(MOTORLATCH, OUTPUT);
  pinMode(MOTORENABLE, OUTPUT);
  pinMode(MOTORDATA, OUTPUT);
  pinMode(MOTORCLK, OUTPUT);

  latch_state = 0;

  latch_tx();  // "reset"

  //ENABLE_PORT &= ~_BV(ENABLE); // enable the chip outputs!
  digitalWrite(MOTORENABLE, LOW);
}

void AFMotorController::latch_tx(void) {
  uint8_t i;

  //LATCH_PORT &= ~_BV(LATCH);
  digitalWrite(MOTORLATCH, LOW);

  //SER_PORT &= ~_BV(SER);
  digitalWrite(MOTORDATA, LOW);

  for (i=0; i<8; i++) {
    //CLK_PORT &= ~_BV(CLK);
    digitalWrite(MOTORCLK, LOW);

    if (latch_state & _BV(7-i)) {
      //SER_PORT |= _BV(SER);
      digitalWrite(MOTORDATA, HIGH);
    } else {
      //SER_PORT &= ~_BV(SER);
      digitalWrite(MOTORDATA, LOW);
    }
    //CLK_PORT |= _BV(CLK);
    digitalWrite(MOTORCLK, HIGH);
  }
  //LATCH_PORT |= _BV(LATCH);
  digitalWrite(MOTORLATCH, HIGH);
}

static AFMotorController MC;


/******************************************
               MOTORS
******************************************/

/*

   timer 2A controls *both* P1 and P2, respectively, of stepper 1
   timer 2B controls *both* P4 and P5, respectively, of stepper 1
   
   timer 0A controls *both* P1 and P2, respectively, of stepper 2
   timer 0B controls *both* P4 and P5, respectively, of stepper 2
   
               Arduino outputs        motor/stepper pins         "PWM"
timer          A          B            A          B          A          B
  0            6          5           M4S2      M3S2        PWM4      PWM3
  1            9         10
  2           11          3           M1S1      M2S1        PWM1      PWM2
 
  so to control three phases, we need to use timers 0 and 2
   
*/

inline void initPWM1(uint8_t freq) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer2A on PB3 (Arduino pin #11)
    TCCR2A |= _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); // fast PWM, turn on oc2a
    TCCR2B = freq & 0x7;
    OCR2A = 0;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 11 is now PB5 (OC1A)
    TCCR1A |= _BV(COM1A1) | _BV(WGM10); // fast PWM, turn on oc1a
    TCCR1B = (freq & 0x7) | _BV(WGM12);
    OCR1A = 0;
#endif
    pinMode(11, OUTPUT);
}
inline void setPWM1(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer2A on PB3 (Arduino pin #11)
    OCR2A = s;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 11 is now PB5 (OC1A)
    OCR1A = s;
#endif
}

inline void initPWM2(uint8_t freq) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer2B (pin 3)
    TCCR2A |= _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // fast PWM, turn on oc2b
    TCCR2B = freq & 0x7;
    OCR2B = 0;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 3 is now PE5 (OC3C)
    TCCR3A |= _BV(COM1C1) | _BV(WGM10); // fast PWM, turn on oc3c
    TCCR3B = (freq & 0x7) | _BV(WGM12);
    OCR3C = 0;
#endif
    pinMode(3, OUTPUT);
}
inline void setPWM2(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer2A on PB3 (Arduino pin #11)
    OCR2B = s;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 11 is now PB5 (OC1A)
    OCR3C = s;
#endif
}

inline void initPWM3(uint8_t freq) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer0A / PD6 (pin 6)
    TCCR0A |= _BV(COM0A1) | _BV(WGM00) | _BV(WGM01); // fast PWM, turn on OC0A
    //TCCR0B = freq & 0x7;
    OCR0A = 0;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 6 is now PH3 (OC4A)
    TCCR4A |= _BV(COM1A1) | _BV(WGM10); // fast PWM, turn on oc4a
    TCCR4B = (freq & 0x7) | _BV(WGM12);
    //TCCR4B = 1 | _BV(WGM12);
    OCR4A = 0;
#endif
    pinMode(6, OUTPUT);
}
inline void setPWM3(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer0A on PB3 (Arduino pin #6)
    OCR0A = s;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 6 is now PH3 (OC4A)
    OCR4A = s;
#endif
}

inline void initPWM4(uint8_t freq) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer0B / PD5 (pin 5)
    TCCR0A |= _BV(COM0B1) | _BV(WGM00) | _BV(WGM01); // fast PWM, turn on oc0a
    //TCCR0B = freq & 0x7;
    OCR0B = 0;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 5 is now PE3 (OC3A)
    TCCR3A |= _BV(COM1A1) | _BV(WGM10); // fast PWM, turn on oc3a
    TCCR3B = (freq & 0x7) | _BV(WGM12);
    //TCCR4B = 1 | _BV(WGM12);
    OCR3A = 0;
#endif
    pinMode(5, OUTPUT);
}
inline void setPWM4(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) || \
    defined(__AVR_ATmega88__) || \
    defined(__AVR_ATmega168__) || \
    defined(__AVR_ATmega328P__)
    // use PWM from timer0A on PB3 (Arduino pin #6)
    OCR0B = s;
#elif defined(__AVR_ATmega1280__)
    // on arduino mega, pin 6 is now PH3 (OC4A)
    OCR3A = s;
#endif
}

/******************************************
            3-PHASE STEPPERS
******************************************/

cbm_3Stepper::cbm_3Stepper(uint16_t steps) {
  MC.enable();

  revsteps = steps;
  wstep = 0;
   mstep = 0;

   latch_state &= ~_BV(MOTOR1_A) & ~_BV(MOTOR1_B) &
                         ~_BV(MOTOR2_A) & ~_BV(MOTOR2_B) &
                         ~_BV(MOTOR3_A) & ~_BV(MOTOR3_B) &
                          ~_BV(MOTOR4_A) & ~_BV(MOTOR4_B); // all motor pins to 0
   MC.latch_tx();
   
   // enable all three H bridges (motor 1, motor2, motor 4, pins 11, 3, and 6 respectively)
   pinMode(11, OUTPUT);
   pinMode( 3, OUTPUT);
   pinMode( 6, OUTPUT);
   digitalWrite(11, HIGH);
   digitalWrite( 3, HIGH);
   digitalWrite( 6, HIGH);

   // use PWM for microstepping support
   initPWM1(MOTOR12_64KHZ);
   initPWM2(MOTOR12_64KHZ);
   initPWM4(1);                                          // ???
   initPWM4(MOTOR34_64KHZ);      // was 1                                                // ???
   Serial.println("Generating cosine table:");
   for (int i = 0; i <= MICROSTEPS3; i++) {
      cosineTable[i] = cos(PI / 2.0 * i / MICROSTEPS3) * 255;
#ifdef MOTORDEBUGh
      Serial.print(i); Serial.print(": "); Serial.print(cosineTable[i]);
      Serial.println();
      delay(1);
#endif
   }
   
   setPWM1 (255);
   setPWM2 (  0);
   setPWM4 (  0);
}

void cbm_3Stepper::setSpeed(uint16_t rpm) {
  usperstep = 60000000 / (revsteps * rpm);
   steppingcounter = 0;
}

void cbm_3Stepper::release(void) {
   latch_state &= ~_BV(MOTOR1_A) & ~_BV(MOTOR1_B) &
                         ~_BV(MOTOR2_A) & ~_BV(MOTOR2_B) &
                         ~_BV(MOTOR3_A) & ~_BV(MOTOR3_B) &
                          ~_BV(MOTOR4_A) & ~_BV(MOTOR4_B); // all motor pins to 0
  MC.latch_tx();
}

void cbm_3Stepper::step(uint16_t steps, uint8_t dir,  uint8_t style) {
  uint32_t uspers = usperstep;
  uint8_t ret = 0;

  if (style == INTERLEAVE) {
    uspers /= 2;
  }
#ifdef MICROSTEPPING
 else if (style == MICROSTEP) {
    uspers /= MICROSTEPS3;
    steps *= MICROSTEPS3;
#ifdef MOTORDEBUG
    Serial.print("steps = "); Serial.println(steps, DEC);
#endif
  }
#endif

  while (steps--) {
    ret = onestep(dir, style);
    // rather than use delaymicros(), which blocks while it waits,
    // we use delay and then delay an additional ms when the
    // microseconds add up to beyond 1 ms
    delay(uspers/1000); // in ms
    steppingcounter += (uspers % 1000);
    if (steppingcounter >= 1000) {
      delay(1);
      steppingcounter -= 1000;
    }
  }
#ifdef MICROSTEPPING
  if (style == MICROSTEP) {
     // do all the microsteps for one step right here
    //Serial.print("last ret = "); Serial.println(ret, DEC);
    while ((ret != 0) && (ret != MICROSTEPS3)) {
      ret = onestep(dir, style);
      delay(uspers/1000); // in ms
      steppingcounter += (uspers % 1000);
      if (steppingcounter >= 1000) {
            delay(1);
            steppingcounter -= 1000;
      }
    }
  }
#endif

}

uint8_t cbm_3Stepper::onestep(uint8_t dir, uint8_t style) {
  uint8_t a, b, c;
   // note _BV(x) is defined as 1 << x
   a = _BV(MOTOR1_A);
   b = _BV(MOTOR2_A);
   c = _BV(MOTOR4_A);

  //Serial.print("step "); Serial.print(step, DEC); Serial.print("\t");
  // next determine what sort of stepping procedure we're up to
  if (style == SINGLE) {
      if (dir == FORWARD)
         wstep = (wstep + 1) % 3;
      else
         wstep = (wstep + 2) % 3;
      mstep = 0; // by def one coil on at a time
 
  } else if (style == DOUBLE) {
      if (dir == FORWARD)
         wstep = (wstep + 1) % 3;
      else
         wstep = (wstep + 2) % 3;
      mstep = HALFSTEP; // by def two coils equally on at a time
      
  } else if (style == INTERLEAVE) {
    if (dir == FORWARD)
       bump (&wstep, &mstep, HALFSTEP);
    else
       bump (&wstep, &mstep, -HALFSTEP);
      
  }  else if (style == MICROSTEP) {
    if (dir == FORWARD)
       bump (&wstep, &mstep, 1);
    else
       bump (&wstep, &mstep, -1);
   }


  //Serial.print(" -> step = "); Serial.print(step/MICROSTEPS3, DEC); Serial.print("\t");

  setLocation(wstep, mstep);

  // release all
  // latch_state &= ~a & ~b & ~c & ~d; // all motor pins to 0

  //Serial.println(step, DEC);

  latch_state |= a | b | c ; // energize all three coils
   
  MC.latch_tx();
  return mstep;
}

void cbm_3Stepper::bump(int *pwstep, int *pmstep, int msteps) {
#ifdef MOTORDEBUGh
   Serial.print("Bump: (");
   Serial.print(*pwstep, DEC);
   Serial.print(" : ");
   Serial.print(*pmstep, DEC);
   Serial.print(") + ");
   Serial.print(msteps, DEC);
#endif
   *pmstep += msteps;
   if (*pmstep < 0) {
      *pwstep -= 1;
      *pmstep += MICROSTEPS3;
   }
   if (*pmstep >= MICROSTEPS3) {
      *pwstep += 1;
      *pmstep -= MICROSTEPS3;
   }
   if (*pwstep < 0) {
      *pwstep += 3;
   }
   if (*pwstep >= 3) {
      *pwstep -= 3;
   }
#ifdef MOTORDEBUGh
   Serial.print("= (");
   Serial.print(*pwstep, DEC);
   Serial.print(" : ");
   Serial.print(*pmstep, DEC);
   Serial.println(")");
#endif
}

void cbm_3Stepper::setLocation(uint8_t wstep, uint8_t mstep) {
   /*
   at (0,0), coil a only; at (1,0), coil b only; at (2,0), coil c only
   at (0,2), coil a mostly, coil b a little
   */
   
#ifdef MOTORDEBUG
   Serial.print("setLocation (");
   Serial.print(wstep, DEC);
   Serial.print(", ");
   Serial.print(mstep, DEC);
   Serial.print("): <");
   Serial.print(mstep, DEC);
   Serial.print(", ");
   Serial.print(MICROSTEPS3 - mstep, DEC);
   Serial.print("> ");
   Serial.print(cosineTable[mstep], DEC);
   Serial.print(", ");
   Serial.print(cosineTable[MICROSTEPS3 - mstep], DEC);
   Serial.print("\n");
#endif
   switch (wstep) {
   case 0:
      setPWM1 (cosineTable[mstep]);
      setPWM2 (cosineTable[MICROSTEPS3 - mstep]);
      setPWM4 (0);
      break;
   case 1:
      setPWM2 (cosineTable[mstep]);
      setPWM4 (cosineTable[MICROSTEPS3 - mstep]);
      setPWM1 (0);
      break;
   case 2:
      setPWM4 (cosineTable[mstep]);
      setPWM1 (cosineTable[MICROSTEPS3 - mstep]);
      setPWM2 (0);
      break;
   }
}



And here's a demo program:

Code: Select all | TOGGLE FULL SIZE

#include <avr/io.h>
#include "WProgram.h"
#include <inttypes.h>
#include "cbm_AFMotor.h"
#include <stdio.h>

cbm_3Stepper pancake(24);

void setup() {
   Serial.begin(115200);
   delay(300);
  pancake.release();
  pancake.setSpeed(5000);
}

void loop() {
  // pancake.step
   switch (6) {
      case 0:
         pancake.onestep(FORWARD, SINGLE);
         delay(500);
         break;
      case 1:
         pancake.step(10, FORWARD, SINGLE);
         delay(500);
         break;
      case 2:
         pancake.step(10, FORWARD, DOUBLE);
         delay(500);
         break;
      case 3:
         pancake.step(10, FORWARD, INTERLEAVE);
         delay(500);
         break;
      case 4:
         pancake.step(10, FORWARD, MICROSTEP);
         delay(500);
         break;
      case 5:
         pancake.step(10, BACKWARD, MICROSTEP);
         delay(500);
         break;
      case 6:
         for (int i = 0; i < 100; i++) {
            pancake.onestep(FORWARD, MICROSTEP);
            delay(50);
         }
         delay(500);
         break;
   }
}



I very much simplified it to handle all the control modes (SINGLE, DOUBLE, INTERLEAVE, and MICROSTEP) as special cases of the same code. I precalculate a sinusoid table to speed up the microstepping. I haven't checked how fast it can run, though, and it may be that it won't go as fast as if it were coded in the style of the original. And since I use the microstepping technique for all the motion modes, I require MICROSTEPPING to be defined, so no library shrinkage is allowed for.

I plan to develop acceleration and deceleration functions and adaptation to speed, so I can go from microstepping at a slow rotational speed to interleaving or even single at higher speed. The fun begins!

I think I'd develop a separate shield (hint, hint, Lady Ada...) to run a three-phase motor like this one, though, since it will take all 3 timers to run a pair of three-phase motors.

Love to have comments.
cbm

cbmalloch
 
Posts: 8
Joined: Sat Dec 12, 2009 10:52 pm

Re: Driving a BLDC (brushless DC motor from harddisk)

by adafruit on Mon Dec 14, 2009 12:05 am

wow! great work. while -we- have not plans for a BDC motor driver shield it is certain someone ELSE would be interested. you should do it! :)

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm
Location: nyc

Re: Driving a BLDC (brushless DC motor from harddisk)

by nbass on Thu Sep 13, 2012 10:19 pm

Waking up this old thread, I wired a BLDC from a hard disk as CBM suggested, to M1 M2 and M4 on the adafruit motor shield, ran the code (had to replace Wprogram.h with Arduino.h, but otherwise unmodified.)

Tried a 12 V power supply, and while the different settings can make some led's respond nothing at all happened with a motor connected. I admit, I would have been equally surprised if it had worked, as I have put almost no effort into learning about motor control so far. Here I am thinking I wouldn't have to! alas, nothing in life comes for free.

The question is, should I go find another motor? Should I try to figure out whether the code was intended for more than proof of concept? I would rather not give up on this just yet.

nbass
 
Posts: 1
Joined: Thu Sep 13, 2012 9:12 pm

Re: Driving a BLDC (brushless DC motor from harddisk)

by adafruit_support_bill on Fri Sep 14, 2012 5:24 am

Most BLDC motor drivers use feedback - typically hall-effect sensors to time the phase changes. Getting one to run 'open loop' will probably require some experimentation.

adafruit_support_bill
 
Posts: 84034
Joined: Sat Feb 07, 2009 10:11 am

Please be positive and constructive with your questions and comments.