Stepper Motor Upgrades to Eliminate VFA's (Vertical Fine Artifacts)
 

Stepper Motor Upgrades to Eliminate VFA's (Vertical Fine Artifacts)  

Page 2 / 49
  RSS
guy.k2
(@guy-k2)
Noble Member

Got firmware 3.6.0 compiled and running with patches for 0.9 degree Moons.

Wasted two hours troubleshooting because I forgot to modify config.h file to set language mode. Leaving that file as supplied by Prusa puts the printer in a boot loop. It was the last thing I would think of because I haven't had to modify that file but ONCE when I first downloaded the 3.5.1 firmware.

#define LANG_MODE 0 // primary language only

Yes, we can run 0.9 degree Moon's under firmware 3.6.0
The patches needed are the same as for 3.5.1

BTW i have been compiling under Arduino 1.8.8 / MacOSX successfully

Posted : 04/03/2019 8:57 pm
guy.k2
(@guy-k2)
Noble Member

https://github.com/guykuo/Prusa-Firmware-0.9-Moons-XY should be a fork of the mk3 firmware that supports Moon's 0.9 degree steppers on x and y axes.

Edit:

Added new defines in Configuration_prusa.h to allow easier switching between 0.9 and 1.8 degree motors.

// Define to use 0.9 degree stepper on x or y axis
#define X_AXIS_MOTOR_09 //Kuo
#define Y_AXIS_MOTOR_09 //Kuo

Defining X_AXIS_MOTOR_09 sets x-axis motor to be 0.9 degree stepper
Defining Y_AXIS_MOTOR_09 sets y-axis motor to be 0.9 degree stepper

Rest of patches in fork are applied at compile time to implement the new defines.

Posted : 04/03/2019 9:52 pm
bhawkeye
(@bhawkeye)
Eminent Member

@guy.k2, is this the way you wanted me to post the constant torque set wave changes? I downloaded 3.6.0, made my changes to that & was able to compile it with no errors. If you wanted something different, let me know. 😉

NOTE: fac1000 values are now signed and each change of 1 in value represents double the amount of correction as in the original version. This means you will need to halve the values in your ecor script, to get similar correction results & +100 is the largest fac1000 value that can be set. (you can also set negative fac1000 values >= -100)

These changes were made off the 3.6.0 release of firmware.
They contain modifications to 5 files:
Dcodes.cpp,
Marlin_main.cpp,
ultralcd.cpp,
tmc2130.h,
tmc2130.cpp

in file Dcodes.cpp /
~line 623

// uint8_t fac1000 = atoi(strchr_pointer + 11) & 0xffff;
// if (fac1000 < TMC2130_WAVE_FAC1000_MIN) fac1000 = 0;
// if (fac1000 > TMC2130_WAVE_FAC1000_MAX) fac1000 = TMC2130_WAVE_FAC1000_MAX;
// tmc2130_set_wave(axis, 247, fac1000);
Replace with -->
int16_t fac1000t = atoi(strchr_pointer + 11) & 0xffff;
int8_t fac1000;
if (fac1000t < TMC2130_WAVE_FAC1000_MIN) fac1000 = TMC2130_WAVE_FAC1000_MIN;
if (fac1000t > TMC2130_WAVE_FAC1000_MAX) fac1000 = TMC2130_WAVE_FAC1000_MAX;
tmc2130_set_wave(axis, TMC2130_WAVE_MAX, fac1000);

-----------------------------------------------------------------------------
in file Marlin_main.cpp

~line 1247
// tmc2130_wave_fac[X_AXIS] = eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_X_FAC);
// tmc2130_wave_fac[Y_AXIS] = eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_Y_FAC);
// tmc2130_wave_fac[Z_AXIS] = eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_Z_FAC);
Replace with -->
tmc2130_wave_fac[X_AXIS] = eeprom_read_byte((int8_t*)EEPROM_TMC2130_WAVE_X_FAC);
tmc2130_wave_fac[Y_AXIS] = eeprom_read_byte((int8_t*)EEPROM_TMC2130_WAVE_Y_FAC);
tmc2130_wave_fac[Z_AXIS] = eeprom_read_byte((int8_t*)EEPROM_TMC2130_WAVE_Z_FAC);

~line 1251
// tmc2130_wave_fac[E_AXIS] = eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_E_FAC);
Replace with -->
tmc2130_wave_fac[E_AXIS] = eeprom_read_byte((int8_t*)EEPROM_TMC2130_WAVE_E_FAC);

