LamjaStep – Bipolar Stepper Motor Driver

Here is a schematics preview of the driver. There might be som minor typos in this. I will post PCB layout soon.

I’m working on a new stepper motor driver. It will have 1/32 -1/128 microstep resolution, power save feature,  programmed on an Arduino (Atmega328), and designed from opamps, NAND-gates, transistors and MOSFET’s.Speed test at 24V 1/8 microstep:

Testing at 12V:

Schematics and PCB layout will be available soon.

Updated:

Below is the Arduino code that I used in the test videos. Feel free to use the code, but keep my name in it.


//
// LamjaStep v1
// Copyright Arvid Mortensen
//
// Bipolar microstep enabled
// stepper motor driver with power save
// www.lamja.com
//
//This code is for use with Atmega328 under Arduino enviroment.
//
// Inputs:
// - Direction
// - Step
// - Enable
// - Step resolution bit 0-2
// - Power save
//
// Outputs:
// - DAC A bit 0-5
// - DAC B bit 0-5
//
//
//
//

int StepNumber = 0; // Step number in current table
int MS = 8; // Microstep skip in table with direction +/-
int MSNew = 8; // Microstep skip in table, new without direction +
int J1 = 0; // J1 Jumper bit (1=open, 0=short)
int J2 = 0; // J2 Jumper bit (1=open, 0=short)
int J3 = 0; // J3 Jumper bit (1=open, 0=short)
int J4 = 0; // J4 Jumper bit (1=open, 0=short)
int D = 0; // Direction Bit (0 or 1)
int Direction = 1; // Direction (-1 or 1)
int StepResolution = 0; // Stepresolution (0-7)
int t; // General counter
unsigned int PowerSaveCount = 0; // Counter for power save time out
int PS = 0; // Power Save reached(% of full power)
int PSNew = 0; // Power Save reached new reading (% of full power)
int E = 0; // Enable (1=disabled, 0=enabled)
int ENew = 0; // Enable new reading (1=disabled, 0=enabled)
unsigned char TempB = 0;
unsigned char TempC = 0;
unsigned char PhaseCurrentOriginal[641] = {32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,38,38,38,39,39,40,40,40,41,41,41,42,42,42,43,43,44,44,44,45,45,45,46,46,46,47,47,47,48,48,48,49,49,49,50,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,56,56,56,56,57,57,57,57,58,58,58,58,58,59,59,59,59,59,60,60,60,60,60,60,60,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,62,62,62,62,62,62,62,62,62,62,62,61,61,61,61,61,61,61,60,60,60,60,60,60,60,59,59,59,59,59,58,58,58,58,58,57,57,57,57,56,56,56,56,55,55,55,55,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,50,50,49,49,49,48,48,48,47,47,47,46,46,46,45,45,45,44,44,44,43,43,42,42,42,41,41,41,40,40,40,39,39,38,38,38,37,37,37,36,36,35,35,35,34,34,34,33,33,32,32,32,31,31,30,30,30,29,29,29,28,28,27,27,27,26,26,26,25,25,24,24,24,23,23,23,22,22,22,21,21,20,20,20,19,19,19,18,18,18,17,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,12,12,12,11,11,11,11,10,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6,6,5,5,5,5,5,4,4,4,4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,13,13,13,14,14,14,14,15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20,21,21,22,22,22,23,23,23,24,24,24,25,25,26,26,26,27,27,27,28,28,29,29,29,30,30,30,31,31,32,32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,38,38,38,39,39,40,40,40,41,41,41,42,42,42,43,43,44,44,44,45,45,45,46,46,46,47,47,47,48,48,48,49,49,49,50,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,56,56,56,56,57,57,57,57,58,58,58,58,58,59,59,59,59,59,60,60,60,60,60,60,60,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63}; // SIN Phase current levels for microstepping. Zero = 32, min = 1, max = 63
unsigned char PhaseCurrentCopy[641];

