Wednesday, March 11, 2009

Arduino L298 stepper motor driver

Here is an example Arduino sketch to drive a stepper motor using the L298 chip. The actual driver is the "StepperL298N" class, and then there is some code to test it. When you instantiate the class, pass in the 4 Arduino pins that you have connected to the stepper motor. For example, "StepperL298N motor(4,5,6,7)" will drive a motor connected to Arduino pins 4,5,6,7. If you need to drive 2 motors, just create 2 instances of the class with different pins!

To move the motor 1 tick, call the step(bool direction) member function. Call it with 1 to go forward, and 0 to go backwards. The faster you call it, the faster the motor will go. Note that the Arduino can easily call step() much faster than the motor can possibly be driven, so you must insert a delay by calling the delay function. For my test motor (and old AMP 2.3 volt 60oz*in, which I got from taking apart a printer) you have to delay at least 5ms for the motor to work.



/*
* stepperL298N
* Copyright 2008 G. Andrew Stone
* Licensed under the Common Public License V1.0
* For the full text of this license see: http://www.opensource.org/licenses/cpl1.0.php
*
*/

int ledPin = 13;

void setup() // run once, when the sketch starts
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
}

void mydelay(int clk)
{
int i;
for (i=0;i< clk;i++);
//delay(clk);
}

class StepperL298N
{
public:
byte pins[4];
byte curState;
byte* lut;
StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4);

void step(boolean dir);
void halfStep(boolean yes) { if (yes) lut = halfStepLut; else lut = fullStepLut;}
void start() { curState = 0xa;}
void free() { curState = 0; }
void brake() { curState = 0; }

byte fullStepLut[16];
byte halfStepLut[16];
};


StepperL298N::StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4)
{
pins[0] = pin1; pins[1] = pin2; pins[2]=pin3; pins[3] = pin4;
lut = fullStepLut;
for (int i=0;i<4;i++)
{
pinMode(pins[i], OUTPUT); // sets the digital pin as output
}

memset(&fullStepLut,sizeof(byte)*16,0);
memset(&halfStepLut,sizeof(byte)*16,0);

// High nibble goes one way, low the other
fullStepLut[0] = 0;
// 1010 -> 1001 -> 0101 -> 0110
fullStepLut[0xa] = 0x9 | 0x60;
fullStepLut[0x9] = 0x5 | 0xa0;
fullStepLut[0x5] = 0x6 | 0x90;
fullStepLut[0x6] = 0xa | 0x50;

halfStepLut[0] = 0;
// 1010 -> 1000 -> 1001 -> 0001 -> 0101 -> 0100 -> 0110 -> 0010
halfStepLut[0xa] = 0x8 | 0x20;
halfStepLut[0x8] = 0x9 | 0xa0;
halfStepLut[0x9] = 0x1 | 0x80;
halfStepLut[0x1] = 0x5 | 0x90;
halfStepLut[0x5] = 0x4 | 0x10;
halfStepLut[0x4] = 0x6 | 0x50;
halfStepLut[0x6] = 0x2 | 0x40;
halfStepLut[0x2] = 0xa | 0x60;
}

void StepperL298N::step(boolean dir)
{
curState = lut[curState];
if (dir) curState &= 0xf;
else curState >>= 4;
for (int i=1,j=0;i< 16;i*= 2,j++)
{
byte lvl = LOW;
if (i&curState) lvl=HIGH;
digitalWrite(pins[j],lvl);
}
}


int serDataVal = LOW;
void loop() // run over and over again
{
unsigned long int j;

StepperL298N motor(3,4,5,6);
motor.halfStep(true);
motor.start();

for (j=0;j< 1000;j++)
{
motor.step(1);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10); // Change the delay to change the rate the motor spins
}
motor.halfStep(false);
motor.start();
for (j=0;j< 1000;j++)
{
motor.step(0);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10);
}
}

Michael Margolis ('mem' on the Arduino.cc forum) was kind enough to let me cross-post a schematic:

