$4.5 Better Stepper Driver

30K11647

Intro: $4.5 Better Stepper Driver

In the last few years, 3D printers, personal CNC machines, and plotters have gained enormous popularity in the DIY community.  The powerhouse begind these is the stepper motor, which is capable of precise movement, and can be salvaged easily for next to nothing.  Driving these motors, however, is more complicated than almost any other motor, and though many options exist, a few stepper drivers can cost upwards of $40.  Enough to run a 3D printer can be the more expensive athan any other part. 
After I made a very cheap driver using the L293 and attiny 85 (https://www.instructables.com/id/5-stepper-driver/), I thought I had a good solution, but I quickly became dissatisfied with the speed, tendancy to lose steps, and lack of current regulation.  This stepper driver seeks to fix these issues, and for even less than the original project.  

STEP 1: What You Need:

Most of the parts can be bought from Jameco.com, or most electronics suppliers.  The L6219 cannot, but more on that later. 
1xAttiny 2313 (or any arduino-compatable with at least 16 IO pins and 2 interrupts.  An uno or leonardo will work, but an attiny is just $3 from Jameco.com, roughly one tenth the cost of a normal arduino.  The 8 pin attinies (attinys?), such as the 85 will not work)
2xL6219
8x820pf capacitor
4x1k resistor
4x1ohm resistor
4x30K-60K resistor
4x100nf capacitors
2x100+uf capacitors
(I didn't use the last two as my power supply already has some big caps on it.  A good electronicist would put at least the 100nf caps on, but I just forgot to)

STEP 2: An L2619, What Is That?

The L2619 is a rather unusual part, and is seldom used by hobbyists, despite several advantages it has over more common motor drivers.  It does require a small ammount of circuitry (though just 10 common components), but when operating correctly, it can regulate current, requires little or no heatsink, takes phase/current input, and doesn't make a high-pitched whine whenever the steppers are running.
Hook up all the parts as shown on page 6 of the datasheet here: http://pdf.datasheetcatalog.com/datasheet/SGSThomsonMicroelectronics/mXsqwuv.pdf
Test it first with some LEDs with 1k resistors, at 9V on Vs (Vss MUST remain at 5V), and note that the I0 and I1 pins set current, phase sets polarity, and that it recoginizes low signal (ground) as active.  Once you have established that it operates correctly, hook it up to a DC motor on each output, and connect the I0 and I1 pins separately.  The motor should speed up when you connect both and slow down (but not stop) when one pin is connected.  If it all checks out, then solder it, check it again, and make another one. 

When you have finially made it past the trials of the electronics gods and have two functional motor drivers, we move to the fun part.  Software. 

STEP 3: Pre-software

I lied. No software yet.  Soon.  First, you need to set up that blue thingy with a magic thingy on it to get the magic stuff to the magic black caterpillar thingy.  Or you could setup your arduino to send code to the attiny.  Either way.
This has been written about something around ten billion times since the cores were released a few years ago, so just follow the directions here: https://www.instructables.com/id/Arduino-ATtiny2313-Programming-Shield/#intro You can just use a breadboard, as you only need to send the code once, though attiny2313s are great, so I would make a more permanent programmer for other projects.  Make sure you are using an arduino software of 1.0.3 or earlier, as 1.5+ does not work. 
Once you have an LED blinking on your attiny, or some other bit of test code, it's time to move to the fun part. 

STEP 4: Software. That Thing That Makes Stuff Do Stuff.

Now, just copy and paste the software below to the IDE and click on...tools?
Yes, tools. Then go to "board" and select "attiny2313 @ 8Mhz". Yes, I know the other instrucable said to set it to 1MHz earlier.  This uses 8MHz becuase it makes it run 8 times faster, with absolutely no modification, software or otherwise.  Making sure than your normal arduino is configured to send programs to the attiny (it may wreck the arduino if it is not), click " tools>Burn Bootloader". Some lights should blink, and then it should say "done burning bootloader". If it did not return an error, then just upload the code normally. When the blinky lights stop, disconnect the arduino, pull out the attiny, and break out your breadboard once more.

//attiny 2313 stepper driver by Jduffy. Full instructions on instrucatables.
const byte I011=1;//the pins for each function.
const byte I111=2;//names ending in 1 are for stepper 1
const byte dr11=3;
const byte I021=8;
const byte I121=9;
const byte dr21=10;
const byte I012=11;//same for stepper 2
const byte I112=12;
const byte dr12=13;
const byte I022=14;
const byte I122=15;
const byte dr22=16;//for all of the following, 0 represents on "on" pin
// as the 2619 registers low as active.
//lists like this are used because they take very little of the
//chips memory, which for the attiny2313, is in short supply (just 2k!)
//It also greatly simplifies the code below.
boolean stp10[]={1,0,1,0,1,0,1,0,1,0,1,0};//output to LSB current limiting 1
boolean stp11[]={1,1,0,0,0,1,1,1,0,0,0,1};//output to MSB current limiting 1
boolean stpd1[]={1,1,1,1,1,1,0,0,0,0,0,0};//output to direction 1
boolean stp20[]={0,1,0,1,0,1,0,1,0,1,0,1};//output to LSB current limiting 2
boolean stp21[]={0,0,1,1,1,0,0,0,1,1,1,0};//output to MSB current limiting 2
boolean stpd2[]={0,0,0,1,1,1,1,1,1,0,0,0};//output to direction 2
byte stepp1;//step part for stepper 1
long pos1;//"actual" position of stepper 1
long dpos1;//desired position of stepper 1
byte in1=6;//direction input pin for stepper 1
byte stepp2;//same stuff for stepper 2
long pos2;
long dpos2;
byte in2=7;
void setup(){
DDRB = B11111111;//This is very important, as this is the
DDRD = B1000011;//same as "pinMode();, but takes very little space.
//If you use anything other than an attiny 2313, then you must change all of these to
//the standard "pinMode();" command.
DDRA = B011;//if you use pins 6 or 7 as outputs, you MUST change these lines
//
//If you don't know what these lines mean, there is an
//explanation at http://arduino.cc/en/Reference/PortManipulation
attachInterrupt(0,step1,RISING);//ALWAYS on digital 4 or 5 (0=4, 1=5), the interrupts cannot be
attachInterrupt(1,step2,RISING);//on ANY other pins, unless you use pcinterrupts, which offers little
}//to no advantage. The 0 and 1 are same for all avr boards, though the pin number itself will be different
void loop(){
if (pos1!=dpos1){//if the stepper isn't where it should be...
if (pos1<dpos1){//and it needs to go foward...
stepfwd1();//go foward!
}else{//otherwise
stepbck1();//go backward!
}
}

if (pos2!=dpos2){//do the same thing for stepper 2
if (pos2<dpos2){
stepfwd2();
}else{
stepbck2();
}
}
delayMicroseconds(3); //wait a little bit
}

void step1(){//if the "step" pin for stepper 1 was brought high
if(digitalRead(in1)==LOW){//and the "direction" pin is low
dpos1++;//tell the loop to step foward 1
}else{//otherwire
dpos1--;//step back 1
}
}

void step2(){//same for stepper 2
if(digitalRead(in2)==LOW){
dpos2++;
}else{
dpos2--;
}
}

void stepfwd1(){//if stepper 1 needs to go foward
stepp1++;//advance the step sequence 1
pos1++;//increase the percieved position by 1
if (stepp1 > 11){//if it finished a full step then
stepp1=0;//reset it to the beginning of the step sequence
}
out();//digitalWrite all the pins that need it.
}
void stepfwd2(){//same for stepper 2
stepp2++;
pos2++;
if (stepp2 > 11){//if it finished a full step then
stepp2=0;//reset it to the beginning of the step sequence
}
out();
}


void stepbck1(){//if it needs to go back
stepp1--;//move the step sequence back one
pos1--;//move the percieved position back one
if (stepp1 >12 ){//if it finished a full step then
stepp1=11;//reset it to the beginning of the step sequence
}
out();
}

void stepbck2(){//same for stepper 2
stepp2--;
pos2--;
if (stepp2 >12 ){//if it finished a full step then
stepp2=11;//reset it to the beginning of the step sequence
}
out();
}

void out(){
if(stepp1>11){//these keep the "stepp" byte from going out of the data
stepp1=0; //in the step lists.
}
if(stepp2>11){
stepp2=0;
}
digitalWrite(I011,stp10[stepp1]);//writes the values of each list to its pin.
digitalWrite(I111,stp11[stepp1]);
digitalWrite(dr11,stpd1[stepp1]);
digitalWrite(I021,stp20[stepp1]);
digitalWrite(I121,stp21[stepp1]);
digitalWrite(dr21,stpd2[stepp1]);
digitalWrite(I012,stp10[stepp2]);
digitalWrite(I112,stp11[stepp2]);
digitalWrite(dr12,stpd1[stepp2]);
digitalWrite(I022,stp20[stepp2]);
digitalWrite(I122,stp21[stepp2]);
digitalWrite(dr22,stpd2[stepp2]);
}

STEP 5: Final Wiring. Finally.

We're almost done.  The final step is just connecting a few pins and testing it.  Going by the included pinout, attach the two motor drivers you made earlier to the attiny.  The I0, I1, and DIR indicate the pin on each motor driver.  The first digit after that indicates which half of the driver to connect it to, the seconds indicates which driver.  The step-# and dir-# pins are the inputs. Once the wires are all connected, I would add some LEDs on 2k2 resistors to ground on some pins (any pins will give you feedback, though the step and direction pins are especially useful).  Then, connect some controller (I used an UNO with grbl) to the four inputs, (make sure to connect the grounds!) and start some sweet, silent, simple stepper stepping.  If the motors vibrate, but do not step, check for loose connections, and ensure that the drivers are connected to the correct attiny pins.   

If it (somehow) works correctly when breadboarded, then transfer it all to PC board, and enclose it (I used an old speaker housing).  Test it again, and if it all checks out, congratulations! You're done. 

This is not an extremely fast, accurate, or powerful stepper driver, but it is a significant step up from using counters and decoders.  It has 1/3rd microstepping, which though an unusual number, (most are multiples of 2) still gives pretty good performace.  I've gotten it to step a NEMA23 motor at as high as 8300 steps per second.  Note that higher speeds require acceleration from the controller, the attiny does not slowly accelerate the motors itself.  As long as it can take at least 750mA, this can drive most motors very fast, as any voltage up to 40V can be applied without exceeding the current rating.  The supply voltage should be able to handle at least 1.5A RMS, and should be greater than 10V.       

47 Comments

That should work, it accepts standard step pulses, and logic level for direction.

lol. What a mess! I guess if it works

I'm planning for my first 3D printer build (potentially chocolate), and am wondering what the pros and cons of this driver are. I hope I haven't missed that in the instructable.

The pros are:

-cheaper

-current-regulated

-repairable

-configurable (software is easy to edit, if you want to switch to DC with encoders or something, you can just reconfigure these drivers to do it)

cons:

-At extremely high speeds, this may miss steps (above ~500K steps/sec, over a hundred times the average max speed of a normal stepper)

-no feedback-based position sensing and adjustment (some very high-end drivers do this, runs it like a synchronous motor at extremely high speeds)

-larger than most single-chip solutions

-introduces a small amount of latency (though not really noticeable)

-Can't handle as much current as many other drivers (only up to 750mA)

Hmmm should have dumped my intellect here first :)

This is full step;

AB,CD
HL,HL

HL,LH

LH,LH

LH,HL

Pretty simple, Micro stepping is the art of shifting from either a H to L or L to H.

The only way to shift from HIGH to LOW or vice versa is with Pulse Width Modulation.

Luck enough the Attiny AVR's have this feature.

Nothing is instant, going from a LOW to a HIGH takes a WIDTH of time.

If we say the WIDTH in any direction is 20 Microseconds(ms).

A DUTY of 50% is 1ms LOW & 1ms HIGH alternating over the period of our WIDTH 20ms, 50% of the time (10ms) the signal is either H or L.

If we say Half step is;

AB,CD
HL,HL

HL,LL

HL,LH

LL,LH

LH,LH

LH,LL

LH,HL

LL,HL

In order to gain more steps or 1/4 microstepping we must introduce a 50% Duty between a H/L to L/H which we will denote as P.

AB,CD
HL,HL

HL,PL

HL,LL

HL,LP

HL,LH

LL,LH

LH,LH

LH,LL

LH,HL

LL,HL

And this is microstepping in a nutshell :)

