Closed DerAndere1 closed 4 years ago
Hello I am interested in this functionality and I am unclear on the current status. I can see multiple branches, the most recent of which was touched in June, but it appears these are intending to get E-axis homing merged into upstream.
I thought I might rebase the main commit from bf2_6axis_dev on to the current Marlin but I am not so fluent in conflict resolution so I might have to work off of the slightly stale bf2_6axis_dev. What should I expect?
My alternative, which might "work" but which I don't like, is to perform CAM with A and B axes and then post-process into a fake mixing extruder, enabling MIXING_EXTRUDER and performing M165 between each movement. I'm not sure if the mix factors can be negative and in any case this would be an ugly approach.
Anyway the main question is, if I were to use this bf2_6axis_dev as a starting point, what should I expect and is there anything else I should know before trying to go forward?
I never could make it work. This was a toy project for me to learn programming, so I did not research but there is a high chance that 8 bit AVRs are too slow to process more than 4 Axes (including extruder) in parallel. Note that my Idea was to support cartesian robots where I,J,K move additional steppers that do not influence positioning of the tool in any way. If you need support for a specific kinematic model (e.g. 5 axis arm), I don't know how much code you have to add. If you need more than 3 axes for a serious project , currently the best is to base your project on Smoothieware + their supported hardware. Smoothieware has support for 6 axes. Also, check if https://github.com/fra589/grbl-Mega-5X/ meets your requirements. If you still want to add 6 axis support to Marlin, I would first search for all the commits I made and analyze the diff to understand the changes. If you see you can fix the 6axis_dev branch easily, do that in this branch, the merge with current Marlin can be done later by someone else. If you think the fix will be difficult, it will make sense for you to first learn how to resolve conflics during a merge. Use a visual tool to do the conflict resolution (I use Eclipse+Egit). If something goes wrong during the merge, you can always create a new branch that is a clone of my bf2_6axis_dev and try the merge again. To give more feedback, I would need to know what your requirements are: What type of kinematic model? do you need endstops on all axes? do you need auto-homing on all axes? do you have a printer or engraver where extrusion/laser power needs to be calculated based on XYZ-movement speed? Is grbl-Mega-5X enough?
Thank you for the clarification. I don't really "need" 5-axis, I am mostly curious to play with it. I think I will try 3+2 and postprocess A and B into fake extruder movements. I have no extruder so I have 2 free stepper drivers anyway, and I am wanting to play with 3-axis milling with 2 rotary axes.
I appreciate the work that you put into this. It's definitely a challenge. In the end I think I can achieve my objective with other means.
If grbl-Mega-5X or Smoothieware is not a solution, Repetier Firmware 2.0 (https://github.com/repetier/Repetier-Firmware/tree/dev2) supports 7 axes and CNC milling tools. It is currently in development but you can already try it on Ardunino Due-based boards.
Hello, I control the axes i, j, k but they run very slowly :-D there is still much work to do too Sorry I'm not good at English
@nguyennhattam268 Hi, thats great news. Could You send me your source code for inspection? If you don't want to share certain parts of your code, leave those parts out. If you are not familiar with github, you can put the source code into a zip-compressed folder and drag and drop that zip-compressed folder into a comment here.
Yes, I will post here because I am not good at English 2 is that I don't have much time for this because I have to go to work I did 6asix on Repetier_firmware last year but I have to use arduino due and RAMPS_FD I wanted to use ramps1.4 and marlin so I made it with passion This is my test video and I only changed a few parameters in the stepper file the most important is to change LOOP_NUM_AXIS_N in CONFIGRUATION_STORE.CPP https://www.youtube.com/channel/UCnRniVi-9ObhQ87oIsQxihQ?view_as=subscriber
@nguyennhattam268 Can you copy your stepper.cpp file into a comment here? I would love to mention your name (give attribution to you) and fix 6_axis_dev for everyone.
O of course I will send it right here I could not find any place to download zip files here stepper_indirection.cpp /**
*/
/**
// // TMC26X Driver objects and inits //
#include "../HAL/HAL_STM32F7/TMC2660.h"
#include <TMC26XStepper.h>
_TMC26X_DEFINE(X);
_TMC26X_DEFINE(X2);
_TMC26X_DEFINE(Y);
_TMC26X_DEFINE(Y2);
_TMC26X_DEFINE(Z);
_TMC26X_DEFINE(Z2);
_TMC26X_DEFINE(Z3);
_TMC26X_DEFINE(E0);
_TMC26X_DEFINE(E1);
_TMC26X_DEFINE(E2);
_TMC26X_DEFINE(E3);
_TMC26X_DEFINE(E4);
_TMC26X_DEFINE(E5);
stepper##A.setMicrosteps(A##_MICROSTEPS); \
stepper##A.start(); \
}while(0)
void tmc26x_init_to_defaults() {
_TMC26X_INIT(X);
#endif
#if AXIS_DRIVER_TYPE_X2(TMC26X)
_TMC26X_INIT(X2);
#endif
#if AXIS_DRIVER_TYPE_Y(TMC26X)
_TMC26X_INIT(Y);
#endif
#if AXIS_DRIVER_TYPE_Y2(TMC26X)
_TMC26X_INIT(Y2);
#endif
#if AXIS_DRIVER_TYPE_Z(TMC26X)
_TMC26X_INIT(Z);
#endif
#if AXIS_DRIVER_TYPE_Z2(TMC26X)
_TMC26X_INIT(Z2);
#endif
#if AXIS_DRIVER_TYPE_Z3(TMC26X)
_TMC26X_INIT(Z3);
#endif
#if AXIS_DRIVER_TYPE_E0(TMC26X)
_TMC26X_INIT(E0);
#endif
#if AXIS_DRIVER_TYPE_E1(TMC26X)
_TMC26X_INIT(E1);
#endif
#if AXIS_DRIVER_TYPE_E2(TMC26X)
_TMC26X_INIT(E2);
#endif
#if AXIS_DRIVER_TYPE_E3(TMC26X)
_TMC26X_INIT(E3);
#endif
#if AXIS_DRIVER_TYPE_E4(TMC26X)
_TMC26X_INIT(E4);
#endif
#if AXIS_DRIVER_TYPE_E5(TMC26X)
_TMC26X_INIT(E5);
#endif
}
enum StealthIndex : uint8_t { STEALTH_AXIS_XY, STEALTH_AXIS_Z, STEALTH_AXIS_E };
// // TMC2130 Driver objects and inits //
#define _TMC2130_DEFINE(ST, L) TMCMarlin<TMC2130Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE, TMC_SW_MOSI, TMC_SW_MISO, TMC_SW_SCK)
#define TMC2130_DEFINE(ST) _TMC2130_DEFINE(ST, TMC_##ST##_LABEL)
#define _TMC2130_DEFINE(ST, L) TMCMarlin<TMC2130Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE)
#define TMC2130_DEFINE(ST) _TMC2130_DEFINE(ST, TMC_##ST##_LABEL)
// Stepper objects of TMC2130 steppers used
TMC2130_DEFINE(X);
TMC2130_DEFINE(X2);
TMC2130_DEFINE(Y);
TMC2130_DEFINE(Y2);
TMC2130_DEFINE(Z);
TMC2130_DEFINE(Z2);
TMC2130_DEFINE(Z3);
TMC2130_DEFINE(E0);
TMC2130_DEFINE(E1);
TMC2130_DEFINE(E2);
TMC2130_DEFINE(E3);
TMC2130_DEFINE(E4);
TMC2130_DEFINE(E5);
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC2130Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm, const bool stealth) { st.begin();
CHOPCONF_t chopconf{0};
chopconf.tbl = 1;
chopconf.toff = chopper_timing.toff;
chopconf.intpol = INTERPOLATE;
chopconf.hend = chopper_timing.hend + 3;
chopconf.hstrt = chopper_timing.hstrt - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA, HOLD_MULTIPLIER);
st.microsteps(microsteps);
st.iholddelay(10);
st.TPOWERDOWN(128); // ~2s until driver lowers to hold current
st.en_pwm_mode(stealth);
st.stored.stealthChop_enabled = stealth;
PWMCONF_t pwmconf{0};
pwmconf.pwm_freq = 0b01; // f_pwm = 2/683 f_clk
pwmconf.pwm_autoscale = true;
pwmconf.pwm_grad = 5;
pwmconf.pwm_ampl = 180;
st.PWMCONF(pwmconf.sr);
#if ENABLED(HYBRID_THRESHOLD)
st.TPWMTHRS(12650000UL*microsteps/(256*thrs*spmm));
#else
UNUSED(thrs);
UNUSED(spmm);
#endif
st.GSTAT(); // Clear GSTAT
}
// // TMC2160 Driver objects and inits //
#define _TMC2160_DEFINE(ST, L) TMCMarlin<TMC2160Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE, TMC_SW_MOSI, TMC_SW_MISO, TMC_SW_SCK)
#define TMC2160_DEFINE(ST) _TMC2160_DEFINE(ST, TMC_##ST##_LABEL)
#define _TMC2160_DEFINE(ST, L) TMCMarlin<TMC2160Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE)
#define TMC2160_DEFINE(ST) _TMC2160_DEFINE(ST, TMC_##ST##_LABEL)
// Stepper objects of TMC2160 steppers used
TMC2160_DEFINE(X);
TMC2160_DEFINE(X2);
TMC2160_DEFINE(Y);
TMC2160_DEFINE(Y2);
TMC2160_DEFINE(Z);
TMC2160_DEFINE(Z2);
TMC2160_DEFINE(Z3);
TMC2160_DEFINE(E0);
TMC2160_DEFINE(E1);
TMC2160_DEFINE(E2);
TMC2160_DEFINE(E3);
TMC2160_DEFINE(E4);
TMC2160_DEFINE(E5);
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC2160Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm, const bool stealth) { st.begin();
static constexpr int8_t timings[] = CHOPPER_TIMING; // Default 4, -2, 1
CHOPCONF_t chopconf{0};
chopconf.tbl = 1;
chopconf.toff = timings[0];
chopconf.intpol = INTERPOLATE;
chopconf.hend = timings[1] + 3;
chopconf.hstrt = timings[2] - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA, HOLD_MULTIPLIER);
st.microsteps(microsteps);
st.iholddelay(10);
st.TPOWERDOWN(128); // ~2s until driver lowers to hold current
st.TCOOLTHRS(0xFFFFF);
#if ENABLED(ADAPTIVE_CURRENT)
COOLCONF_t coolconf{0};
coolconf.semin = INCREASE_CURRENT_THRS;
coolconf.semax = REDUCE_CURRENT_THRS;
st.COOLCONF(coolconf.sr);
#endif
st.en_pwm_mode(stealth);
PWMCONF_t pwmconf{0};
pwmconf.pwm_freq = 0b01; // f_pwm = 2/683 f_clk
pwmconf.pwm_autoscale = true;
pwmconf.pwm_grad = 5;
pwmconf.pwm_ampl = 180;
st.PWMCONF(pwmconf.sr);
#if ENABLED(HYBRID_THRESHOLD)
st.TPWMTHRS(12650000UL*microsteps/(256*thrs*spmm));
#else
UNUSED(thrs);
UNUSED(spmm);
#endif
st.GSTAT(); // Clear GSTAT
}
// // TMC2208 Driver objects and inits //
// Stepper objects of TMC2208 steppers used
#ifdef X_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(X);
#else
TMC2208_DEFINE_SOFTWARE(X);
#endif
#ifdef X2_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(X2);
#else
TMC2208_DEFINE_SOFTWARE(X2);
#endif
#ifdef Y_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(Y);
#else
TMC2208_DEFINE_SOFTWARE(Y);
#endif
#ifdef Y2_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(Y2);
#else
TMC2208_DEFINE_SOFTWARE(Y2);
#endif
#ifdef Z_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(Z);
#else
TMC2208_DEFINE_SOFTWARE(Z);
#endif
#ifdef Z2_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(Z2);
#else
TMC2208_DEFINE_SOFTWARE(Z2);
#endif
#ifdef Z3_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(Z3);
#else
TMC2208_DEFINE_SOFTWARE(Z3);
#endif
#ifdef E0_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E0);
#else
TMC2208_DEFINE_SOFTWARE(E0);
#endif
#ifdef E1_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E1);
#else
TMC2208_DEFINE_SOFTWARE(E1);
#endif
#ifdef E2_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E2);
#else
TMC2208_DEFINE_SOFTWARE(E2);
#endif
#ifdef E3_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E3);
#else
TMC2208_DEFINE_SOFTWARE(E3);
#endif
#ifdef E4_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E4);
#else
TMC2208_DEFINE_SOFTWARE(E4);
#endif
#ifdef E5_HARDWARE_SERIAL
TMC2208_DEFINE_HARDWARE(E5);
#else
TMC2208_DEFINE_SOFTWARE(E5);
#endif
void tmc2208_serial_begin() {
#ifdef X_HARDWARE_SERIAL
X_HARDWARE_SERIAL.begin(115200);
#else
stepperX.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_X2(TMC2208)
#ifdef X2_HARDWARE_SERIAL
X2_HARDWARE_SERIAL.begin(115200);
#else
stepperX2.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_Y(TMC2208)
#ifdef Y_HARDWARE_SERIAL
Y_HARDWARE_SERIAL.begin(115200);
#else
stepperY.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_Y2(TMC2208)
#ifdef Y2_HARDWARE_SERIAL
Y2_HARDWARE_SERIAL.begin(115200);
#else
stepperY2.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z(TMC2208)
#ifdef Z_HARDWARE_SERIAL
Z_HARDWARE_SERIAL.begin(115200);
#else
stepperZ.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z2(TMC2208)
#ifdef Z2_HARDWARE_SERIAL
Z2_HARDWARE_SERIAL.begin(115200);
#else
stepperZ2.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z3(TMC2208)
#ifdef Z3_HARDWARE_SERIAL
Z3_HARDWARE_SERIAL.begin(115200);
#else
stepperZ3.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E0(TMC2208)
#ifdef E0_HARDWARE_SERIAL
E0_HARDWARE_SERIAL.begin(115200);
#else
stepperE0.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E1(TMC2208)
#ifdef E1_HARDWARE_SERIAL
E1_HARDWARE_SERIAL.begin(115200);
#else
stepperE1.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E2(TMC2208)
#ifdef E2_HARDWARE_SERIAL
E2_HARDWARE_SERIAL.begin(115200);
#else
stepperE2.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E3(TMC2208)
#ifdef E3_HARDWARE_SERIAL
E3_HARDWARE_SERIAL.begin(115200);
#else
stepperE3.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E4(TMC2208)
#ifdef E4_HARDWARE_SERIAL
E4_HARDWARE_SERIAL.begin(115200);
#else
stepperE4.beginSerial(115200);
#endif
#endif
#if AXIS_DRIVER_TYPE_E5(TMC2208)
#ifdef E5_HARDWARE_SERIAL
E5_HARDWARE_SERIAL.begin(115200);
#else
stepperE5.beginSerial(115200);
#endif
#endif
}
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC2208Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm, const bool stealth) { TMC2208_n::GCONF_t gconf{0}; gconf.pdn_disable = true; // Use UART gconf.mstep_reg_select = true; // Select microsteps with UART gconf.i_scale_analog = false; gconf.en_spreadcycle = !stealth; st.GCONF(gconf.sr); st.stored.stealthChop_enabled = stealth;
TMC2208_n::CHOPCONF_t chopconf{0};
chopconf.tbl = 0b01; // blank_time = 24
chopconf.toff = chopper_timing.toff;
chopconf.intpol = INTERPOLATE;
chopconf.hend = chopper_timing.hend + 3;
chopconf.hstrt = chopper_timing.hstrt - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA, HOLD_MULTIPLIER);
st.microsteps(microsteps);
st.iholddelay(10);
st.TPOWERDOWN(128); // ~2s until driver lowers to hold current
TMC2208_n::PWMCONF_t pwmconf{0};
pwmconf.pwm_lim = 12;
pwmconf.pwm_reg = 8;
pwmconf.pwm_autograd = true;
pwmconf.pwm_autoscale = true;
pwmconf.pwm_freq = 0b01;
pwmconf.pwm_grad = 14;
pwmconf.pwm_ofs = 36;
st.PWMCONF(pwmconf.sr);
#if ENABLED(HYBRID_THRESHOLD)
st.TPWMTHRS(12650000UL*microsteps/(256*thrs*spmm));
#else
UNUSED(thrs);
UNUSED(spmm);
#endif
st.GSTAT(0b111); // Clear
delay(200);
}
// // TMC2660 Driver objects and inits //
#define _TMC2660_DEFINE(ST, L) TMCMarlin<TMC2660Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE, TMC_SW_MOSI, TMC_SW_MISO, TMC_SW_SCK)
#define TMC2660_DEFINE(ST) _TMC2660_DEFINE(ST, TMC_##ST##_LABEL)
#define _TMC2660_DEFINE(ST, L) TMCMarlin<TMC2660Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE)
#define TMC2660_DEFINE(ST) _TMC2660_DEFINE(ST, TMC_##ST##_LABEL)
// Stepper objects of TMC2660 steppers used
TMC2660_DEFINE(X);
TMC2660_DEFINE(X2);
TMC2660_DEFINE(Y);
TMC2660_DEFINE(Y2);
TMC2660_DEFINE(Z);
TMC2660_DEFINE(Z2);
TMC2660_DEFINE(E0);
TMC2660_DEFINE(E1);
TMC2660_DEFINE(E2);
TMC2660_DEFINE(E3);
TMC2660_DEFINE(E4);
TMC2660_DEFINE(E5);
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC2660Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t, const float, const bool) { st.begin();
TMC2660_n::CHOPCONF_t chopconf{0};
chopconf.tbl = 1;
chopconf.toff = chopper_timing.toff;
chopconf.hend = chopper_timing.hend + 3;
chopconf.hstrt = chopper_timing.hstrt - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA);
st.microsteps(microsteps);
st.intpol(INTERPOLATE);
st.diss2g(true); // Disable short to ground protection. Too many false readings?
#if ENABLED(TMC_DEBUG)
st.rdsel(0b01);
#endif
}
// // TMC5130 Driver objects and inits //
#define _TMC5130_DEFINE(ST, L) TMCMarlin<TMC5130Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE, TMC_SW_MOSI, TMC_SW_MISO, TMC_SW_SCK)
#define TMC5130_DEFINE(ST) _TMC5130_DEFINE(ST, TMC_##ST##_LABEL)
#define _TMC5130_DEFINE(ST, L) TMCMarlin<TMC5130Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE)
#define TMC5130_DEFINE(ST) _TMC5130_DEFINE(ST, TMC_##ST##_LABEL)
// Stepper objects of TMC5130 steppers used
TMC5130_DEFINE(X);
TMC5130_DEFINE(X2);
TMC5130_DEFINE(Y);
TMC5130_DEFINE(Y2);
TMC5130_DEFINE(Z);
TMC5130_DEFINE(Z2);
TMC5130_DEFINE(Z3);
TMC5130_DEFINE(E0);
TMC5130_DEFINE(E1);
TMC5130_DEFINE(E2);
TMC5130_DEFINE(E3);
TMC5130_DEFINE(E4);
TMC5130_DEFINE(E5);
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC5130Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm, const bool stealth) { st.begin();
CHOPCONF_t chopconf{0};
chopconf.tbl = 1;
chopconf.toff = chopper_timing.toff;
chopconf.intpol = INTERPOLATE;
chopconf.hend = chopper_timing.hend + 3;
chopconf.hstrt = chopper_timing.hstrt - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA, HOLD_MULTIPLIER);
st.microsteps(microsteps);
st.iholddelay(10);
st.TPOWERDOWN(128); // ~2s until driver lowers to hold current
st.en_pwm_mode(stealth);
st.stored.stealthChop_enabled = stealth;
PWMCONF_t pwmconf{0};
pwmconf.pwm_freq = 0b01; // f_pwm = 2/683 f_clk
pwmconf.pwm_autoscale = true;
pwmconf.pwm_grad = 5;
pwmconf.pwm_ampl = 180;
st.PWMCONF(pwmconf.sr);
#if ENABLED(HYBRID_THRESHOLD)
st.TPWMTHRS(12650000UL*microsteps/(256*thrs*spmm));
#else
UNUSED(thrs);
UNUSED(spmm);
#endif
st.GSTAT(); // Clear GSTAT
}
// // TMC5160 Driver objects and inits //
#define _TMC5160_DEFINE(ST, L) TMCMarlin<TMC5160Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE, TMC_SW_MOSI, TMC_SW_MISO, TMC_SW_SCK)
#define TMC5160_DEFINE(ST) _TMC5160_DEFINE(ST, TMC_##ST##_LABEL)
#define _TMC5160_DEFINE(ST, L) TMCMarlin<TMC5160Stepper, L> stepper##ST(ST##_CS_PIN, ST##_RSENSE)
#define TMC5160_DEFINE(ST) _TMC5160_DEFINE(ST, TMC_##ST##_LABEL)
// Stepper objects of TMC5160 steppers used
TMC5160_DEFINE(X);
TMC5160_DEFINE(X2);
TMC5160_DEFINE(Y);
TMC5160_DEFINE(Y2);
TMC5160_DEFINE(Z);
TMC5160_DEFINE(Z2);
TMC5160_DEFINE(Z3);
TMC5160_DEFINE(E0);
TMC5160_DEFINE(E1);
TMC5160_DEFINE(E2);
TMC5160_DEFINE(E3);
TMC5160_DEFINE(E4);
TMC5160_DEFINE(E5);
template<char AXIS_LETTER, char DRIVER_ID> void tmc_init(TMCMarlin<TMC5160Stepper, AXIS_LETTER, DRIVER_ID> &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm, const bool stealth) { st.begin();
int8_t timings[] = CHOPPER_TIMING; // Default 4, -2, 1
CHOPCONF_t chopconf{0};
chopconf.tbl = 1;
chopconf.toff = timings[0];
chopconf.intpol = INTERPOLATE;
chopconf.hend = timings[1] + 3;
chopconf.hstrt = timings[2] - 1;
st.CHOPCONF(chopconf.sr);
st.rms_current(mA, HOLD_MULTIPLIER);
st.microsteps(microsteps);
st.iholddelay(10);
st.TPOWERDOWN(128); // ~2s until driver lowers to hold current
#if ENABLED(ADAPTIVE_CURRENT)
COOLCONF_t coolconf{0};
coolconf.semin = INCREASE_CURRENT_THRS;
coolconf.semax = REDUCE_CURRENT_THRS;
st.COOLCONF(coolconf.sr);
#endif
st.en_pwm_mode(stealth);
PWMCONF_t pwmconf{0};
pwmconf.pwm_freq = 0b01; // f_pwm = 2/683 f_clk
pwmconf.pwm_autoscale = true;
pwmconf.pwm_grad = 5;
pwmconf.pwm_ampl = 180;
st.PWMCONF(pwmconf.sr);
#if ENABLED(HYBRID_THRESHOLD)
st.TPWMTHRS(12650000UL*microsteps/(256*thrs*spmm));
#else
UNUSED(thrs);
UNUSED(spmm);
#endif
st.GSTAT(); // Clear GSTAT
}
void restore_stepper_drivers() {
stepperX.push();
stepperX2.push();
stepperY.push();
stepperY2.push();
#if NON_E_AXES > 3
#if AXIS_IS_TMC(I)
stepperI.push();
#endif
#if NON_E_AXES > 4
#if AXIS_IS_TMC(J)
stepperJ.push();
#endif
#if NON_E_AXES > 5
#if AXIS_IS_TMC(K)
stepperK.push();
#endif
#endif
#endif
#endif
stepperZ.push();
stepperZ2.push();
stepperZ3.push();
stepperE0.push();
stepperE1.push();
stepperE2.push();
stepperE3.push();
stepperE4.push();
stepperE5.push();
}
void reset_stepper_drivers() {
tmc26x_init_to_defaults();
L6470.init_to_defaults();
static constexpr bool stealthchop_by_axis[] = {
#if ENABLED(STEALTHCHOP_XY)
true
#else
false
#endif
,
#if ENABLED(STEALTHCHOP_Z)
true
#else
false
#endif
,
#if ENABLED(STEALTHCHOP_E)
true
#else
false
#endif
};
_TMC_INIT(X, X_AXIS, STEALTH_AXIS_XY);
_TMC_INIT(X2, X_AXIS, STEALTH_AXIS_XY);
_TMC_INIT(Y, Y_AXIS, STEALTH_AXIS_XY);
_TMC_INIT(Y2, Y_AXIS, STEALTH_AXIS_XY);
#if AXIS_IS_TMC(Y2)
_TMC_INIT(I, I_AXIS, STEALTH_AXIS_XY);
#endif
#if NON_E_AXES > 4
#if AXIS_IS_TMC(Y2)
_TMC_INIT(J, J_AXIS, STEALTH_AXIS_XY);
#endif
#if NON_E_AXES > 5
#if AXIS_IS_TMC(Y2)
_TMC_INIT(K, K_AXIS, STEALTH_AXIS_XY);
#endif
#endif
#endif
#endif
_TMC_INIT(Z, Z_AXIS, STEALTH_AXIS_Z);
_TMC_INIT(Z2, Z_AXIS, STEALTH_AXIS_Z);
_TMC_INIT(Z3, Z_AXIS, STEALTH_AXIS_Z);
_TMC_INIT(E0, E_AXIS, STEALTH_AXIS_E);
_TMC_INIT(E1, E_AXIS_N(1), STEALTH_AXIS_E);
_TMC_INIT(E2, E_AXIS_N(2), STEALTH_AXIS_E);
_TMC_INIT(E3, E_AXIS_N(3), STEALTH_AXIS_E);
_TMC_INIT(E4, E_AXIS_N(4), STEALTH_AXIS_E);
_TMC_INIT(E5, E_AXIS_N(5), STEALTH_AXIS_E);
#if X_SENSORLESS
#if AXIS_HAS_STALLGUARD(X)
stepperX.sgt(X_STALL_SENSITIVITY);
#endif
#if AXIS_HAS_STALLGUARD(X2)
stepperX2.sgt(X_STALL_SENSITIVITY);
#endif
#endif
#if Y_SENSORLESS
#if AXIS_HAS_STALLGUARD(Y)
stepperY.sgt(Y_STALL_SENSITIVITY);
#endif
#if AXIS_HAS_STALLGUARD(Y2)
stepperY2.sgt(Y_STALL_SENSITIVITY);
#endif
#endif
#if Z_SENSORLESS
#if AXIS_HAS_STALLGUARD(Z)
stepperZ.sgt(Z_STALL_SENSITIVITY);
#endif
#if AXIS_HAS_STALLGUARD(Z2)
stepperZ2.sgt(Z_STALL_SENSITIVITY);
#endif
#if AXIS_HAS_STALLGUARD(Z3)
stepperZ3.sgt(Z_STALL_SENSITIVITY);
#endif
#endif
TMC_ADV()
stepper.set_directions();
}
// // L6470 Driver objects and inits //
// create stepper objects
// L6470 Stepper objects
_L6470_DEFINE(X);
_L6470_DEFINE(X2);
_L6470_DEFINE(Y);
_L6470_DEFINE(Y2);
_L6470_DEFINE(Z);
_L6470_DEFINE(Z2);
_L6470_DEFINE(Z3);
_L6470_DEFINE(E0);
_L6470_DEFINE(E1);
_L6470_DEFINE(E2);
_L6470_DEFINE(E3);
_L6470_DEFINE(E4);
_L6470_DEFINE(E5);
// not using L6470 library's init command because it // briefly sends power to the steppers
stepper##Q.resetDev(); \
stepper##Q.softFree(); \
stepper##Q.SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 \
| CONFIG_PWM_MUL_2 \
| CONFIG_SR_290V_us \
| CONFIG_OC_SD_DISABLE \
| CONFIG_VS_COMP_DISABLE \
| CONFIG_SW_HARD_STOP \
| CONFIG_INT_16MHZ); \
stepper##Q.SetParam(L6470_KVAL_RUN, 0xFF); \
stepper##Q.SetParam(L6470_KVAL_ACC, 0xFF); \
stepper##Q.SetParam(L6470_KVAL_DEC, 0xFF); \
stepper##Q.setMicroSteps(Q##_MICROSTEPS); \
stepper##Q.setOverCurrent(Q##_OVERCURRENT); \
stepper##Q.setStallCurrent(Q##_STALLCURRENT); \
stepper##Q.SetParam(L6470_KVAL_HOLD, Q##_MAX_VOLTAGE); \
stepper##Q.SetParam(L6470_ABS_POS, 0); \
stepper##Q.getStatus(); \
}while(0)
void L6470_Marlin::init_to_defaults() {
_L6470_INIT_CHIP(X);
#endif
#if AXIS_DRIVER_TYPE_X2(L6470)
_L6470_INIT_CHIP(X2);
#endif
#if AXIS_DRIVER_TYPE_Y(L6470)
_L6470_INIT_CHIP(Y);
#endif
#if AXIS_DRIVER_TYPE_Y2(L6470)
_L6470_INIT_CHIP(Y2);
#endif
#if AXIS_DRIVER_TYPE_Z(L6470)
_L6470_INIT_CHIP(Z);
#endif
#if AXIS_DRIVER_TYPE_Z2(L6470)
_L6470_INIT_CHIP(Z2);
#endif
#if AXIS_DRIVER_TYPE_Z3(L6470)
_L6470_INIT_CHIP(Z3);
#endif
#if AXIS_DRIVER_TYPE_E0(L6470)
_L6470_INIT_CHIP(E0);
#endif
#if AXIS_DRIVER_TYPE_E1(L6470)
_L6470_INIT_CHIP(E1);
#endif
#if AXIS_DRIVER_TYPE_E2(L6470)
_L6470_INIT_CHIP(E2);
#endif
#if AXIS_DRIVER_TYPE_E3(L6470)
_L6470_INIT_CHIP(E3);
#endif
#if AXIS_DRIVER_TYPE_E4(L6470)
_L6470_INIT_CHIP(E4);
#endif
#if AXIS_DRIVER_TYPE_E5(L6470)
_L6470_INIT_CHIP(E5);
#endif
}
stepper.cpp
Stepper stepper; // Singleton
// public:
bool Stepper::separate_multi_axis = false;
uint32_t Stepper::motor_current_setting[3]; // Initialized by settings.load()
// private:
block_t* Stepper::current_block; // (= NULL) A pointer to the block currently being traced Một con trỏ tới khối hiện đang được theo dõi
uint8_t Stepper::last_direction_bits, // = 0 Stepper::axis_did_move; // = 0
bool Stepper::abort_current_block;
uint8_t Stepper::last_moved_extruder = 0xFF;
bool Stepper::locked_X_motor = false, Stepper::locked_X2_motor = false;
bool Stepper::locked_Y_motor = false, Stepper::locked_Y2_motor = false;
bool Stepper::locked_Z_motor = false, Stepper::locked_Z2_motor = false;
bool Stepper::locked_I_motor = false, Stepper::locked_I2_motor = false;
bool Stepper::locked_J_motor = false, Stepper::locked_J2_motor = false;
bool Stepper::locked_K_motor = false, Stepper::locked_K2_motor = false;
bool Stepper::locked_Z3_motor = false;
uint32_t Stepper::acceleration_time, Stepper::deceleration_time; uint8_t Stepper::steps_per_isr;
constexpr
uint8_t Stepper::oversampling_factor;
int32_t Stepper::delta_error[NUM_AXIS] = { 0 };
uint32_t Stepper::advance_dividend[NUM_AXIS] = { 0 }, Stepper::advance_divisor = 0, Stepper::step_events_completed = 0, // The number of step events executed in the current block Stepper::accelerate_until, // The point from where we need to stop acceleration Stepper::decelerate_after, // The point from where we need to start decelerating Stepper::step_event_count; // The total event count for the current block
uint8_t Stepper::stepper_extruder;
constexpr uint8_t Stepper::stepper_extruder;
int32_t attribute((used)) Stepper::bezier_A asm("bezier_A"); // A coefficient in Bézier speed curve with alias for assembler int32_t attribute((used)) Stepper::bezier_B asm("bezier_B"); // B coefficient in Bézier speed curve with alias for assembler int32_t attribute((used)) Stepper::bezier_C asm("bezier_C"); // C coefficient in Bézier speed curve with alias for assembler uint32_t attribute((used)) Stepper::bezier_F asm("bezier_F"); // F coefficient in Bézier speed curve with alias for assembler uint32_t attribute((used)) Stepper::bezier_AV asm("bezier_AV"); // AV coefficient in Bézier speed curve with alias for assembler
bool __attribute__((used)) Stepper::A_negative __asm__("A_negative"); // If A coefficient was negative
bool Stepper::bezier_2nd_half; // =false If Bézier curve has been initialized or not
uint32_t Stepper::nextMainISR = 0;
constexpr uint32_t LA_ADV_NEVER = 0xFFFFFFFF; uint32_t Stepper::nextAdvanceISR = LA_ADV_NEVER, Stepper::LA_isr_rate = LA_ADV_NEVER; uint16_t Stepper::LA_current_adv_steps = 0, Stepper::LA_final_adv_steps, Stepper::LA_max_adv_steps;
int8_t Stepper::LA_steps = 0;
bool Stepper::LA_use_advance_lead;
int32_t Stepper::ticks_nominal = -1;
uint32_t Stepper::acc_step_rate; // needed for deceleration start point
volatile int32_t Stepper::endstops_trigsteps[NON_E_AXES];
volatile int32_t Stepper::count_position[NUM_AXIS] = { 0 }; int8_t Stepper::count_direction[NUM_AXIS] = { 0 };
if (separate_multi_axis) { \ if (A##_HOME_DIR < 0) { \ if (!(TEST(endstops.state(), A##_MIN) && count_direction[AXIS(A)] < 0) && !locked##A##_motor) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MIN) && count_direction[AXIS(A)] < 0) && !locked##A##2_motor) A##2_STEP_WRITE(V); \ } \ else { \ if (!(TEST(endstops.state(), A##_MAX) && count_direction[AXIS(A)] > 0) && !locked##A##_motor) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MAX) && count_direction[AXIS(A)] > 0) && !locked##A##2_motor) A##2_STEP_WRITE(V); \ } \ } \ else { \ A##_STEP_WRITE(V); \ A##2_STEP_WRITE(V); \ }
if (separate_multiaxis) { \ if (!locked##A##_motor) A##_STEPWRITE(V); \ if (!locked##A##2_motor) A##2_STEP_WRITE(V); \ } \ else { \ A##_STEP_WRITE(V); \ A##2_STEP_WRITE(V); \ }
if (separate_multi_axis) { \ if (A##_HOME_DIR < 0) { \ if (!(TEST(endstops.state(), A##_MIN) && count_direction[AXIS(A)] < 0) && !locked##A##_motor) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MIN) && count_direction[AXIS(A)] < 0) && !locked##A##2_motor) A##2_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##3_MIN) && count_direction[AXIS(A)] < 0) && !locked##A##3_motor) A##3_STEP_WRITE(V); \ } \ else { \ if (!(TEST(endstops.state(), A##_MAX) && count_direction[AXIS(A)] > 0) && !locked##A##_motor) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MAX) && count_direction[AXIS(A)] > 0) && !locked##A##2_motor) A##2_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##3_MAX) && count_direction[AXIS(A)] > 0) && !locked##A##3_motor) A##3_STEP_WRITE(V); \ } \ } \ else { \ A##_STEP_WRITE(V); \ A##2_STEP_WRITE(V); \ A##3_STEP_WRITE(V); \ }
if (separate_multiaxis) { \ if (!locked##A##_motor) A##_STEPWRITE(V); \ if (!locked##A##2_motor) A##2_STEPWRITE(V); \ if (!locked##A##3_motor) A##3_STEP_WRITE(V); \ } \ else { \ A##_STEP_WRITE(V); \ A##2_STEP_WRITE(V); \ A##3_STEP_WRITE(V); \ }
#define X_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(X,v)
#define X_APPLY_STEP(v,Q) do{ X_STEP_WRITE(v); X2_STEP_WRITE(v); }while(0)
if (extruder_duplication_enabled || ALWAYS) { \
X_DIR_WRITE(v); \
X2_DIR_WRITE(v); \
} \
else { \
if (movement_extruder()) X2_DIR_WRITE(v); else X_DIR_WRITE(v); \
}
if (extruder_duplication_enabled || ALWAYS) { \
X_STEP_WRITE(v); \
X2_STEP_WRITE(v); \
} \
else { \
if (movement_extruder()) X2_STEP_WRITE(v); else X_STEP_WRITE(v); \
}
#define Y_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(Y,v)
#define Y_APPLY_STEP(v,Q) do{ Y_STEP_WRITE(v); Y2_STEP_WRITE(v); }while(0)
#define Z_APPLY_STEP(v,Q) TRIPLE_ENDSTOP_APPLY_STEP(Z,v)
#define Z_APPLY_STEP(v,Q) TRIPLE_SEPARATE_APPLY_STEP(Z,v)
#define Z_APPLY_STEP(v,Q) do{ Z_STEP_WRITE(v); Z2_STEP_WRITE(v); Z3_STEP_WRITE(v); }while(0)
#define Z_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(Z,v)
#define Z_APPLY_STEP(v,Q) DUAL_SEPARATE_APPLY_STEP(Z,v)
#define Z_APPLY_STEP(v,Q) do{ Z_STEP_WRITE(v); Z2_STEP_WRITE(v); }while(0)
#define J_APPLY_DIR(v,Q) J_DIR_WRITE(v)
#define J_APPLY_STEP(v,Q) J_STEP_WRITE(v)
#if NON_E_AXES > 5
#define K_APPLY_DIR(v,Q) K_DIR_WRITE(v)
#define K_APPLY_STEP(v,Q) K_STEP_WRITE(v)
#endif
void Stepper::wake_up() { // TCNT1 = 0; ENABLE_STEPPER_DRIVER_INTERRUPT(); }
/**
COREYZ: Y_AXIS=B_AXIS and Z_AXIS=C_AXIS */ void Stepper::set_directions() {
uint8_t L6470_buf[MAX_L6470 + 1]; // chip command sequence - element 0 not used
if (motor_direction(_AXIS(A))) { \ A##_APPLYDIR(INVERT## A##_DIR, false); \ count_direction[_AXIS(A)] = -1; \ } \ else { \ A##_APPLYDIR(!INVERT## A##_DIR, false); \ count_direction[_AXIS(A)] = 1; \ }
SET_STEP_DIR(X); // A
SET_STEP_DIR(Y); // B
SET_STEP_DIR(Z); // C
SET_STEP_DIR(I); // I
SET_STEP_DIR(J); // J
SET_STEP_DIR(K); // K
#if ENABLED(MIXING_EXTRUDER)
// Because this is valid for the whole block we don't know
// what e-steppers will step. Likely all. Set all.
if (motor_direction(E_AXIS)) {
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
count_direction[E_AXIS] = -1;
}
else {
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
count_direction[E_AXIS] = 1;
}
#else
if (motor_direction(E_AXIS)) {
REV_E_DIR(stepper_extruder);
count_direction[E_AXIS] = -1;
}
else {
NORM_E_DIR(stepper_extruder);
count_direction[E_AXIS] = 1;
}
#endif
if (L6470.spi_active) {
L6470.spi_abort = true; // interrupted a SPI transfer - need to shut it down gracefully
for (uint8_t j = 1; j <= L6470::chain[0]; j++)
L6470_buf[j] = dSPIN_NOP; // fill buffer with NOOP commands
L6470.transfer(L6470_buf, L6470::chain[0]); // send enough NOOPs to complete any command
L6470.transfer(L6470_buf, L6470::chain[0]);
L6470.transfer(L6470_buf, L6470::chain[0]);
}
// The L6470.dir_commands[] array holds the direction command for each stepper
//scan command array and copy matches into L6470.transfer
for (uint8_t j = 1; j <= L6470::chain[0]; j++)
L6470_buf[j] = L6470.dir_commands[L6470::chain[j]];
L6470.transfer(L6470_buf, L6470::chain[0]); // send the command stream to the drivers
// A small delay may be needed after changing direction
DELAY_NS(MINIMUM_STEPPER_DIR_DELAY);
}
/**
Coefficient calculation takes 70 cycles. Bezier point evaluation takes 150 cycles. */
// For AVR we use assembly to maximize speed void Stepper::_calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av) {
// Store advance bezier_AV = av;
// Calculate the rest of the coefficients uint8_t r2 = v0 & 0xFF; uint8_t r3 = (v0 >> 8) & 0xFF; uint8_t r12 = (v0 >> 16) & 0xFF; uint8_t r5 = v1 & 0xFF; uint8_t r6 = (v1 >> 8) & 0xFF; uint8_t r7 = (v1 >> 16) & 0xFF; uint8_t r4,r8,r9,r10,r11;
asm volatile( / Calculate the Bézier coefficients / / %10:%1:%0 = v0/ / %5:%4:%3 = v1/ / %7:%6:%10 = temporary/ / %9 = val (must be high register!)/ / %10 (must be high register!)/
/* Store initial velocity*/
A("sts bezier_F, %0")
A("sts bezier_F+1, %1")
A("sts bezier_F+2, %10") /* bezier_F = %10:%1:%0 = v0 */
/* Get delta speed */
A("ldi %2,-1") /* %2 = 0xFF, means A_negative = true */
A("clr %8") /* %8 = 0 */
A("sub %0,%3")
A("sbc %1,%4")
A("sbc %10,%5") /* v0 -= v1, C=1 if result is negative */
A("brcc 1f") /* branch if result is positive (C=0), that means v0 >= v1 */
/* Result was negative, get the absolute value*/
A("com %10")
A("com %1")
A("neg %0")
A("sbc %1,%2")
A("sbc %10,%2") /* %10:%1:%0 +1 -> %10:%1:%0 = -(v0 - v1) = (v1 - v0) */
A("clr %2") /* %2 = 0, means A_negative = false */
/* Store negative flag*/
L("1")
A("sts A_negative, %2") /* Store negative flag */
/* Compute coefficients A,B and C [20 cycles worst case]*/
A("ldi %9,6") /* %9 = 6 */
A("mul %0,%9") /* r1:r0 = 6*LO(v0-v1) */
A("sts bezier_A, r0")
A("mov %6,r1")
A("clr %7") /* %7:%6:r0 = 6*LO(v0-v1) */
A("mul %1,%9") /* r1:r0 = 6*MI(v0-v1) */
A("add %6,r0")
A("adc %7,r1") /* %7:%6:?? += 6*MI(v0-v1) << 8 */
A("mul %10,%9") /* r1:r0 = 6*HI(v0-v1) */
A("add %7,r0") /* %7:%6:?? += 6*HI(v0-v1) << 16 */
A("sts bezier_A+1, %6")
A("sts bezier_A+2, %7") /* bezier_A = %7:%6:?? = 6*(v0-v1) [35 cycles worst] */
A("ldi %9,15") /* %9 = 15 */
A("mul %0,%9") /* r1:r0 = 5*LO(v0-v1) */
A("sts bezier_B, r0")
A("mov %6,r1")
A("clr %7") /* %7:%6:?? = 5*LO(v0-v1) */
A("mul %1,%9") /* r1:r0 = 5*MI(v0-v1) */
A("add %6,r0")
A("adc %7,r1") /* %7:%6:?? += 5*MI(v0-v1) << 8 */
A("mul %10,%9") /* r1:r0 = 5*HI(v0-v1) */
A("add %7,r0") /* %7:%6:?? += 5*HI(v0-v1) << 16 */
A("sts bezier_B+1, %6")
A("sts bezier_B+2, %7") /* bezier_B = %7:%6:?? = 5*(v0-v1) [50 cycles worst] */
A("ldi %9,10") /* %9 = 10 */
A("mul %0,%9") /* r1:r0 = 10*LO(v0-v1) */
A("sts bezier_C, r0")
A("mov %6,r1")
A("clr %7") /* %7:%6:?? = 10*LO(v0-v1) */
A("mul %1,%9") /* r1:r0 = 10*MI(v0-v1) */
A("add %6,r0")
A("adc %7,r1") /* %7:%6:?? += 10*MI(v0-v1) << 8 */
A("mul %10,%9") /* r1:r0 = 10*HI(v0-v1) */
A("add %7,r0") /* %7:%6:?? += 10*HI(v0-v1) << 16 */
A("sts bezier_C+1, %6")
" sts bezier_C+2, %7" /* bezier_C = %7:%6:?? = 10*(v0-v1) [65 cycles worst] */
: "+r" (r2),
"+d" (r3),
"=r" (r4),
"+r" (r5),
"+r" (r6),
"+r" (r7),
"=r" (r8),
"=r" (r9),
"=r" (r10),
"=d" (r11),
"+r" (r12)
:
: "r0", "r1", "cc", "memory"
); }
FORCE_INLINE int32_t Stepper::_eval_bezier_curve(const uint32_t curr_step) {
// If dealing with the first step, save expensive computing and return the initial speed if (!curr_step) return bezier_F;
uint8_t r0 = 0; / Zero register / uint8_t r2 = (curr_step) & 0xFF; uint8_t r3 = (curr_step >> 8) & 0xFF; uint8_t r4 = (curr_step >> 16) & 0xFF; uint8_t r1,r5,r6,r7,r8,r9,r10,r11; / Temporary registers /
asm __volatile( / umul24x24to16hi(t, bezier_AV, curr_step); t: Range 0 - 1^16 = 16 bits/ A("lds %9,bezier_AV") / %9 = LO(AV)/ A("mul %9,%2") / r1:r0 = LO(bezier_AV)LO(curr_step)/ A("mov %7,r1") / %7 = LO(bezier_AV)LO(curr_step) >> 8/ A("clr %8") / %8:%7 = LO(bezier_AV)LO(curr_step) >> 8/ A("lds %10,bezier_AV+1") / %10 = MI(AV)/ A("mul %10,%2") / r1:r0 = MI(bezier_AV)LO(curr_step)/ A("add %7,r0") A("adc %8,r1") / %8:%7 += MI(bezier_AV)LO(curr_step)/ A("lds r1,bezier_AV+2") / r11 = HI(AV)/ A("mul r1,%2") / r1:r0 = HI(bezier_AV)LO(curr_step)/ A("add %8,r0") / %8:%7 += HI(bezier_AV)LO(curr_step) << 8/ A("mul %9,%3") / r1:r0 = LO(bezier_AV)MI(curr_step)/ A("add %7,r0") A("adc %8,r1") / %8:%7 += LO(bezier_AV)MI(curr_step)/ A("mul %10,%3") / r1:r0 = MI(bezier_AV)MI(curr_step)/ A("add %8,r0") / %8:%7 += LO(bezier_AV)MI(curr_step) << 8/ A("mul %9,%4") / r1:r0 = LO(bezier_AV)HI(curr_step)/ A("add %8,r0") / %8:%7 += LO(bezier_AV)HI(curr_step) << 8/ / %8:%7 = t*/
/* uint16_t f = t;*/
A("mov %5,%7") /* %6:%5 = f*/
A("mov %6,%8")
/* %6:%5 = f*/
/* umul16x16to16hi(f, f, t); / Range 16 bits (unsigned) [17] */
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %9,r1") /* store MIL(LO(f) * LO(t)) in %9, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %9,r0") /* %9 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %9,r0") /* %9 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t)) */
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 = */
A("mov %6,%11") /* f = %10:%11*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 =*/
A("mov %6,%11") /* f = %10:%11*/
/* [15 +17*2] = [49]*/
/* %4:%3:%2 will be acc from now on*/
/* uint24_t acc = bezier_F; / Range 20 bits (unsigned)*/
A("clr %9") /* "decimal place we get for free"*/
A("lds %2,bezier_F")
A("lds %3,bezier_F+1")
A("lds %4,bezier_F+2") /* %4:%3:%2 = acc*/
/* if (A_negative) {*/
A("lds r0,A_negative")
A("or r0,%0") /* Is flag signalling negative? */
A("brne 3f") /* If yes, Skip next instruction if A was negative*/
A("rjmp 1f") /* Otherwise, jump */
/* uint24_t v; */
/* umul16x24to24hi(v, f, bezier_C); / Range 21bits [29] */
/* acc -= v; */
L("3")
A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
A("sub %9,r1")
A("sbc %2,%0")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_C) * LO(f))*/
A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * LO(f)*/
A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_C) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
A("sub %3,r0")
A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 =*/
A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_B); / Range 22bits [29]*/
/* acc += v; */
A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
A("add %9,r1")
A("adc %2,%0")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_B) * LO(f))*/
A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * LO(f)*/
A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_B) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
A("add %3,r0")
A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^5 (unsigned) [17]*/
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 =*/
A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_A); / Range 21bits [29]*/
/* acc -= v; */
A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
A("sub %9,r1")
A("sbc %2,%0")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_A) * LO(f))*/
A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * LO(f)*/
A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_A) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
A("sub %3,r0")
A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 16*/
A("jmp 2f") /* Done!*/
L("1")
/* uint24_t v; */
/* umul16x24to24hi(v, f, bezier_C); / Range 21bits [29]*/
/* acc += v; */
A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
A("add %9,r1")
A("adc %2,%0")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_C) * LO(f))*/
A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * LO(f)*/
A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_C) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
A("add %3,r0")
A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 =*/
A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_B); / Range 22bits [29]*/
/* acc -= v;*/
A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
A("sub %9,r1")
A("sbc %2,%0")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_B) * LO(f))*/
A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * LO(f)*/
A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
A("sub %9,r0")
A("sbc %2,r1")
A("sbc %3,%0")
A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_B) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
A("sub %2,r0")
A("sbc %3,r1")
A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
A("sub %3,r0")
A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^5 (unsigned) [17]*/
A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
A("clr %10") /* %10 = 0*/
A("clr %11") /* %11 = 0*/
A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
A("adc %11,%0") /* %11 += carry*/
A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
A("mov %5,%10") /* %6:%5 =*/
A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_A); / Range 21bits [29]*/
/* acc += v; */
A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
A("add %9,r1")
A("adc %2,%0")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_A) * LO(f))*/
A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * LO(f)*/
A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 8*/
A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
A("add %9,r0")
A("adc %2,r1")
A("adc %3,%0")
A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_A) * MI(f)*/
A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
A("add %2,r0")
A("adc %3,r1")
A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * MI(f) << 8*/
A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
A("add %3,r0")
A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 16*/
L("2")
" clr __zero_reg__" /* C runtime expects r1 = __zero_reg__ = 0 */
: "+r"(r0),
"+r"(r1),
"+r"(r2),
"+r"(r3),
"+r"(r4),
"+r"(r5),
"+r"(r6),
"+r"(r7),
"+r"(r8),
"+r"(r9),
"+r"(r10),
"+r"(r11)
:
:"cc","r0","r1"
); return (r2 | (uint16_t(r3) << 8)) | (uint32_t(r4) << 16); }
// For all the other 32bit CPUs FORCE_INLINE void Stepper::_calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av) { // Calculate the Bézier coefficients bezier_A = 768 (v1 - v0); bezier_B = 1920 (v0 - v1); bezier_C = 1280 (v1 - v0); bezier_F = 128 v0; bezier_AV = av; }
FORCE_INLINE int32_t Stepper::_eval_bezier_curve(const uint32_t curr_step) {
// For ARM Cortex M3/M4 CPUs, we have the optimized assembler version, that takes 43 cycles to execute
uint32_t flo = 0;
uint32_t fhi = bezier_AV * curr_step;
uint32_t t = fhi;
int32_t alo = bezier_F;
int32_t ahi = 0;
int32_t A = bezier_A;
int32_t B = bezier_B;
int32_t C = bezier_C;
__asm__ __volatile__(
".syntax unified" "\n\t" // is to prevent CM0,CM1 non-unified syntax
A("lsrs %[ahi],%[alo],#1") // a = F << 31 1 cycles
A("lsls %[alo],%[alo],#31") // 1 cycles
A("umull %[flo],%[fhi],%[fhi],%[t]") // f *= t 5 cycles [fhi:flo=64bits]
A("umull %[flo],%[fhi],%[fhi],%[t]") // f>>=32; f*=t 5 cycles [fhi:flo=64bits]
A("lsrs %[flo],%[fhi],#1") // 1 cycles [31bits]
A("smlal %[alo],%[ahi],%[flo],%[C]") // a+=(f>>33)*C; 5 cycles
A("umull %[flo],%[fhi],%[fhi],%[t]") // f>>=32; f*=t 5 cycles [fhi:flo=64bits]
A("lsrs %[flo],%[fhi],#1") // 1 cycles [31bits]
A("smlal %[alo],%[ahi],%[flo],%[B]") // a+=(f>>33)*B; 5 cycles
A("umull %[flo],%[fhi],%[fhi],%[t]") // f>>=32; f*=t 5 cycles [fhi:flo=64bits]
A("lsrs %[flo],%[fhi],#1") // f>>=33; 1 cycles [31bits]
A("smlal %[alo],%[ahi],%[flo],%[A]") // a+=(f>>33)*A; 5 cycles
A("lsrs %[alo],%[ahi],#6") // a>>=38 1 cycles
: [alo]"+r"( alo ) ,
[flo]"+r"( flo ) ,
[fhi]"+r"( fhi ) ,
[ahi]"+r"( ahi ) ,
[A]"+r"( A ) , // <== Note: Even if A, B, C, and t registers are INPUT ONLY
[B]"+r"( B ) , // GCC does bad optimizations on the code if we list them as
[C]"+r"( C ) , // such, breaking this function. So, to avoid that problem,
[t]"+r"( t ) // we list all registers as input-outputs.
:
: "cc"
);
return alo;
// For non ARM targets, we provide a fallback implementation. Really doubt it
// will be useful, unless the processor is fast and 32bit
uint32_t t = bezier_AV * curr_step; // t: Range 0 - 1^32 = 32 bits
uint64_t f = t;
f *= t; // Range 32*2 = 64 bits (unsigned)
f >>= 32; // Range 32 bits (unsigned)
f *= t; // Range 32*2 = 64 bits (unsigned)
f >>= 32; // Range 32 bits : f = t^3 (unsigned)
int64_t acc = (int64_t) bezier_F << 31; // Range 63 bits (signed)
acc += ((uint32_t) f >> 1) * (int64_t) bezier_C; // Range 29bits + 31 = 60bits (plus sign)
f *= t; // Range 32*2 = 64 bits
f >>= 32; // Range 32 bits : f = t^3 (unsigned)
acc += ((uint32_t) f >> 1) * (int64_t) bezier_B; // Range 29bits + 31 = 60bits (plus sign)
f *= t; // Range 32*2 = 64 bits
f >>= 32; // Range 32 bits : f = t^3 (unsigned)
acc += ((uint32_t) f >> 1) * (int64_t) bezier_A; // Range 28bits + 31 = 59bits (plus sign)
acc >>= (31 + 7); // Range 24bits (plus sign)
return (int32_t) acc;
}
/**
HAL_STEP_TIMER_ISR { HAL_timer_isr_prologue(STEP_TIMER_NUM);
Stepper::isr();
HAL_timer_isr_epilogue(STEP_TIMER_NUM); }
void Stepper::isr() {
// Disable interrupts, to avoid ISR preemption while we reprogram the period
// (AVR enters the ISR with global interrupts disabled, so no need to do it here)
DISABLE_ISRS();
// Program timer compare for the maximum period, so it does NOT // flag an interrupt while this ISR is running - So changes from small // periods to big periods are respected and the timer does not reset to 0 HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX);
// Count of ticks for the next ISR hal_timer_t next_isr_ticks = 0;
// Limit the amount of iterations uint8_t max_loops = 10;
// We need this variable here to be able to use it in the following loop hal_timer_t min_ticks; do { // Enable ISRs to reduce USART processing latency ENABLE_ISRS();
// Run main stepping pulse phase ISR if we have to
if (!nextMainISR) Stepper::stepper_pulse_phase_isr();
#if ENABLED(LIN_ADVANCE)
// Run linear advance stepper ISR if we have to
if (!nextAdvanceISR) nextAdvanceISR = Stepper::advance_isr();
#endif
// ^== Time critical. NOTHING besides pulse generation should be above here!!!
// Run main stepping block processing ISR if we have to
if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr();
uint32_t interval =
#if ENABLED(LIN_ADVANCE)
MIN(nextAdvanceISR, nextMainISR) // Nearest time interval
#else
nextMainISR // Remaining stepper ISR time
#endif
;
// Limit the value to the maximum possible value of the timer
NOMORE(interval, HAL_TIMER_TYPE_MAX);
// Compute the time remaining for the main isr
nextMainISR -= interval;
#if ENABLED(LIN_ADVANCE)
// Compute the time remaining for the advance isr
if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval;
#endif
/**
* This needs to avoid a race-condition caused by interleaving
* of interrupts required by both the LA and Stepper algorithms.
*
* Assume the following tick times for stepper pulses:
* Stepper ISR (S): 1 1000 2000 3000 4000
* Linear Adv. (E): 10 1010 2010 3010 4010
*
* The current algorithm tries to interleave them, giving:
* 1:S 10:E 1000:S 1010:E 2000:S 2010:E 3000:S 3010:E 4000:S 4010:E
*
* Ideal timing would yield these delta periods:
* 1:S 9:E 990:S 10:E 990:S 10:E 990:S 10:E 990:S 10:E
*
* But, since each event must fire an ISR with a minimum duration, the
* minimum delta might be 900, so deltas under 900 get rounded up:
* 900:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E
*
* It works, but divides the speed of all motors by half, leading to a sudden
* reduction to 1/2 speed! Such jumps in speed lead to lost steps (not even
* accounting for double/quad stepping, which makes it even worse).
*/
// Compute the tick count for the next ISR
next_isr_ticks += interval;
/**
* The following section must be done with global interrupts disabled.
* We want nothing to interrupt it, as that could mess the calculations
* we do for the next value to program in the period register of the
* stepper timer and lead to skipped ISRs (if the value we happen to program
* is less than the current count due to something preempting between the
* read and the write of the new period value).
*/
DISABLE_ISRS();
/**
* Get the current tick value + margin
* Assuming at least 6µs between calls to this ISR...
* On AVR the ISR epilogue+prologue is estimated at 100 instructions - Give 8µs as margin
* On ARM the ISR epilogue+prologue is estimated at 20 instructions - Give 1µs as margin
*/
min_ticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t(
#ifdef __AVR__
8
#else
1
#endif
* (STEPPER_TIMER_TICKS_PER_US)
);
/**
* NB: If for some reason the stepper monopolizes the MPU, eventually the
* timer will wrap around (and so will 'next_isr_ticks'). So, limit the
* loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
* timing, since the MCU isn't fast enough.
*/
if (!--max_loops) next_isr_ticks = min_ticks;
// Advance pulses if not enough time to wait for the next ISR
} while (next_isr_ticks < min_ticks);
// Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are // sure that the time has not arrived yet - Warrantied by the scheduler
// Set the next ISR to fire at the proper time HAL_timer_set_compare(STEP_TIMER_NUM, hal_timer_t(next_isr_ticks));
// Don't forget to finally reenable interrupts ENABLE_ISRS(); }
/**
is to keep pulse timing as regular as possible. */ void Stepper::stepper_pulse_phase_isr() {
// If we must abort the current block, do so! if (abort_current_block) { abort_current_block = false; if (current_block) { axis_did_move = 0; current_block = NULL; planner.discard_current_block(); } }
// If there is no current block, do nothing if (!current_block) return;
// Count of pending loops and events for this iteration const uint32_t pending_events = step_event_count - step_events_completed; uint8_t events_to_do = MIN(pending_events, steps_per_isr);
// Just update the value we will get at the end of the loop step_events_completed += events_to_do;
// Get the timer count and estimate the end of the pulse hal_timer_t pulse_end = HAL_timer_get_count(PULSE_TIMER_NUM) + hal_timer_t(MIN_PULSE_TICKS);
const hal_timer_t added_step_ticks = hal_timer_t(ADDED_STEP_TICKS);
// Take multiple steps per interrupt (For high speed moves) Thực hiện nhiều bước cho mỗi lần ngắt (Đối với di chuyển tốc độ cao) do {
// Start an active pulse, if Bresenham says so, and update position Bắt đầu một xung hoạt động, nếu Bresenham nói như vậy và cập nhật vị trí
delta_error[_AXIS(AXIS)] += advance_dividend[_AXIS(AXIS)]; \ if (delta_error[_AXIS(AXIS)] >= 0) { \ _APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS), 0); \ count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \ } \ }while(0)
// Stop an active pulse, if any, and adjust error term Dừng một xung hoạt động, nếu có, và điều chỉnh thời hạn lỗi
if (delta_error[_AXIS(AXIS)] >= 0) { \ delta_error[_AXIS(AXIS)] -= advance_divisor; \ _APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS), 0); \ } \ }while(0)
// Pulse start
PULSE_START(X);
PULSE_START(Y);
PULSE_START(Z);
PULSE_START(I);
PULSE_START(J);
PULSE_START(K);
// Pulse Extruders // Tick the E axis, correct error term and update position
delta_error[E_AXIS] += advance_dividend[E_AXIS]; if (delta_error[E_AXIS] >= 0) { count_position[E_AXIS] += count_direction[E_AXIS];
delta_error[E_AXIS] -= advance_divisor;
// Don't step E here - But remember the number of steps to perform
motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
#else // !LIN_ADVANCE && MIXING_EXTRUDER
// Don't adjust delta_error[E_AXIS] here!
// Being positive is the criteria for ending the pulse.
E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
#endif
}
PULSE_START(E);
i2s_push_sample();
// TODO: need to deal with MINIMUM_STEPPER_PULSE over i2s
// Just wait for the requested pulse duration while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { / nada / }
// Add the delay needed to ensure the maximum driver rate is enforced if (signed(added_step_ticks) > 0) pulse_end += hal_timer_t(added_step_ticks);
// Pulse stop
PULSE_STOP(X);
PULSE_STOP(Y);
PULSE_STOP(Z);
PULSE_STOP(I);
#if NON_E_AXES > 4
PULSE_STOP(J);
#if NON_E_AXES > 5
PULSE_STOP(K);
#endif
#endif
#endif
if (delta_error[E_AXIS] >= 0) {
delta_error[E_AXIS] -= advance_divisor;
E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
}
#if HAS_E0_STEP
PULSE_STOP(E);
#endif
// Decrement the count of pending pulses to do --events_to_do;
// For minimum pulse time wait after stopping pulses also if (events_to_do) { // Just wait for the requested pulse duration while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { / nada / }
// Add to the value, the time that the pulse must be active (to be used on the next loop)
pulse_end += hal_timer_t(MIN_PULSE_TICKS);
}
} while (events_to_do); }
// This is the last half of the stepper interrupt: This one processes and // properly schedules blocks from the planner. This is executed after creating // the step pulses, so it is not time critical, as pulses are already done.
uint32_t Stepper::stepper_block_phase_isr() {
// If no queued movements, just wait 1ms for the next move uint32_t interval = (STEPPER_TIMER_RATE / 1000);
// If there is a current block if (current_block) {
// If current block is finished, reset pointer
if (step_events_completed >= step_event_count) {
#if FILAMENT_RUNOUT_DISTANCE_MM > 0
runout.block_completed(current_block);
#endif
axis_did_move = 0;
current_block = NULL;
planner.discard_current_block();
}
else {
// Step events not completed yet...
// Are we in acceleration phase ?
if (step_events_completed <= accelerate_until) { // Calculate new timer value
#if ENABLED(S_CURVE_ACCELERATION)
// Get the next speed to use (Jerk limited!)
uint32_t acc_step_rate =
acceleration_time < current_block->acceleration_time
? _eval_bezier_curve(acceleration_time)
: current_block->cruise_rate;
#else
acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate;
NOMORE(acc_step_rate, current_block->nominal_rate);
#endif
// acc_step_rate is in steps/second
// step_rate to timer interval and steps per stepper isr
interval = calc_timer_interval(acc_step_rate, oversampling_factor, &steps_per_isr);
acceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
if (LA_use_advance_lead) {
// Fire ISR if final adv_rate is reached
if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0;
}
else if (LA_steps) nextAdvanceISR = 0;
#endif // LIN_ADVANCE
}
// Are we in Deceleration phase ?
else if (step_events_completed > decelerate_after) {
uint32_t step_rate;
#if ENABLED(S_CURVE_ACCELERATION)
// If this is the 1st time we process the 2nd half of the trapezoid...
if (!bezier_2nd_half) {
// Initialize the Bézier speed curve
_calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse);
bezier_2nd_half = true;
// The first point starts at cruise rate. Just save evaluation of the Bézier curve
step_rate = current_block->cruise_rate;
}
else {
// Calculate the next speed to use
step_rate = deceleration_time < current_block->deceleration_time
? _eval_bezier_curve(deceleration_time)
: current_block->final_rate;
}
#else
// Using the old trapezoidal control
step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate);
if (step_rate < acc_step_rate) { // Still decelerating?
step_rate = acc_step_rate - step_rate;
NOLESS(step_rate, current_block->final_rate);
}
else
step_rate = current_block->final_rate;
#endif
// step_rate is in steps/second
// step_rate to timer interval and steps per stepper isr
interval = calc_timer_interval(step_rate, oversampling_factor, &steps_per_isr);
deceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
if (LA_use_advance_lead) {
// Wake up eISR on first deceleration loop and fire ISR if final adv_rate is reached
if (step_events_completed <= decelerate_after + steps_per_isr || (LA_steps && LA_isr_rate != current_block->advance_speed)) {
nextAdvanceISR = 0;
LA_isr_rate = current_block->advance_speed;
}
}
else if (LA_steps) nextAdvanceISR = 0;
#endif // LIN_ADVANCE
}
// We must be in cruise phase otherwise
else {
#if ENABLED(LIN_ADVANCE)
// If there are any esteps, fire the next advance_isr "now"
if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0;
#endif
// Calculate the ticks_nominal for this nominal speed, if not done yet
if (ticks_nominal < 0) {
// step_rate to timer interval and loops for the nominal speed
ticks_nominal = calc_timer_interval(current_block->nominal_rate, oversampling_factor, &steps_per_isr);
}
// The timer interval is just the nominal value for the nominal speed
interval = ticks_nominal;
}
}
}
// If there is no current block at this point, attempt to pop one from the buffer // and prepare its movement if (!current_block) {
// Anything in the buffer?
if ((current_block = planner.get_current_block())) {
// Sync block? Sync the stepper counts and return
while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) {
_set_position(
current_block->position[A_AXIS], current_block->position[B_AXIS],
current_block->position[C_AXIS],
#if NON_E_AXES > 3
current_block->position[I_AXIS],
#if NON_E_AXES > 4
current_block->position[J_AXIS],
#if NON_E_AXES > 5
current_block->position[K_AXIS],
#endif
#endif
#endif
current_block->position[E_AXIS]
);
planner.discard_current_block();
// Try to get a new block
if (!(current_block = planner.get_current_block()))
return interval; // No more queued movements!
}
// Flag all moving axes for proper endstop handling
#if IS_CORE
// Define conditions for checking endstops
#define S_(N) current_block->steps[CORE_AXIS_##N]
#define D_(N) TEST(current_block->direction_bits, CORE_AXIS_##N)
#endif
#if CORE_IS_XY || CORE_IS_XZ
/**
* Head direction in -X axis for CoreXY and CoreXZ bots.
*
* If steps differ, both axes are moving.
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z, handled below)
* If DeltaA == DeltaB, the movement is only in the 1st axis (X)
*/
#if ENABLED(COREXY) || ENABLED(COREXZ)
#define X_CMP ==
#else
#define X_CMP !=
#endif
#define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) X_CMP D_(2)) )
#else
#define X_MOVE_TEST !!current_block->steps[A_AXIS]
#endif
#if CORE_IS_XY || CORE_IS_YZ
/**
* Head direction in -Y axis for CoreXY / CoreYZ bots.
*
* If steps differ, both axes are moving
* If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
*/
#if ENABLED(COREYX) || ENABLED(COREYZ)
#define Y_CMP ==
#else
#define Y_CMP !=
#endif
#define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Y_CMP D_(2)) )
#else
#define Y_MOVE_TEST !!current_block->steps[B_AXIS]
#endif
#if CORE_IS_XZ || CORE_IS_YZ
/**
* Head direction in -Z axis for CoreXZ or CoreYZ bots.
*
* If steps differ, both axes are moving
* If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y, already handled above)
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
*/
#if ENABLED(COREZX) || ENABLED(COREZY)
#define Z_CMP ==
#else
#define Z_CMP !=
#endif
#define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Z_CMP D_(2)) )
#else
#define Z_MOVE_TEST !!current_block->steps[C_AXIS]
#endif
#if NON_E_AXES > 3
#if CORE_IS_XY || CORE_IS_YZ
/**
* Head direction in -Y axis for CoreXY / CoreYZ bots.
*
* If steps differ, both axes are moving
* If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
*/
#if ENABLED(COREYX) || ENABLED(COREYZ)
#define I_CMP ==
#else
#define I_CMP !=
#endif
#define I_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) I_CMP D_(2)) )
#else
#define I_MOVE_TEST !!current_block->steps[I_AXIS]
#endif
#if NON_E_AXES > 4
#if CORE_IS_XY || CORE_IS_YZ
/**
* Head direction in -Y axis for CoreXY / CoreYZ bots.
*
* If steps differ, both axes are moving
* If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
*/
#if ENABLED(COREYX) || ENABLED(COREYZ)
#define J_CMP ==
#else
#define J_CMP !=
#endif
#define J_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) J_CMP D_(2)) )
#else
#define J_MOVE_TEST !!current_block->steps[J_AXIS]
#endif
#if NON_E_AXES > 5
#if CORE_IS_XY || CORE_IS_YZ
/**
* Head direction in -Y axis for CoreXY / CoreYZ bots.
*
* If steps differ, both axes are moving
* If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
*/
#if ENABLED(COREYX) || ENABLED(COREYZ)
#define K_CMP ==
#else
#define K_CMP !=
#endif
#define K_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) K_CMP D_(2)) )
#else
#define K_MOVE_TEST !!current_block->steps[K_AXIS]
#endif
#endif // NON_E_AXES > 5
#endif // NON_E_AXES > 4
#endif // NON_E_AXES > 3
uint8_t axis_bits = 0;
if (X_MOVE_TEST) SBI(axis_bits, A_AXIS);
if (Y_MOVE_TEST) SBI(axis_bits, B_AXIS);
if (Z_MOVE_TEST) SBI(axis_bits, C_AXIS);
#if NON_E_AXES > 3
if (I_MOVE_TEST) SBI(axis_bits, I_AXIS);
#if NON_E_AXES > 4
if (J_MOVE_TEST) SBI(axis_bits, J_AXIS);
#if NON_E_AXES > 5
if (K_MOVE_TEST) SBI(axis_bits, K_AXIS);
#endif
#endif
#endif
//if (!!current_block->steps[E_AXIS]) SBI(axis_bits, E_AXIS);
//if (!!current_block->steps[A_AXIS]) SBI(axis_bits, X_HEAD);
//if (!!current_block->steps[B_AXIS]) SBI(axis_bits, Y_HEAD);
//if (!!current_block->steps[C_AXIS]) SBI(axis_bits, Z_HEAD);
axis_did_move = axis_bits;
// No acceleration / deceleration time elapsed so far
acceleration_time = deceleration_time = 0;
uint8_t oversampling = 0; // Assume we won't use it
#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
// At this point, we must decide if we can use Stepper movement axis smoothing.
uint32_t max_rate = current_block->nominal_rate; // Get the maximum rate (maximum event speed)
while (max_rate < MIN_STEP_ISR_FREQUENCY) {
max_rate <<= 1;
if (max_rate >= MAX_STEP_ISR_FREQUENCY_1X) break;
++oversampling;
}
oversampling_factor = oversampling;
#endif
// Based on the oversampling factor, do the calculations
step_event_count = current_block->step_event_count << oversampling;
// Initialize Bresenham delta errors to 1/2
delta_error[X_AXIS] = delta_error[Y_AXIS] = delta_error[Z_AXIS] = delta_error[E_AXIS]
#if NON_E_AXES > 3
= delta_error[I_AXIS]
#if NON_E_AXES > 4
= delta_error[J_AXIS]
#if NON_E_AXES > 5
= delta_error[K_AXIS]
#endif
#endif
#endif
= -int32_t(step_event_count);
// Calculate Bresenham dividends
advance_dividend[X_AXIS] = current_block->steps[X_AXIS] << 1;
advance_dividend[Y_AXIS] = current_block->steps[Y_AXIS] << 1;
advance_dividend[Z_AXIS] = current_block->steps[Z_AXIS] << 1;
#if NON_E_AXES > 3
advance_dividend[I_AXIS] = current_block->steps[I_AXIS] << 1;
#if NON_E_AXES > 4
advance_dividend[J_AXIS] = current_block->steps[J_AXIS] << 1;
#if NON_E_AXES > 5
advance_dividend[K_AXIS] = current_block->steps[K_AXIS] << 1;
#endif
#endif
#endif
advance_dividend[E_AXIS] = current_block->steps[E_AXIS] << 1;
// Calculate Bresenham divisor
advance_divisor = step_event_count << 1;
// No step events completed so far
step_events_completed = 0;
// Compute the acceleration and deceleration points
accelerate_until = current_block->accelerate_until << oversampling;
decelerate_after = current_block->decelerate_after << oversampling;
#if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_SETUP();
#endif
#if EXTRUDERS > 1
stepper_extruder = current_block->extruder;
#endif
// Initialize the trapezoid generator from the current block.
#if ENABLED(LIN_ADVANCE)
#if DISABLED(MIXING_EXTRUDER) && E_STEPPERS > 1
// If the now active extruder wasn't in use during the last move, its pressure is most likely gone.
if (stepper_extruder != last_moved_extruder) LA_current_adv_steps = 0;
#endif
if ((LA_use_advance_lead = current_block->use_advance_lead)) {
LA_final_adv_steps = current_block->final_adv_steps;
LA_max_adv_steps = current_block->max_adv_steps;
//Start the ISR
nextAdvanceISR = 0;
LA_isr_rate = current_block->advance_speed;
}
else LA_isr_rate = LA_ADV_NEVER;
#endif
if (
#if HAS_DRIVER(L6470)
true // Always set direction for L6470 (This also enables the chips)
#else
current_block->direction_bits != last_direction_bits
#if DISABLED(MIXING_EXTRUDER)
|| stepper_extruder != last_moved_extruder
#endif
#endif
) {
last_direction_bits = current_block->direction_bits;
#if EXTRUDERS > 1
last_moved_extruder = stepper_extruder;
#endif
set_directions();
}
// At this point, we must ensure the movement about to execute isn't
// trying to force the head against a limit switch. If using interrupt-
// driven change detection, and already against a limit then no call to
// the endstop_triggered method will be done and the movement will be
// done against the endstop. So, check the limits here: If the movement
// is against the limits, the block will be marked as to be killed, and
// on the next call to this ISR, will be discarded.
endstops.update();
#if ENABLED(Z_LATE_ENABLE)
// If delayed Z enable, enable it now. This option will severely interfere with
// timing between pulses when chaining motion between blocks, and it could lead
// to lost steps in both X and Y axis, so avoid using it unless strictly necessary!!
if (current_block->steps[Z_AXIS]) enable_Z();
#endif
// Mark the time_nominal as not calculated yet
ticks_nominal = -1;
#if DISABLED(S_CURVE_ACCELERATION)
// Set as deceleration point the initial rate of the block Đặt làm điểm giảm tốc tốc độ ban đầu của khối
acc_step_rate = current_block->initial_rate;
#endif
#if ENABLED(S_CURVE_ACCELERATION)
// Initialize the Bézier speed curve
_calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
// We haven't started the 2nd half of the trapezoid
bezier_2nd_half = false;
#endif
// Calculate the initial timer interval Tính khoảng thời gian hẹn giờ ban đầu
interval = calc_timer_interval(current_block->initial_rate, oversampling_factor, &steps_per_isr);
}
}
// Return the interval to wait Trả lại khoảng thời gian chờ đợi return interval; }
// Timer interrupt for E. LA_steps is set in the main routine uint32_t Stepper::advance_isr() { uint32_t interval;
if (LA_use_advance_lead) {
if (step_events_completed > decelerate_after && LA_current_adv_steps > LA_final_adv_steps) {
LA_steps--;
LA_current_adv_steps--;
interval = LA_isr_rate;
}
else if (step_events_completed < decelerate_after && LA_current_adv_steps < LA_max_adv_steps) {
//step_events_completed <= (uint32_t)accelerate_until) {
LA_steps++;
LA_current_adv_steps++;
interval = LA_isr_rate;
}
else
interval = LA_isr_rate = LA_ADV_NEVER;
}
else
interval = LA_ADV_NEVER;
#if ENABLED(MIXING_EXTRUDER)
// We don't know which steppers will be stepped because LA loop follows,
// with potentially multiple steps. Set all.
if (LA_steps >= 0)
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
else
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
#else
if (LA_steps >= 0)
NORM_E_DIR(stepper_extruder);
else
REV_E_DIR(stepper_extruder);
#endif
// Get the timer count and estimate the end of the pulse
hal_timer_t pulse_end = HAL_timer_get_count(PULSE_TIMER_NUM) + hal_timer_t(MIN_PULSE_TICKS);
const hal_timer_t added_step_ticks = hal_timer_t(ADDED_STEP_TICKS);
// Step E stepper if we have steps
while (LA_steps) {
// Set the STEP pulse ON
#if ENABLED(MIXING_EXTRUDER)
E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
#else
E_STEP_WRITE(stepper_extruder, !INVERT_E_STEP_PIN);
#endif
// Enforce a minimum duration for STEP pulse ON
#if MINIMUM_STEPPER_PULSE
// Just wait for the requested pulse duration
while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ }
#endif
// Add the delay needed to ensure the maximum driver rate is enforced
if (signed(added_step_ticks) > 0) pulse_end += hal_timer_t(added_step_ticks);
LA_steps < 0 ? ++LA_steps : --LA_steps;
// Set the STEP pulse OFF
#if ENABLED(MIXING_EXTRUDER)
E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
#else
E_STEP_WRITE(stepper_extruder, INVERT_E_STEP_PIN);
#endif
// For minimum pulse time wait before looping
// Just wait for the requested pulse duration
if (LA_steps) {
while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ }
#if MINIMUM_STEPPER_PULSE
// Add to the value, the time that the pulse must be active (to be used on the next loop)
pulse_end += hal_timer_t(MIN_PULSE_TICKS);
#endif
}
} // LA_steps
return interval;
}
// Check if the given block is busy or not - Must not be called from ISR contexts // The current_block could change in the middle of the read by an Stepper ISR, so // we must explicitly prevent that! bool Stepper::is_block_busy(const block_t* const block) {
// A SW memory barrier, to ensure GCC does not overoptimize loops
#define sw_barrier() asm volatile("": : :"memory");
// Keep reading until 2 consecutive reads return the same value,
// meaning there was no update in-between caused by an interrupt.
// This works because stepper ISRs happen at a slower rate than
// successive reads of a variable, so 2 consecutive reads with
// the same value means no interrupt updated it.
block_t* vold, *vnew = current_block;
sw_barrier();
do {
vold = vnew;
vnew = current_block;
sw_barrier();
} while (vold != vnew);
block_t *vnew = current_block;
// Return if the block is busy or not return block == vnew; }
void Stepper::init() {
// Init Digipot Motor Current
digipot_init();
const float motor_current[] = MOTOR_CURRENT;
unsigned int digipot_motor = 0;
for (uint8_t i = 0; i < 3 + EXTRUDERS; i++) {
digipot_motor = 255 * (motor_current[i] / 2.5);
dac084s085::setValue(i, digipot_motor);
}
// Init Microstepping Pins
microstep_init();
// Init Dir Pins
X_DIR_INIT;
X2_DIR_INIT;
Y_DIR_INIT;
#if ENABLED(Y_DUAL_STEPPER_DRIVERS) && HAS_Y2_DIR
Y2_DIR_INIT;
#endif
Z_DIR_INIT;
#if Z_MULTI_STEPPER_DRIVERS && HAS_Z2_DIR
Z2_DIR_INIT;
#endif
#if ENABLED(Z_TRIPLE_STEPPER_DRIVERS) && HAS_Z3_DIR
Z3_DIR_INIT;
#endif
I_DIR_INIT;
J_DIR_INIT;
K_DIR_INIT;
E0_DIR_INIT;
E1_DIR_INIT;
E2_DIR_INIT;
E3_DIR_INIT;
E4_DIR_INIT;
E5_DIR_INIT;
// Init Enable Pins - steppers default to disabled.
X_ENABLE_INIT;
if (!X_ENABLE_ON) X_ENABLE_WRITE(HIGH);
#if (ENABLED(DUAL_X_CARRIAGE) || ENABLED(X_DUAL_STEPPER_DRIVERS)) && HAS_X2_ENABLE
X2_ENABLE_INIT;
if (!X_ENABLE_ON) X2_ENABLE_WRITE(HIGH);
#endif
Y_ENABLE_INIT;
if (!Y_ENABLE_ON) Y_ENABLE_WRITE(HIGH);
#if ENABLED(Y_DUAL_STEPPER_DRIVERS) && HAS_Y2_ENABLE
Y2_ENABLE_INIT;
if (!Y_ENABLE_ON) Y2_ENABLE_WRITE(HIGH);
#endif
Z_ENABLE_INIT;
if (!Z_ENABLE_ON) Z_ENABLE_WRITE(HIGH);
#if Z_MULTI_STEPPER_DRIVERS && HAS_Z2_ENABLE
Z2_ENABLE_INIT;
if (!Z_ENABLE_ON) Z2_ENABLE_WRITE(HIGH);
#endif
#if ENABLED(Z_TRIPLE_STEPPER_DRIVERS) && HAS_Z3_ENABLE
Z3_ENABLE_INIT;
if (!Z_ENABLE_ON) Z3_ENABLE_WRITE(HIGH);
#endif
I_ENABLE_INIT;
if (!I_ENABLE_ON) I_ENABLE_WRITE(HIGH);
J_ENABLE_INIT;
if (!J_ENABLE_ON) J_ENABLE_WRITE(HIGH);
K_ENABLE_INIT;
if (!K_ENABLE_ON) K_ENABLE_WRITE(HIGH);
E0_ENABLE_INIT;
if (!E_ENABLE_ON) E0_ENABLE_WRITE(HIGH);
E1_ENABLE_INIT;
if (!E_ENABLE_ON) E1_ENABLE_WRITE(HIGH);
E2_ENABLE_INIT;
if (!E_ENABLE_ON) E2_ENABLE_WRITE(HIGH);
E3_ENABLE_INIT;
if (!E_ENABLE_ON) E3_ENABLE_WRITE(HIGH);
E4_ENABLE_INIT;
if (!E_ENABLE_ON) E4_ENABLE_WRITE(HIGH);
E5_ENABLE_INIT;
if (!E_ENABLE_ON) E5_ENABLE_WRITE(HIGH);
_STEP_INIT(AXIS); \
_WRITE_STEP(AXIS, _INVERT_STEP_PIN(PIN)); \
_DISABLE(AXIS)
// Init Step Pins
#if ENABLED(X_DUAL_STEPPER_DRIVERS) || ENABLED(DUAL_X_CARRIAGE)
X2_STEP_INIT;
X2_STEP_WRITE(INVERT_X_STEP_PIN);
#endif
AXIS_INIT(X, X);
#if ENABLED(Y_DUAL_STEPPER_DRIVERS)
Y2_STEP_INIT;
Y2_STEP_WRITE(INVERT_Y_STEP_PIN);
#endif
AXIS_INIT(Y, Y);
#if Z_MULTI_STEPPER_DRIVERS
Z2_STEP_INIT;
Z2_STEP_WRITE(INVERT_Z_STEP_PIN);
#endif
#if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
Z3_STEP_INIT;
Z3_STEP_WRITE(INVERT_Z_STEP_PIN);
#endif
AXIS_INIT(Z, Z);
AXIS_INIT(I, I);
AXIS_INIT(J, J);
AXIS_INIT(K, K);
E_AXIS_INIT(0);
E_AXIS_INIT(1);
E_AXIS_INIT(2);
E_AXIS_INIT(3);
E_AXIS_INIT(4);
E_AXIS_INIT(5);
HAL_timer_start(STEP_TIMER_NUM, 122); // Init Stepper ISR to 122 Hz for quick starting
ENABLE_STEPPER_DRIVER_INTERRUPT();
sei();
// Init direction bits for first moves Ban đầu hướng bit cho di chuyển đầu tiên last_direction_bits = 0 | (INVERT_X_DIR ? _BV(X_AXIS) : 0) | (INVERT_Y_DIR ? _BV(Y_AXIS) : 0) | (INVERT_Z_DIR ? _BV(Z_AXIS) : 0)
| (INVERT_I_DIR ? _BV(I_AXIS) : 0)
#if NON_E_AXES > 4
| (INVERT_J_DIR ? _BV(J_AXIS) : 0)
#if NON_E_AXES > 5
| (INVERT_K_DIR ? _BV(K_AXIS) : 0)
#endif
#endif
#endif
;
set_directions(); }
/**
, const int32_t &i
, const int32_t &j
, const int32_t &k
, const int32_t &e) {
// corexy positioning // these equations follow the form of the dA and dB equations on http://www.corexy.com/theory.html count_position[A_AXIS] = a + b; count_position[B_AXIS] = CORESIGN(a - b); count_position[Z_AXIS] = c;
// corexz planning count_position[A_AXIS] = a + c; count_position[Y_AXIS] = b; count_position[C_AXIS] = CORESIGN(a - c);
// coreyz planning count_position[X_AXIS] = a; count_position[B_AXIS] = b + c; count_position[C_AXIS] = CORESIGN(b - c);
// default non-h-bot planning count_position[X_AXIS] = a; count_position[Y_AXIS] = b; count_position[Z_AXIS] = c;
count_position[I_AXIS] = i;
count_position[J_AXIS] = j;
#if NON_E_AXES > 5
count_position[K_AXIS] = k;
#endif
count_position[E_AXIS] = e; }
/**
Get a stepper's position in steps. */ int32_t Stepper::position(const AxisEnum axis) {
// Protect the access to the position. Only required for AVR, as // any 32bit CPU offers atomic access to 32bit variables const bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
const int32_t v = count_position[axis];
// Reenable Stepper ISR if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
return v; }
// Signal endstops were triggered - This function can be called from // an ISR context (Temperature, Stepper or limits ISR), so we must // be very careful here. If the interrupt being preempted was the // Stepper ISR (this CAN happen with the endstop limits ISR) then // when the stepper ISR resumes, we must be very sure that the movement // is properly cancelled void Stepper::endstop_triggered(const AxisEnum axis) {
const bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
endstops_trigsteps[axis] = 0.5f * (
axis == CORE_AXIS_2 ? CORESIGN(count_position[CORE_AXIS_1] - count_position[CORE_AXIS_2])
: count_position[CORE_AXIS_1] + count_position[CORE_AXIS_2]
);
endstops_trigsteps[axis] = count_position[axis];
// Discard the rest of the move if there is a current block quick_stop();
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); }
int32_t Stepper::triggered_position(const AxisEnum axis) {
// Protect the access to the position. Only required for AVR, as
// any 32bit CPU offers atomic access to 32bit variables
const bool was_enabled = STEPPER_ISR_ENABLED();
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
const int32_t v = endstops_trigsteps[axis];
// Reenable Stepper ISR
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
return v; }
void Stepper::report_positions() {
// Protect the access to the position. const bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
const int32_t xpos = count_position[X_AXIS] , ypos = count_position[Y_AXIS] , zpos = count_position[Z_AXIS] ,
ipos = count_position[I_AXIS],
#if NON_E_AXES > 4
jpos = count_position[J_AXIS],
#if NON_E_AXES > 5
kpos = count_position[K_AXIS];
#endif
#endif
#endif
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
SERIAL_ECHOPGM(MSG_COUNT_A);
SERIAL_ECHOPGM(MSG_COUNT_X);
SERIAL_ECHO(xpos);
SERIAL_ECHOPGM(" B:");
SERIAL_ECHOPGM(" Y:");
SERIAL_ECHO(ypos);
SERIAL_ECHOPGM(" C:");
SERIAL_ECHOPGM(" Z:");
SERIAL_ECHO(zpos);
SERIAL_ECHOPGM(" I:");
SERIAL_ECHO(ipos);
#if NON_E_AXES > 4
SERIAL_ECHOPGM(" J:");
SERIAL_ECHO(jpos);
#if NON_E_AXES > 5
SERIAL_ECHOPGM(" K:");
SERIAL_ECHO(kpos);
#endif
#endif
SERIAL_EOL(); }
#define STEP_PULSE_CYCLES ((MINIMUM_STEPPER_PULSE) * CYCLES_PER_MICROSECOND)
#define STEP_PULSE_CYCLES 0
#define CYCLES_EATEN_BABYSTEP (2 * 15)
#define CYCLES_EATEN_BABYSTEP 0
#define _SAVE_START const hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM)
#define _PULSE_WAIT while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
#define _SAVE_START NOOP
#if EXTRA_CYCLES_BABYSTEP > 0
#define _PULSE_WAIT DELAY_NS(EXTRA_CYCLES_BABYSTEP * NANOSECONDS_PER_CYCLE)
#elif STEP_PULSE_CYCLES > 0
#define _PULSE_WAIT NOOP
#elif ENABLED(DELTA)
#define _PULSE_WAIT DELAY_US(2);
#else
#define _PULSE_WAIT DELAY_US(4);
#endif
const uint8_t old_dir = _READ_DIR(AXIS); \
_ENABLE(AXIS); \
_APPLY_DIR(AXIS, _INVERT_DIR(AXIS)^DIR^INVERT); \
DELAY_NS(MINIMUM_STEPPER_DIR_DELAY); \
_SAVE_START; \
_APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS), true); \
_PULSE_WAIT; \
_APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS), true); \
_APPLY_DIR(AXIS, old_dir); \
}
// MUST ONLY BE CALLED BY AN ISR, // No other ISR should ever interrupt this! void Stepper::babystep(const AxisEnum axis, const bool direction) { cli();
switch (axis) {
#if ENABLED(BABYSTEP_XY)
case X_AXIS:
#if CORE_IS_XY
BABYSTEP_AXIS(X, false, direction);
BABYSTEP_AXIS(Y, false, direction);
#elif CORE_IS_XZ
BABYSTEP_AXIS(X, false, direction);
BABYSTEP_AXIS(Z, false, direction);
#else
BABYSTEP_AXIS(X, false, direction);
#endif
break;
case Y_AXIS:
#if CORE_IS_XY
BABYSTEP_AXIS(X, false, direction);
BABYSTEP_AXIS(Y, false, direction^(CORESIGN(1)<0));
#elif CORE_IS_YZ
BABYSTEP_AXIS(Y, false, direction);
BABYSTEP_AXIS(Z, false, direction^(CORESIGN(1)<0));
#else
BABYSTEP_AXIS(Y, false, direction);
#endif
break;
#endif
case Z_AXIS: {
#if CORE_IS_XZ
BABYSTEP_AXIS(X, BABYSTEP_INVERT_Z, direction);
BABYSTEP_AXIS(Z, BABYSTEP_INVERT_Z, direction^(CORESIGN(1)<0));
#elif CORE_IS_YZ
BABYSTEP_AXIS(Y, BABYSTEP_INVERT_Z, direction);
BABYSTEP_AXIS(Z, BABYSTEP_INVERT_Z, direction^(CORESIGN(1)<0));
#elif DISABLED(DELTA)
BABYSTEP_AXIS(Z, BABYSTEP_INVERT_Z, direction);
#else // DELTA
const bool z_direction = direction ^ BABYSTEP_INVERT_Z;
enable_X();
enable_Y();
enable_Z();
#if NON_E_AXES > 3
enable_I();
#if NON_E_AXES > 4
enable_J();
#if NON_E_AXES > 5
enable_K();
#endif
#endif
#endif
const uint8_t old_x_dir_pin = X_DIR_READ,
old_y_dir_pin = Y_DIR_READ,
old_z_dir_pin = Z_DIR_READ;
#if NON_E_AXES > 3
const uint8_t old_i_dir_pin = I_DIR_READ;
#if NON_E_AXES > 3
const uint8_t old_j_dir_pin = J_DIR_READ;
#if NON_E_AXES > 3
const uint8_t old_k_dir_pin = K_DIR_READ;
#endif
#endif
#endif
X_DIR_WRITE(INVERT_X_DIR ^ z_direction);
Y_DIR_WRITE(INVERT_Y_DIR ^ z_direction);
Z_DIR_WRITE(INVERT_Z_DIR ^ z_direction);
#if NON_E_AXES > 3
I_DIR_WRITE(INVERT_I_DIR ^ z_direction);
#if NON_E_AXES > 3
J_DIR_WRITE(INVERT_J_DIR ^ z_direction);
#if NON_E_AXES > 3
K_DIR_WRITE(INVERT_K_DIR ^ z_direction);
#endif
#endif
#endif
#if MINIMUM_STEPPER_DIR_DELAY > 0
DELAY_NS(MINIMUM_STEPPER_DIR_DELAY);
#endif
_SAVE_START;
X_STEP_WRITE(!INVERT_X_STEP_PIN);
Y_STEP_WRITE(!INVERT_Y_STEP_PIN);
Z_STEP_WRITE(!INVERT_Z_STEP_PIN);
#if NON_E_AXES > 3
I_STEP_WRITE(!INVERT_I_STEP_PIN);
#if NON_E_AXES > 4
J_STEP_WRITE(!INVERT_J_STEP_PIN);
#if NON_E_AXES > 5
K_STEP_WRITE(!INVERT_K_STEP_PIN);
#endif
#endif
#endif
_PULSE_WAIT;
X_STEP_WRITE(INVERT_X_STEP_PIN);
Y_STEP_WRITE(INVERT_Y_STEP_PIN);
Z_STEP_WRITE(INVERT_Z_STEP_PIN);
#if NON_E_AXES > 3
I_STEP_WRITE(INVERT_I_STEP_PIN);
#if NON_E_AXES > 4
J_STEP_WRITE(INVERT_J_STEP_PIN);
#if NON_E_AXES > 5
K_STEP_WRITE(INVERT_K_STEP_PIN);
#endif
#endif
#endif
// Restore direction bits
X_DIR_WRITE(old_x_dir_pin);
Y_DIR_WRITE(old_y_dir_pin);
Z_DIR_WRITE(old_z_dir_pin);
#if NON_E_AXES > 3
I_DIR_WRITE(old_i_dir_pin);
#if NON_E_AXES > 4
J_DIR_WRITE(old_j_dir_pin);
#if NON_E_AXES > 5
K_DIR_WRITE(old_k_dir_pin);
#endif
#endif
#endif
#endif
} break;
#if NON_E_AXES > 3
case I_AXIS:
BABYSTEP_AXIS(I, false, direction);
break;
#if NON_E_AXES > 4
case J_AXIS:
BABYSTEP_AXIS(J, false, direction);
break;
#if NON_E_AXES > 5
case K_AXIS:
BABYSTEP_AXIS(K, false, direction);
break;
#endif // NON_E_AXES > 5
#endif // NON_E_AXES > 4
#endif // NON_E_AXES > 3
default: break;
}
sei();
}
/**
// From Arduino DigitalPotControl example void Stepper::digitalPotWrite(const int16_t address, const int16_t value) { WRITE(DIGIPOTSS_PIN, LOW); // Take the SS pin low to select the chip SPI.transfer(address); // Send the address and value via SPI SPI.transfer(value); WRITE(DIGIPOTSS_PIN, HIGH); // Take the SS pin high to de-select the chip //delay(10); }
void Stepper::refresh_motor_power() { LOOP_L_N(i, COUNT(motor_current_setting)) { switch (i) {
case 0:
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
case 1:
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E) || PIN_EXISTS(MOTOR_CURRENT_PWM_E0) || PIN_EXISTS(MOTOR_CURRENT_PWM_E1)
case 2:
#endif
digipot_current(i, motor_current_setting[i]);
default: break;
}
}
}
void Stepper::digipot_current(const uint8_t driver, const int16_t current) {
#if HAS_DIGIPOTSS
const uint8_t digipot_ch[] = DIGIPOT_CHANNELS;
digitalPotWrite(digipot_ch[driver], current);
#elif HAS_MOTOR_CURRENT_PWM
if (WITHIN(driver, 0, COUNT(motor_current_setting) - 1))
motor_current_setting[driver] = current; // update motor_current_setting
#define _WRITE_CURRENT_PWM(P) analogWrite(MOTOR_CURRENT_PWM_## P ##_PIN, 255L * current / (MOTOR_CURRENT_PWM_RANGE))
switch (driver) {
case 0:
#if PIN_EXISTS(MOTOR_CURRENT_PWM_X)
_WRITE_CURRENT_PWM(X);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Y)
_WRITE_CURRENT_PWM(Y);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_XY)
_WRITE_CURRENT_PWM(XY);
#endif
break;
case 1:
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
_WRITE_CURRENT_PWM(Z);
#endif
break;
case 2:
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
_WRITE_CURRENT_PWM(E);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E0)
_WRITE_CURRENT_PWM(E0);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E1)
_WRITE_CURRENT_PWM(E1);
#endif
break;
}
#endif
}
void Stepper::digipot_init() {
#if HAS_DIGIPOTSS
static const uint8_t digipot_motor_current[] = DIGIPOT_MOTOR_CURRENT;
SPI.begin();
SET_OUTPUT(DIGIPOTSS_PIN);
for (uint8_t i = 0; i < COUNT(digipot_motor_current); i++) {
//digitalPotWrite(digipot_ch[i], digipot_motor_current[i]);
digipot_current(i, digipot_motor_current[i]);
}
#elif HAS_MOTOR_CURRENT_PWM
#if PIN_EXISTS(MOTOR_CURRENT_PWM_X)
SET_OUTPUT(MOTOR_CURRENT_PWM_X_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Y)
SET_OUTPUT(MOTOR_CURRENT_PWM_Y_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_XY)
SET_OUTPUT(MOTOR_CURRENT_PWM_XY_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
SET_OUTPUT(MOTOR_CURRENT_PWM_Z_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
SET_OUTPUT(MOTOR_CURRENT_PWM_E_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E0)
SET_OUTPUT(MOTOR_CURRENT_PWM_E0_PIN);
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E1)
SET_OUTPUT(MOTOR_CURRENT_PWM_E1_PIN);
#endif
refresh_motor_power();
// Set Timer5 to 31khz so the PWM of the motor power is as constant as possible. (removes a buzzing noise)
#ifdef __AVR__
SET_CS5(PRESCALER_1);
#endif
#endif
}
/**
Software-controlled Microstepping */
void Stepper::microstep_init() {
SET_OUTPUT(X_MS1_PIN); SET_OUTPUT(X_MS2_PIN);
SET_OUTPUT(X_MS3_PIN);
SET_OUTPUT(X2_MS1_PIN); SET_OUTPUT(X2_MS2_PIN);
SET_OUTPUT(X2_MS3_PIN);
SET_OUTPUT(Y_MS1_PIN); SET_OUTPUT(Y_MS2_PIN);
SET_OUTPUT(Y_MS3_PIN);
SET_OUTPUT(Y2_MS1_PIN); SET_OUTPUT(Y2_MS2_PIN);
SET_OUTPUT(Y2_MS3_PIN);
SET_OUTPUT(Z_MS1_PIN); SET_OUTPUT(Z_MS2_PIN);
SET_OUTPUT(Z_MS3_PIN);
SET_OUTPUT(Z2_MS1_PIN); SET_OUTPUT(Z2_MS2_PIN);
SET_OUTPUT(Z2_MS3_PIN);
SET_OUTPUT(Z3_MS1_PIN); SET_OUTPUT(Z3_MS2_PIN);
SET_OUTPUT(Z3_MS3_PIN);
SET_OUTPUT(I_MS1_PIN); SET_OUTPUT(I_MS2_PIN);
SET_OUTPUT(I_MS3_PIN);
SET_OUTPUT(J_MS1_PIN); SET_OUTPUT(J_MS2_PIN);
SET_OUTPUT(J_MS3_PIN);
SET_OUTPUT(K_MS1_PIN); SET_OUTPUT(K_MS2_PIN);
SET_OUTPUT(K_MS3_PIN);
SET_OUTPUT(E0_MS1_PIN); SET_OUTPUT(E0_MS2_PIN);
SET_OUTPUT(E0_MS3_PIN);
SET_OUTPUT(E1_MS1_PIN); SET_OUTPUT(E1_MS2_PIN);
SET_OUTPUT(E1_MS3_PIN);
SET_OUTPUT(E2_MS1_PIN); SET_OUTPUT(E2_MS2_PIN);
SET_OUTPUT(E2_MS3_PIN);
SET_OUTPUT(E3_MS1_PIN); SET_OUTPUT(E3_MS2_PIN);
SET_OUTPUT(E3_MS3_PIN);
SET_OUTPUT(E4_MS1_PIN); SET_OUTPUT(E4_MS2_PIN);
SET_OUTPUT(E4_MS3_PIN);
SET_OUTPUT(E5_MS1_PIN); SET_OUTPUT(E5_MS2_PIN);
SET_OUTPUT(E5_MS3_PIN);
static const uint8_t microstep_modes[] = MICROSTEP_MODES; for (uint16_t i = 0; i < COUNT(microstep_modes); i++) microstep_mode(i, microstep_modes[i]); }
void Stepper::microstep_ms(const uint8_t driver, const int8_t ms1, const int8_t ms2, const int8_t ms3) { if (ms1 >= 0) switch (driver) {
case 0:
#if HAS_X_MICROSTEPS
WRITE(X_MS1_PIN, ms1);
#endif
#if HAS_X2_MICROSTEPS
WRITE(X2_MS1_PIN, ms1);
#endif
break;
case 1:
#if HAS_Y_MICROSTEPS
WRITE(Y_MS1_PIN, ms1);
#endif
#if HAS_Y2_MICROSTEPS
WRITE(Y2_MS1_PIN, ms1);
#endif
break;
case 2:
#if HAS_Z_MICROSTEPS
WRITE(Z_MS1_PIN, ms1);
#endif
#if HAS_Z2_MICROSTEPS
WRITE(Z2_MS1_PIN, ms1);
#endif
#if HAS_Z3_MICROSTEPS
WRITE(Z3_MS1_PIN, ms1);
#endif
break;
case 3: WRITE(E0_MS1_PIN, ms1); break;
case 4: WRITE(E1_MS1_PIN, ms1); break;
case 5: WRITE(E2_MS1_PIN, ms1); break;
case 6: WRITE(E3_MS1_PIN, ms1); break;
case 7: WRITE(E4_MS1_PIN, ms1); break;
case 8: WRITE(E5_MS1_PIN, ms1); break;
case 9: WRITE(I_MS1_PIN, ms1); break
case 10: WRITE(J_MS1_PIN, ms1); break
case 11: WRITE(K_MS1_PIN, ms1); break
} if (ms2 >= 0) switch (driver) {
case 0:
#if HAS_X_MICROSTEPS
WRITE(X_MS2_PIN, ms2);
#endif
#if HAS_X2_MICROSTEPS
WRITE(X2_MS2_PIN, ms2);
#endif
break;
case 1:
#if HAS_Y_MICROSTEPS
WRITE(Y_MS2_PIN, ms2);
#endif
#if HAS_Y2_MICROSTEPS
WRITE(Y2_MS2_PIN, ms2);
#endif
break;
case 2:
#if HAS_Z_MICROSTEPS
WRITE(Z_MS2_PIN, ms2);
#endif
#if HAS_Z2_MICROSTEPS
WRITE(Z2_MS2_PIN, ms2);
#endif
#if HAS_Z3_MICROSTEPS
WRITE(Z3_MS2_PIN, ms2);
#endif
break;
case 3: WRITE(E0_MS2_PIN, ms2); break;
case 4: WRITE(E1_MS2_PIN, ms2); break;
case 5: WRITE(E2_MS2_PIN, ms2); break;
case 6: WRITE(E3_MS2_PIN, ms2); break;
case 7: WRITE(E4_MS2_PIN, ms2); break;
case 8: WRITE(E5_MS2_PIN, ms2); break;
case 9: WRITE(I_MS2_PIN, ms2); break
case 10: WRITE(J_MS2_PIN, ms2); break
case 11: WRITE(K_MS2_PIN, ms2); break
} if (ms3 >= 0) switch (driver) {
case 0:
#if HAS_X_MICROSTEPS && PIN_EXISTS(X_MS3)
WRITE(X_MS3_PIN, ms3);
#endif
#if HAS_X2_MICROSTEPS && PIN_EXISTS(X2_MS3)
WRITE(X2_MS3_PIN, ms3);
#endif
break;
case 1:
#if HAS_Y_MICROSTEPS && PIN_EXISTS(Y_MS3)
WRITE(Y_MS3_PIN, ms3);
#endif
#if HAS_Y2_MICROSTEPS && PIN_EXISTS(Y2_MS3)
WRITE(Y2_MS3_PIN, ms3);
#endif
break;
case 2:
#if HAS_Z_MICROSTEPS && PIN_EXISTS(Z_MS3)
WRITE(Z_MS3_PIN, ms3);
#endif
#if HAS_Z2_MICROSTEPS && PIN_EXISTS(Z2_MS3)
WRITE(Z2_MS3_PIN, ms3);
#endif
#if HAS_Z3_MICROSTEPS && PIN_EXISTS(Z3_MS3)
WRITE(Z3_MS3_PIN, ms3);
#endif
break;
case 3: WRITE(E0_MS3_PIN, ms3); break;
case 4: WRITE(E1_MS3_PIN, ms3); break;
case 5: WRITE(E2_MS3_PIN, ms3); break;
case 6: WRITE(E3_MS3_PIN, ms3); break;
case 7: WRITE(E4_MS3_PIN, ms3); break;
case 8: WRITE(E5_MS3_PIN, ms3); break;
case 9: WRITE(I_MS3_PIN, ms3); break
case 10: WRITE(J_MS3_PIN, ms3); break
case 11: WRITE(K_MS3_PIN, ms3); break
} }
void Stepper::microstep_mode(const uint8_t driver, const uint8_t stepping_mode) { switch (stepping_mode) {
case 1: microstep_ms(driver, MICROSTEP1); break;
case 2: microstep_ms(driver, MICROSTEP2); break;
case 4: microstep_ms(driver, MICROSTEP4); break;
case 8: microstep_ms(driver, MICROSTEP8); break;
case 16: microstep_ms(driver, MICROSTEP16); break;
case 32: microstep_ms(driver, MICROSTEP32); break;
case 64: microstep_ms(driver, MICROSTEP64); break;
case 128: microstep_ms(driver, MICROSTEP128); break;
default: SERIAL_ERROR_MSG("Microsteps unavailable"); break; } }
void Stepper::microstep_readings() { SERIAL_ECHOPGM("MS1,MS2,MS3 Pins\nX: ");
SERIAL_CHAR('0' + READ(X_MS1_PIN)); SERIAL_CHAR('0' + READ(X_MS2_PIN));
SERIAL_ECHOLN((int)READ(X_MS3_PIN));
SERIAL_ECHOPGM("Y: "); SERIAL_CHAR('0' + READ(Y_MS1_PIN)); SERIAL_CHAR('0' + READ(Y_MS2_PIN));
SERIAL_ECHOLN((int)READ(Y_MS3_PIN));
SERIAL_ECHOPGM("Z: "); SERIAL_CHAR('0' + READ(Z_MS1_PIN)); SERIAL_CHAR('0' + READ(Z_MS2_PIN));
SERIAL_ECHOLN((int)READ(Z_MS3_PIN));
SERIAL_ECHOPGM("I: "); SERIAL_CHAR('0' + READ(I_MS1_PIN)); SERIAL_CHAR('0' + READ(I_MS2_PIN));
SERIAL_ECHOLN((int)READ(I_MS3_PIN));
SERIAL_ECHOPGM("J: "); SERIAL_CHAR('0' + READ(J_MS1_PIN)); SERIAL_CHAR('0' + READ(J_MS2_PIN));
SERIAL_ECHOLN((int)READ(J_MS3_PIN));
SERIAL_ECHOPGM("K: "); SERIAL_CHAR('0' + READ(K_MS1_PIN)); SERIAL_CHAR('0' + READ(K_MS2_PIN));
SERIAL_ECHOLN((int)READ(K_MS3_PIN));
SERIAL_ECHOPGM("E0: "); SERIAL_CHAR('0' + READ(E0_MS1_PIN)); SERIAL_CHAR('0' + READ(E0_MS2_PIN));
SERIAL_ECHOLN((int)READ(E0_MS3_PIN));
SERIAL_ECHOPGM("E1: "); SERIAL_CHAR('0' + READ(E1_MS1_PIN)); SERIAL_CHAR('0' + READ(E1_MS2_PIN));
SERIAL_ECHOLN((int)READ(E1_MS3_PIN));
SERIAL_ECHOPGM("E2: "); SERIAL_CHAR('0' + READ(E2_MS1_PIN)); SERIAL_CHAR('0' + READ(E2_MS2_PIN));
SERIAL_ECHOLN((int)READ(E2_MS3_PIN));
SERIAL_ECHOPGM("E3: "); SERIAL_CHAR('0' + READ(E3_MS1_PIN)); SERIAL_CHAR('0' + READ(E3_MS2_PIN));
SERIAL_ECHOLN((int)READ(E3_MS3_PIN));
SERIAL_ECHOPGM("E4: "); SERIAL_CHAR('0' + READ(E4_MS1_PIN)); SERIAL_CHAR('0' + READ(E4_MS2_PIN));
SERIAL_ECHOLN((int)READ(E4_MS3_PIN));
SERIAL_ECHOPGM("E5: "); SERIAL_CHAR('0' + READ(E5_MS1_PIN)); SERIAL_ECHOLN((int)READ(E5_MS2_PIN));
SERIAL_ECHOLN((int)READ(E5_MS3_PIN));
}
Thanks, but I cannot find the changes you made. I guess you have to change something in configuration.h . But I would need your complete Marlin source code as a zip to help you. You can make a new comment here using the github web interface. Below the text input field, You will see the message "Attach files by dragging & dropping, selecting or pasting them" . Click on that message and a file browser will open. Use that to attach the zip or place your Source code in a cloud storage like dropbox and send us the link.
@nguyennhattam268 Could you post the full marlin code either in a git repository, or here, as a zipfile / drag and drop below please? This would help me, you, and DerAndere1!
Bạn có thể gửi mã marlin đầy đủ trong kho git hoặc ở đây, dưới dạng zipfile / kéo và thả bên dưới không? Điều này sẽ giúp tôi, bạn và DerAndere1!
@DerAndere1 I know this will not give 6 axis capabilities, but it seems latest 2.0.1 upwards Marlin release has X2 capability, and that X2 can be fully controlled by Gcode. Does it means that this could be the real 4 axis I look for? There is a Y2 also, but this one is "just" a Y replication.
@hobiseven : DUAL_X_CARRIAGE can be used for two independent extruders (IDEX). In the following discussion you can find an example config: https://github.com/MarlinFirmware/Marlin/issues/12464 . However you will have to switch between extruders using the G-code comand T0
for tool 0 and T1
for tool 1. So I guess you cannot move axes X1 and X2 simultaneously. If you have more questions regarding existing options, please refer to the following information: https://github.com/MarlinFirmware/Marlin/issues/14694
@DerAndere1 I rebuilt the files posted above, as there was many tiny format changes in the comments, highlighting differences, and also some other ones which are rather strange to me like removing the _ in front of AVR ... You will see that he added a few lines of code in both cPP files. New_files.zip
I added the changes I found plausible. Note that I started the bf2_6axis_dev branch for adding 6 axis support for a minimal setup (no Trinamic drivers, no delta kinematics, no CoreXY kinematics, no EEPPROM, no muti endstops). The latest changes go beyond that and will have to be acompanied with changes in stepper.h, stepper_indirection.h and tmc_util.cpp if you want multi enstops and TMC support. Things might have become rather more broken than fixed.
@nguyennhattam268 : I added you as the author of the last commit in the bf2_6axis_dev branch
I started to bring in your code into the Alfawise STM32 code. Will report to you once I will download the code, and give it a try. At least, I will not have a CPU speed issue. it runs at 72Mhz. I renamed all I,J, K axis to U,V,W as those are meant to be secondary axis, and I also saw the issue posted about the names conflicting with some Gcode commands.
@hobiseven thanks that you take over and incorporate the 6 axis code into a more updated code basis. Don't expect it to work. It will require more work to get the basic test setup going and a huge amount of work to get anything beyond G28 and G1 working with 6 axes. Note that most other firmwares (g2core, smoothie, RRF, Repetier Firmware, grbl-5X etc.) already have 6 axes working, so consider using one of those
@DerAndere1 Thank you for the clear warning. Actually, G28 is definitely needed to me, and my Gcode is G1 based. There are a few commands with no 6 axis arguments. Let's see wether or not this will be complicated to get this to work. Typical Gcode is as here :
( Plan XY ) G17 ( SET LENGTH UNITS ) G21 ( SET DISTANCE MODE ) G91 ( SET CUTTER COMPENSATION ) G40 ( SET TOOL LENGTH OFFSET ) G49 ( SET PATH CONTROL MODE ) G64 ( SET FEED RATE MODE ) G94
( Turn on hotwire ) M3 ( Set wire heat using PWM spindle speed [Value relative in %] ) S10 ( Move max speed ) F100 G1 X0 Y20 U0 V20 ( Pause [millisec] ) G4 P2000 ( Move cut speed ) F10 G1 X10 Y0 U10 V0 ( Move cut speed ) G1 X0,482 Y0,694 U0,482 V0,694 G1 X0,911 Y0,792 U0,911 V0,792 .......
I will check wether or not the header commands are all needed... Let's see. but looking at the youtube where we see the A axis turning, ( with a wrong speed), I would hope I am not signing for 10 years effort. I know very well the alfawise board, and Marlin on it.
@DerAndere1 I just saw that you have an other branch called marlin2forpipetbot where apparently you use E0 as an axis with an endstop. When you make a g28 do you get x y z e homing? That could be enough for me .... i am half way importing your changes for the 6 axis in marlin 2.0.4.2 . I might need a bit of help to change a few macros that I have a hard time to read...I will open my repo as soon as it compiles.
hi @hobiseven. I am happy to help with the translation of the macros to "human readable". Some are really cryptic, indeed :D Marlin2ForPipetbot branch should work as intended (endstops + G28 for xyze). I used it on my machine some time ago. I probably removed some temperature code because it is used as lab robot firmware. I also added some artificial(?) feature restrictions using sanity checks to reduce code I had to change. I thought about pointing you to that branch but then thought it wouldn't be the a clean solution. You would need a post processor to translate G1 Xxxx Yyyy Uuuu Vvvv Fnnn syntax into the G1 Xxxx Yyyy Zzzz Eeee Fnnn syntax understood by Marlin2ForPipetBot. Also I think the latter approach would not support G40 or G49. In contrast, with the new 6 axis code it would make sense to later implement G40, G49 and all other G codes currently not supported by Marlin (https://marlinfw.org/meta/gcode/)
@hobiseven: If you need full support for RS-274 standard compliant 6 axis G-code I still recommend to add support for your board to this HAL-ified grbl fork: https://github.com/terjeio/grblHAL
@DerAndere1 Actually I believe G40 and G49 are not needed . The machine is a hot wire cutter and the only requirement is to position the wire at both ends , nothing more. And I do not even need a gcode post processor as the axis names are selectable in jedicut. I have a long rainy week end in front of me to look at this more in depth.
to get a better overview of the changes that enable e axis homing, see commits https://github.com/MarlinFirmware/Marlin/pull/13076/commits from my rejected PR . There might be one or two bugs in the cleaned up code but those idease are the basis of my working Marlin2ForPipetBot branch
Thank you! I will check the pr and your code
Indeed, the code changes in your PR https://github.com/MarlinFirmware/Marlin/pull/13076/commits are lot simpler than on the full blown 6 axis compared the last up to date Marlin! Serial.cpp started to be a problem, and there was quite some changes in the Marlin code since you worked on the 6 axis, which makes thing even more complicated. I think E endstop will do the job for me! I will definitely restart on that one.
Hello guys, some time ago i managed to fix all the issues, including eeprom storing and calculate the correct feedrate for all axes, in Cartesian kinematics. I'm using a 12864 lcd display and all the functions for this display was implemented too! Due a personal project and lack of experience in github i changed to my personal repository, now its time to share!
I need some help to port this to the current version of marlin, and this way contribute to the community!
https://github.com/GabrielBeraldo/Marlin/tree/bf2_6axis_dev
Edit: Sorry for my latency in sharing my work, i was really busy finishing my engineering graduation and working on my new business. Now i want to contribute and make this new possibilities accessible to everyone.
@GabrielBeraldo hehe this looks a great news to me! So you say that you got this to work including a 12864 lcd... this is nice as our alfawise code uses an 2x zoom of the regular 128x64 marlin screen... I think they call it dogm. Our LCD is a color TFT 320x240, but we use DOGM zoomed in the middle, and we have 4 touchscreen buttons on the bottom, and we have a few flags on the top for BL touch, SD card, filament sensor.
Well seems I have many options now. G28 and g1 working fine for your code?? Or you even made moge Gcodes?
And you do not have to apologize for being late! You are not. We all do those works on the voluntary basis. Thanks for this great piece of work that will for sure please @DerAndere1 .
I hope I do check for differences correctly.... The Alfawise maintainned repo is the one of Tpruvot...
https://github.com/tpruvot/Marlin/compare/2.0.4.4-longer3D...GabrielBeraldo:bf2_6axis_dev
@tpruvot is this the right comparaison method. 52 files...
@hobiseven Yes! fully functional G28 and G1. I assume, but not ensure, that all the other commands will work too! This development took me a LOT of debugging with the logic analyzer to figure out where the problem was, not complicate to fix but really hard to find.
I made several mods that will not affect the movimentation factor, like graphical ones. The main differences should be checked in the files that receive, process and execute the moving actions, as well as the endstops and peripheral files linked to G28 execution.
I hope I do check for differences correctly.... The Alfawise maintainned repo is the one of Tpruvot...
tpruvot/Marlin@2.0.4.4-longer3D...GabrielBeraldo:bf2_6axis_dev
@tpruvot is this the right comparaison method. 52 files...
I was not used to work with git, so the commit logic might be messed, but i suggest you to look commit by commit and find the relevant modifications for you. Anyway, i'll be available to help you guys out!
@GabrielBeraldo Thanks a lot for sharing your fixes. The changes look good! I don't even have a logic analyzer, so thanks for bringing in your engineering expertise.
@GabrielBeraldo hi. I have one question regarding axis acceleration management . Have you replicated xyz acceleration to ijk? Or xyz drives the acceleration for the secondary axis?
@GabrielBeraldo hi. I have one question regarding axis acceleration management . Have you replicated xyz acceleration to ijk? Or xyz drives the acceleration for the secondary axis?
All the non extruder axes (XYZ and IJK) has the same privileges and features, like independent Min and Max positions, Min or Max endstops, Feedrates, Accelerations, Jerks and dynamic feedrate calculation when moving alongside with other non extruder axis.
All the parameters can be found in Configuration.h and Configurations_adv.h, in your case:
define DEFAULT_MAX_ACCELERATION { 500, 500, 500, 9, 9, 500 } //X, Y, Z, I, J, K, E
NOTE: this example are only using I and J as additional axes (9 and 9) ,following the extruder acceleration (500), if you wanna use K axis or only I axis, you should add or remove one more parameter before the extruder value.
I forgot some traces of my personal development, like the parametric definition of these parameters defined as MODULE_50 and MODULE_20, related to my syringe pump modules of 50mL and 20mL.
Ok clear... I was thinking marlin was computing something sort of integral acceleration of the head but likely not! Great job anyhow @GabrielBeraldo
@hobiseven: This branch adds 6 axis support for the basic kinematics models (cartesian, maybe CORE_XY) where optional steppers I,J,K do not influence positioning of the tool-head. For those setups it manages positioning of the tool-head including junction deviation (or jerk) for controlling the speed at corners for XYZ axes. The additional steppers I, J,K may be used to drive accessories like grippers, pumps etc. For those axes, the speed is controlled using linear acceleration management (linear or sigmoidal acceleration) but no coordination with other axes is done for IJK axes when they are moved simultaneously with other axes. I have no experience but I think that if more than 3 axes are affecting tool-head positioning, you have to take account of the kinematics. I suggest to keep the code for new kinematics out of Marlin and instead develop a post processor that takes the output of your CAM software and calculates the corrected G-code by applying the kinematics for your machine.
I have a new branch that was rebased on current upstream MarlinFirmware/Marlin bugfix-2.0.x: During the rebase there were so manny conflicts that it was almost a complete rewrite of the 6 axis code. Please test and give feedback: https://github.com/DerAndere1/Marlin/tree/bf2_6axis_dev5
Ouaaaa already a version 2? Need to give it a try...
@DerAndere1 Nice! The updates look good! Happy to see this evolving again
Dont have time to test compile. I expect some typos etc., but we will get there now that Gabriels branch can be used as a reference.
especially have a look at the parts that have a comment // TODO: Test ... or // TODO: Add support for ... Im AFK, clubbing for the rest of the night.
Have fun! I introduced 3/4 of the code in the working tag 2.0.4.2 for my board . The rebase you did has some small issues. 2/3 typos so far. Will do the rest tonight and try to compile. Overall that is quite a lot of changes!
Here is Alfawise_Marlin_6_axis code, 2.0.4.4 . There are still some issues to get to compile, even in 3 axis. ... More to come. There are some issues in macros. Marlin-2.0.4.4-longer3D_6axis.zip
I fixed some compilation errors in my bf2_6axis_dev5 branch. I even have problems compiling bugfix-2.0.x right now. Also there is still a problem with definition of MSG_DWELL
and similar MSG_... strings.
Well, this is exactly why I use the code with tag 2.0.4.4, as I know this one compiles for my machine, with the custom touch screen and FSMC STM32 LCD. In the rebase you did, there is a mix of changes due to the 6 axis, as well as ongoing changes they do in the live branch.
I am currently stuck there :
Compiling .pio\build\alfawise_U30\src\src\gcode\geometry\G53-G59.cpp.o In file included from Marlin\src\gcode../inc/../core/boards.h:24:0, from Marlin\src\gcode../inc/MarlinConfigPre.h:35, from Marlin\src\gcode../inc/MarlinConfig.h:28, from Marlin\src\gcode\gcode.h:286, from Marlin\src\gcode\gcode.cpp:28: Marlin\src\gcode../inc/../core/macros.h:249:25: error: 'LIST_NUM_AXIS' was not declared in this scope
^
Marlin\src\gcode../inc/../core/macros.h:251:27: note: in expansion of macro '_LIST_N'
^~~~~~~
Marlin\src\gcode../inc/../../Configuration_adv.h:732:29: note: in expansion of macro 'ARRAY_N'
^~~~~~~
Marlin\src\gcode\gcode.cpp:61:40: note: in expansion of macro 'AXIS_RELATIVE_MODES'
static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES;
^~~~~~~
Marlin\src\gcode../inc/../core/macros.h:249:25: note: suggested alternative: 'LOOP_NUM_AXIS'
^
Marlin\src\gcode../inc/../core/macros.h:251:27: note: in expansion of macro '_LIST_N'
^~~~~~~
Marlin\src\gcode../inc/../../Configuration_adv.h:732:29: note: in expansion of macro 'ARRAY_N'
^~~~~~~
Marlin\src\gcode\gcode.cpp:61:40: note: in expansion of macro 'AXIS_RELATIVE_MODES'
static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES;
^~~~~~~
*** [.pio\build\alfawise_U30\src\src\gcode\gcode.cpp.o] Error 1
I fixed some compilation errors in my bf2_6axis_dev5 branch. I even have problems compiling bugfix-2.0.x right now. Also there is still a problem with definition of
MSG_DWELL
and similar MSG_... strings.
is these messages properly defined on language files?
edit: just cloned your dev5 branch and look like the messages are defined, so it must be some import error.
I have posted a new git, with the 3 axis compile OK. KO for more axis, as we still have some mistakes / non declarations...
@DerAndere1 I see you are pushing quite some branches... now 6 and 7... Fixing a bit more code each time!?
@DerAndere1 now entering in DOGM, and I think that one was never touched so far...
Marlin\src\lcd\dogm\status_screen_DOGM.cpp:393:55: required from here
Marlin\src\lcd\dogm../../inc/../core/types.h:864:126: error: could not convert '{((float)((const XYZEval
Description
With the files from the bf_6axes_dev branch downloaded today, when specifying
#define NON_E_AXES = 4
or#define NON_E_AXES = 5
or#define NON_E_AXES = 6
only axes XYZ can be moved by issuing G-code G1.Steps to Reproduce
G28 I
orG28
or G-code commands likeG1 I10 F100
orG1 X5 Y6 Z3 I40 J50 K60 E70 F100
(e.g. via USB serial connection using either printrun or Universal G-code sender (UGS))Expected behavior: The stepper motors connected to the pins for AXIS_I, AXIS_J, AXIS_K should move similar to those connected to pins for AXIS_X, AXIS_Y and AXIS_Z.
Actual behavior: Only stepper motors connected to the pins for AXIS_X, AXIS_Y and AXIS_Z move. Steppers for I,J and K cannot be moved manually without applying high load. This indicates that these steppers are enabled.
Additional Information
None