This is my first project working with stepper motors, so I may have a bit of a shaky understanding of the electronic side of the project. I'm trying to create a simple device with 4 buttons connected to a stepper motor driver/NEMA 17 motor, with the ability to increase/decrease RPM, switch motor spin direction, and run the motor with the button being held down. (LCD screen included for output as well)
I'm noticing that when changing the RPM on my motor, i.e. from 3.2 to 3.1 to 3.0, the speed of the motor is really slow, then when set to 3.0, suddenly increases a lot, but goes down when setting the RPM back down to 2.8 or so. This occurs multiple times along different intervals of RPMs that I have tested as well.
I think that there may be an issue with the method with which I am calculating the pulse delay given the RPM in my function setRPM() at the end of my code or with the way that I am micro-stepping, which I am doing because I need extremely precise movement.
Included below is the code that I used to run with my TMC2208 driver. (Sorry if it is rather long, but I included comments to document my thought process when programming the Arduino.)
Included list of RPMs and pulse delays
| rpm | pulse_delay (us) |
|---|---|
| 1 | 37500 (too fast) |
| 2 | 18750 (too fast) |
| 2.25 | 16666.6667 (works) |
| 2.5 | 15000 (works) |
| 3 | 12500 (works) |
| 5 | 7500 (works) |
| 10 | 3759 (works) |
// Stepper motor run code with TMC2208 driver and LCD code
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Stepper.h>
// Define Pins for Step Motor Driver
#define stepPin 7 // define pin for step
#define dirPin 8 // define pin for direction
#define enPin 9 // define pin for enable ^^ Do I need this?
// Define Buttons for Speed and Direction
#define buttGo 10 // define pin for button input
#define buttSwitch 11 // define pin for button input
#define incSpeed 12 // define button for increasing speed
#define decSpeed 13 // define button for decreeasing speed
// Define LCD pinout
const int en = 2, rw = 1, rs = 0, d4 = 4, d5 = 5, d6 = 6, d7 = 7, bl = 3; // Adapter Pinout
const int i2c_addr = 0x27; // Adapter pinout
LiquidCrystal_I2C lcd(i2c_addr, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE);
/* Formulas for determining RPM
Assuming one step per pulse, delay is: wait = (μs/min)(1/rpm)(1/pulses per revolution) - overhead
Overhead = extra time it takes to run digitalWrite twice and loop
RPM = (steps per second)/(steps per revolution) * (60 seconds/minute)
step angle = 1.8 degrees ; 200 steps per revolution ; .005 revolutions per step
Solving for Steps Per Second: SPS = (RPM)/((REV_P_STEP)*(60 sec/min))
According to Notes:
C0 = 15 * M_PI ; Motor X Circumference
XSPR = 1 * (200 * 11) ; Motor X Steps per Rotation
dVx = dS * (XSPR/C0) ; Desired X-Axis time from mm/s to x-axis steps/sec (dS represents desired x-axis speed)
Assuming we use a button/knob, each increment/decrement would change the mm/s by 0.1
So, to get the necessary pulse delay, increment/decrement by:
dVx = 0.1 * (1 * (200 * 11))/(15*M_PI)
Example: If we have an initial target rpm of 10, that gives us a dS of 10
As such, dVx which is our speed in steps will be 10 * (1 * (200 * 11)) / (15 * M_PI)
All of these variables are set globally
wait = (microsecondsPminute)(1/RPM)(1/pulsesPrevolution) - overhead
rpm / 60 = rps
60 / rpm = spr
(60 / rpm)/360 = spd
((60 / rpm)/360) * 1.8 = sps
Frequency = (RPM)/((Resolution/360)60)
Resolution = 360/(Steps/Revolution)
For us, Resolution = 360/(200); so Resolution = 1.8*
Frequency = (RPM)/(0.005 * 60) = RPM/(0.3)
T_inc = incremental torque produced with each microstep
T_hfs = holding torque (full-step operation)
SDR = step division ratio (number of microsteps per full step)
T_inc = T_hfs * sin(90/SDR)
T_inc = 0.14 * sin(90/256)
T_inc = 0.00085902385
*/
float resolution = 1.8;
float rpm = 10;
float pulse_delay; // Temporary starting value that will change after void setup
bool buttonState = false;
bool onState = false;
// Set up output types
void setup() {
Serial.begin(9600); // Change this baud rate to whatever rate the LCD screen runs on - should generally be 9600
// Set up LCD screen
pulse_delay = setRPM(rpm, resolution);
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.setCursor(0, 0);
lcd.print("Current RPM: ");
lcd.setCursor(12, 0);
lcd.print(rpm);
lcd.setCursor(0, 1);
lcd.print("MOVE RIGHT");
// Sets up buttons
pinMode(buttSwitch, INPUT);
pinMode(buttGo, INPUT);
pinMode(incSpeed, INPUT);
pinMode(decSpeed, INPUT);
// Establish initials
pinMode(enPin, OUTPUT);
digitalWrite(enPin, HIGH); // deactivate driver
pinMode(dirPin, OUTPUT);
digitalWrite(dirPin, HIGH);
pinMode(stepPin, OUTPUT);
digitalWrite(enPin, LOW); // activates driver
}
// Revolutions per second should be able specified to the tenths
// Run code to continual
void loop() {
// Read Buttons Being Pressed
int pressSwitch = digitalRead(buttSwitch);
int pressGo = digitalRead(buttGo);
int pressInc = digitalRead(incSpeed);
int pressDec = digitalRead(decSpeed);
pulse_delay = setRPM(rpm, resolution); //pulse_delay = (rpm * 200)/60;
if (pressSwitch == HIGH) // Moves motor Right (Counter-Clockwise)
{
if (buttonState == 0) {
digitalWrite(dirPin, LOW); // sets direction of the motor turning
buttonState = 1;
lcd.setCursor(0, 1);
lcd.print("MOVE LEFT");
delay(500);
}
else {
if (buttonState == 1) {
digitalWrite(dirPin, HIGH); // sets direction of the motor turning
buttonState = 0;
lcd.setCursor(0, 1);
lcd.print("MOVE RIGHT");
delay(500);
}
}
}
if (pressGo == HIGH) // Moves motor
{
digitalWrite(stepPin, HIGH); // This LOW to HIGH change is what creates the "Rising Edge" so the easydriver knows when to step.
delayMicroseconds(pulse_delay);
digitalWrite(stepPin, LOW);
}
if (pressInc == HIGH) // Increases RPM
{
rpm = rpm + 0.1;
delay(150);
lcd.setCursor(12, 0);
lcd.print(rpm);
}
if (pressDec == HIGH) // Decreases RPM
{
rpm = rpm - 0.1;
delay(150);
lcd.setCursor(12, 0);
lcd.print(rpm);
}
}
// Function to get pulse delay time based off of inputted RPM value
float setRPM(float rpm, float resolution) {
/*
float temper = ((60 / rpm) / 360);
float pulsetime = ((temper * resolution) * 1000000);
1 rpm = 37500 us delay (too fast)
2 rpm = 18750 us delay (too fast)
2.25 rpm = 16666.6667 us delay
2.5 rpm = 15000 us delay
3 rpm = 12500 us delay
5 rpm = 7500 us delay
10 rpm = 3750 us delay
/
unsigned int pulsetime = (60000000 / (1600 rpm));
return pulsetime; // Converts to microseconds
}