diff --git a/Firmware/Dcodes.cpp b/Firmware/Dcodes.cpp
index f943b4a3..1f902c5e 100644
--- a/Firmware/Dcodes.cpp
+++ b/Firmware/Dcodes.cpp
@@ -635,6 +635,98 @@ void dcode_12()
}
+#ifdef HEATBED_ANALYSIS
+ /*!
+ ### D80 - Bed check D80: Bed check
+ This command will log data to SD card file "mesh.txt".
+ #### Usage
+
+ D80 [ E | F | G | H | I | J ]
+
+ #### Parameters
+ - `E` - Dimension X (default 40)
+ - `F` - Dimention Y (default 40)
+ - `G` - Points X (default 40)
+ - `H` - Points Y (default 40)
+ - `I` - Offset X (default 74)
+ - `J` - Offset Y (default 34)
+ */
+void dcode_80()
+{
+ float dimension_x = 40;
+ float dimension_y = 40;
+ int points_x = 40;
+ int points_y = 40;
+ float offset_x = 74;
+ float offset_y = 33;
+
+ if (code_seen('E')) dimension_x = code_value();
+ if (code_seen('F')) dimension_y = code_value();
+ if (code_seen('G')) {points_x = code_value(); }
+ if (code_seen('H')) {points_y = code_value(); }
+ if (code_seen('I')) {offset_x = code_value(); }
+ if (code_seen('J')) {offset_y = code_value(); }
+ printf_P(PSTR("DIM X: %f\n"), dimension_x);
+ printf_P(PSTR("DIM Y: %f\n"), dimension_y);
+ printf_P(PSTR("POINTS X: %d\n"), points_x);
+ printf_P(PSTR("POINTS Y: %d\n"), points_y);
+ printf_P(PSTR("OFFSET X: %f\n"), offset_x);
+ printf_P(PSTR("OFFSET Y: %f\n"), offset_y);
+ bed_check(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
+}
+
+
+ /*!
+ ### D81 - Bed analysis D80: Bed analysis
+ This command will log data to SD card file "wldsd.txt".
+ #### Usage
+
+ D81 [ E | F | G | H | I | J ]
+
+ #### Parameters
+ - `E` - Dimension X (default 40)
+ - `F` - Dimention Y (default 40)
+ - `G` - Points X (default 40)
+ - `H` - Points Y (default 40)
+ - `I` - Offset X (default 74)
+ - `J` - Offset Y (default 34)
+ */
+void dcode_81()
+{
+ float dimension_x = 40;
+ float dimension_y = 40;
+ int points_x = 40;
+ int points_y = 40;
+ float offset_x = 74;
+ float offset_y = 33;
+
+ if (code_seen('E')) dimension_x = code_value();
+ if (code_seen('F')) dimension_y = code_value();
+ if (code_seen("G")) { strchr_pointer+=1; points_x = code_value(); }
+ if (code_seen("H")) { strchr_pointer+=1; points_y = code_value(); }
+ if (code_seen("I")) { strchr_pointer+=1; offset_x = code_value(); }
+ if (code_seen("J")) { strchr_pointer+=1; offset_y = code_value(); }
+
+ bed_analysis(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
+
+}
+
+#endif //HEATBED_ANALYSIS
+
+ /*!
+ ### D106 - Print measured fan speed for different pwm values D106: Print measured fan speed for different pwm values
+ */
+void dcode_106()
+{
+ for (int i = 255; i > 0; i = i - 5) {
+ fanSpeed = i;
+ //delay_keep_alive(2000);
+ for (int j = 0; j < 100; j++) {
+ delay_keep_alive(100);
+ }
+ printf_P(_N("%d: %d\n"), i, fan_speed[1]);
+ }
+}
#ifdef TMC2130
#include "planner.h"
diff --git a/Firmware/Dcodes.h b/Firmware/Dcodes.h
index eaf849ed..894cba52 100644
--- a/Firmware/Dcodes.h
+++ b/Firmware/Dcodes.h
@@ -2,26 +2,40 @@
#define DCODES_H
extern void dcode__1(); //D-1 - Endless loop (to simulate deadlock)
-
extern void dcode_0(); //D0 - Reset
extern void dcode_1(); //D1 - Clear EEPROM
extern void dcode_2(); //D2 - Read/Write RAM
+
+#ifdef DEBUG_DCODE3
extern void dcode_3(); //D3 - Read/Write EEPROM
+#endif //DEBUG_DCODE3
+
extern void dcode_4(); //D4 - Read/Write PIN
+
+#ifdef DEBUG_DCODE5
extern void dcode_5(); //D5 - Read/Write FLASH
+#endif //DEBUG_DCODE5
+
extern void dcode_6(); //D6 - Read/Write external FLASH
extern void dcode_7(); //D7 - Read/Write Bootloader
extern void dcode_8(); //D8 - Read/Write PINDA
extern void dcode_9(); //D9 - Read/Write ADC (Write=enable simulated, Read=disable simulated)
-
extern void dcode_10(); //D10 - XYZ calibration = OK
+extern void dcode_12(); //D12 - Log time. Writes the current time in the log file.
+
+#ifdef HEATBED_ANALYSIS
+extern void dcode_80(); //D80 - Bed check. This command will log data to SD card file "mesh.txt".
+extern void dcode_81(); //D81 - Bed analysis. This command will log data to SD card file "wldsd.txt".
+#endif //HEATBED_ANALYSIS
+
+ extern void dcode_106(); //D106 - Print measured fan speed for different pwm values
#ifdef TMC2130
-extern void dcode_2130(); //D2130 - TMC2130
+ extern void dcode_2130(); //D2130 - TMC2130
#endif //TMC2130
#ifdef PAT9125
-extern void dcode_9125(); //D9125 - PAT9125
+ extern void dcode_9125(); //D9125 - PAT9125
#endif //PAT9125
diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h
index fb70dfed..363407e2 100755
--- a/Firmware/Marlin.h
+++ b/Firmware/Marlin.h
@@ -310,9 +310,9 @@ extern int8_t lcd_change_fil_state;
extern float default_retraction;
#ifdef TMC2130
-bool homeaxis(int axis, bool doError = true, uint8_t cnt = 1, uint8_t* pstep = 0);
+void homeaxis(int axis, uint8_t cnt = 1, uint8_t* pstep = 0);
#else
-bool homeaxis(int axis, bool doError = true, uint8_t cnt = 1);
+void homeaxis(int axis, uint8_t cnt = 1);
#endif //TMC2130
diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index a6becaa6..8b3d4a73 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -2197,9 +2197,24 @@ bool calibrate_z_auto()
#endif //TMC2130
#ifdef TMC2130
-bool homeaxis(int axis, bool doError, uint8_t cnt, uint8_t* pstep)
+static void check_Z_crash(void)
+{
+ if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
+ FORCE_HIGH_POWER_END;
+ current_position[Z_AXIS] = 0;
+ plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
+ current_position[Z_AXIS] += MESH_HOME_Z_SEARCH;
+ plan_buffer_line_curposXYZE(max_feedrate[Z_AXIS], active_extruder);
+ st_synchronize();
+ kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+ }
+}
+#endif //TMC2130
+
+#ifdef TMC2130
+void homeaxis(int axis, uint8_t cnt, uint8_t* pstep)
#else
-bool homeaxis(int axis, bool doError, uint8_t cnt)
+void homeaxis(int axis, uint8_t cnt)
#endif //TMC2130
{
bool endstops_enabled = enable_endstops(true); //RP: endstops should be allways enabled durring homing
@@ -2312,13 +2327,7 @@ bool homeaxis(int axis, bool doError, uint8_t cnt)
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
#ifdef TMC2130
- if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
- FORCE_HIGH_POWER_END;
- if (doError) kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
- current_position[axis] = -5; //assume that nozzle crashed into bed
- plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
- return 0;
- }
+ check_Z_crash();
#endif //TMC2130
current_position[axis] = 0;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
@@ -2330,13 +2339,7 @@ bool homeaxis(int axis, bool doError, uint8_t cnt)
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
#ifdef TMC2130
- if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
- FORCE_HIGH_POWER_END;
- if (doError) kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
- current_position[axis] = -5; //assume that nozzle crashed into bed
- plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
- return 0;
- }
+ check_Z_crash();
#endif //TMC2130
axis_is_at_home(axis);
destination[axis] = current_position[axis];
@@ -2348,7 +2351,6 @@ bool homeaxis(int axis, bool doError, uint8_t cnt)
#endif
}
enable_endstops(endstops_enabled);
- return 1;
}
/**/
@@ -8982,28 +8984,7 @@ Sigma_Exit:
- `J` - Offset Y (default 34)
*/
case 80:
- {
- float dimension_x = 40;
- float dimension_y = 40;
- int points_x = 40;
- int points_y = 40;
- float offset_x = 74;
- float offset_y = 33;
-
- if (code_seen('E')) dimension_x = code_value();
- if (code_seen('F')) dimension_y = code_value();
- if (code_seen('G')) {points_x = code_value(); }
- if (code_seen('H')) {points_y = code_value(); }
- if (code_seen('I')) {offset_x = code_value(); }
- if (code_seen('J')) {offset_y = code_value(); }
- printf_P(PSTR("DIM X: %f\n"), dimension_x);
- printf_P(PSTR("DIM Y: %f\n"), dimension_y);
- printf_P(PSTR("POINTS X: %d\n"), points_x);
- printf_P(PSTR("POINTS Y: %d\n"), points_y);
- printf_P(PSTR("OFFSET X: %f\n"), offset_x);
- printf_P(PSTR("OFFSET Y: %f\n"), offset_y);
- bed_check(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
- }break;
+ dcode_80(); break;
/*!
### D81 - Bed analysis D80: Bed analysis
@@ -9021,24 +9002,7 @@ Sigma_Exit:
- `J` - Offset Y (default 34)
*/
case 81:
- {
- float dimension_x = 40;
- float dimension_y = 40;
- int points_x = 40;
- int points_y = 40;
- float offset_x = 74;
- float offset_y = 33;
-
- if (code_seen('E')) dimension_x = code_value();
- if (code_seen('F')) dimension_y = code_value();
- if (code_seen("G")) { strchr_pointer+=1; points_x = code_value(); }
- if (code_seen("H")) { strchr_pointer+=1; points_y = code_value(); }
- if (code_seen("I")) { strchr_pointer+=1; offset_x = code_value(); }
- if (code_seen("J")) { strchr_pointer+=1; offset_y = code_value(); }
-
- bed_analysis(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
-
- } break;
+ dcode_81(); break;
#endif //HEATBED_ANALYSIS
#ifdef DEBUG_DCODES
@@ -9047,17 +9011,7 @@ Sigma_Exit:
### D106 - Print measured fan speed for different pwm values D106: Print measured fan speed for different pwm values
*/
case 106:
- {
- for (int i = 255; i > 0; i = i - 5) {
- fanSpeed = i;
- //delay_keep_alive(2000);
- for (int j = 0; j < 100; j++) {
- delay_keep_alive(100);
-
- }
- printf_P(_N("%d: %d\n"), i, fan_speed[1]);
- }
- }break;
+ dcode_106(); break;
#ifdef TMC2130
/*!
diff --git a/Firmware/heatbed_pwm.cpp b/Firmware/heatbed_pwm.cpp
index e8551501..4cd0bf5e 100755
--- a/Firmware/heatbed_pwm.cpp
+++ b/Firmware/heatbed_pwm.cpp
@@ -1,190 +1,190 @@
-#include
-#include
-#include "io_atmega2560.h"
-
-// All this is about silencing the heat bed, as it behaves like a loudspeaker.
-// Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
-// frequency for both power supply units (i.e. both PSUs are reasonably silent).
-// The only trouble is the rising or falling edge of bed heating - that creates an audible click.
-// This audible click may be suppressed by making the rising or falling edge NOT sharp.
-// Of course, making non-sharp edges in digital technology is not easy, but there is a solution.
-// It is possible to do a fast PWM sequence with duty starting from 0 to 255.
-// Doing this at higher frequency than the bed "loudspeaker" can handle makes the click barely audible.
-// Technically:
-// timer0 is set to fast PWM mode at 62.5kHz (timer0 is linked to the bed heating pin) (zero prescaler)
-// To keep the bed switching at 30Hz - we don't want the PWM running at 62kHz all the time
-// since it would burn the heatbed's MOSFET:
-// 16MHz/256 levels of PWM duty gives us 62.5kHz
-// 62.5kHz/256 gives ~244Hz, that is still too fast - 244/8 gives ~30Hz, that's what we need
-// So the automaton runs atop of inner 8 (or 16) cycles.
-// The finite automaton is running in the ISR(TIMER0_OVF_vect)
-
-// 2019-08-14 update: the original algorithm worked very well, however there were 2 regressions:
-// 1. 62kHz ISR requires considerable amount of processing power,
-// USB transfer speed dropped by 20%, which was most notable when doing short G-code segments.
-// 2. Some users reported TLed PSU started clicking when running at 120V/60Hz.
-// This looks like the original algorithm didn't maintain base PWM 30Hz, but only 15Hz
-// To address both issues, there is an improved approach based on the idea of leveraging
-// different CLK prescalers in some automaton states - i.e. when holding LOW or HIGH on the output pin,
-// we don't have to clock 62kHz, but we can increase the CLK prescaler for these states to 8 (or even 64).
-// That shall result in the ISR not being called that much resulting in regained performance
-// Theoretically this is relatively easy, however one must be very carefull handling the AVR's timer
-// control registers correctly, especially setting them in a correct order.
-// Some registers are double buffered, some changes are applied in next cycles etc.
-// The biggest problem was with the CLK prescaler itself - this circuit is shared among almost all timers,
-// we don't want to reset the prescaler counted value when transiting among automaton states.
-// Resetting the prescaler would make the PWM more precise, right now there are temporal segments
-// of variable period ranging from 0 to 7 62kHz ticks - that's logical, the timer must "sync"
-// to the new slower CLK after setting the slower prescaler value.
-// In our application, this isn't any significant problem and may be ignored.
-// Doing changes in timer's registers non-correctly results in artefacts on the output pin
-// - it can toggle unnoticed, which will result in bed clicking again.
-// That's why there are special transition states ZERO_TO_RISE and ONE_TO_FALL, which enable the
-// counter change its operation atomically and without artefacts on the output pin.
-// The resulting signal on the output pin was checked with an osciloscope.
-// If there are any change requirements in the future, the signal must be checked with an osciloscope again,
-// ad-hoc changes may completely screw things up!
-
-// 2020-01-29 update: we are introducing a new option to the automaton that will allow us to force the output state
-// to either full ON or OFF. This is so that interference during the MBL probing is minimal.
-// To accomplish this goal we use bedPWMDisabled. It is only supposed to be used for brief periods of time as to
-// not make the bed temperature too unstable. Also, careful consideration should be used when using this
-// option as leaving this enabled will also keep the bed output in the state it stopped in.
-
-///! Definition off finite automaton states
-enum class States : uint8_t {
- ZERO_START = 0,///< entry point of the automaton - reads the soft_pwm_bed value for the next whole PWM cycle
- ZERO, ///< steady 0 (OFF), no change for the whole period
- ZERO_TO_RISE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
- RISE, ///< 16 fast PWM cycles with increasing duty up to steady ON
- RISE_TO_ONE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
- ONE, ///< steady 1 (ON), no change for the whole period
- ONE_TO_FALL, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
- FALL, ///< 16 fast PWM cycles with decreasing duty down to steady OFF
- FALL_TO_ZERO ///< metastate allowing the timer change its state atomically without artefacts on the output pin
-};
-
-///! Inner states of the finite automaton
-static States state = States::ZERO_START;
-
-bool bedPWMDisabled = 0;
-
-///! Fast PWM counter is used in the RISE and FALL states (62.5kHz)
-static uint8_t slowCounter = 0;
-///! Slow PWM counter is used in the ZERO and ONE states (62.5kHz/8 or 64)
-static uint8_t fastCounter = 0;
-///! PWM counter for the whole cycle - a cache for soft_pwm_bed
-static uint8_t pwm = 0;
-
-///! The slow PWM duty for the next 30Hz cycle
-///! Set in the whole firmware at various places
-extern unsigned char soft_pwm_bed;
-
-/// fastMax - how many fast PWM steps to do in RISE and FALL states
-/// 16 is a good compromise between silenced bed ("smooth" edges)
-/// and not burning the switching MOSFET
-static const uint8_t fastMax = 16;
-
-/// Scaler 16->256 for fast PWM
-static const uint8_t fastShift = 4;
-
-/// Increment slow PWM counter by slowInc every ZERO or ONE state
-/// This allows for fine-tuning the basic PWM switching frequency
-/// A possible further optimization - use a 64 prescaler (instead of 8)
-/// increment slowCounter by 1
-/// but use less bits of soft PWM - something like soft_pwm_bed >> 2
-/// that may further reduce the CPU cycles required by the bed heating automaton
-/// Due to the nature of bed heating the reduced PID precision may not be a major issue, however doing 8x less ISR(timer0_ovf) may significantly improve the performance
-static const uint8_t slowInc = 1;
-
-ISR(TIMER0_OVF_vect) // timer compare interrupt service routine
-{
- switch(state){
- case States::ZERO_START:
- if (bedPWMDisabled) return; // stay in the OFF state and do not change the output pin
- pwm = soft_pwm_bed << 1;// expecting soft_pwm_bed to be 7bit!
- if( pwm != 0 ){
- state = States::ZERO; // do nothing, let it tick once again after the 30Hz period
- }
- break;
- case States::ZERO: // end of state ZERO - we'll either stay in ZERO or change to RISE
- // In any case update our cache of pwm value for the next whole cycle from soft_pwm_bed
- slowCounter += slowInc; // this does software timer_clk/256 or less (depends on slowInc)
- if( slowCounter > pwm ){
- return;
- } // otherwise moving towards RISE
- state = States::ZERO_TO_RISE; // and finalize the change in a transitional state RISE0
- break;
- // even though it may look like the ZERO state may be glued together with the ZERO_TO_RISE, don't do it
- // the timer must tick once more in order to get rid of occasional output pin toggles.
- case States::ZERO_TO_RISE: // special state for handling transition between prescalers and switching inverted->non-inverted fast-PWM without toggling the output pin.
- // It must be done in consequent steps, otherwise the pin will get flipped up and down during one PWM cycle.
- // Also beware of the correct sequence of the following timer control registers initialization - it really matters!
- state = States::RISE; // prepare for standard RISE cycles
- fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
- TCNT0 = 255; // force overflow on the next clock cycle
- TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
- TCCR0A &= ~(1 << COM0B0); // Clear OC0B on Compare Match, set OC0B at BOTTOM (non-inverting mode)
- break;
- case States::RISE:
- OCR0B = (fastMax - fastCounter) << fastShift;
- if( fastCounter ){
- --fastCounter;
- } else { // end of RISE cycles, changing into state ONE
- state = States::RISE_TO_ONE;
- OCR0B = 255; // full duty
- TCNT0 = 254; // make the timer overflow in the next cycle
- // @@TODO these constants are still subject to investigation
- }
- break;
- case States::RISE_TO_ONE:
- state = States::ONE;
- OCR0B = 255; // full duty
- TCNT0 = 255; // make the timer overflow in the next cycle
- TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
- break;
- case States::ONE: // state ONE - we'll either stay in ONE or change to FALL
- OCR0B = 255;
- if (bedPWMDisabled) return; // stay in the ON state and do not change the output pin
- slowCounter += slowInc; // this does software timer_clk/256 or less
- if( slowCounter < pwm ){
- return;
- }
- if( (soft_pwm_bed << 1) >= (255 - slowInc - 1) ){ //@@TODO simplify & explain
- // if slowInc==2, soft_pwm == 251 will be the first to do short drops to zero. 252 will keep full heating
- return; // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
- }
- // otherwise moving towards FALL
- // @@TODO it looks like ONE_TO_FALL isn't necessary, there are no artefacts at all
- state = States::ONE;//_TO_FALL;
-// TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
-// break;
-// case States::ONE_TO_FALL:
-// OCR0B = 255; // zero duty
- state=States::FALL;
- fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
- TCNT0 = 255; // force overflow on the next clock cycle
- TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
- // must switch to inverting mode already here, because it takes a whole PWM cycle and it would make a "1" at the end of this pwm cycle
- // COM0B1 remains set both in inverting and non-inverting mode
- TCCR0A |= (1 << COM0B0); // inverting mode
- break;
- case States::FALL:
- OCR0B = (fastMax - fastCounter) << fastShift; // this is the same as in RISE, because now we are setting the zero part of duty due to inverting mode
- //TCCR0A |= (1 << COM0B0); // already set in ONE_TO_FALL
- if( fastCounter ){
- --fastCounter;
- } else { // end of FALL cycles, changing into state ZERO
- state = States::FALL_TO_ZERO;
- TCNT0 = 128; //@@TODO again - need to wait long enough to propagate the timer state changes
- OCR0B = 255;
- }
- break;
- case States::FALL_TO_ZERO:
- state = States::ZERO_START; // go to read new soft_pwm_bed value for the next cycle
- TCNT0 = 128;
- OCR0B = 255;
- TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
- break;
- }
-}
+#include
+#include
+#include "io_atmega2560.h"
+
+// All this is about silencing the heat bed, as it behaves like a loudspeaker.
+// Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
+// frequency for both power supply units (i.e. both PSUs are reasonably silent).
+// The only trouble is the rising or falling edge of bed heating - that creates an audible click.
+// This audible click may be suppressed by making the rising or falling edge NOT sharp.
+// Of course, making non-sharp edges in digital technology is not easy, but there is a solution.
+// It is possible to do a fast PWM sequence with duty starting from 0 to 255.
+// Doing this at higher frequency than the bed "loudspeaker" can handle makes the click barely audible.
+// Technically:
+// timer0 is set to fast PWM mode at 62.5kHz (timer0 is linked to the bed heating pin) (zero prescaler)
+// To keep the bed switching at 30Hz - we don't want the PWM running at 62kHz all the time
+// since it would burn the heatbed's MOSFET:
+// 16MHz/256 levels of PWM duty gives us 62.5kHz
+// 62.5kHz/256 gives ~244Hz, that is still too fast - 244/8 gives ~30Hz, that's what we need
+// So the automaton runs atop of inner 8 (or 16) cycles.
+// The finite automaton is running in the ISR(TIMER0_OVF_vect)
+
+// 2019-08-14 update: the original algorithm worked very well, however there were 2 regressions:
+// 1. 62kHz ISR requires considerable amount of processing power,
+// USB transfer speed dropped by 20%, which was most notable when doing short G-code segments.
+// 2. Some users reported TLed PSU started clicking when running at 120V/60Hz.
+// This looks like the original algorithm didn't maintain base PWM 30Hz, but only 15Hz
+// To address both issues, there is an improved approach based on the idea of leveraging
+// different CLK prescalers in some automaton states - i.e. when holding LOW or HIGH on the output pin,
+// we don't have to clock 62kHz, but we can increase the CLK prescaler for these states to 8 (or even 64).
+// That shall result in the ISR not being called that much resulting in regained performance
+// Theoretically this is relatively easy, however one must be very carefull handling the AVR's timer
+// control registers correctly, especially setting them in a correct order.
+// Some registers are double buffered, some changes are applied in next cycles etc.
+// The biggest problem was with the CLK prescaler itself - this circuit is shared among almost all timers,
+// we don't want to reset the prescaler counted value when transiting among automaton states.
+// Resetting the prescaler would make the PWM more precise, right now there are temporal segments
+// of variable period ranging from 0 to 7 62kHz ticks - that's logical, the timer must "sync"
+// to the new slower CLK after setting the slower prescaler value.
+// In our application, this isn't any significant problem and may be ignored.
+// Doing changes in timer's registers non-correctly results in artefacts on the output pin
+// - it can toggle unnoticed, which will result in bed clicking again.
+// That's why there are special transition states ZERO_TO_RISE and ONE_TO_FALL, which enable the
+// counter change its operation atomically and without artefacts on the output pin.
+// The resulting signal on the output pin was checked with an osciloscope.
+// If there are any change requirements in the future, the signal must be checked with an osciloscope again,
+// ad-hoc changes may completely screw things up!
+
+// 2020-01-29 update: we are introducing a new option to the automaton that will allow us to force the output state
+// to either full ON or OFF. This is so that interference during the MBL probing is minimal.
+// To accomplish this goal we use bedPWMDisabled. It is only supposed to be used for brief periods of time as to
+// not make the bed temperature too unstable. Also, careful consideration should be used when using this
+// option as leaving this enabled will also keep the bed output in the state it stopped in.
+
+///! Definition off finite automaton states
+enum class States : uint8_t {
+ ZERO_START = 0,///< entry point of the automaton - reads the soft_pwm_bed value for the next whole PWM cycle
+ ZERO, ///< steady 0 (OFF), no change for the whole period
+ ZERO_TO_RISE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+ RISE, ///< 16 fast PWM cycles with increasing duty up to steady ON
+ RISE_TO_ONE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+ ONE, ///< steady 1 (ON), no change for the whole period
+ ONE_TO_FALL, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+ FALL, ///< 16 fast PWM cycles with decreasing duty down to steady OFF
+ FALL_TO_ZERO ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+};
+
+///! Inner states of the finite automaton
+static States state = States::ZERO_START;
+
+bool bedPWMDisabled = 0;
+
+///! Fast PWM counter is used in the RISE and FALL states (62.5kHz)
+static uint8_t slowCounter = 0;
+///! Slow PWM counter is used in the ZERO and ONE states (62.5kHz/8 or 64)
+static uint8_t fastCounter = 0;
+///! PWM counter for the whole cycle - a cache for soft_pwm_bed
+static uint8_t pwm = 0;
+
+///! The slow PWM duty for the next 30Hz cycle
+///! Set in the whole firmware at various places
+extern unsigned char soft_pwm_bed;
+
+/// fastMax - how many fast PWM steps to do in RISE and FALL states
+/// 16 is a good compromise between silenced bed ("smooth" edges)
+/// and not burning the switching MOSFET
+static const uint8_t fastMax = 16;
+
+/// Scaler 16->256 for fast PWM
+static const uint8_t fastShift = 4;
+
+/// Increment slow PWM counter by slowInc every ZERO or ONE state
+/// This allows for fine-tuning the basic PWM switching frequency
+/// A possible further optimization - use a 64 prescaler (instead of 8)
+/// increment slowCounter by 1
+/// but use less bits of soft PWM - something like soft_pwm_bed >> 2
+/// that may further reduce the CPU cycles required by the bed heating automaton
+/// Due to the nature of bed heating the reduced PID precision may not be a major issue, however doing 8x less ISR(timer0_ovf) may significantly improve the performance
+static const uint8_t slowInc = 1;
+
+ISR(TIMER0_OVF_vect) // timer compare interrupt service routine
+{
+ switch(state){
+ case States::ZERO_START:
+ if (bedPWMDisabled) return; // stay in the OFF state and do not change the output pin
+ pwm = soft_pwm_bed << 1;// expecting soft_pwm_bed to be 7bit!
+ if( pwm != 0 ){
+ state = States::ZERO; // do nothing, let it tick once again after the 30Hz period
+ }
+ break;
+ case States::ZERO: // end of state ZERO - we'll either stay in ZERO or change to RISE
+ // In any case update our cache of pwm value for the next whole cycle from soft_pwm_bed
+ slowCounter += slowInc; // this does software timer_clk/256 or less (depends on slowInc)
+ if( slowCounter > pwm ){
+ return;
+ } // otherwise moving towards RISE
+ state = States::ZERO_TO_RISE; // and finalize the change in a transitional state RISE0
+ break;
+ // even though it may look like the ZERO state may be glued together with the ZERO_TO_RISE, don't do it
+ // the timer must tick once more in order to get rid of occasional output pin toggles.
+ case States::ZERO_TO_RISE: // special state for handling transition between prescalers and switching inverted->non-inverted fast-PWM without toggling the output pin.
+ // It must be done in consequent steps, otherwise the pin will get flipped up and down during one PWM cycle.
+ // Also beware of the correct sequence of the following timer control registers initialization - it really matters!
+ state = States::RISE; // prepare for standard RISE cycles
+ fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
+ TCNT0 = 255; // force overflow on the next clock cycle
+ TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
+ TCCR0A &= ~(1 << COM0B0); // Clear OC0B on Compare Match, set OC0B at BOTTOM (non-inverting mode)
+ break;
+ case States::RISE:
+ OCR0B = (fastMax - fastCounter) << fastShift;
+ if( fastCounter ){
+ --fastCounter;
+ } else { // end of RISE cycles, changing into state ONE
+ state = States::RISE_TO_ONE;
+ OCR0B = 255; // full duty
+ TCNT0 = 254; // make the timer overflow in the next cycle
+ // @@TODO these constants are still subject to investigation
+ }
+ break;
+ case States::RISE_TO_ONE:
+ state = States::ONE;
+ OCR0B = 255; // full duty
+ TCNT0 = 255; // make the timer overflow in the next cycle
+ TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
+ break;
+ case States::ONE: // state ONE - we'll either stay in ONE or change to FALL
+ OCR0B = 255;
+ if (bedPWMDisabled) return; // stay in the ON state and do not change the output pin
+ slowCounter += slowInc; // this does software timer_clk/256 or less
+ if( slowCounter < pwm ){
+ return;
+ }
+ if( (soft_pwm_bed << 1) >= (255 - slowInc - 1) ){ //@@TODO simplify & explain
+ // if slowInc==2, soft_pwm == 251 will be the first to do short drops to zero. 252 will keep full heating
+ return; // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
+ }
+ // otherwise moving towards FALL
+ // @@TODO it looks like ONE_TO_FALL isn't necessary, there are no artefacts at all
+ state = States::ONE;//_TO_FALL;
+// TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
+// break;
+// case States::ONE_TO_FALL:
+// OCR0B = 255; // zero duty
+ state=States::FALL;
+ fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
+ TCNT0 = 255; // force overflow on the next clock cycle
+ TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
+ // must switch to inverting mode already here, because it takes a whole PWM cycle and it would make a "1" at the end of this pwm cycle
+ // COM0B1 remains set both in inverting and non-inverting mode
+ TCCR0A |= (1 << COM0B0); // inverting mode
+ break;
+ case States::FALL:
+ OCR0B = (fastMax - fastCounter) << fastShift; // this is the same as in RISE, because now we are setting the zero part of duty due to inverting mode
+ //TCCR0A |= (1 << COM0B0); // already set in ONE_TO_FALL
+ if( fastCounter ){
+ --fastCounter;
+ } else { // end of FALL cycles, changing into state ZERO
+ state = States::FALL_TO_ZERO;
+ TCNT0 = 128; //@@TODO again - need to wait long enough to propagate the timer state changes
+ OCR0B = 255;
+ }
+ break;
+ case States::FALL_TO_ZERO:
+ state = States::ZERO_START; // go to read new soft_pwm_bed value for the next cycle
+ TCNT0 = 128;
+ OCR0B = 255;
+ TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
+ break;
+ }
+}
diff --git a/Firmware/tmc2130.cpp b/Firmware/tmc2130.cpp
index 3a4e5b51..108d00b1 100755
--- a/Firmware/tmc2130.cpp
+++ b/Firmware/tmc2130.cpp
@@ -994,7 +994,7 @@ bool tmc2130_home_calibrate(uint8_t axis)
uint8_t step[16];
uint8_t cnt[16];
uint8_t val[16];
- homeaxis(axis, true, 16, step);
+ homeaxis(axis, 16, step);
bubblesort_uint8(step, 16, 0);
printf_P(PSTR("sorted samples:\n"));
for (uint8_t i = 0; i < 16; i++)
diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp
index daf9f017..62c41072 100755
--- a/Firmware/ultralcd.cpp
+++ b/Firmware/ultralcd.cpp
@@ -168,10 +168,10 @@ static void reset_crash_det(unsigned char axis);
static bool lcd_selfcheck_axis_sg(unsigned char axis);
static bool lcd_selfcheck_axis(int _axis, int _travel);
#else
-static bool lcd_selfcheck_endstops();
static bool lcd_selfcheck_axis(int _axis, int _travel);
static bool lcd_selfcheck_pulleys(int axis);
#endif //TMC2130
+static bool lcd_selfcheck_endstops();
static bool lcd_selfcheck_check_heater(bool _isbed);
enum class TestScreen : uint_least8_t
@@ -7667,11 +7667,7 @@ bool lcd_selftest()
if (_result)
{
_progress = lcd_selftest_screen(TestScreen::FansOk, _progress, 3, true, 2000);
-#ifndef TMC2130
- _result = lcd_selfcheck_endstops();
-#else
- _result = true;
-#endif
+ _result = lcd_selfcheck_endstops(); //With TMC2130, only the Z probe is tested.
}
if (_result)
@@ -7738,7 +7734,7 @@ bool lcd_selftest()
set_destination_to_current();
_progress = lcd_selftest_screen(TestScreen::AxisZ, _progress, 3, true, 1500);
#ifdef TMC2130
- _result = homeaxis(Z_AXIS, false);
+ homeaxis(Z_AXIS); //In case of failure, the code gets stuck in this function.
#else
_result = lcd_selfcheck_axis(Z_AXIS, Z_MAX_POS);
#endif //TMC2130
@@ -8137,31 +8133,42 @@ static bool lcd_selfcheck_pulleys(int axis)
}
return(true);
}
+#endif //not defined TMC2130
static bool lcd_selfcheck_endstops()
{
bool _result = true;
- if (((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
+ if (
+ #ifndef TMC2130
+ ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) ||
+ #endif //!TMC2130
((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1))
{
+ #ifndef TMC2130
if ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) current_position[0] += 10;
if ((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) current_position[1] += 10;
+ #endif //!TMC2130
if ((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1) current_position[2] += 10;
}
plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
- _delay(500);
+ st_synchronize();
- if (((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
+ if (
+ #ifndef TMC2130
+ ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) ||
+ #endif //!TMC2130
((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1))
{
_result = false;
char _error[4] = "";
+ #ifndef TMC2130
if ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "X");
if ((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "Y");
+ #endif //!TMC2130
if ((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "Z");
lcd_selftest_error(TestError::Endstops, _error, "");
}
@@ -8169,7 +8176,6 @@ static bool lcd_selfcheck_endstops()
manage_inactivity(true);
return _result;
}
-#endif //not defined TMC2130
static bool lcd_selfcheck_check_heater(bool _isbed)
{
diff --git a/README.md b/README.md
index 54070dce..9d62d9ae 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
- For MK3 --> skip to step 3.
- If you have a different printer model, follow step [2.b](#2b) from Windows build
-3. Run `sudo ./build.sh`
+3. Run `./build.sh`
- Output hex file is at `"PrusaFirmware/lang/firmware.hex"` . In the same folder you can hex files for other languages as well.
4. Connect your printer and flash with PrusaSlicer ( Configuration --> Flash printer firmware ) or Slic3r PE.
diff --git a/lang/lang_en_fr.txt b/lang/lang_en_fr.txt
index f6add948..51ddf159 100755
--- a/lang/lang_en_fr.txt
+++ b/lang/lang_en_fr.txt
@@ -20,7 +20,7 @@
#MSG_CRASH_DET_STEALTH_FORCE_OFF c=20 r=4
"WARNING:\x0aCrash detection\x0adisabled in\x0aStealth mode"
-"ATTENTION:\x0aDetection de crash\x0adesactivee en\x0amode feutre"
+"ATTENTION:\x0aDetection de crash\x0adesactivee en\x0amode furtif"
#
">Cancel"
@@ -550,7 +550,7 @@
#MSG_SILENT
"Silent"
-"Feutre"
+"Furtif"
#
"MMU needs user attention."