Control Stepper Motor Serial Monitor Arduino
Stepper Motor Serial Monitor Arduino Running At Same Time – Previously I have made an article about Keyes CNC Shield V4 GRBL and how to control via a4988 driver. But a problem arises when we can’t control the stepper motor to rotate at the same time.
Therefore, I tried several methods and using a timer interrupt was the right choice after learning the program code from the video Coordinated stepper motor control (arduino) and then I edited a few lines of the program and set the right interrupt to run on the Arduino Uno / Nano.
A4988 CNC Shield Stepper Motor Schematic
In this article, I still use the CNC Shield V4 a4988 as a stepper motor controller to facilitate this test. For the schematic, please look at the following picture:
The stepper motor has 4 wires, red blue green and black, please make sure you connect it to the CNC Shield correctly. If the connection is wrong, it could be that the stepper is spinning incorrectly. I have provided the program code for you below.
Arduino Code Motor Stepper
This is a program that I modified, you can copy and paste into your arduino IDE, then upload it to arduino.
#define X_DIR_PIN 2
#define X_STEP_PIN 5
#define X_ENABLE_PIN 8
#define Y_DIR_PIN 3
#define Y_STEP_PIN 6
#define Y_ENABLE_PIN 8
#define Z_DIR_PIN 4
#define Z_STEP_PIN 7
#define Z_ENABLE_PIN 8
#define X_STEP_HIGH PORTD |= (1 << X_STEP_PIN);
#define X_STEP_LOW PORTD &= ~(1 << X_STEP_PIN);
#define Y_STEP_HIGH PORTD |= (1 << Y_STEP_PIN);
#define Y_STEP_LOW PORTD &= ~(1 << Y_STEP_PIN);
//
#define Z_STEP_HIGH PORTD |= (1 << Z_STEP_PIN);
#define Z_STEP_LOW PORTD &= ~(1 << Z_STEP_PIN);
#define TIMER1_INTERRUPTS_ON TIMSK1 |= (1 << OCIE1A);
#define TIMER1_INTERRUPTS_OFF TIMSK1 &= ~(1 << OCIE1A);
byte a;
int x = 0;
int y = 0;
int z = 0;
String get_data[3];
String data;
int stringData;
String per_data;
struct stepperInfo {
// externally defined parameters
float acceleration;
volatile unsigned int minStepInterval; // ie. max speed, smaller is faster
void (*dirFunc)(int);
void (*stepFunc)();
// derived parameters
unsigned int c0; // step interval for first step, determines acceleration
long stepPosition; // current position of stepper (total of all movements taken so far)
// per movement variables (only changed once per movement)
volatile int dir; // current direction of movement, used to keep track of position
volatile unsigned int totalSteps; // number of steps requested for current movement
volatile bool movementDone = false; // true if the current movement has been completed (used by main program to wait for completion)
volatile unsigned int rampUpStepCount; // number of steps taken to reach either max speed, or half-way to the goal (will be zero until this number is known)
// per iteration variables (potentially changed every interrupt)
volatile unsigned int n; // index in acceleration curve, used to calculate next interval
volatile float d; // current interval length
volatile unsigned long di; // above variable truncated
volatile unsigned int stepCount; // number of steps completed in current movement
};
void xStep() {
X_STEP_HIGH
X_STEP_LOW
}
void xDir(int dir) {
digitalWrite(X_DIR_PIN, dir);
}
void yStep() {
Y_STEP_HIGH
Y_STEP_LOW
}
void yDir(int dir) {
digitalWrite(Y_DIR_PIN, dir);
}
void zStep() {
Z_STEP_HIGH
Z_STEP_LOW
}
void zDir(int dir) {
digitalWrite(Z_DIR_PIN, dir);
}
void resetStepperInfo( stepperInfo& si ) {
si.n = 0;
si.d = 0;
si.di = 0;
si.stepCount = 0;
si.rampUpStepCount = 0;
si.totalSteps = 0;
si.stepPosition = 0;
si.movementDone = false;
}
#define NUM_STEPPERS 3
volatile stepperInfo steppers[NUM_STEPPERS];
void setup() {
Serial.begin(9600);
pinMode(X_STEP_PIN, OUTPUT);
pinMode(X_DIR_PIN, OUTPUT);
pinMode(X_ENABLE_PIN, OUTPUT);
pinMode(Y_STEP_PIN, OUTPUT);
pinMode(Y_DIR_PIN, OUTPUT);
pinMode(Y_ENABLE_PIN, OUTPUT);
pinMode(Z_STEP_PIN, OUTPUT);
pinMode(Z_DIR_PIN, OUTPUT);
pinMode(Z_ENABLE_PIN, OUTPUT);
digitalWrite(X_ENABLE_PIN, LOW);
digitalWrite(Y_ENABLE_PIN, LOW);
digitalWrite(Z_ENABLE_PIN, LOW);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 1000; // compare value
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= ((1 << CS11) | (1 << CS10)); // 64 prescaler
interrupts();
steppers[0].dirFunc = xDir;
steppers[0].stepFunc = xStep;
steppers[0].acceleration = 1000;
steppers[0].minStepInterval = 250;
steppers[1].dirFunc = yDir;
steppers[1].stepFunc = yStep;
steppers[1].acceleration = 1000;
steppers[1].minStepInterval = 250;
steppers[2].dirFunc = zDir;
steppers[2].stepFunc = zStep;
steppers[2].acceleration = 1000;
steppers[2].minStepInterval = 250;
}
void resetStepper(volatile stepperInfo& si) {
si.c0 = si.acceleration;
si.d = si.c0;
si.di = si.d;
si.stepCount = 0;
si.n = 0;
si.rampUpStepCount = 0;
si.movementDone = false;
}
volatile byte remainingSteppersFlag = 0;
void prepareMovement(int whichMotor, int steps) {
volatile stepperInfo& si = steppers[whichMotor];
si.dirFunc( steps < 0 ? HIGH : LOW );
si.dir = steps > 0 ? 1 : -1;
si.totalSteps = abs(steps);
resetStepper(si);
remainingSteppersFlag |= (1 << whichMotor);
}
volatile byte nextStepperFlag = 0;
volatile int ind = 0;
volatile unsigned int intervals[100];
void setNextInterruptInterval() {
bool movementComplete = true;
unsigned int mind = 999999;
for (int i = 0; i < NUM_STEPPERS; i++) {
if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di < mind ) {
mind = steppers[i].di;
}
}
nextStepperFlag = 0;
for (int i = 0; i < NUM_STEPPERS; i++) {
if ( ! steppers[i].movementDone )
movementComplete = false;
if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di == mind )
nextStepperFlag |= (1 << i);
}
if ( remainingSteppersFlag == 0 ) {
OCR1A = 65500;
}
OCR1A = mind;
}
ISR(TIMER1_COMPA_vect)
{
unsigned int tmpCtr = OCR1A;
OCR1A = 65500;
for (int i = 0; i < NUM_STEPPERS; i++) {
if ( ! ((1 << i) & remainingSteppersFlag) )
continue;
if ( ! (nextStepperFlag & (1 << i)) ) {
steppers[i].di -= tmpCtr;
continue;
}
volatile stepperInfo& s = steppers[i];
if ( s.stepCount < s.totalSteps ) {
s.stepFunc();
s.stepCount++;
s.stepPosition += s.dir;
if ( s.stepCount >= s.totalSteps ) {
s.movementDone = true;
remainingSteppersFlag &= ~(1 << i);
}
}
if ( s.rampUpStepCount == 0 ) {
s.n++;
s.d = s.d - (2 * s.d) / (4 * s.n + 1);
if ( s.d <= s.minStepInterval ) {
s.d = s.minStepInterval;
s.rampUpStepCount = s.stepCount;
}
if ( s.stepCount >= s.totalSteps / 2 ) {
s.rampUpStepCount = s.stepCount;
}
}
else if ( s.stepCount >= s.totalSteps - s.rampUpStepCount ) {
s.d = (s.d * (4 * s.n + 1)) / (4 * s.n + 1 - 2);
s.n--;
}
s.di = s.d; // integer
}
setNextInterruptInterval();
TCNT1 = 0;
}
void runAndWait() {
setNextInterruptInterval();
while ( remainingSteppersFlag );
}
void loop() {
TIMER1_INTERRUPTS_ON
while (Serial.available() != 0)
{
data = Serial.readString();
for (a = 0; a < 3; a++)
{
get_data[a] = split(data, ' ', a);
if (get_data[a] != NULL)
{
if (a == 0)
{
x = get_data[0].toInt();
}
if (a == 1)
{
y = get_data[1].toInt();
}
if (a == 2)
{
z = get_data[2].toInt();
}
}
}
if (x != NULL)
{
prepareMovement( 0, x );
Serial.print("Stepper X : ");
Serial.print(x);
Serial.println(" steps");
}
if (y != 0)
{
prepareMovement( 1, y );
Serial.print("Stepper Y : ");
Serial.print(y);
Serial.println(" steps");
}
if (z != 0)
{
prepareMovement( 2, z );
Serial.print("Stepper Z : ");
Serial.print(z);
Serial.println(" steps");
Serial.println();
}
runAndWait();
x = 0;
y = 0;
z = 0;
}
}
String split(String data, char character, int pos)
{
stringData = 0;
per_data = "";
for (int i = 0; i < data.length() - 1; i++)
{
if (data[i] == character)
{
stringData++;
}
else if (stringData == pos)
{
per_data.concat(data[i]);
}
else if (stringData > pos)
{
return per_data;
break;
}
}
return per_data;
}
After that, open your Arduino Serial Monitor, and send the steps value in “x y z” format (make sure you are using space). For example, you want to rotate the stepper motor together with the steps position:
X is 30 steps
Y is 100 steps
Z is 360 steps
In the serial monitor form, please write 30 100 360, then Enter. You can see the motors spinning together.
To simplify this illustration, I attached a video of the program above below.
I understand that the program above still has many shortcomings, but it all went well and I have tested it with my stepper.
If this article is useful, please share it with your friends, hopefully this can help your project.
Have a nice day.