$4.5 Better Stepper Driver





Introduction: $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)
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!
stepbck1();//go backward!

if (pos2!=dpos2){//do the same thing for stepper 2
if (pos2<dpos2){
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
dpos1--;//step back 1

void step2(){//same for stepper 2

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
if (stepp2 > 11){//if it finished a full step then
stepp2=0;//reset it to the beginning of the step sequence

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

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

void out(){
if(stepp1>11){//these keep the "stepp" byte from going out of the data
stepp1=0; //in the step lists.
digitalWrite(I011,stp10[stepp1]);//writes the values of each list to its pin.

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.       



    • Pocket-Sized Contest

      Pocket-Sized Contest
    • Pro Tips Challenge

      Pro Tips Challenge
    • Paper Contest 2018

      Paper Contest 2018

    We have a be nice policy.
    Please be positive and constructive.




    can i connect this to a breakoutboard like mk 1 ???

    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:




    -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)


    -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;





    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;









    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.











    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


    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?