L is 0%..................100% is H there are so many duty cycles that fit between 0 & 100. 1/400 micro steps & beyond is only inconceivable if you run out of storage for your code or you give up! See if you can place the P in the correct postion?

So to iterate, we can PWM from H to L, or , L to H with P going up & down through our step.

Just like counting forward from 0 to 5. 0,1,2,3,4,5 we do the same in reverse 5,4,3,2,1,0 when we count from 5 to 0.



I am aware of how microstepping works. This does microstep, but without PWM. Using hardware interrupts, a logic table and the hardware current regulation within the motor driver itself means that this is simpler, and much faster that could be a achieved with PWM (on these attinys). Also, the slew rate of the output of an avr is much faster than it's PWM, it shouldn't make a significant difference.

DDRB |= (1<<PB4)|(1<<PB3)|(1<<PB2);// OC1A, OC1B, OC0A
DDRD |= (1<<PD5);// OC0B
TCCR0A = (1 << COM0A1) | (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);
TCCR0B = (1 << CS01);
TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);
TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);
ICR1 = 100; //100ms WIDTH

Since its faster to poll a pin with bitread in a while loop than it is to catch & release the interrupt vector & writing to the OCnx registers directly, then this needs no explanation!?

ICR1 = 100; //100ms WIDTH

OOPS!