-----------------------------------------------------------------------------
in file ultralcd.cpp
at ~line 5093
// MENU_ITEM_EDIT_int3_P(_i("X-correct"), &tmc2130_wave_fac[X_AXIS], TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
// MENU_ITEM_EDIT_int3_P(_i("Y-correct"), &tmc2130_wave_fac[Y_AXIS], TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
// MENU_ITEM_EDIT_int3_P(_i("Z-correct"), &tmc2130_wave_fac[Z_AXIS], TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
Replace with -->
MENU_ITEM_EDIT_int3_P(_i("X-correct"), (uint8_t *) (&tmc2130_wave_fac[X_AXIS]), TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
MENU_ITEM_EDIT_int3_P(_i("Y-correct"), (uint8_t *) (&tmc2130_wave_fac[Y_AXIS]), TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
MENU_ITEM_EDIT_int3_P(_i("Z-correct"), (uint8_t *) (&tmc2130_wave_fac[Z_AXIS]), TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0

at ~line 5097
// MENU_ITEM_EDIT_int3_P(_i("E-correct"), &tmc2130_wave_fac[E_AXIS], TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0
Replace with -->
MENU_ITEM_EDIT_int3_P(_i("E-correct"), (uint8_t *) (&tmc2130_wave_fac[E_AXIS]), TMC2130_WAVE_FAC1000_MIN-TMC2130_WAVE_FAC1000_STP, TMC2130_WAVE_FAC1000_MAX);////MSG_EXTRUDER_CORRECTION c=9 r=0

-----------------------------------------------------------------------------
in file tmc2130.h
at ~line 28
// #define TMC2130_WAVE_FAC1000_MIN -101
// #define TMC2130_WAVE_FAC1000_MAX 100
Replace with -->
#define TMC2130_WAVE_FAC1000_MIN 30
#define TMC2130_WAVE_FAC1000_MAX 200

at ~line 31 (just adds)
#define TMC2130_WAVE_SIN0 1
// sin wave maximum value. This value + hyst must not exceed 255
#define TMC2130_WAVE_MAX ((uint8_t)248)

at ~line 40
// extern uint8_t tmc2130_wave_fac[4];
Replace with -->
extern int8_t tmc2130_wave_fac[4];

at ~line 108
// extern void tmc2130_set_wave(uint8_t axis, uint8_t amp, uint8_t fac1000);
Replace with -->
extern void tmc2130_set_wave(uint8_t axis, uint8_t amp, int8_t fac1000);

-----------------------------------------------------------------------------
in file tmc2130.cpp

at ~line 55
// uint8_t tmc2130_wave_fac[4] = {0, 0, 0, 0};
Replace with -->
int8_t tmc2130_wave_fac[4] = {0, 0, 0, 0};

at ~line 212
// tmc2130_set_wave(X_AXIS, 247, tmc2130_wave_fac[X_AXIS]);
// tmc2130_set_wave(Y_AXIS, 247, tmc2130_wave_fac[Y_AXIS]);
// tmc2130_set_wave(Z_AXIS, 247, tmc2130_wave_fac[Z_AXIS]);
Replace with -->
tmc2130_set_wave(X_AXIS, TMC2130_WAVE_MAX, tmc2130_wave_fac[X_AXIS]);
tmc2130_set_wave(Y_AXIS, TMC2130_WAVE_MAX, tmc2130_wave_fac[Y_AXIS]);
tmc2130_set_wave(Z_AXIS, TMC2130_WAVE_MAX, tmc2130_wave_fac[Z_AXIS]);

at ~line 216
// tmc2130_set_wave(E_AXIS, 247, tmc2130_wave_fac[E_AXIS]);
Replace with -->
tmc2130_set_wave(E_AXIS, TMC2130_WAVE_MAX, tmc2130_wave_fac[E_AXIS]);

replace complete tmc2130_set_wave, ~line 847, with :

void tmc2130_set_wave(uint8_t axis, uint8_t amp, int8_t fac1000)
{
/*
NOTE: fac1000 values are now signed and each change of 1 in value represents double the amount of correction as
in the original version.

Original code only allowed decreasing the slope of the "wave" near zero crossings. It also caused the total torque
to become lower toward the middle of the wave. This code attempts to keep total torque constant, and
allows both increasing and decreasing the wave slope near zero crossings.

Changing to a signed 8 bit value of fac1000 allows both increasing and decreasing the slope near zero crossings.
In order for fac1000 to still fit in an 8-bit value, fac(used in the pow function) is limited to the range of 0.8 to 1.2 -
in .002 increments.
(fac1000 of -1 is not useable, since 0xff(-1) is interpreted elsewhere in the firmware as uninitialized,
so negative values of fac1000 are incremented by 1 to allow fac value of .998)
fac1000 value of 0 gives pure sin function
negative fac1000 values increase slope near the zero crossings
positive fac1000 values decrease slope near the zero crossings

sin0 values other than 0 will compute correctly - however, this makes sin0 & -sin0 the values on either side
of the zero cross point, with no actual zero point in the wave. If sin0 is zero, the zero value will be repeated
twice at the zero cross points. The TMC2660 uses a sin0 value of 1, and this seems to give the smoothest
waveforms on a 'scope. The sin0 value is now set in TMC2130.h by TMC2130_WAVE_SIN0, and the default
value is 1.
*/
float tcorr;
float fac;
uint32_t reg; //tmc2130 register
uint16_t ampamp = amp * amp;
int i = 0;
uint8_t w[4] = {1,1,1,1}; //W bits (MSLUTSEL)
int8_t wtbl[4][2]=
{
-1, 0,
0, 1,
1, 2,
2, 3
};
uint8_t x[3] = {255,255,255}; //X segment bounds (MSLUTSEL)
uint8_t val[256];
uint8_t va;
int8_t d0 = 0; //delta0 from MSLUTSEL W bits
int8_t d1 = 1; //delta1 MSLUTSEL W bits
int8_t maxd;
uint8_t s = 0; //current segment
int8_t b; //encoded MSLUT bit value
int8_t dA; //int delta value
uint8_t midpoint = sqrt((float)((amp-TMC2130_WAVE_SIN0)*(amp-TMC2130_WAVE_SIN0)/2));
uint8_t missed_i ;
uint8_t chgcnt = 0;

if(fac1000 < 0)
{ // Since -1 not allowed, add 1 to all neg fac1000 values
fac1000 = (fac1000 < TMC2130_WAVE_FAC1000_MIN ? TMC2130_WAVE_FAC1000_MIN:fac1000) + 1;
}
else if(fac1000 > TMC2130_WAVE_FAC1000_MAX) fac1000 = TMC2130_WAVE_FAC1000_MAX;
fac = 1. + .002 * fac1000;
// find correction factor that puts midpoint of curve at sqrt of (amp^2/2) - for smooth curve transition
tcorr = (float)(midpoint) / (float)(pow(sin((PI*(127))/512.),fac)*(amp-TMC2130_WAVE_SIN0) + 0.5);
va = val[0] = TMC2130_WAVE_SIN0;
for(i=1; i<126; i++)
{
if(i < 33)
maxd =3;
else if( i < 125)
maxd = 2;
else
maxd = 1;
va = ((amp-TMC2130_WAVE_SIN0) * pow((float)(sin(PI*i/512)), fac) * tcorr) + TMC2130_WAVE_SIN0 + 0.5;
if(va <= (val[i-1] + maxd))
val[i] = va;
else
val[i] = val[i-1] + maxd; // curve can't exceed TMC2130's max slope
}
val[127] = midpoint+TMC2130_WAVE_SIN0;
val[126] = (val[125]+val[127] +1)/2;
for(i=128; i<255; i++)
{
va = (uint8_t)(sqrt((float)(ampamp - (uint16_t)val[255 - i] * val[255 - i])) +.5);
val[i] = va > amp ? amp:va;
}
val[255] = amp;
tmc2130_wr_MSLUTSTART(axis, TMC2130_WAVE_SIN0, amp);
redocompress:
missed_i = 0;
s = 0;
// get initial segment slope, based on fac for, values more neg than ~-54 lim w[0]=3, values above ~80 w[0]=1, for all others w[0]=2
dA = val[1] - val[0]; // find delta between 0th & 1st
switch (dA)
{
default:
case 1: d0 = 0; d1 = 1; w[s] = 1; break;
case 2: d0 = 1; d1 = 2; w[s] = 2; break;
case 3: d0 = 2; d1 = 3; w[s] = 3; break;
}

va = TMC2130_WAVE_SIN0;
// TMC2130 wave compression algorithm
// for minimal memory requirements in the TMC2130 chip
// printf_P(PSTR("tmc2130_set_wave %hhd %hhd\n"), axis, fac1000);
// printf_P(PSTR(" factor: %s\n"), ftostr43(fac));
for (i = 0; i < 255; i++) // compress wave
{
if ((i & 0x1f) == 0)
reg = 0;
dA = val[i+1] - va; // calculate delta to next
dA = dA < 0 ? 0:dA;
dA = dA > 3 ? 3:dA;
b = 0;
int sw;
if ((dA < d0) && (d0 > -1)) // delta < delta0 => switch wbit down or wait to catch up
{
if(i > 120 && fac1000 > 100 && ((val[i+2] - va) > 3*d0)) // mid-range is steeper when fac1000 is positive,
goto nomoresegs; // so leave mid section at 1,2
//printf("dn\n");
if(s==3)
{
w[s]=1;
d0 = 0; d1 = 1;
goto nomoresegs;
}
sw = i < 120 ? (w[s]-1): 1; // always switch down 1 from current on lower half of wave & switch to 0:1 on upper half, when it's time to switch
switch (sw)
{
case 1: d0 = 0; d1 = 1; w[s+1] = 1; break;
case 2: d0 = 1; d1 = 2; w[s+1] = 2; break;
default: b=-1; break;
}
if (b >= 0)
{
x[s] = i;
s++;
}
}
else if ((dA > d1) && (d1 < 3) && (i < 128) && (i<16 ||(val[i+2] - va) > (3*d1)) ) // delta > delta0 => switch wbit up
{
if(s==3)
{
w[s]=1;
d0 = 0; d1 = 1;
goto nomoresegs;
}
int sw = w[s] + 1; // always switch up 1 from current
switch (sw)
{
case 1: d0 = 0; d1 = 1; w[s+1] = 1; break;
case 2: d0 = 1; d1 = 2; w[s+1] = 2; break;
case 3: d0 = 2; d1 = 3; w[s+1] = 3; break;
default: b = -1; break;
}
if (b >= 0)
{
x[s] = i; s++;
}
}
nomoresegs:
if (dA <= d0)
b = 0; //delta == delta0 => bit=0
else
b = 1; //delta == delta1 => bit=1
if (b == 1)
reg |= 0x80000000;
va = va + wtbl[w[s]][b]; // update value for next compare
if(va != val[i+1])
{
missed_i = i; // remember last miss
val[i+1] = va; // change next value to the one that was achievable
}
if ((i & 31) == 31)
tmc2130_wr_MSLUT(axis, (uint8_t)(i >> 5), reg);
else
reg >>= 1;
}

if((chgcnt == 0) && (missed_i != 0)) // only try once
{
// make minor corrections to 1st half of wave values - to make up for error caused by compression
// sometimes 2nd half of wave can't get exact match, due to running out of compression segments
missed_i = 255-missed_i; // last miss paired value is the 1st we need to check
if(missed_i <16)
missed_i = 16; // don't adjust 1st 16 entries
for(i=missed_i; i < 125; i++)
{
va = (uint8_t)(sqrt((float)(((uint32_t)val[i] * val[i]) + ((uint32_t)val[255-i] * val[255-i]))) + .5);
if(va < amp)
{
val[i]++;
chgcnt++;
}
else if(va > amp)
{
val[i]--;
chgcnt++;
}
}
if(chgcnt != 0)
goto redocompress;

}

tmc2130_wr_MSLUTSEL(axis, x[0], x[1], x[2], w[0], w[1], w[2], w[3]);
}

Posted : 05/03/2019 12:51 am
guy.k2
(@guy-k2)
Noble Member

is this the way you wanted me to post the constant torque set wave changes?

Yes. That is exactly what I wanted. Will be easy to edit into my files.
I need to add your changes rather than outright replacing the files because I have other test code I don't want to lose.
I will see how that affects my EXY wave tower. I suspect it's really only going to have visible effects on the extruder. The 0.9 degree Moons x and y motors are so fine that I need a magnifying glass to see waveform changes on those axes.

1.8 degree LDO cooler motors have arrived from Printed Solid. They actually shipped the correct LDO-42STH40-1004ASR motors this time. They have an integrated cable and full length shaft flats like the stock motors. Also included one Haribo package. 🙂 We'll soon see how these compare against Moons 1.8's on the y axis.

Posted : 05/03/2019 4:09 am
bhawkeye
(@bhawkeye)
Eminent Member

@guy.k2, I also should have mentioned that for negative fac1000 codes, 1 is added to the specified code - before scaling it by .002. This is because elsewhere in the firmware, -1 (0xff) is recognized as uninitialized. So to get a power of .8, you would specify fac1000 as -101(and -101 is the most negative value supported). Hope this makes sense. It is documented in the code, but thought I should have mentioned it before.

Posted : 05/03/2019 5:32 am
guy.k2
(@guy-k2)
Noble Member

I know you put a lot of effort into supporting negative values for correction values, but may I suggest that is more complicated than needed?
It also runs into that ugly discontinuity of avoiding -1 and different scaling below zero. The user ends up having to remember two separate segments which behave differently when specifying the power factor.

You're expanding the allowed power factor to run from 0.8 to 1.2 rather than the stock 1.030 to 1.200.
Why not just keep the values positive and scale + offset them? It is conceptually consistent for users having a continuous adjustment range.
We don't really need to step through the power any better by 0.001. Keeping it about every 0.002 is sufficient. That is the adjustment resolution you are implementing for negative numbers anyways.

I suggest...

Special case 0 as off

Let values 1..201 specify the power range from 0.8 to 1.2. It is simpler to remember a continuous adjustment range than having a different scaling for negative numbers.

something like...
fac = 0.8 + 0.4 * ((value - 1) / 200)

with the required type conversion ugliness of c++
fac = 0.8 + 0.4 * ((float)(fac1000 - TMC2130_WAVE_FAC1000_MIN) / (float)(TMC2130_WAVE_FAC1000_MAX - TMC2130_WAVE_FAC1000_MIN));

Sure, 100 would no longer means 1.1, but there is no need to keep that sacrosanct. User must print and check for their specific correction factor anyways.

Anyways, I somehow can't get any motion out of the motors with your tmc2130_set_wave. I'm wondering if it is a numerical type conversion issue in the formulas, because tcorr always seems to be 0 when I check it. I do see fac and fac1000 getting reasonable values, but should tcorr be zero here?

I'm not a c programmer and needing to explicitly convert types inside formulas is not intuitive to me.


// find correction factor that puts midpoint of curve at sqrt of (amp^2/2) - for smooth curve transition
tcorr = (float)(midpoint) / (float)(pow(sin((PI*(127))/512.),fac)*(amp-TMC2130_WAVE_SIN0) + 0.5);
printf_P(PSTR(" tcorr: %s\n"), ftostr43(tcorr));

Posted : 05/03/2019 7:31 am
bhawkeye
(@bhawkeye)
Eminent Member

I suggest...

Special case 0 as off

Let values 1..201 specify the power range from 0.8 to 1.2. It is simpler to remember a continuous adjustment range than having a different scaling for negative numbers.

something like...
fac = 0.8 + 0.4 * ((value - 1) / 200)

I really like your idea. Once the wave computes correctly, I'll make this change.

Meantime, sorry to drag you into this - but I'm coding & debugging this with visual C++ in windows on my pc. So, I can't get my debug environment to exactly match the arduino, but I'm not seeing the problem that you do with the tcorr value. It should be between ~.935 and 1.074
Could you please make the following change to tmc2130.cpp?

Replace:
tcorr = (float)(midpoint) / (float)(pow(sin((PI*(127))/512.),fac)*(amp-TMC2130_WAVE_SIN0) + 0.5);
With:
uint8_t pwrmid = pow(sin((PI*(127))/512),fac)*(amp-TMC2130_WAVE_SIN0) + 0.5;
tcorr = (float)(midpoint) / pwrmid;

and then print midpoint, pwrmid, tcorr, and fac

This would definitely help me understand what's going on(at least I hope)

Posted : 05/03/2019 7:41 pm
guy.k2
(@guy-k2)
Noble Member

Could you please make the following change to tmc2130.cpp?

Sure. I'll give that a whirl after tonight's 1.8 Lido Cooler vs Moons 1.8 test.

Meanwhile, I went ahead and jiggered the Prusa power function to run from 0.8 to 1.2 using the stock FAC1000 input range of 30 to 200. It is nowhere near as sophisticated as your adjustments for slope limits etc, but gives a rough idea of how the power factor affects things from 0.800 to 1.030.

With the 1.8 Moons stepper on extruder...

I could not get any y motion until fac was above 0.840. Above that, things could move.

There was a violent band around fac 0.847. No net motion, but the motors massively shook the printer as I have never seen. My normally still x-belt was bobbing up and down 1 cm!

Reset things and started a new EXY wave tower above that dead zone from fac 0.918 to 1.118. I got a layer shift on y right change from power 1 to 0.918 at bottom of tower.

Looks like somewhere around 1.010 is the best - just under the Prusa linear correction adjustment range.

Below that power, surface wave crests are flatter but the troughs get sharper and deeper - kind of like the VFA's.
Above that value we get a new wave in the midst of the crest.

I'm printing a EXY waveform tower to better examine the middle values 0.965 - 1.049

Again, this is with a Moon's 1.8 on the extruder.

By comparison, the stock LDO 1.8 produced a greater amplitude diagonal wave at a higher frequency. Unfortunately, these are not identical test towers, but you can see the character is quite different. The Moons 1.8 yields a softer, lower frequency, lower amplitude wave. The stock extruder LDO's create an almost braided surface nearly swamping out layer lines.

======
Almost forgot. While doing the waveform tower prints, I discovered the 0.9 degree Moons Stallguard would not detect x-axis home when z was near top. I had it working reliably near print bed. It being different at top of Z was a surprise. May be something to do with EINSY box at bottom. Happily, Increasing x-axis homing velocity a bit more (from 3300 to 3500 mm/sec) fixed the newly discovered issue.

Posted : 05/03/2019 11:35 pm
bhawkeye
(@bhawkeye)
Eminent Member

@guy.k2, lots of good information 🙂 . I'm not surprised about the power function values less than 1 don't work well. It was the results from your 1st tests of this(seems like long ago 😆 ) that started me looking into this topic in more depth.

The power values under 1, cause the total torque to increase from the ends toward the center of the full step, whereas the values of greater than 1 cause total torque to decrease from the ends toward the center.
When total torque gets above ~250, you start to lose steps(overflow, I think). I think that may have been part of the reason to stay with values greater than 1 in the original code. It avoided the overflow problem, but didn't maintain a constant total torque. I believe that this causes motion artifacts(your diagonal wave pattern - yet to be proven, of course).

The constant torque version generates almost identical step locations as the straight power function, for all the microsteps, while maintaining constant total torque. According to everything that I have read it is highly desirable to maintain constant total torque while microstepping, to generate the smoothest motor movement.

Again, thanks for all your help & your idea for improving the usability was great & it means the only files that need to be touched are tmc2130.cpp & tmc2130.h

Bye the way, how did you attach the 2130 heatsinks to the einsy?

Posted : 06/03/2019 12:27 am
guy.k2
(@guy-k2)
Noble Member

The expanded mid-range EXY Waveform print looks best to me at a power factor of 1.021. It's pretty subtle, but I think that is where the Moons 1.8 is best. It will be interesting to see if your constant torque curve functions does better once we get it actually working on the EINSY.

Heatsinks were secured with press and stick thermal transfer tape that was already on the heatsinks. I thoroughly cleaned the PC board surface with IPA before sticking the heatsinks in place. They are pretty tough to remove once in place. I had to move one and it took a spudger to remove it. Prying with finger pressure was insufficient.

=============

For those who are aghast at the horribly noticeable wave pattern with the Moons' 1.8 motor on my test towers, let me reassure you it yields much better consistency than the stock LDO's. Both my lighting angle and EXY Waveform Tower make extruder inconsistency more visible. Not much point to test objects that hide the flaw. You want an object and lighting that makes it show up. On a normal object, the extruder doing infill breaks surface wave sync. By the time extrusion moves to next surface perimeter, extruder is no longer at same phase on e-cycle. On the test tower, there is no infill, so periodic extruder inconsistencies remain in phase and show as diagonal surface waves. On normal prints, the Moon's low amplitude inconsistency would be hidden. On the other hand, the the larger amplitude inconsistencies of the stock LDO are big enough to show up on normal objects.

Can one use a Moon's 0.9 on a direct drive extruder? I rather doubt it would have enough torque. Then again every comment I found about using 0.9s on the x and y axis said it would not work.

The LDO cooler 1.8 motor might be an option instead of the Moon's 1.8, but that won't be trialed until late tonight. I modified my firmware to support easily switching between 0.9 and 1.8 degree motors in anticipation of this test. I need change just one define to switch x or y motor between 0.9 and 1.8 degree units. Beats editing multiple places in the firmware.

I will test a LDO Cooler 1.8 on Y-axis vs a Moons' 1.8. Print object will be an XYZ cubes. If y-face VFA reduction is better with the LDO than a 1.8 Moons on y-axis, it may be worth testing on it on the extruder. If they are about the same, it's not worth my tearing down the extruder. Swapping motors on the extruder is a huge hassle. If only the motors could be removed without taking the back of the extruder apart.

VFA's are the X and Y axis equivalent of the E-axis diagonal wave. So, we'll still get a relevant comparison.

Posted : 06/03/2019 2:07 am
KLab
 klab
(@klab)
Active Member


Can one use a Moon's 0.9 on a direct drive extruder? I rather doubt it would have enough torque. Then again every comment I found about using 0.9s on the x and y axis said it would not work.

You can use the Skelestruder with an 0.9° "pancake" stepper motor.
Thats what I am planing to use in the future.
The Skelestruder has less weight and the belt drive decouples the motor noise from the bondtech gears (to name only two of the many advantages).

Posted : 06/03/2019 12:05 pm
guy.k2
(@guy-k2)
Noble Member

Yes, a geared extruder like the Bondtech or Skelestruder increases torque enough to use a 0.9 stepper on the extruder. That also helps hide non-monotonicity by making any irregularities closer together. Direct drive doesn't get those advantages, but can feed faster.

Back to the main topic.... Effect of Stepper Motor on VFA's.

The LDO 1.8º "Cooler" LDO-42STH40-1004ASR was promoted as LDO's response to the Moons 1.8 MS17HD2P4100. We already know the MS17HD2P4100 is an improvement over the stock LDO motors. The question was how the LDO Cooler compares to Moon 1.8.

Here is a comparison of the Y-surface with motors of interest. I chose the y-surface because it is the one that tends to have the worst VFA's. The lighter loaded x-surface usually has smaller amplitude VFA's. You can see that changing the stepper motor produces different surface finishes. I have included stock and 0.9 degree prints as well.

From left to right...
Stock Prusa-LDO / LDO Cooler / Moons' 1.8º / Moons 0.9º

LDO 1.8º "Cooler" LDO-42STH40-1004ASR result is best described stock Prusa/LDO's surface artifacts smeared. It does hide VFA's better than the stock motor. The VFA's are about the same amplitude but smoothed. Inter-VFA extrusion is less constant. It goes up and down between VFA's

Moons 1.8º produces finer, lower amplitude VFA pattern. Inter-VFA extrusion is quite constant. You see that as marked flatness between its fine VFA's.

None of the 1.8º motors can compete against the Moons 0.9º MS17HA2P4100. That motor on x and y yields far superior surface finish - nearly VFA free, almost polished in appearance when viewed live.

Between a $41 Moons 1.8º and $19 LDO Cooler 1.8º for the extruder, I would select the Moons based on its consistency.
LDO Cooler would be chosen only if the $22 price difference is primary deciding factor.

As for the x and y axes, it makes less sense to upgrade with Moons 1.8 or LDO Cooler in the face of much better print quality withMoons 0.9º MS17HA2P4100 motors. On the other hand, 0.9º motors require customized firmware.

Modified Firmware, $135 for maximal surface quality improvement
X Moons 0.9º MS17HA2P4100
Y Moons 0.9º MS17HA2P4100
E Moons 1.8º MS17HD2P4100

Stock firmware, $123 but considerably less improvement in surface finish quality
X Moons 1.8º MS17HD2P4100
Y Moons 1.8º MS17HD2P4100
E Moons 1.8º MS17HD2P4100

For the cost of one McDonalds meal plus using custom firmware you can leap way ahead.

Posted : 06/03/2019 4:06 pm
guy.k2
(@guy-k2)
Noble Member

Bhawkeye, I took your wave generation and changed it to expand FAC1000 values to specify 0.8 to 1.200 for fac.

It still didn't work right as written, but I found the problem is c++ in the Arduino compiler isn't promoting or demoting data types the same way your development compiler is working.

I ended up brute forcing your formula calculations and broke them into explicit step by step usage of floats.
Took about 40 cycles of reflashing firmware with different casting variations until I decided to brute force the conversions.

[/begin rant]
Man I fckin hate c++. That's why most of my own stuff is in Xojo - super easy to read. Lets me maintain a 2,000 page app even years after code was written. Not mainstream language, but this typecasting BS makes apparently correct formulas produce unexpected results.
[/end rant]

I never could figure out to typecast the appropriate parts of the original formulas.
At least, I have the motors actually moving with this super ugly approach to doing the midpoint and tcorr calculations.

I am printing an e-axis waveform tower with fac from 0.8 to 1.2.

BTW, it would be good if your code implemented fac1000 = 0 to do the default sine wave. That would make it easier to test.
I had to manually go through and find a reasonably smooth value for x, y, z motion before I could test with a print.

If zero di the default sine wave, I could have just .....

TMC_SET_WAVE_X0
TMC_SET_WAVE_Y0
TMC_SET_WAVE_Z0

The below were manually arrived upon to allow the test print.
TMC_SET_WAVE_X150
TMC_SET_WAVE_Y150
TMC_SET_WAVE_Z150


void tmc2130_set_wave(uint8_t axis, uint8_t amp, uint8_t fac1000)
{
/*
fac1000 parameter interpreted differently
fac1000 30 to 200 to set fac 0.8 to 1.200
fac1000 0 sets fac = 0

sin0 values other than 0 will compute correctly - however, this makes sin0 & -sin0 the values on either side
of the zero cross point, with no actual zero point in the wave. If sin0 is zero, the zero value will be repeated
twice at the zero cross points. The TMC2660 uses a sin0 value of 1, and this seems to give the smoothest
waveforms on a 'scope. The sin0 value is now set in TMC2130.h by TMC2130_WAVE_SIN0, and the default
value is 1.
*/
float tcorr;
uint32_t reg; //tmc2130 register
uint16_t ampamp = amp * amp;
int i = 0;
uint8_t w[4] = {1,1,1,1}; //W bits (MSLUTSEL)
int8_t wtbl[4][2]=
{
-1, 0,
0, 1,
1, 2,
2, 3
};
uint8_t x[3] = {255,255,255}; //X segment bounds (MSLUTSEL)
uint8_t val[257];
uint8_t va;
int8_t d0 = 0; //delta0 from MSLUTSEL W bits
int8_t d1 = 1; //delta1 MSLUTSEL W bits
int8_t maxd;
uint8_t s = 0; //current segment
int8_t b; //encoded MSLUT bit value
int8_t dA; //int delta value
uint8_t missed_i ;
uint8_t chgcnt = 0;
uint8_t midpoint;
float midpointf;

midpointf = round(amp - TMC2130_WAVE_SIN0);
printf_P(PSTR("midpointf1 %s\n"), ftostr43(midpointf));

midpointf = midpointf * midpointf / 2.0;
printf_P(PSTR("midpointf2 %s\n"), ftostr43(midpointf));

midpointf = sqrt(midpointf);
printf_P(PSTR("midpointf3 %s\n"), ftostr43(midpointf));

midpoint = (uint8_t)round(midpointf);
printf_P(PSTR("midpointf4 %d\n"), midpoint);

if (fac1000 < TMC2130_WAVE_FAC1000_MIN) fac1000 = 0;
if (fac1000 > TMC2130_WAVE_FAC1000_MAX) fac1000 = TMC2130_WAVE_FAC1000_MAX;

float fac = 0;
if (fac1000)
{
fac = (float)(fac1000 - TMC2130_WAVE_FAC1000_MIN);
fac = fac / (float)(TMC2130_WAVE_FAC1000_MAX - TMC2130_WAVE_FAC1000_MIN);
fac = 0.8 + 0.4 * fac;
}

// find correction factor that puts midpoint of curve at sqrt of (amp^2/2) - for smooth curve transition
//uint8_t pwrmid = pow(sin((PI*(127))/512),fac)*(amp-TMC2130_WAVE_SIN0) + 0.5;

uint8_t pwrmid;
float pwrmidf;

pwrmidf = (PI*(127))/512.0;
printf_P(PSTR("pwrmidf1 %s\n"), ftostr43(pwrmidf));

pwrmidf = sin(pwrmidf);
printf_P(PSTR("pwrmidf2 %s\n"), ftostr43(pwrmidf));

pwrmidf = pow(pwrmidf, fac);
printf_P(PSTR("pwrmidf3 %s\n"), ftostr43(pwrmidf));

pwrmidf = pwrmidf * (amp-TMC2130_WAVE_SIN0) + 0.5;
printf_P(PSTR("pwrmidf4 %s\n"), ftostr43(pwrmidf));

pwrmid = (uint8_t)round(pwrmidf);

tcorr = (float)(midpoint) / pwrmid;

printf_P(PSTR("midpoint %d\n"), midpoint);
printf_P(PSTR("pwrmid %d\n"), pwrmid);
printf_P(PSTR("tcorr %s\n"), ftostr43(tcorr));
printf_P(PSTR("fac1000 %d\n"),fac1000);
printf_P(PSTR("fac %s\n"), ftostr43(fac));

va = val[0] = TMC2130_WAVE_SIN0;
for(i=1; i<126; i++)
{
if(i < 33)
maxd =3;
else if( i < 125)
maxd = 2;
else
maxd = 1;
va = ((amp-TMC2130_WAVE_SIN0) * pow((float)(sin(PI*i/512)), fac) * tcorr) + TMC2130_WAVE_SIN0 + 0.5;
if(va <= (val[i-1] + maxd))
val[i] = va;
else
val[i] = val[i-1] + maxd; // curve can't exceed TMC2130's max slope
}
val[127] = midpoint+TMC2130_WAVE_SIN0;
val[126] = (val[125]+val[127] +1)/2;
for(i=128; i<255; i++)
{
va = (uint8_t)(sqrt((float)(ampamp - (uint16_t)val[255 - i] * val[255 - i])) +.5);
val[i] = va > amp ? amp:va;
}
val[256] = amp;
tmc2130_wr_MSLUTSTART(axis, TMC2130_WAVE_SIN0, amp);
redocompress:
missed_i = 0;
s = 0;
// get initial segment slope, based on fac for, values more neg than ~-54 lim w[0]=3, values above ~80 w[0]=1, for all others w[0]=2
dA = val[1] - val[0]; // find delta between 0th & 1st
switch (dA)
{
default:
case 1: d0 = 0; d1 = 1; w[s] = 1; break;
case 2: d0 = 1; d1 = 2; w[s] = 2; break;
case 3: d0 = 2; d1 = 3; w[s] = 3; break;
}

va = TMC2130_WAVE_SIN0;
// TMC2130 wave compression algorithm
// for minimal memory requirements in the TMC2130 chip
// printf_P(PSTR("tmc2130_set_wave %hhd %hhd\n"), axis, fac1000);
// printf_P(PSTR(" factor: %s\n"), ftostr43(fac));
for (i = 0; i < 256; i++) // compress wave
{
if ((i & 0x1f) == 0)
reg = 0;
dA = val[i+1] - va; // calculate delta to next
dA = dA < 0 ? 0:dA;
dA = dA > 3 ? 3:dA;
b = 0;
int sw;
if ((dA < d0) && (d0 > -1)) // delta < delta0 => switch wbit down or wait to catch up
{
if(i > 120 && fac1000 > 100 && ((val[i+2] - va) > 3*d0)) // mid-range is steeper when fac1000 is positive,
goto nomoresegs; // so leave mid section at 1,2
//printf("dn\n");
if(s==3)
{
w[s]=1;
d0 = 0; d1 = 1;
goto nomoresegs;
}
sw = i < 120 ? (w[s]-1): 1; // always switch down 1 from current on lower half of wave & switch to 0:1 on upper half, when it's time to switch
switch (sw)
{
case 1: d0 = 0; d1 = 1; w[s+1] = 1; break;
case 2: d0 = 1; d1 = 2; w[s+1] = 2; break;
default: b=-1; break;
}
if (b >= 0)
{
x[s] = i;
s++;
}
}
else if ((dA > d1) && (d1 < 3) && (i < 128) && (i<16 ||(val[i+2] - va) > (3*d1)) ) // delta > delta0 => switch wbit up
{
if(s==3)
{
w[s]=1;
d0 = 0; d1 = 1;
goto nomoresegs;
}
int sw = w[s] + 1; // always switch up 1 from current
switch (sw)
{
case 1: d0 = 0; d1 = 1; w[s+1] = 1; break;
case 2: d0 = 1; d1 = 2; w[s+1] = 2; break;
case 3: d0 = 2; d1 = 3; w[s+1] = 3; break;
default: b = -1; break;
}
if (b >= 0)
{
x[s] = i; s++;
}
}
nomoresegs:
if (dA <= d0)
b = 0; //delta == delta0 => bit=0
else
b = 1; //delta == delta1 => bit=1
if (b == 1)
reg |= 0x80000000;
va = va + wtbl[w[s]][b]; // update value for next compare
if(va != val[i+1])
{
missed_i = i; // remember last miss
val[i+1] = va; // change next value to the one that was achievable
}
if ((i & 31) == 31)
tmc2130_wr_MSLUT(axis, (uint8_t)(i >> 5), reg);
else
reg >>= 1;
}

if((chgcnt == 0) && (missed_i != 0)) // only try once
{
// make minor corrections to 1st half of wave values - to make up for error caused by compression
// sometimes 2nd half of wave can't get exact match, due to running out of compression segments
missed_i = 255-missed_i; // last miss paired value is the 1st we need to check
if(missed_i <16)
missed_i = 16; // don't adjust 1st 16 entries
for(i=missed_i; i < 125; i++)
{
va = (uint8_t)(sqrt((float)(((uint32_t)val[i] * val[i]) + ((uint32_t)val[255-i] * val[255-i]))) + .5);
if(va < amp)
{
val[i]++;
chgcnt++;
}
else if(va > amp)
{
val[i]--;
chgcnt++;
}
}
if(chgcnt != 0)
goto redocompress;

}

tmc2130_wr_MSLUTSEL(axis, x[0], x[1], x[2], w[0], w[1], w[2], w[3]);

Posted : 06/03/2019 7:40 pm
michael.k95
(@michael-k95)
Active Member

Regarding motor amps, what is the ideal motor amperage? I think the einsy limits to 1A, so is 1A the ideal amperage? Would there be any benefit to a lower amp motor, say 0.5A, 0.6A, 0.8A etc?

Thanks!

Posted : 06/03/2019 9:21 pm
guy.k2
(@guy-k2)
Noble Member

Lower amps is cooler, but then we're into too low torque territory. The EINSY is happily within thermal limits with the 1 amp steppers and the axes are actually moving well.

Bhawkeye, here is the e wavefore tower using your constant torque waveform generator. I ran it with fac from 0.8 to 1.2. I think it looks best at fac 1.058
The printout for that setting was...

Send: TMC_SET_WAVE_E140
Recv: midpoint 174
Recv: pwrmid 170
Recv: tcorr 1.023
Recv: fac1000 140
Recv: fac 1.058

The best region on your waveforms looks a lot like best on the standard power waveform. Your curve covers a wider range of wave behavior and the motors kept running all through the range - even down at fac 0.8 - albeit not very smoothly when down that low.

I'm afraid, though, the standard power may be a bit less wavy when it is at fac 1.012.

We really need your printer up and running.

Posted : 06/03/2019 10:20 pm
bhawkeye
(@bhawkeye)
Eminent Member

We really need your printer up and running.
Agreed!
Thanks for your efforts on this. It seems like pretty meager return on investment (my changes only! Your work has been terrific & very worthwhile 😀 ).
I'll turn my efforts back to finishing my stock build(had lots of mods planned, but I'll save those for later) & do further testing once I'm up & running.

Posted : 06/03/2019 10:43 pm
guy.k2
(@guy-k2)
Noble Member

For those unable to compile their own firmware, but wishing to test Moons' 0.9 degree X & Y motors on their Mk3.

Here are my 3.60 Mk3 and Mk3s firmware for Moons 0.9 degree motors on BOTH x and y axis.

These will ONLY work with BOTH x and y motors replaced by Moons' 0.9 degree MS17HA2P4100 motors.

If you have any other combination of motors on x and y, you cannot use this test firmware. Both x and y must have 0.9 degree motors.

**** Please set Linearity Correction OFF (or 1.124 and higher) for all axes before attempting use.
This experimental firmware has an expanded linearity correction range and lower values will probably stall your motors.
Above 1.100 on linearity correction is usually ok. I found 1.124 as optimal for Moons 1.8 on extruder.

This version includes support for G80 N7 meshbed leveling and also the G80 R parameter.
G80 M7 R1 does 7 x 7 mesh with single repetition touches.

Experimental firmware use is at your own risk. Be certain to select correct one for you printer model

--------------

Mk3

-------------

Mk3s

Posted : 07/03/2019 1:48 am
guy.k2
(@guy-k2)
Noble Member

I have already confirmed that the Moons MS17HA2P4100 works well, but we also found that the 0.9 degree LDO-42STH40-1684MAC also produces a similar leap in surface finish quality. The LDO's problem was too high current draw if run in Stealthchop. They were simply too much of a load for the TMC2130's. Also, the LDO's were louder than the Moons'

Both 0.9's similarly removed VFA's. Monotonic motion improvement seems to be more the result of going to a 0.9 degree stepper than brand/build of the stepper.

This $20, no name motor caught my eye. It is also a 0.9 degree unit. It's specs are under 1 amp and torque is similar to the Moon's. Resistance is high like stock LDO's. It should be ok current load for the TMC2130's. Inductance is pretty high.

Is this no name motor built like cr*p or usable?
Will it produce as good a print result as the other 0.9 degree motors?
Are the specs accurate letting it remain within current load limits?
Can it support Stallguard sensorless homing?

At $20, we simply must find out. The specs are so close the Moons that it might run with the same settings. That's assuming it's actually anywhere near its specs.

https://www.amazon.com/gp/product/B00W98OYE4

STEPPERONLINE 0.9° 17HM15-0904S
Voltage: 12-24V
Resistance 6.0ohms
Hold Torque 0.36 Nm
Current 0.9A
Weight 280 gm
Inductance 12.0mH

Posted : 07/03/2019 9:05 am
KLab
 klab
(@klab)
Active Member

I came to the same conclusion, that the quality improvement is simply down to the smoother movement of 0.9° steppers.
So I began to search for easier available and cheaper alternatives to the Moons (two of the 0.9° Moons would be around $110 for me here in germany).

The cheap stepper you mentioned is even cheaper on their own webshop:
https://www.omc-stepperonline.com/nema-17-stepper-motor/nema-17-bipolar-09deg-36ncm-51ozin-09a-54v-42x42x40mm-4-wires-17hm15-0904s.html

It's one of the steppers I also found during my search.

Another quality brand I consider as an alternative, is one of the Nanotec steppers. (similar expensive as the Moons but since it's a german company, it's cheaper for me and maybe others):
https://us.nanotec.com/products/451-st4209-stepper-motor-09-nema-17/

Posted : 07/03/2019 11:22 am
Dreide
(@dreide)
Trusted Member


Is this no name motor built like cr*p or usable?
Will it produce as good a print result as the other 0.9 degree motors?
Are the specs accurate letting it remain within current load limits?
Can it support Stallguard sensorless homing?

Which is similar to michael.k95's question: what is the ideal motor amperage?
If I only knew. For some reason I think higher amperage is better (maybe not as high as 1.68A), but I actually have no hard evidence. It is so difficult to compare these motors. How to set up the motors in a comparable way, and by which criterion to compare them (noise, accuracy, VFAs, ringing, ...), and how to measure the criteria objectively?
I now have short (L35) 0.9° 1.3A motors installed on X and Y (GM42HSM34-1304), and, to my surprise, they run just fine at basically the same currents as the stock motors. How come? According to the specs, the stock motors deliver 50Ncm at 1A, mine deliver 24Ncm at 1.3A??? So, are the stock motors completely over-powered, or do I under-power mine now? I don't know. I guess I have to put the stock motors back in and run more tests, if I really want to know. One reason for me using higher currents with the stock motors, still at or below the Prusa settings, was to overcome step skipping in very rare situations (StealthChop). I didn't go further up because of the noise and, instead, lowered jerk from 10 to 6. But this situation was not satisfying, knowing that I could print much quieter and faster if StealthChop was just more reliable.
Along the way of going to 0.9° I had to raise the limits for the maximum step frequency in the firmware after I got horrible current curves above certain speeds which were caused by the firmware going into quad-stepping (4/8=2 microsteps per full motor step - oops). Now I've set the limit to 16kHz, which is good for going up to 160mm/s without reducing microstep resolution and the firmware seems to keep up with it (this is Repetier firmware, BTW). This helps reducing the noise of long travel moves.
BTW, I think I made my peace with StealthChop. The key was using auto_scale=0, which lets the driver control the current duty cycle just based on the step pulse interval, i.e. without even trying to measure the current. The control is very fast and very consistent, but at the same time does not cope well at all with inconsistent step frequencies, like you get with double- or quad-stepping (or with LinearAdvance in case of the E motor). This is why I have to limit X travel moves to 160mm/s, which otherwise could be 200mm/s without being louder than Y. I printed two benchys, one with SteathChop and one with SpreadCycle (same currents and same g-code). Surprisingly, I couldn't make out any differences between the two printouts whatsoever. I would have expected StealthChop to be less accurate, but it was just quieter.
On one hand, nice that it works; on the other hand, I wished that my supposedly educated guesses would be right more often.

Posted : 07/03/2019 12:16 pm
Page 2 / 49
Share:

Please Login or Register