Merge remote-tracking branch 'upstream/Mk3' into MK3_Dcodes_fix1
This commit is contained in:
commit
e635ce49d9
@ -635,6 +635,98 @@ void dcode_12()
|
||||
|
||||
}
|
||||
|
||||
#ifdef HEATBED_ANALYSIS
|
||||
/*!
|
||||
### D80 - Bed check <a href="https://reprap.org/wiki/G-code#D80:_Bed_check">D80: Bed check</a>
|
||||
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 <a href="https://reprap.org/wiki/G-code#D81:_Bed_analysis">D80: Bed analysis</a>
|
||||
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 <a href="https://reprap.org/wiki/G-code#D106:_Print_measured_fan_speed_for_different_pwm_values">D106: Print measured fan speed for different pwm values</a>
|
||||
*/
|
||||
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"
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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 <a href="https://reprap.org/wiki/G-code#D81:_Bed_analysis">D80: Bed analysis</a>
|
||||
@ -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 <a href="https://reprap.org/wiki/G-code#D106:_Print_measured_fan_speed_for_different_pwm_values">D106: Print measured fan speed for different pwm values</a>
|
||||
*/
|
||||
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
|
||||
/*!
|
||||
|
@ -1,190 +1,190 @@
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#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 <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
@ -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++)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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."
|
||||
|
Loading…
Reference in New Issue
Block a user