In the lesson I talk about my StepMotor library, designed to control unipolar and bipolar stepper motors.
Previous lesson List of lessons Next lesson
The previous lesson was about connecting unipolar stepper motors to the Arduino board and the standard Stepper library.
In my opinion, the Stepper library can only be used to demonstrate and test the operation of motors. In practical applications, the use of this library is extremely limited due to a number of disadvantages:
- when controlling the motor, the Stepper functions suspend the program, take all the computing resources of the microcontroller;
- only one motor can operate at a time;
- not all basic winding switching modes are supported;
- limitedfunctionality.
I developed my StepMotor library, which works in a parallel process, can control several stepper motors at the same time, supports all possible modes.
StepMotor library.
From the standard Stepper library, my library is distinguished by:
- works in a parallel process, does not block the execution of the program;
- can control several motors simultaneously;
- supports step, half step and between step modes;
- when the motor is stopped, it supports the following modes: complete shutdown of phases and with a holding current for fixing the position of the rotor;
- uses a minimum of microcontroller performance resources;
- takes up little FLASH and RAM.
Download the StepMotor.h library at this link.
How to install is written in several previous lessons, for example, in lesson 9.
Description of the StepMotor class.
I give only public methods.
classStepMotor {
public:
StepMotor(byte pinA, byte pinB, byte pinC, byte pinD); // constructor
void control(); // control, the method should be called regularly with the maximum frequency of phase switching
void step(int steps); // initiates the motor rotation by a specified number of steps
void setMode(byte stepMode, boolean fixStop); // sets the phase switching and stop modes
void setDivider(int divider); // setting the frequency divider for phase switching
int readSteps(); // read the remaining steps
} ;
The methods
void control ()
The method should be called regularly in a parallel process. The frequency of the method call determines the frequency of the motor phase switching. The frequency divider set by the setDivider function divides precisely the frequency of the control() call.
// interrupt handler
voidtimerInterrupt () {
myMotor.control (); // motor control
}
void step (int steps)
Initiates engine rotation by a specified number of steps. When the engine is operating in half-step mode, this refers not to the physical steps of the motor, but to half steps. An argument with a positive value causes counterclockwise rotation; a negative argument causes the motor to rotate clockwise.
By starting the engine rotation with the step() function, for example
myMotor.step (400); // take 400 steps counterclockwise
the program can do other tasks. The engine will stop by itself. If necessary, you can stop the motor at any time with the command
myMotor.step (0); // stop the motor
You can set a new number of steps without waiting for the motor to stop. For example, for continuous rotation, you should periodically send a command with a large number of steps
myMotor.step (32000); // permanent rotation
The program can find out that the motor has stopped using the readSteps() function.
void setMode (byte stepMode, boolean fixStop)
The method sets the switching mode of the motor phases and the state of the stopped motor.
- stepMode - motor control mode.
stepMode | Mode |
0 | Step |
1 | Half step |
2 | Between steps |
You can find information about switching modes of stepper motor windings at this link.
- fixStop-determines the mode of the stopped motor.
fixStop | Motor stopped state |
false | The voltage on the windings is off, the motor is unlocked |
true | The holding current continues to flow through the windings, the rotor is in a fixed position |
myMotor.setMode (0, false); // step mode, without fixing when stopped
myMotor.setMode (1, true); // half-step mode, with the rotor fixed when stopped
void setDivider (int divider)
The method sets the frequency division coefficient for calling the control() function, which means that it determines the motor speed. You can use the following formula for calculations:
Rpm = 60,000 / (divider * Tcontrol * Nmotor)
- Rpm - rotation speed in revolutions per minute;
- Tcontrol – period of calling the control() method in ms;
- Nmotor - the number of motor steps per full revolution.
For half-step mode, the formula changes slightly.
Rpm = 30,000 / (divider * Tcontrol * Nmotor).
For instance,
myMotor.setDivider (21); // frequency divider 21 (when interrupting 1 ms, the phase switching period is 21 ms)
means that the phases will switch every 21 ms. This corresponds to the rotation speed:
Rpm = 60,000 / (21 * 1 * 48) = 59.5 rpm or approximately one revolution per second.
int readSteps()
This method allows you to find out the number of steps remaining before the motor stops. If it returns 0, it means that the engine has stopped.
if (myMotor.readSteps() == 0) {
// the motor stopped
}
Examples of using the StepMotor library.
The library must be downloaded and installed.
To test the examples, I used the PM35S-048 motor and the driver from the previous lesson. The motor has 48 steps per revolution.
In the next lesson, I will connect a more powerful motor through the same driver.
Here is an example sketch of a program that rotates the engine all the time.
// stepper motor control program using StepMotor library
// the stepper motor rotates all the time counterclockwise at a speed of 60 rpm
#include <MsTimer2.h>
#include <StepMotor.h>
StepMotor myMotor(10, 11, 12, 13); // create an object of type StepMotor, set pins for the phases
void setup() {
MsTimer2::set(1, timerInterrupt); // set the timer interrupt period to 1 ms
MsTimer2::start(); // enable timer interrupt
myMotor.setMode(0, false); // step mode, without fixing when stopped
myMotor.setDivider(21); // frequency divider 21 (when interrupting 1 ms, the phase switching period is 21 ms)
}
void loop() {
myMotor.step(1000);
}
//-------------------------------------- 1 ms interrupt handler
void timerInterrupt() {
myMotor.control(); // motor control
}
In the loop() cycle, the step() method is called periodically, preventing the motor from stopping. If the number of steps in the argument of the step() function is set to be large, for example, 30000, then calling the method is enough every few seconds. The rest of the time can be used for other tasks.
Here is a sketch of an analogue of the program from the previous lesson. The motor makes 5 revolutions counterclockwise, then a pause of 1 second. Another 5 turns clockwise, pause 1 second, and so on in an infinite loop.
// stepper motor control program using StepMotor library
// the motor makes 5 revolutions counterclockwise,
// then a pause of 1 second, another 5 turns clockwise,
// pause 1 second, and so on in an infinite loop
#include <MsTimer2.h>
#include <StepMotor.h>
StepMotor myMotor(10, 11, 12, 13); // create an object of type StepMotor, set pins for the phases
unsigned int timeCounter; // time counter
byte md; // mode: 0 - rotation counterclockwise, 1 - pause, 2 - rotation clockwise, 3 - pause
void setup() {
MsTimer2::set(1, timerInterrupt); // set the timer interrupt period to 1 ms
MsTimer2::start(); // enable timer interrupt
myMotor.setMode(0, false); // step mode, without fixing when stopped
myMotor.setDivider(21); // frequency divider 21 (when interrupting 1 ms, the phase switching period is 21 ms)
md= 0; // initial mode
myMotor.step(240); // initial start
}
void loop() {
// control motor rotation
if (md == 0) {
// five turns counterclockwise
if (myMotor.readSteps() == 0) { md=1; timeCounter=0; }
}
else if (md == 1) {
// pause 1 sec
if (timeCounter >= 1000) { md=2; myMotor.step(-240); }
}
else if (md == 2) {
// five turns clockwise
if (myMotor.readSteps() == 0) { md=3; timeCounter=0; }
}
else {
// pause 1 sec
if (timeCounter >= 1000) { md=0; myMotor.step(240); }
}
}
//-------------------------------------- 1 ms interrupt handler
void timerInterrupt() {
myMotor.control(); // motor control
timeCounter++; // time counter
}
The motor control software block is placed in the loop() cycle, but it does not suspend the program and takes a minimum of time. The rest of the time can be used for other tasks. In principle, this block can be placed in the interrupt handler.
The StepMotor library counts down the phase switching time from the period when the control() function is called. The control() function is usually called by a timer interrupt. The interruption period determines the motor speed according to the formula described above:
Rpm = 60,000 / (divider * Tcontrol * Nmotor).
It can be seen that the dependence of the rotation speed on the period of calling the control() method is nonlinear, with a hyperbolic dependence. For example, for the engine from the previous lesson (48 steps per revolution) and the call period of the control() method of 1 ms, we obtain the following speeds.
divider | Rotation speed, rpm |
1 | 1250 |
2 | 625 |
3 | 417 |
4 | 312 |
5 | 250 |
6 | 208 |
7 | 179 |
8 | 156 |
. . . | . . . |
For small values of the divider, the rotation speed is set in significant increments. When the divider coefficient is increased, the step decreases. That is, if you need smooth speed control, you should reduce the period of calling control() and increase the value of the divider. And the MsTimer2 library allows you to generate an interrupt with a minimum period of 1 ms. For interrupts with a shorter period, there is another timer control library.
TimerOne library.
The library uses timer 1 and allows you togenerate interrupts with a period from 1 μs to 8.4 seconds. The resolution of the time setting is 1 μs.
In addition, it controls the PWM modes on the microcontroller pins, but now we will use TimerOne only for the formation of time intervals.
Working with the library is quite simple. You can download it from this link TimerOne.zip.
TimerOne Methods.
void initialize (long microseconds)
The method initializes the timer. Must be called before using any other method.
The microseconds argument sets the timer period in microseconds. The default is 1 sec.
Timer1.initialize (250); // initialization of timer 1, period 250 μs
void setPeriod (long microseconds)
The method sets the timer period in the range of 1 μs ... 8.4 sec.
Timer1.setPeriod (250); // period 250 μs
void start ()
The method starts the timer from a new period. Should be used if the timer has been stopped.
void stop ()
The method stops the timer.
void restart ()
Restarts the timer from a new period.
void attachInterrupt (void (* isr) (), long microseconds)
The method calls a function with a specified interrupt period.
Timer1.attachInterrupt (timerInterrupt, 250); // set the interrupt handler of the timerInterrupt function
void detachInterrupt ()
The method disables interruption.
Timer1.detachInterrupt (); // disable timer 1 interrupt
unsigned long read ()
The method allows you to read the current timer time.
void pwm (char pin, int duty, long microseconds)
Generates a PWM signal on the pin specified by the pin argument. Pins 9 and 10 can be used. PWM duty cycle is set by the duty argument in the range 0 ... 1023.
void setPwmDuty (char pin, int duty)
Sets the value of the PWM duty on the output.
void disablePwm (char pin)
Disables PWM mode for output.
Here is an example sketch of a program using a timer that rotates the motor all the time.
// stepper motor control program using StepMotor library
// the stepper motor rotates all the time counterclockwise at a speed of 60 rpm
#include <TimerOne.h>
#include <StepMotor.h>
StepMotor myMotor(10, 11, 12, 13); // create an object of type StepMotor, set pins for the phases
void setup() {
Timer1.initialize(250); // initialization of timer 1, period 250 μs
Timer1.attachInterrupt(timerInterrupt, 250); // set the interrupt handler
myMotor.setMode(0, false); // step mode, without fixing when stopped
myMotor.setDivider(83); // frequency divider
}
void loop() {
myMotor.step(1000);
}
//-------------------------------------- 250 μs interrupt handler
void timerInterrupt() {
myMotor.control(); // motor control
}
You can't set the time of the timer interrupt period too small, otherwise the program will only handle interrupts. For Arduino UNO R3 in simple applications, I would limit it to 100 μs. For more complex tasks, it is better to increase the timer period.
If at the same time interrupts with a lower frequency are required, for example, for the regeneration of LED display, then it can be implemented by program timers in the interrupt handler. We must strive to ensure that the system has only one interrupt from the timer with the highest frequency, and the rest are formed by program timers.
In the next few lessons, we will use the StepMotor library to control both unipolar and bipolar stepper motors.
In the next lesson, we will develop a program for the stepper motor driver on Arduino and learn how to control it from a computer.
Hi!
Your code and tutorial is awesome!
Is there any idea how to make acceleration to motor (without it I can have step loss) with multiple axes?
Hi!
On the forum of my Russian-language site, someone added acceleration to my library for a STEP/DIR driver. I have not tested. http://mypractic-forum.ru/viewtopic.php?t=133
Very informative series especially on stepper motors.
I am looking forward to you publishing (in English) the next part on using Stepper Drivers like the A4988 and the DRV 8825
Dear Sir,
I have no words to appreciate you.
Sir, How to map StepMotor library with drivers like A4988.
Hi!
For STEP/DIR drivers I have another library StepDirDriver.h. I will post it a little later.
Here is a link to the Russian version http://mypractic.ru/urok-35-podklyuchenie-stepdir-drajverov-shagovyx-dvigatelej-k-arduino-biblioteka-stepdirdriver.html.
In the coming days, I will continue to publish new lessons.