void setup()
{
DDRB = 63; //Bit 0-5 as ouput on B and C
DDRC = 63;

EICRA=3; //External interrupt INT0
EIMSK=1;

PCICR = 4; //External interrupt PCINT2
PCMSK2 = 8;

pinMode(5, INPUT); // J1 (Step resolution)
pinMode(6, INPUT); // J2 (Step resolution)
pinMode(7, INPUT); // J3 (Step resolution)
pinMode(0, INPUT); // J4 = Power Save
pinMode(4, INPUT); // Enable

for(t=0;t<641;t++) // Copy phase current table
{
PhaseCurrentCopy[t] = PhaseCurrentOriginal[t];
}

D = digitalRead(3); //Read direction pin
if(D == 1)
{
Direction = 1;
}
else
{
Direction = -1;
}
MS = MSNew*Direction;

}

void loop()
{
J1 = digitalRead(5); // Read pin Step resolution bit 2
J2 = digitalRead(6); // Read pin Step resolution bit 1
J3 = digitalRead(7); // Read pin Step resolution bit 0
J4 = digitalRead(0); // Read pin Power Save
ENew = digitalRead(4); // Read pin Enable

StepResolution = J1*4 + J2*2 + J3; // Calculate step resolution setting
if(StepResolution == 0)MSNew = 1; // 1/128 Microstepping (less accurate)
if(StepResolution == 1)MSNew = 2; // 1/64 Microstepping (almost accurate)
if(StepResolution == 2)MSNew = 4; // 1/32 Microstepping (should be accurate)
if(StepResolution == 3)MSNew = 8; // 1/16 Microstepping (should be accurate)
if(StepResolution == 4)MSNew = 16; // 1/8 Microstepping (should be accurate)
if(StepResolution == 5)MSNew = 32; // 1/4 Microstepping (should be accurate)
if(StepResolution == 6)MSNew = 64; // 1/2 Microstepping (should be accurate)
if(StepResolution == 7)MSNew = 128; // 1/1 Microstepping (should be accurate)
if(MSNew != abs(MS)) // Step resolution has changed. Reset to step position 0 and activate step resolution.
{
StepNumber = 0;
MS = MSNew*Direction;
}

PowerSaveCount++; // Hold track of power save feature
PSNew = 100; // Power save time limit not reached, as default
if(PowerSaveCount > 30000)
{
PSNew = 90; // Power save time limit reached
}
if(PowerSaveCount > 35000)
{
PSNew = 80; // Power save time limit reached
}
if(PowerSaveCount > 40000)
{
PSNew = 70; // Power save time limit reached
}
if(PowerSaveCount > 45000)
{
PSNew = 60; // Power save time limit reached
}
if(PowerSaveCount > 50000)
{
PSNew = 50; // Power save time limit reached
}
if(PowerSaveCount > 55000)
{
PSNew = 40; // Power save time limit reached
PowerSaveCount--; // Stay at 55000
}

if(PSNew != PS) // Change in power save from old reading
{
if((PSNew != 100) and (J4 == 0)) // New power save mode is on
{
cli();
TempB = (((int)PhaseCurrentCopy[StepNumber]-32)*PSNew)/100+32;
TempC = (((int)PhaseCurrentCopy[StepNumber+128]-32)*PSNew)/100+32;
PORTB = TempB;
PORTC = TempC;
sei();
}
PS = PSNew; //Store new power save setting
}

if(ENew != E) //Enable has changed
{
if(ENew == 1) // Enable is set. Turn on power
{
for(t=0;t<641;t++)
{
PhaseCurrentCopy[t] = PhaseCurrentOriginal[t];
}
}
if(ENew == 0) // Enable is not set. Turn off power.
{
for(t=0;t<641;t++)
{
PhaseCurrentCopy[t] = 32;
}
}
E = ENew; // Store new enable setting
}
}

ISR(INT0_vect) // Step rising edge interrupt
{
cli();
PowerSaveCount = 0;
StepNumber += MS;
StepNumber &= 511; //if StepNumber >= 512, then StepNumber = Stepnumber - 512. If StepNumber <= 0, then StepNumber = StepNumber + 512.
//if(StepNumber > 511)
//{
// StepNumber = 0;
//}
//if(StepNumber < 0)
//{
// StepNumber = 511;
//}
PORTB = PhaseCurrentCopy[StepNumber];
PORTC = PhaseCurrentCopy[StepNumber+128];
}

