Recently, I took a stab at trying to write a stepper motor driver for an Arduino, mainly to see what it would entail to get precise control for all three axes of the CNC and do other tasks as well. I have seen a lot of short example Arduino programs around online to do this, but usually only for one motor. To my surprise (or not), creating three, asynchronous pulse trains for the stepper motors, while simultaneously making the necessary computations for each block of g-code, was a bit more difficult to do, mainly due to the processing limitations of the Arduino. This is especially a problem if you want to anything else on top of this, like an LCD display, joystick manual control, etc.
To create precisely timed, asynchronous pulse trains requires interrupt programming, where you commandeer one or both of the PWM timers in the Arduino to use as an internal clock, independent of the main clock, to time the pulses. Interrupt programming basically counts down from a user-specified time and when reaching zero: pauses the main program, runs the interrupt code, which in this case is a single stepper pulse, then resumes the main program. But, along with being more difficult to program for, the main drawback is the more interrupts you have, the slower the main code runs.
For my Sherline DIY CNC, the frequency of the interrupt pulse trains can cause a problem. Suppose we look at the worst case scenario and we set the maximum feedrate to 20 inch/min for all three axes. With a 20 threads/inch leadscrew and 200 steps/rev stepper motor in full step mode, each stepper control pulses require a rate of 20*20*200/60 = 1333.3 step/sec. Not too bad. But, if we’d like to microstep the stepper motors at 1/4 steps, each stepper pulse rate goes up to 4 pulse/step * 1333.3 step/sec = 5333.3 pulse/sec. That’s 5.3 kHz… for one motor. All together, the Arduino needs to supply an asynchronous pulse train to all three motors at rate of roughly 16 kHz, or every 63 microseconds.
So, now the question is how much processing time is there left to still run the main program to load, interpret, and execute the next g-code block and other tasks. In short, not much. Allegro-based stepper motor drivers have a minimum 1 microsecond pulse rise and fall to process and read a step has occurred. So, let’s set a relatively conservative pulse total time at 15 microseconds to ensure the pulse is received, and the interrupt program has a 5 microsecond overhead. This leaves about 43 microseconds between pulses for the main program to run. Given that the Arduino runs at 16MHz (16 CPU cycles per microsecond), the main program will only go through very short segments of program execution and will effectively run at most 68% of normal speed. If you decide to microstep at 1/8 step, things go from bad to worse, where the main program will run at most 37.5% speed (under the same assumptions). Additionally, this is assuming that internal interrupt handling during runtime is 100% efficient, which is likely not the case.
Although this is only a worst-case scenario, this just shows how the Arduino has the potential of struggling to multi-task with high-frequency pulse trains and other desired tasks in certain situations. If the Arduino can’t keep up, what will likely happen is stalling during CNC operation between g-code blocks, motor jitter which can lead to skipped steps due to rapid stops and accelerations, or the Arduino itself crashing and not finishing the job, where you then have to figure out how to reset your g-code to start in a middle of the program while likely having lost your reference point. This is not good for you, on the part or the tool, let alone finish of a machined part (a stall causes the cutting tool to dwell, leaving unsightly tool marks).
So, where does this leave me? Sure, it’s very possible to write code that will do everything that I’d like an Arduino to do. Sure, if the steppers jitter, it might not be that bad. BUT, just looking at this interrupt issue makes me cringe at the thought of how much time programming and testing and programming and debugging and programming and etc,etc. will be involved in making sure the code is solid and dependable. Plus, with the great possibility that the Arduino compilers doesn’t optimize well, it will likely have be written in a traditional programming language, as in C++. But, luckily somebody already has. Thank you, Simen!
Grbl (github,blog), written by Simen S.S. of Norway, is an open-source g-code interpreter for driving stepper motors on standard 328 Arduinos. It’s written in C to create a highly optimized, efficient, stable controller capable of independently driving 3 stepper motors, for up to 30k (according to site) jitter-free step pulses. It’s well-tested, handles acceleration and deceleration, queues up to 20 g-code blocks, and even with a planner to anticipate and execute efficient movements.
Although the code itself is beautifully written and well commented, there isn’t much information yet on how it works and how to interface with it for those of you that can’t read C code. In my next post, I’ll take a shot at it for the rest of us out there.
For me, grbl is simple and nearly the perfect solution, but can be difficult to implement into my CNC system, if I want to modify the code for my own personal changes, i.e. head-less control, manual control via joystick, adjusting feedrates, LCD display with read-outs on position, read-write SD-card with g-code, etc. Meaning that for every new release, the new code must be re-modified for each of my personal changes and then thoroughly re-proofed that it did not screw up the performance of motor pulse trains. Not to mention, this must all be written in C as well, since Simen’s code cannot be efficiently translated to Arduino language. Rather than deal with this, I like Ed’s two controller solution (Ed’s Life Daily), where he uses one ‘slave’ grbl Arduino dedicated to g-code and motor control and another ‘master’ Arduino for reading an SD-card and streaming the ‘slave’ the g-code commands. This keeps grbl and the user interface independent of each other and resistant to any other new features that Simen decides to add in the future. But, rather than just streaming code, you could program the ‘master’ Arduino in the simpler Arduino environment using their standard libraries to just perform the majority of tasks that you would want headlessly, which, IMO is an order of magnitude easier and simplier to program and maintain.
As the post title states, why reinvent the wheel?
UPDATE: First, let me say that the conclusions of this post need some clarification. A little over a year since I wrote this post, much has happened, such as becoming a main developer for Grbl, and I have learned quite a lot. When this was written, I came from a background of interpreted programming languages, like Matlab and Python, where programs run much like scripts, direct and linear, with little to no multitasking. The same could be said with the Arduino IDE in the way its designed to work easily for beginning users. The point is that, even though an Arduino and other microcontrollers can easily perform high frequency stepper pulses, a fully functioning and reliable CNC controller requires advanced algorithms and techniques to account for how to handle everything else, such as the incoming serial data stream, parsing the data, planning the accelerations (which you have to do with the open-loop control of the steppers), and add any other features like real-time status reports and control switches. If the controller either can’t keep a steady stream of stepper pulses or can’t keep up with feeding the steppers the correct motions, the steppers will likely lose steps (hence position) and crash into something. One of the many of Grbl’s tricks to solve the problem described in this post is by combining all of the different axes stepper pulses with a Bresenham line algorithm into one stepper pulse ‘tick’, rather than three different ones, and manage the memory and data flow very efficiently. As a part of Grbl, we’re constantly pushing the limits of what the little Arduino can do and adding more features all the time. If you’d like to learn more, contribute, or would like to see a new feature added to Grbl, please visit us at Github.