I will ask my student to take some pictures and will post them later tonight or tomorrow, whenever he gets them to me.
OK, here’s the whole thing. In the photos, one of the photos shows the joystick control.
This code had previously been used to control a “classic” NEMA-17 stepper, using the A4988 board (also from Pololu). It worked perfectly. So we modified the code (the command table previously only went to 1/16, and it had MS1, MS2 and MS3 members; the new command table has more entries and uses the symbols from HighPowerStepper.h). So this code has been tested and works with another board. However, various attempts to cause the 36V4 to control the NEMA-23 stepper motor have been fruitless.
#include <SPI.h>
#include <HighPowerStepperDriver.h>
HighPowerStepperDriver driver;
//#undef F
//#define F(x) x
static const int vrxAPin = A7;
static const int vryAPin = A6;
static const int swAPin = 7;
static const int vrxBPin = A4;
static const int vryBPin = A1;
static const int swBPin = 6;
static const int DriverSelectPin = 5;
static const int potPin = A0;
static int fractionStep = 8;// step interval is 1/fractionStep
static int stepsPerRevolution =200* fractionStep;
static const int moveFast = 500;
static const int moveSlow = 800;
static const int moveVariable = -1;
static char stepMode = ' ';
static int moveDirection = 1;
static int minWait = 50;
static int maxWait = 3000;
//static const int driftX = 8;
//static const int driftY = 8;
static bool useJoystick = true;
static const int MAXTEST = 100;
/*************************************************************************
* The nominal zero positions of the joysticks. These will be recomputed
* by the calibrate() procedure
*************************************************************************/
static int vrxAZero = 508;
static int vryAZero = 516;
/**************************************************************************
* class Joystick
**************************************************************************/
class Joystick {
public:
int XPin; //analog input pin
int YPin; //analog input pin
int swPin; // switch pin
int XZero; // X Zero value
int YZero; //Y Zero value
int Xdrift;
int Ydrift;
Joystick ( int XP,int YP,int SPi,int XZ = 508,int YZ = 516,int XD = 8, int YD = 8)
{
XPin = XP;
YPin = YP;
swPin = SPi;
XZero = XZ;
YZero = YZ;
Xdrift = XD;
Ydrift = YD;
}
void calibrate()
{
XZero = average(XPin);
YZero = average(YPin);
}
void showcalibrate()
{
Serial.print(F("x = "));
Serial.print(XZero);
Serial.print(F(" y = "));
Serial.print(YZero);
}
/*******************************************************************
* Joystick::getXWait
* Inputs:
* bool & nothing: Set 'true' if there is nothing to do
* bool & direction: Set 'true' for positive motion, false for negative motion
*
* Effect:
* Computes the "wait time" to control the speed of the X-axis
* This is done by reading the X-position potentiometer, and computing
* the absolute value from the zero point
* X0
* 0 | 1023
* V
* -X0...................1023-X0
* |
*********************************************************************/
int getXWait(bool & nothing, bool & direction)
{
int vrx = analogRead(XPin);
int speed = map( vrx, 0, 1023, - XZero, 1023 - XZero);
int abspeed = abs(speed);
nothing = abspeed == 0;
int limit = vrx < XZero ? XZero : 1023 - XZero;
int wait = map (abspeed, 0, limit, maxWait, minWait);
direction = speed > 0;
return wait;
}
int getX ()
{
return analogRead(XPin);
}
int getY ()
{
return analogRead(YPin);
}
int getsw ()
{
return analogRead(swPin);
}
/************************************************
* inDriftX, inDriftY
* Inputs:
* int x (or y): The x- (or y-) coordinate read from the joystick
* Result: bool
* true if the value is within the nominal
* "drift" of the zero point
************************************************/
bool inDriftX(int x )
{
return (abs(x - XZero) <= Xdrift);
}
bool inDriftY ( int y )
{
return (abs(y - YZero) <= Ydrift);
}
protected:
int average(int Pin)
{
long Center = 0;
for(int i = 0; i < MAXTEST; ++ i)
{
Center += analogRead(Pin);
}
return Center / MAXTEST;
}
}; // class Joystick
Joystick JSleft(vrxAPin,vryAPin,swAPin);
Joystick JSright(vrxBPin,vryBPin,swBPin);
static bool debug = false;
static bool showStep = false;
/***********************************************************
* uSteps
*
* This is the table of microstep values and the corresponding
* command names. This table will be printed out as part of the
* 'help' message
***********************************************************/
typedef struct {
char name; // command letter
int minWait; // minimum delay in microseconds
HPSDStepMode Mode; // the value to use in setStepMode
int fraction; // human-readable fraction number
} MSTABLE;
MSTABLE uSteps[] = {
//cmd |minWait| Mode | fraction
//-----+-------|---------------------------+----------
{'w', 530, HPSDStepMode::MicroStep1, 1}, // [0]
{'h', 220, HPSDStepMode::MicroStep2, 2}, // [1]
{'q', 70, HPSDStepMode::MicroStep4, 4}, // [2]
{'e', 0, HPSDStepMode::MicroStep8, 8}, // [3]
{'s', 0, HPSDStepMode::MicroStep16, 16}, // [5]
{'V', 0, HPSDStepMode::MicroStep32, 32}, // [6]
{'X', 0, HPSDStepMode::MicroStep64, 64}, // [7]
{'Y', 0, HPSDStepMode::MicroStep128, 128}, // [8]
{'Z', 0, HPSDStepMode::MicroStep256, 256}, // [9]
{0} // EOT // [10]
};
static int offset = 0;
/*************************************************************
* calibrate
*
* Effect:
* Computes the "at rest" position of the joystick potentiometer
* It is nominally 512, but there is no guarantee
* Notes:
* This is currently called by explicit user command. In a
* user-input-free environment, this should probably be called
* from setup() to establish the current best-guess value
**************************************************************/
void calibrate()
{
Serial.println(F("CALIBRATION STARTING. DO NOT TOUCH JOYSTICK"));
JSleft.calibrate();
//JSright.calibrate();
Serial.print(F("Calibration complete"));
Serial.println();
JSleft.showcalibrate();
//JSright.showcalibrate();
Serial.println();
} // calibrate
/*******************************************************************
* setStep
*
* Inputs:
* char step: The step interval, one of the commands in the table
* Result: bool
* true if the step mode was set successfully, or is already correct
* false if the step mode is not recognized
********************************************************************/
bool setStep(char step)
{
if(step == stepMode)
return true; // not changed because already set
// Look up the command in the table
for(int i = 0; uSteps [i].name != 0 ; i++)
{ /* search step table */
if(uSteps[i].name == step)
{ /* valid command */
driver.setStepMode(uSteps[i]. Mode);
stepMode = step;
fractionStep = uSteps[i].fraction;
stepsPerRevolution = 200 * fractionStep;
minWait = uSteps [i].minWait + offset;
minWait = max(minWait,0);
if(debug)
{ /* debug output */
Serial.print(F("step size set to '"));
Serial.print(step);
Serial.print(F("' 1/"));
Serial.print(fractionStep);
Serial.println();
} /* debug output */
return true; // successfully changed
} /* valid command */
} /* search step table */
return false;
} // setStep
/****************************************************************************
* getVariableWait
* Inputs:
* bool & nothing: set to 'true' if nothing to be done
* bool & direction: 'true' for 'forward', 'false' for 'backward'
* Result: int
* a variable Wait interval,in microseconds, based on the potentiometer
* a negative value indicates a reverse direction
* Notes:
* In potentiometer mode, direction is set by the 'r' command and is ignored
* In potentiometer mode, nothing is meaningless and is not set
*
* 'forward' and 'backward' are defined by how the motor is wired in
* (phase and phase orientation matter) and therefore cannot be said to
* be "clockwise" or "counterclockwise", but simply two directions which
* are opposite
*****************************************************************************/
int getVariableWait(bool & nothing, bool & direction)
{
if (!useJoystick)
return map( analogRead(potPin), 0, 1023, minWait, maxWait);
// For the joystick, read the joystick x,y values
return JSleft.getXWait(nothing, direction);
} // getVariableWait
/**********************************************************************
* move
* inputs:
* int motor: the motor number(1 = x, 2 = y,3 =z)
* long distance: number of steps to move ( + = cw - = ccw)
* int wait: The delay between steps
* If < 0, reads the potentiometer or joystick
* If > 0, the actual delay time in ms
* Notes:
* At the moment, since there is only one motor, the motor parameter
* is ignored.
**********************************************************************/
void move(int motor, long distance, int Wait)
{
driver.setDirection( distance >= 0 ? true : false);
// Serial.println(distance);
for (long i = 0; i < abs(distance); i++) {
if (Serial.available() )
return; // If user has typed a command, abort the loop
bool nothing;
bool direction;
int loopWait = Wait > 0 ? Wait : getVariableWait(nothing, direction);
if( useJoystick)
{ /* joystick motion */
static int lastloopWait = 0;
static int lastnothing =-1;
static int lastdirection = -1;
if (debug && (nothing != lastnothing ||
direction != lastdirection ||
loopWait != lastloopWait))
{ /* trace */
Serial.print(F("loopWait = "));
Serial.print(loopWait);
lastloopWait = loopWait;
Serial.print(F(" nothing = "));
Serial.print(nothing);
lastnothing = nothing;
Serial.print(F(" direction = "));
Serial.print (direction);
lastdirection = direction;
Serial.println();
} /* trace */
if (nothing)
return;
driver.setDirection( direction ? true : false);
} /* joystick motion */
driver.step();
if (showStep)
{
static int count = 0; // Keep lines short
count++;
if(count % 50 == 0) // if value modulo is zero, we have printed out enough on this line
Serial.println();
Serial.print(direction ? F(">") : F("<"));
}
}
} // move
/******************************************************************
* setup
* Initializes the various states required
*
******************************************************************/
void setup() {
SPI.begin(); // Required for Pololu 36V4 driver board
Serial.begin(115200);
pinMode(DriverSelectPin,OUTPUT);
delay(1);
//Serial.print(1);
driver.setChipSelectPin(DriverSelectPin);
//Serial.print(2);
driver.resetSettings();
driver.clearStatus();
//Serial.print(3);
driver.setCurrentMilliamps36v4(4000);
//Serial.print(4);
driver.enableDriver();
pinMode(vrxAPin,INPUT);
pinMode(vryAPin,INPUT);
pinMode(swAPin,INPUT_PULLUP);
Serial.println(F("type m for help"));
} // setup
static const int Wait = 500;
char ticket = 's';
bool changed = false;
bool query = false;
static const long shortRevs = 2;
static const long longRevs = 100;
static const int dt = 10;
/********************************************************************
* help
*
* Effect:
* Prints out the help message to remind us of the commands
********************************************************************/
void help()
{
Serial.println(F(__FILE__));
Serial.println(F(__DATE__ __TIME__));
Serial.println(F("m this message"));
Serial.println("? current status");
Serial.print(F("+ add "));
Serial.print(dt);
Serial.println(F("us to step wait time"));
Serial.print(F("- subtract "));
Serial.print(dt);
Serial.println(F("us from step wait time"));
Serial.println(F("c calibrate joystick"));
Serial.println(F("d toggle debug Mode"));
Serial.println(F("D toggle show-step Mode"));
Serial.println(F("F clear faults"));
if(! useJoystick)
Serial.println ( F("j set Joystick mode"));
if(useJoystick)
Serial.println ( F("p set potentiometer mode "));
if(! useJoystick)
Serial.println(F("r reverse direction"));
Serial.println(F("S clear status"));
Serial.print(F("t set revolutions to "));
Serial.println(shortRevs);
Serial.print(F("T set revolutions to "));
Serial.println(longRevs);
for(int i = 0; uSteps [i].name != 0 ; i++)
{
Serial.print(uSteps [i].name);
Serial.print(F(" set 1/"));
Serial.print(uSteps [i].fraction);
Serial.println(F(" step"));
}
} // help
/********************************************************************
* showStatus
* Inputs:
* uint8_t status: The status bits from readStatus or readFaults
* Effect:
* displays the bits as symbolic status
********************************************************************/
void showStatus(uint8_t status)
{
if(status != 0)
{
/********************************************************************
* STATUS
* Inputs:
* x: The status flag shift value HPSDStatusBit::x
* s: The textual explanation of the bit
* Effect:
* Generates the string
* 0xDD NAME explanation
* for the bit in the status flag value 'status' at position x
* DD is the status bit in hexadecimal
* NAME is the name of the status bit, e.g., "OTS"
* explanation is the description of the status bit
* Notes:
* The string provided as the s argument must not use F(); that
* is provided in the body of the macro
* The \ ***MUST*** be the last character on the line, nothing may
* follow it, even a space.
*********************************************************************/
#define STATUS(x, s) if(status & (1 << (uint8_t)(HPSDStatusBit::x))) \
{\
Serial.print(F(" 0x"));\
Serial.print(1 << (uint8_t)HPSDStatusBit::x,HEX);\
Serial.println(F(" " #x " " s));\
}
STATUS(OTS,"overtemperature shutdown");
STATUS(AOCP,"channel A overcurrent shutdown");
STATUS(BOCP,"channel B overcurrent shutdown");
STATUS(APDF,"channel A predriver fault");
STATUS(BPDF,"channel B predriver fault");
STATUS(UVLO,"undervoltage lockout");
// Note that for faults, these two bits will always be zero
STATUS(STD,"stall detected");
STATUS(STDLAT,"latched stall detected");
}
}
static long revs = 100;
/**************************************************************
* loop
*
* Effect:
* The main polling loop. If there are characters at the serial
* port, process them first.
***************************************************************/
void loop()
{
if(Serial.available())
{ /* serial pending */
char ch = Serial.read();
if( ch <= ' ')
return; // ignore spaces and control characters
changed = true;
query = false;
switch(ch)
{ /* ch */
case'?':
query = true;
break;
case '+':
offset += dt;
return;
case '-':
offset -= dt;
return;
case 'c':
calibrate();
return;
case 'd':
debug = !debug;
Serial.print(F("debug "));
Serial.println(debug ? F("ON"): F("OFF"));
return;
case 'D':
showStep = !showStep;
Serial.print(F("show-step "));
Serial.println(showStep ? F("ON"): F("OFF"));
return;
case 'F':
{
driver. clearFaults();
uint8_t faults = driver.readFaults(); // verify that they were cleared and not stuck
Serial.println(F("Faults cleared"));
showStatus(faults);
}
return;
case 'j':
useJoystick = true;
return;
case 'p':
useJoystick = false;
return;
case 'r':
if(! useJoystick)
{
moveDirection = - moveDirection;
changed = true;
}
return;
case 'S':
driver.clearStatus();
Serial.println(F("status cleared"));
{
uint8_t status = driver.readStatus();
showStatus(status);
}
return;
case't':
revs = shortRevs;
return;
case'T':
revs = longRevs;
return;
case 'm':
help();
return;
default:
// It might be a step request, or complete nonsense
ticket = ch;
break;
} /* ch */
} /* serial pending */
if(!query && !setStep(ticket))
{ /* complete nonsense */
Serial.print(F("unknown command'"));
Serial.print(ticket);
Serial.print(F("'"));
Serial.println();
return;
} /* complete nonsense */
if(query || (debug && changed))
{ /* report status */
Serial.print(query ? F("status '") : F("changed '"));
Serial.print(stepMode);
Serial.print(F("' mw="));
Serial.print(minWait);
Serial.print(F(" vw="));
bool nothing;
bool direction;
Serial.print(getVariableWait(nothing, direction));
Serial.print(F(" direction = "));
Serial.print(direction);
Serial.print(useJoystick ? F(" Joystick mode") : F(" potentiometer mode"));
uint8_t status = driver.readStatus();
Serial.print(F(" status = 0x"));
Serial.print(status,HEX);
uint8_t faults = driver.readFaults();
Serial.print(F(" faults = 0x"));
Serial.print(faults,HEX);
Serial.println();
if(status != 0)
{ /* has status */
Serial.println(F("Status/Faults"));
showStatus(status);
} /* has status */
} /* report status */
// Clear the changed and query flags
changed = false;
query = false;
if (useJoystick)
{ /* joystick mode */
static int lastvrxA = 0;
static int lastvryA = 0;
static int lastswA = -1;
int vrxA = JSleft.getX();
int vryA = JSleft.getY();
int swA = JSleft.getsw();
if(JSleft.inDriftX(vrxA)&&
JSleft.inDriftY(vryA))
return; // ignore values within the "drift range" of the joystick
if (debug && (vrxA != lastvrxA ||
vryA != lastvryA ||
swA != lastswA))
{ /* debug trace */
Serial.print(F("vrxA = "));
Serial.print(vrxA);
lastvrxA = vrxA;
Serial.print (F(" vryA = "));
Serial.print ( vryA);
lastvryA = vryA;
Serial.print (F(" swA = "));
Serial.print (swA);
lastswA = swA;
Serial.println();
} /* debug trace */
move(0, 1, moveVariable);
} /* joystick mode */
else
{ /* potentiomenter mode */
move(0,moveDirection * revs * stepsPerRevolution, moveVariable);// will cause it to turn revs.
} /* potentiomenter mode */
} // loop
The photos are attached. My student took several. The power supply shown is 36V @ 10A. The NEMA-23 motor is rated 24V-48V @ 4.2A/phase. We can see the program uploaded; the ? command tells us that the status flags are 0x00. There are C and F commands to clear status and clear faults. We had to use “m” to get the help because “h” was already allocated to “half-step”.
Yes, we tried the example program (the SPI example) and nothing happened
There is a lot going on in your code, but if the “BasicSteppingSPI” example is not working, I recommend getting that running correctly first. When you ran that example, did you change the CSPin to 5 to match your setup? Could you try running that example again without the motor connected and see if you can measure any voltage at the driver’s motor outputs?
Also, could you try printing the result of “verifySettings()” to the Serial Monitor at the end of the setup() function, and tell us what is returned? For example, add the following code at the end of setup():
Serial.begin(115200);
Serial.println(driver.verifySettings());
Brandon
yes, we changed the select pin in BasicSteppingSPI to pin 5, as I indicated in my message, and it does not work. I would love to get it working, because then I would know that the remaining problems were in my code. But it does not move the stepper. We will try your suggestions at our next session (delayed because of a crisis in my student’s life, may be several days before he gets back to me) and add the code you suggested. Thank you, and the delay is not intentional, just an accident of life.
Yes, this is complex code, and it represents a couple months’ effort to build it all, but it was working perfectly with the 4988 and a NEMA-17. I am having serious upload problems at my side, and I want to get it running with a NEMA-8 using the 36V4.
It took many weeks, but my student has finally recovered (he’s also retired, and has health issues). We put it all together again, and using the latest version of my program, it works, for a while. Then it stops. If we take down the serial monitor and bring up the serial monitor, it resumes, and works for a while, and stops again. It looks like bringing up the serial monitor is resetting the board. I put in some additional printout, and did the verifySettings
you requested. The output is as shown below (the 474 and 477 come from a macro.
#define Here() Serial.print(__LINE__)
There is no stall that we can detect, since the motor is just spinning under no load. The chip documentation suggests that we might want to change one of the parameters to the chip, but there is no external interface provided for that.
Closing and reopening the Serial Monitor will restart the Arduino Nano (and other Arduino boards based on the ATmega328). Does your system always run for the same amount of time before stopping each time you reset the Arduino? Could you try running the BasicSteppingSPI example from our library and see if that stops working with your setup after a similar amount of time?
Brandon
We’ll give that a try tonight. It doesn’t run the same amount of time, and there does not seem to be any pointer arithmetic causing a reset (there isn’t any in the program) or having a stack overflow clobber the heap. I’ve tried most tricks I know to isolate either of those cases, and got a negative.
Weird. First it came up with the verifySettings value as 0, but seemed to run. For a while. Using the BasicSteppingSPI code. Then, we changed the step from 1/32 to 1/8, and verifySettings started returning 1 and it ran just fine.
I fixed a bug in our code that might have been causing the failure, and so far it is now working. Had a && and should have had a ||. Bleah! Thanks for your patience. if we have further problems, I’ll get back to you.
Well, we were overly optimistic. We found that even the BasicSteppingSPI sketch runs only for a couple minutes and then the motor stops.
I put some tracing into my code, and we found that we are calling sd.step() (for sd the motor object) just fine, while the motor stops moving after a couple of minutes. The trace output I added continues to print out, so I know the function is still being called. So the signals are being sent to the 36V4, but somehow the numbers are not doing anything.
We found out that whole-step requires 8000us delay; half-step works at 6000us; quarter-step at 4000us; eighth step at 3000. We put these values in our table and use them to select the delay during stepping. With smaller values, e.g., 3000us, whole, half, and quarter stepping the motor just sits there and vibrates but does not move. That’s fine, since we have the values in the table and we select the delay time to match the step size. But it still just moves and then goes dead. Whole-step went dead after the first turn; the reverse turn was dead and the motor never moved again. Until we reset the board by restarting the serial monitor.
It you try my code, use “m” to turn on motor debug output. You should get 1> or 1< for each call on step(). We are using the nano. We have not measured the output voltages to the motors.
Sorry, hit ‘send’ before attaching the latest source code.
We are using a NEMA-34 motor and set the current limit to 4000.
36v4 (3).ino (23.9 KB)
The demo code stopping after it runs for approximately 5 minutes is concerning. I am worried it might be overheating; could you try running the demo in full-step mode with the current limit set to 3000mA and see if that gives you different results?
As far as the behavior you observed when you adjusted the delay, it sounds like you are adjusting the delay that determines the step rate. If that is the case, please note that the maximum speed is going to be mainly limited by your particular motor and operating voltage, although other factors in your setup can impact it too, such as the current limit, acceleration/deceleration limiting, decay mode, and what kind of load is connected to your stepper motor. You can find some additional detail in this post by Ben, but ultimately, it does not sound like it is related to your problem.
Brandon
Thanks, we’ll give it a try later tonight.
Yes, we are adjusting the delay between the step rate, If it is too short, the motor does not have time to move to the new position before the next signal comes in, so it sort of oscillates back and forth within a step, more or less. So we introduce some microseconds delay so the motor has time to reach its intended position. We found that with the “classic” NEMA-17 stepper, we had delays like 800us for full-step, and 0 for 1/8 step and below. With 800us on the NEMA-34 motor, it just vibrates at 1/8 step or larger. We tossed in a few numbers which I cited and it at least gives the motor time to settle into position. In reading the status, we get a status code of 0x0C, which if I recall are STD and STDLAT status codes, but the motor continues to run, but eventually stops. We do not see the overtemperature or overcurrent errors. The parameters you mention seem to have no interface specifications. Note that after a startup, verifySettings() returns 0 the first time, 1 once we have changed the step size. I will check the post, but our problem is the unexplained stopping.
Well, we tried it. We set the SteppingSPI
example to full steps, set the current limit to 3000mA, and the motor ran for about 30 seconds.
We always get the stall status and the latched stall status bits set. And even when I issue a clearStatus()
call, the bits remain on. They are on when the motor is turning, and they are on when the motor stops. As I indicated, I know I am sending step commands to the motor, so the program has not crashed due to some heap damage, stack overflow, or other reason. It just sits there sending step commands out, and nothing happens.
So, having established this, we made the same changes in my program, and again, the motor ran for about 30 seconds and stopped. Only a reset would get it running again. What I have included here is a screen shot of the output to the serial monitor. The ? command will print out the status, and the F command clears the status bits, yet the STD and STDLAT bits are both set even after the clear.
If you have any other suggestions of what we should be looking for, or other diagnostic information that might be useful, we are meeting again Sunday evening and can do further experiments. My student does not have an oscilloscope, but does have a DVM, if that can help. If you have the patience to put up with these questions, we have the patience to try any experiment you want us to try.
The 497 and 500 numbers printed out are from the Here() macro I put in, which prints out __LINE__
.
status indicates a status report; 'w'
means whole-step mode is set. MinWait is the minimum interstep time. vw
is the variable-wait time, which is based on the distance the joystick is from (0,0); close to center is large; fully pushed up or down modifies x timing to vw=0
. Since we only have one motor at the moment, we have not bothered calling the y-axis code.
X=490
is the reading of the ADC for x. Joystick fully up is 1023; fully down is 0. The Xdrift
is the hysteresis on being centered; we have found that 8 captures the residual error based on the position following a release after full extension in either + or - direction. So it is considered “dead center” if X is anywhere from 482 to 498, with the calibrated 0 at 490.
The
is a status report when the motor stopped. What is odd is thatverifySettings()
returns 0 even though the motor is moving.
shows what happens if I use the F command to clearStatus()
. We tried it twice. My student reported “the motor is quite hot” after we had been playing with it for about 20 minutes. And this was after we had set the current limit to 3000mA (the motor is rated for 4.2A per phase at 12V).
Thank you for your continuing efforts on our behalf.
Hello.
For now, I would like to focus on the library examples since we can be confident that they should work and there aren’t any bugs in the code. As noted on the product page for the High-Power Stepper Motor Driver 36v4, the driver’s SPI interface is more likely to be affected by electrical noise from the driver and stepper motor when using high input voltage and high current limits. Could you try running the BasicSteppingSPI example with the driver powered at a much lower voltage and a lower current limit (e.g. a 12V and the current limit set to 1A, if practical)? Then if that seems to work without issues you could try gradually raising them, and I can suggest some measures that might allow you to use higher voltages and currents more successfully.
By the way, it is not necessarily surprising that the motor still gets hot with 3A, since many electronics and electromechanical components are rated for operation at temperatures well above what would be comfortable to touch (over 100°C).
Brandon
OK, we have tried that. Here’s the results (StepPeriodUs = 8000, HPSDDecay<pde::AutoMixed, HPSDStepMode::MicroStep1)
12V @ 1000mA Motor moved, chattered badly, moved about 5/8 of a revolution at full stepping
13.7V @ 1500mA Motor moves, chatters much less, moves 4 revolutions at full stepping
Changing StepPeriodUs=2000
13.7V @ 1500mA, moves smoothly, 4 revolutions
13.7V @ 2500mA, moves smoothly, 4 revolutions, high torque, does not stop
24V @ 2500mA., unreliable motion, 4 revolutions, torque not good, skips even when not under load
This was the maximum the adjustable power supply. So we switched to a 30A 12V supply
12V @ 3500mA, fast and smooth
12V @ 4000mA, fast and smooth
Conclusion: 12V @ 4000mA works best so far
Reduced SetPeriodUs to 1000
13V @ 2500 mA, No torque, very buzzy, motor vibrates a lot
Changed SetPeriodUs to 3000
12.7V @ 2500mA, Runs great, but not as good as 12V @ 4000mA
24V @ 2500mA, Runs great, but not as good as 12V @ 4000mA
12V @ 2500mA, with our sketch, 8000uS, motor sits there and vibrates, does not turn
12V @ 2500mA, with our sketch, 3000uS, motor barely turns
So we have now identified two conditions that improve it: lower voltage and lower delay. Our problem is now to figure out why our sketch does not work. But you have now given us a fixed point we know works, so now we will try to figure out what our program is doing wrong. Thank you. I will get back to you when we have more to report. One thing we did discover was that when we turned on debug printout, it started working, which suggests that the timing delay between steps is a factor. But we now know that even BasicSteppingSPI does not work at 36V @ 4000mA
Ah, well. We keep trying.
My student was out for over a week due to dental issues (a lot of pain from an infected tooth). We finally got back together tonight.
At this point, he wanted to try a NEMA-8. He has two from Adafruit, rated 3.9V, 600mA.
We tried BasicSteppingSPI, I set it up to cycle through 1/1 through 1/256 with the step delay StepPeriodUs set to 2000 us. The motor got really hot, but didn’t move.
Since we have to put about 8.5V into the 36V4 to avoid low voltage lockout, I suspected that the motor was being overdriven. So I worked out that 3.9*0.600 = 2.34W; I then did 2.34W/=275mA. The motor no longer gets hot, but it also doesn’t move. We put it on the “standard” 4988 control board, and it moves with a good deal of torque; we adjusted the current limit on the 4988 to 480mV, which should give us 600mA.
While I am typing this, my student has changed the wait time to 1000us, 2000us, 3000us and 4000us, which made no difference,
I am attaching the modified BasicSteppingSPI. The table from our program is in there, but mostly because I didn’t feel like retyping the whole thing; we only use the microstepping value, and hold the wait time constant, and the first two columns just do printout so we can see what the motor is supposed to be doing. Note that this worked fine for our 4000mA NEMA-23, and our 1500mA NEMA-17 but the little NEMA-8 just sits there and does nothing (it is being powered, but it doesn’t move).BasicSteppingSPI.ino (4.7 KB)
Thank you for your patience.
I recommend keeping the current limit equal to your stepper motor’s rated current limit for now (i.e. sd.setCurrentMilliamps36v4(600);
). The driver will handle adjusting the output voltage dynamically to prevent problem and it is normal for the motor to get hot under those conditions, as I described in my previous post, especially when it is just holding position and not moving.
I tested the latest version of your code that you posted with a setup here, but I was unable to reproduce the problem (the motor stepped fine and it went through all of the microstepping modes). It is curious that it works for your other stepper motors and the problem is only with the NEMA 8 stepper motor. Can you post pictures of that setup? You might try adjusting the decay mode to see if that changes anything (e.g. slow, fast, mixed).
Brandon
My student is now recovered after a couple days of dental work. We tried using the big NEMA-23 motor. Nothing works. Here is a sample of the attempt.
Nothing happens when we try to activate the motor. So we typed ? to the command line, and got the output below the ======== line
The first two lines are the FILE, DATE, and TIME
The next line indicates that we are in full-step (“whole” step) mode ‘w’, with an interstep delay of 4000us (same as BasicSteppingSPI, and we have no offset adjusting that value.
I then print out some parameters, followed by the driver.readStatus() call, 0x10 and driver.readFaults() call, again, 0x10.
The next line says that we have selected motor [2] from our table of motors, it is the NEMA23 motor (a string in the table) and its current limit is 4200mA (Yes, I know this is high, but since the motor isn’t doing anything, there appears to be no load on the MOSFETS, as they remain cool)
The next line is the joystick calibration; the current position is 488, the approximate center of an untended joystick; the nominal zero position is thought to be 488, and the “dead zone” is 15 joystick units wide. We are not using the Y-axis of the joystick yet, so all we get is its nominal zero position.
Now, we see there is a Channel B predriver fault. I read the documentation on the chip, but I have no idea why this should happen.
We changed the power limit to 2500mA, removed power from both the 36V4 VIN and unplugged the Nano; powered up the Nano, then powered up the motor power, which is 36 volts (the NEMA-23 form-factor motor is rated for 24V to 48V, so we split the difference). Same problem. We tried a clearStatus() in both cases, which changed nothing. As far as we can tell, it only works on the NEMA-17 motor.
We tried BasicSteppingSPI with the current parameter set to 2500mA, and it didn’t work with that, either.
In an attempt to see what might be wrong, we changed 36V4 controllers. No joy.
Basically, the 36V4 works on the NEMA-17, but not a NEMA-8 or NEMA-23. I’m sorry we are being such a pain. We got better results for the NEMA-17 by setting the decay mode to AutoMixed as BasicSteppingSPI does. We are willing to try any other experiments you think of.
Much thanks. We appreciate your help and patience.
I forgot to ask my student for a picture of the setup, but he wants to get together tomorrow night. He was at the dentist’s at 8:30 am, spent a couple hours having his mouth abused by the dentist and was really tired by 1:15am.
Joe
Sigh.
The High-Power Stepper Motor Driver 36v4 has no meaningful over-temperature shut-off, so we strongly recommend you do not use a current limit setting beyond 4 A (unless you can confirm that the temperature of the MOSFETs will stay under 140°C).
At this point, there is probably not much more that we can do to help troubleshoot. We might be able to help you with a discount on a replacement if you think your boards have been damaged, but given that you’ve experienced problems with multiple boards, it seems likely the problem is elsewhere in the system.
My best suggestion is to start with one of our unmodified example programs (only modifying the current limit value to something appropriate), which we know should work, and get something working; then incrementally change one thing at a time until you find something that causes it to stop working and investigate that change closely. Please note that you should generally not make any changes to connections while the system is powered.
If the problem really comes down to simply swapping motors, then it could be something in the driver settings that need to be tuned to a specific motor (although that would be really surprising to me since in our testing, we haven’t encountered a situation where the driver simply didn’t even energize a motor at all with its default settings). It could be that between swapping motors and making changes to the setup that some wires were jostled or connections were inadvertently broken or changed, so you could also try going through with a multimeter to check for continuity and make sure you are seeing appropriate voltages where expected.
You might also consider switching to the Tic 36v4, which uses the same driver chip but has an on-board microcontroller to handle the low-level configuration and communication, which should both simplify your system (even if only for testing), and let you try different settings more easily.
Brandon