ISR(PCINT2_vect) //Direction change interrupt
{
cli();
D = digitalRead(3);
//if((PORTD & 8 ) != 0) // digitalRead(3);
if(D) // digitalRead(3);
{
Direction = 1;
}
else
{
Direction = -1;
}
MS = MSNew*Direction;
}

This entry was posted in Under development. Bookmark the permalink.

30 Responses to LamjaStep – Bipolar Stepper Motor Driver

  1. James Newton says:

    Looks promising! Do you have an idea what it will cost to build? Other than the Arduino?

    • arvid says:

      I really don’t know, and it will vary if you build one or 4. Most expensive components are the Atmega328 with Arduino boot loader and MOSFET’s (about 8 x 2.00$). I use IRLI540NPBF. Then there are 2 opamps, 2 CD4011, 8 fast diodes, 150 resistors :-), some capasitors, small diodes, connectors, trim pots and some small transistors/mosfets.

  2. Arn says:

    Hi, would love to see those schematics and specs!

  3. abdullah says:

    Good job. I want to be Atmega328 program,please

  4. Deian says:

    Hi Arvid, I want to build an H-Bridge also, and I intend to use N-MosFet’s, looking on the web I found out that is not easy to drive a high-side MosFet, can you confirm if you tried the schematic for driver of the high-side MosFet? And also if you would explain what is the role of the 3 resistors of 1k which are connected to diode from 12V.
    Thank you,
    Deian,

    • arvid says:

      The high side driver will only work if the signal to the high-side is PWM or pulsating (on / off). This is because C109, D109 and (R133 || R134 || R172) work as a boost-strap. The voltage over C109 will charge to almost 12V (12-Vdiode). If the pulses stops, and the high-side only get a steady on or off, the charge in C109 will be reduced to zero. The 3 resistors R133,R134 and R172 are in paralell becuase of it will produce som heat. I think this will be about 150mW per resistor. The charge from C109 will then be used to drive the gate-source voltage for the mosfet. This is indeed the design I’m using in the video, on my actual build. It seems to be working pretty well, and I get about 10V or more over gate-source. As long there is a PWM for the high-side.

  5. Lam Pham says:

    Hi!

    I want to build a stepper motor driver like this too, I use atmega8 and program in assembly language.

    I got some questions here, hopefully you can give me the answers:
    – I use fast PWM (OCR1A, OCR1B) as 2 PWM channel and feed this to H bridge. But I see you created PWM from OPAM and other parts, does it the same or different? As your solution, which are advantage when compare with PWM built in of AVR?

    – PORTC and PORTB of your schematic connected with many resistors, can you explain the working theory?

    – What does your PWM frequency? I planned to use 20kHz PWM freq but not sure about those NAND gate can handle the switching rate…

    – If possible, can you share you algorithm of H-bridge control (constant current control or constant voltage control…?

    – I really like your acknowledge in power electronics, but I planned to use IR2112 and MOSFET for safer driving. I’ll use your NAND method for mixing PWM signal. Do you think it’s okay? (more cost, of course…)

    Looking to see your final achievement!

    Thanks & Best regard
    Lam Pham

    • arvid says:

      Hi.

      I started my design using the built in PWM also, but I could never get to control the current exactly. This is because the stepper motor has inductance. So when you turn on power to a coil, it should be constant voltage until the current reach its goal, then you can limit the current with PWM. This time is not constant, because the coil may already partially be energized. There was to many uncertain parameters, and the programming became to complicated. So I decided to move over to OPAMP’s.

      Port B and C is actually a 6-bit current control fo each coil. -100 to +100 current (0-63). So a output of 32 is zero ampere. The resistors are a R2R ladder (a digital to analog converter). These resistors translate the 6-bit output from the AVR to a voltage from 0 to 5 Volts.

      I then use the OPAMP’s to compare the voltage from the DAC to the voltage from the current sensor resistors. This results in total current control.

      The PWM is generated from the rule: Is current to high? Shut down current. Is current to low? Put more current in. (Mostly)

      The PWM frequency is the big pain in the … . If I do nothing, it will run at about 50 kHz and above. So i have used some filters to lower the PWM frequency. And it is not constant. At the current design it will vary from 15-25 kHz.

      I am a fan of NAND gates. It makes the H-bridge more safe.

      • Lam Pham says:

        Hi,

        I feel great, cause you’re clear my headache for a couple of days. I was thinking as I’ll just vary duty cycle of PWM to get xx% of current (sin wave value base on micro step). But even motor can run, I still feel it not right. (I dont have any idea about inductance of motor, cause it’s impossible to find the datasheet of my old motor..)

        And in your schematic, SENSEA and SENSEB, does it missing something? Cause I see your current resistor, but just one SENSEA/B input to OPAM. Don’t know where it come from..

        Btw, best wishes for your work and I’ll follow your site from this time 🙂
        Best regards,
        Lam Pham

        • arvid says:

          SENSEA/B is just a label. It’s not connected to any other SENSEA/B. The sense input is trough R135 and R137.

  6. Lam Pham says:

    Hi,

    Is there any update for this great controller, avrid?

    Bst regards,

  7. benmessaoud says:

    please send me schema and code

  8. benmessaoud says:

    I would like to use PIC16F628A

  9. endurasek says:

    Any progress?

  10. Dzeus says:

    Hi firstly thank you for sharing with us such as valuable info, I start playing with arduino and Id’ like to know how you managed to get PWM IN (Step/Dir input) working
    can you please point me to any doc/example (.pde)/forum where I can learn how it does work, I’d like to build a simple Stepper driver with l298 or your power stage design (which will allow micro-stepping) and drive it using step/dir just like any stepper driver IC do
    Any input about PWM-IN with Arduino is welcome
    Cheers

  11. fare_x says:

    Hi Arvid
    Thank you so much, gonna try to understand every part of your code ( well commented btw )
    Cheers

  12. Frans says:

    Hallo Arvid,

    Firts of all a very greet job!!!!!!
    So i have a question is there any progress with the new version of the stepper driver board and code.
    I think it is version V1.02 ore not????
    If it is finished is i possible to send me the schematics for the connection board and stepper driver board and the total code for teh atmega328.
    So i can see if it is possible for me to make 1 for my self.
    And what is the maximum current for the stpper driver waht thay can deliver
    I have here 4 stepper motors with a max current of 2.8Ampere.
    is it possible to use them with your design????

    greetings,

    frans

    • arvid says:

      Hi.

      The code 1.0 is the current code. I was not too happy with the pwm frequency in this current shematic design, so I’m working on a new one. It’s been taking some time, but I will post new shematics when I’m done. I guess this design can be scaled to almost any ampere. You only have to change the mosfets, maybe put several in paralell also.

    • arvid says:

      But the code I’m pretty happy with. Is can handle pretty fast steps…

  13. arvid says:

    Q: what is THB_INV, THA_INV use for? i saw it conect to gnd
    DMA, DMB jumper ??

    A: I guess you meant to comment on the Lamjastep-schematic. THx and THx_INV is just some labels, and is the Trigger High or Trigger Low signals. They determine how the decay for the PWM works. If THx (inverse of THx_INV) is on, the current error is so much that the current should reverse. if TL is on,its so low that it should reverse. In between, the H-bridge is just freewheeling. DMx jumper (Decay Mode) is used to disable the freewheeling part. DMx off = “Mixed kind of decay”, and on = “Fast decay”.

  14. eeks says:

    GJ!

    Great minds ect. had the same idea after extortionate prices for >1A drivers. Ouch the detail :thumbsup:

  15. pspbear says:

    Can i get the pcb layout from you .i want to make cnc table.i real want to ues you circuit.your pcb layout is so Perfect. can you update layout or send email to me please.

  16. Paul j says:

    It’s great job. Would you send me schematic file and program ?
    I used to use allegro chip. But my machine uses decimal micro step like 10th, 20th.
    So I am thinking of design my own driver. But you already studied a lot of bipolar micro step driver. I want study more your driver and would like to open my code for decimal micro step code.

  17. Cody says:

    Thank you for sharing your knowledge and designs. Very helpful!

  18. Bob says:

    Any progress on this design? It is so attractive being a discrete through hole alternative to the little black boxes that are impossible to solder!

    I’m waiting with baited breath for an update.

    Thanks for you work.

  19. zaky says:

    it would be amazing if you explain it just block by block in general, I kinda lost it where the pwm meets the current sensing. and for sure I completely lost it in the nand gates where the signal is divided to drive each couple of the mosfets

Comments are closed.