ICR1 = 100; //100nanos WIDTH

I've generally found that interrupts are more efficient for catching pin events that a while loop as this allows a few things

1) a delay within the while, which gives a double-step (two steps close together, before the loop runs twice) a chance at working, instead of stalling the motor

2) more functions, and more convenient code, I don't need the loop to optimize at a nanosecond timescale in order to catch events (the code can be readable and more importantly, robust)

Also, what do the timing registers have to do with polling?

You have inspired me to try and play with the Attiny 85s. It seems to lack an input but I am trying to find a solution about the reset pin, being usable and reading voltages too...

I want to mention that this type of driver isn't really Real Time. I suppose it can't be used for very serious machines, like lathes and big mills. Only for homemade things!

true, it is not quite "real time", however, hardware interrupts and a table of values mean that it steps within a few microseconds, and can take up to a few hundred kilohertz, much faster than any stepper that someone would drive with this. for anything hobbyist (as to you pointed out), it should work just as well as any other driver.

Hello there, in step 2 you instruct to get the schematics according to the page 6 of pdf, and there are some 100nf and 100uf (vss and vs) caps that you're not using, or forgot to put in the list? thanks

Good catch, I'll change it immediately.

And how come you can control 3 or rather 4 stepper motors for the 3D printer with a single Arduino ?
What about the driver , can you control any motor bipoar ? Unipolar ?4 wire ? 5 wire ? 6 wire or 8 wire ?
If you're using an automatic translator, i suggest you try a new one. It is very difficult to understand what you are saying.