24 comments:

  1. Hi
    This looks perfect. I was given a stepper motor by a friend and I am not sure which colour goes to which Arduino Pin, do you have a schematic please?
    Thanks
    N.

    ReplyDelete
  2. Hi, did you connect to have the common GND for GND pin for Arduino and GND of power supply?

    ReplyDelete
  3. Yes, always connect all the grounds together as a rule of thumb! That's why its is sometimes called COM or "common".

    ReplyDelete
  4. Hi,

    thanks a lot for this great driver.
    Are the 2 sense resistors compulsory/obligatory ?
    And what wattage are they ?

    Thanks ;)

    ReplyDelete
  5. You are welcome! The sense resistors are not needed in this design (replace with a wire), so long as your motors will not draw > 3A (transient) and 2A steady state. Otherwise they are needed to limit the current through the motor. The full current that goes thru the motor goes thru these resistors!

    But it would be very fun to have them and also connect pins 1 and 15 to the Arduino's analog inputs (make sure the voltage at these pins < 5v)! In this way you could get feedback from the motor that would tell you (for example), if the motor is actually moving or if it is stalled.

    If you use them the full motor current will flow through them at the full voltage (well, it is safest to pretend that the motor coils resistance is zero but go ahead and measure it if you want). So size them big: at 2A*Vin. In this schematic, he is putting 2 in parallel so that the power is distributed between both. But for a big motor that is still not enough. I actually was using 10 1/4 watt resistors in parallel in one of my tests and they would start smoking after 10 seconds :-).

    If you're just fooling around in the lab and don't have these high-wattage resistors, use a 0-ohm resistor (i.e. a wire :-)) instead. But reduce the motor's input voltage to be sure that it does not draw more than 2A.

    ReplyDelete
  6. Thanks a lot !

    I was searching for 2 weeks now trying to drive my motors...I know I'm not very good ^^
    But this driver worked at the second try.
    Because the code and the schematic are not the same for the inputs/outputs of the arduino.

    It works really great now !
    So I don't know how to thank you but you are my saviour ;)

    My motors are 1A. So I suppose my motors musn't go up to 1A per coil? Am I correct ?

    Anyway, thanks a lot for all your explanations ;)

    ReplyDelete
  7. Also,

    When I put 5V for the motor supply, the l298 get slightly hot after 2 minutes but nothing bad.
    But when I put 12V it gets really hot after 5 seconds and I can't even touch the l298...

    Does this happen because I don't put these sense resistors ?
    My motors are 24V so I thought I would put 12V (because I don't have a 24V power supply).
    Anyway at 5V my motors have enough torque, I think, to drive my future cnc.

    Thanks

    ReplyDelete
  8. Are you using the multiwatt package?

    It can dissipate 25 watts (per the spec) but you've got to add a heat sink... that's what the tab with the hole is for on top. You can buy one or get a piece of copper flashing or a brass sheet from a hobby store and drill a hole in it. Attach with screw and CPU heat-sink attachment paste.

    You will want that full 24 volts to push through material.

    But yes until you're really cutting material, its a good idea to run at 5v. Less chance of a mistake or a accidental short wiping out your Arduino.

    BTW, its not a good idea to use the Arduino wall wart and power supply to drive motors this big -- use a separate one and connect their grounds.

    ReplyDelete
  9. Hi,

    Yes I am using the multiwatt package.

    I am using an ATX 350W power supply.
    It is ok at 5V but at 12V the l298 gets really hot and I can't aven touch it.
    So I could buy a 24V power supply but I don't know if the l298 won't burn ?

    Or maybe I should limit the current at 1A with those sense resistors if I want to put 12V or 24V ? So the motor will be working at its original voltage and current (24V 1A)

    Thanks for your help ;)

    ReplyDelete
  10. I think you misunderstood me. The sense resistors aren't really meant to limit the current but you could put resistors there if you had to. But if you do you are just wasting power. The motor's internal resistance should do the job (check the resistance with a ohmmeter if you feel the urge). But double check that its really rated 24V 1A. And while you are verifying that, also check the motor spec to see if it can "hold" at that or just be pulsed. Mostly it is solenoids that can't hold, but best to read the motor's spec.

    Too hot to touch is not necessarily too hot. If I remember the spec the chip is rated up to 135C so that's much too hot to touch. You know without a heat sink your PC's CPU can be used a the world's smallest frying pan (just before it melts itself down -- actually that's old school -- they have internal thermometer and just turn themselves off now).

    The chip is rated to 50V, 2A continuous so your motor sounds well within its bounds.

    I'd measure the current thru the system (put an ammeter in series where the sense resistor would go) to make sure it is <2A at 24v. And then double check the voltage. If it all looks good, add that heat sink, hit it with 24V (drive the motor ideally with some resistance, not hold or free spin) for a few minutes and see if you let the smoke out :-).

    BTW you can breadboard this, you just have to bend the multiwatt pins a little since the rows are offset by .05".

    Is your stepper warming up as well? After a few minutes it ought to be pleasantly warm to the touch. Proves that it is dissipating the power, not partly compromised by an internal short.

    ReplyDelete
  11. Hi,

    I think (but I am not sure) it can be hold at 24V 1A bipolar and 1.2A unipolar.
    Here are my motors:
    http://www.vulnerabilityscanning.com/store/4-Stepper-motors-DIY-CNC-ROUTER-MILL-LATHE-ROBOT_120557925061.html

    I read in the datasheet(http://www.sanyodenki.eu/IMG/pdf/2PH_SH_SS_H_SMSeriesMotor_18_e.pdf):
    Source voltage : DC24V
    operating current : 1.2A/phase, 2-phase energization(full-step)

    I will measure the current this afternoon or this week-end.
    No, my motor is not warming at 5V but well at 12V I think.

    Thanks for your help ;)

    ReplyDelete
  12. Hello,

    I measured the current after the current sensing A pin:
    at 5V I get 0.45A
    and at 12V I get 1.55A
    So I don't really understand...

    Should I try to stay at >=1A or it doesn't matter?
    The resistance of 1 coil is more or less 4 Ohm
    Can I calculate the current with the Ohm law?

    Thanks

    ReplyDelete
  13. Putting too much current through a coil can overheat it, melt the insulation and burn out your motor.

    You can calculate the maximum or steady state current using Ohm's law.

    However, the nature of an inductor is that it resists changes in current. Basically, the creation of the magnetic field slows down the current passing thru the coil and the breakdown of the field pushes extra current thru the wire.

    So if you had an oscilloscope you would find that when you first apply a voltage, the current rises slowly, plateauing approximately where V=IR suggests it would.

    Since the current rises slowly, it is ok to apply a very high voltage in short bursts -- if you do this the current never gets high enough to burn out the motor. And of course, to MOVE a stepper motor you energise the coils in alternating fashion -- that is, you apply voltage in short bursts!

    In fact, it is not only ok to use high voltage, but "better" because the higher the voltage the faster the magnetic field builds. And the speed of this magnetic field buildup determines the maximum speed the motor can spin.

    So I'm guessing that I was right to ask you to check the "holding" vs "running" voltage. Given what you've told me, I think that 24 volts is the maximum pulsed voltage you should give the motor. And I think that the maximum holding current is 1A (probably around 7-8 volts). But I'm no expert on your motor...

    To be safe, use 7-8 volts -- whatever gets you 1A steady state. The downside is that the maximum rotation speed will be lower.

    ReplyDelete
  14. Thanks a lot for all your detailed explanations I really appreciate your help and time you are giving ;)

    Also, I searched on google but didn't find a good explanation about L298-only driver vs L297+L298 driver.
    Are there good advantages to use the second one ?
    except that the 2nd one needs less wires...

    Because I think I make a stupid mistake...
    I bought the parts for 4 L297+L298 drivers cause I thought the inputs of the L298 should be connected to the PWM pins on the arduino but I have only 12 PWM pins on my arduino mega.
    But now I see the digitalwrite function in your library so the L298 is using digital pins I suppose...
    But the L297 outputs PWM so I don't understand really well...

    Thanks ;)

    ReplyDelete
  15. There is no need for PWM, normal digital IOs will work fine. Look, any repetitive on/off signal is "PWM". The Arduino's "PWM" pins do it really fast, but this class does it just fine with normal digital pins. A stepper motor is an incredibly slow device compared to a 16 MILLION instruction per second CPU.

    The L298 essentially amplifies a small signal into a powerful one. But there is no conceptual abstraction. The L297 has some smarts. It converts a concept like "go forward" into the appropriate signals to move the motor forward. Basically it is a hardware version of this class.

    Personally, I would not use the L297. Its really for devices that don't have a CPU, IMHO. As you gain sophistication, you will find that you can extend this class to drive the motor through precise changes in acceleration or velocity, and synchronise these motions with that of the other steppers.

    ReplyDelete
  16. am using 24V supply bipolar stepper motor. it only runs at 1kHz freqency.i want to run with ramp speed.am using l6210 chip for prefeeling diodes...can any help me

    ReplyDelete
  17. Hi,
    I have a question regarding the code.
    I have tried to use is to run a stepper motor via a L298N H-bridge.
    When I try to run the code I get the following error:
    '0xa was not declared in this scope' and it hisghlights the following:
    void start() { curState = Oxa;}

    Do you have a solution to this?

    Kind regards,
    Ninna Seerup,
    art.aau.gr.2@gmail.com

    ReplyDelete
  18. For anonymous,
    don't make a confusion between the alphabet letter O and the digit 0.

    ReplyDelete
  19. Hi
    First, Thanks for this great stepper driver code.
    It is the first one that I it work a little bit for me.
    I'm trying it with several steeper motor.
    With a 6 wire unipolar all 4 phases don't seem to be well drived.
    I carefully read your binary sequence code and I don't find this sequence :
    1000
    0010
    0100
    0001
    wich seem to be the good one to drive my 6 wire unipolar motor.

    but this one
    1001
    0101
    1010
    1001

    I think this will for using unipolar as bipolar or simply bipolar motor.

    I try to change it but I don't completly understand why you do a logical sum !

    I'm new in C++ Languages,
    May you help me ?

    ReplyDelete
  20. I wrote this code quite some time ago. But I seem to remember that you are absolutely correct! I was driving my 6-wire stepper in "bipolar" mode not unipolar. So I think you don't need any help; you've figured it out. But if you have any other specific questions I'm happy to help... ask away!

    ReplyDelete
  21. Hi there,

    Thanks for the very nice tutorial and helpful posts already present! ;)

    I have one question: can this schematic be easily converted to control a 4-wired bipolar stepper motor? Perhaps indications of how to accomplish this?

    Thanks in advance!
    Pepijn

    ReplyDelete
  22. I have made the same circuit but why is it that the torque from my motor is so low?Is it the voltage or what?

    ReplyDelete
  23. Where in the code do you do the speed control?

    ReplyDelete
  24. I must thank you for the efforts you have put in writing this website. I really hope to check out the same high-grade blog posts from you in the future as well. In truth, your creative writing abilities has motivated me to get my own website now ;)

    ReplyDelete