If you're asking how to control 3-4 motors, just build two of these drivers.

The control software on the actual arduino for a CNC machine is called "grbl". For a 3d printer, there are many options, including "repetier" and "teacup".

I have only tested this driver with 4 wire steppers, but it should work with any bipolar motor. It may also run 5 or 6 wire steppers, but I do not believe it will work with 8.
Sorry for my language I was little frustrated.

What I was asking is that you need four pins (of which 3 are PWMs) and there are only 6 PWMs available on Arduino uno . So how come you control for steppers with a single uno.
I can't afford to buy a MEGA or a DUE.

Would you mind explaining me , how stepper motors work? I'm asking this because I want to know why PWMs(3 PWMs) are needed ?

Thank you for your help , I'm only a beginner in world of additive manufacturing and I experience anger sometimes , when things don't go right way .:)
Ill go back through and check the instructable for unclear wording, but you actually need no pwm pins. All the pwm is handled within the l2619 chip. There are 6 wires that go to each 2619, from the attiny. I suggest using attiny, not a full arduino for the driver itself (this thing), as an arduino costs upwards of $30, whereas an attiny is just $3. Each attiny controls two 2619s and therefore two stepper motors, so you will have to buy two attinies, and four 2619s to control four motors.
The attinies are controlled by two inputs, one which tells them to take a step, the other indicates direction. These inputs are supplied by the arduino, or another controller. Therefore, the arduino only needs two pins for each stepper, the attinies and 2619s handle the rest.

In effect, the whole unit described in this instructable acts like a pair of these: https://www.sparkfun.com/products/10267
But with 1/3rd instead of 1/8th microstepping.
What actually I am thinking to do is to use 3 H-bridges (either your IC or L293D) for 3 steppers with an Arduino UNO (I already have an Arduino Uno).So will my circuit be compatible with which software ? I don't want to buy easy drivers , I know they work fine with grbl but they are pretty much expensive.
I hope you got my point.
What I'm trying to do is to run 3 steppers ( or 4 ) with L293D by simply uploading the hex files or sending G-codes.
More Comments