diff --git a/Marlin/src/feature/filwidth.cpp b/Marlin/src/feature/filwidth.cpp index ec90f3adf0..fb685bddc1 100644 --- a/Marlin/src/feature/filwidth.cpp +++ b/Marlin/src/feature/filwidth.cpp @@ -26,11 +26,24 @@ #include "filwidth.h" -bool filament_sensor; // = false; // M405/M406 turns filament sensor control ON/OFF. -float filament_width_nominal = DEFAULT_NOMINAL_FILAMENT_DIA, // Nominal filament width. Change with M404. - filament_width_meas = DEFAULT_MEASURED_FILAMENT_DIA; // Measured filament diameter -uint8_t meas_delay_cm = MEASUREMENT_DELAY_CM; // Distance delay setting -int8_t measurement_delay[MAX_MEASUREMENT_DELAY + 1], // Ring buffer to delayed measurement. Store extruder factor after subtracting 100 - filwidth_delay_index[2] = { 0, -1 }; // Indexes into ring buffer +FilamentWidthSensor filwidth; + +bool FilamentWidthSensor::enabled; // = false; // (M405-M406) Filament Width Sensor ON/OFF. +uint32_t FilamentWidthSensor::accum; // = 0 // ADC accumulator +uint16_t FilamentWidthSensor::raw; // = 0 // Measured filament diameter - one extruder only +float FilamentWidthSensor::nominal_mm = DEFAULT_NOMINAL_FILAMENT_DIA, // (M104) Nominal filament width + FilamentWidthSensor::measured_mm = DEFAULT_MEASURED_FILAMENT_DIA, // Measured filament diameter + FilamentWidthSensor::e_count = 0, + FilamentWidthSensor::delay_dist = 0; +uint8_t FilamentWidthSensor::meas_delay_cm = MEASUREMENT_DELAY_CM; // Distance delay setting +int8_t FilamentWidthSensor::ratios[MAX_MEASUREMENT_DELAY + 1], // Ring buffer to delay measurement. (Extruder factor minus 100) + FilamentWidthSensor::index_r, // Indexes into ring buffer + FilamentWidthSensor::index_w; + +void FilamentWidthSensor::init() { + const int8_t ratio = sample_to_size_ratio(); + for (uint8_t i = 0; i < COUNT(ratios); ++i) ratios[i] = ratio; + index_r = index_w = 0; +} #endif // FILAMENT_WIDTH_SENSOR diff --git a/Marlin/src/feature/filwidth.h b/Marlin/src/feature/filwidth.h index 91122a8605..6802b787f8 100644 --- a/Marlin/src/feature/filwidth.h +++ b/Marlin/src/feature/filwidth.h @@ -22,10 +22,98 @@ #pragma once #include "../inc/MarlinConfig.h" +#include "../module/planner.h" -extern bool filament_sensor; // M405/M406 turns filament sensor control ON/OFF. -extern float filament_width_nominal, // Nominal filament width. Change with M404. - filament_width_meas; // Measured filament diameter -extern uint8_t meas_delay_cm; // Distance delay setting -extern int8_t measurement_delay[MAX_MEASUREMENT_DELAY + 1], // Ring buffer to delayed measurement. Store extruder factor after subtracting 100 - filwidth_delay_index[2]; // Indexes into ring buffer +class FilamentWidthSensor { +public: + static constexpr int MMD_CM = MAX_MEASUREMENT_DELAY + 1, MMD_MM = MMD_CM * 10; + static bool enabled; // (M405-M406) Filament Width Sensor ON/OFF. + static uint32_t accum; // ADC accumulator + static uint16_t raw; // Measured filament diameter - one extruder only + static float nominal_mm, // (M104) Nominal filament width + measured_mm, // Measured filament diameter + e_count, delay_dist; + static uint8_t meas_delay_cm; // Distance delay setting + static int8_t ratios[MMD_CM], // Ring buffer to delay measurement. (Extruder factor minus 100) + index_r, index_w; // Indexes into ring buffer + + FilamentWidthSensor() { init(); } + static void init(); + + static inline void enable(const bool ena) { enabled = ena; } + + static inline void set_delay_cm(const uint8_t cm) { + meas_delay_cm = _MIN(cm, MAX_MEASUREMENT_DELAY); + } + + /** + * Convert Filament Width (mm) to an extrusion ratio + * and reduce to an 8 bit value. + * + * A nominal width of 1.75 and measured width of 1.73 + * gives (100 * 1.75 / 1.73) for a ratio of 101 and + * a return value of 1. + */ + static int8_t sample_to_size_ratio() { + return ABS(nominal_mm - measured_mm) <= FILWIDTH_ERROR_MARGIN + ? int(100.0f * nominal_mm / measured_mm) - 100 : 0; + } + + // Apply a single ADC reading to the raw value + static void accumulate(const uint16_t adc) { + if (adc > 102) // Ignore ADC under 0.5 volts + accum += (uint32_t(adc) << 7) - (accum >> 7); + } + + // Convert raw measurement to mm + static inline float raw_to_mm(const uint16_t v) { return v * 5.0f * (1.0f / 16383.0f); } + static inline float raw_to_mm() { return raw_to_mm(raw); } + + // A scaled reading is ready + // Divide to get to 0-16384 range since we used 1/128 IIR filter approach + static inline void reading_ready() { raw = accum >> 10; } + + // Update mm from the raw measurement + static inline void update_measured_mm() { measured_mm = raw_to_mm(); } + + // Update ring buffer used to delay filament measurements + static inline void advance_e(const float &e_move) { + + // Increment counters with the E distance + e_count += e_move; + delay_dist += e_move; + + // Only get new measurements on forward E movement + if (!UNEAR_ZERO(e_count)) { + + // Loop the delay distance counter (modulus by the mm length) + while (delay_dist >= MMD_MM) delay_dist -= MMD_MM; + + // Convert into an index (cm) into the measurement array + index_r = int8_t(delay_dist * 0.1f); + + // If the ring buffer is not full... + if (index_r != index_w) { + e_count = 0; // Reset the E movement counter + const int8_t meas_sample = sample_to_size_ratio(); + do { + if (++index_w >= MMD_CM) index_w = 0; // The next unused slot + ratios[index_w] = meas_sample; // Store the measurement + } while (index_r != index_w); // More slots to fill? + } + } + } + + // Dynamically set the volumetric multiplier based on the delayed width measurement. + static inline void update_volumetric() { + if (enabled) { + int8_t read_index = index_r - meas_delay_cm; + if (read_index < 0) read_index += MMD_CM; // Loop around buffer if needed + LIMIT(read_index, 0, MAX_MEASUREMENT_DELAY); + planner.apply_filament_width_sensor(ratios[read_index]); + } + } + +}; + +extern FilamentWidthSensor filwidth; diff --git a/Marlin/src/gcode/feature/filwidth/M404-M407.cpp b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp index e65a59850a..5b58416b43 100644 --- a/Marlin/src/gcode/feature/filwidth/M404-M407.cpp +++ b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp @@ -34,12 +34,12 @@ * M404: Display or set (in current units) the nominal filament width (3mm, 1.75mm ) W<3.0> */ void GcodeSuite::M404() { - if (parser.seen('W')) { - filament_width_nominal = parser.value_linear_units(); - planner.volumetric_area_nominal = CIRCLE_AREA(filament_width_nominal * 0.5); + if (parser.seenval('W')) { + filwidth.nominal_mm = parser.value_linear_units(); + planner.volumetric_area_nominal = CIRCLE_AREA(filwidth.nominal_mm * 0.5); } else - SERIAL_ECHOLNPAIR("Filament dia (nominal mm):", filament_width_nominal); + SERIAL_ECHOLNPAIR("Filament dia (nominal mm):", filwidth.nominal_mm); } /** @@ -48,28 +48,17 @@ void GcodeSuite::M404() { void GcodeSuite::M405() { // This is technically a linear measurement, but since it's quantized to centimeters and is a different // unit than everything else, it uses parser.value_byte() instead of parser.value_linear_units(). - if (parser.seen('D')) { - meas_delay_cm = parser.value_byte(); - NOMORE(meas_delay_cm, MAX_MEASUREMENT_DELAY); - } - - if (filwidth_delay_index[1] == -1) { // Initialize the ring buffer if not done since startup - const int8_t temp_ratio = thermalManager.widthFil_to_size_ratio(); - - for (uint8_t i = 0; i < COUNT(measurement_delay); ++i) - measurement_delay[i] = temp_ratio; - - filwidth_delay_index[0] = filwidth_delay_index[1] = 0; - } + if (parser.seenval('D')) + filwidth.set_delay_cm(parser.value_byte()); - filament_sensor = true; + filwidth.enable(true); } /** * M406: Turn off filament sensor for control */ void GcodeSuite::M406() { - filament_sensor = false; + filwidth.enable(false); planner.calculate_volumetric_multipliers(); // Restore correct 'volumetric_multiplier' value } @@ -77,7 +66,7 @@ void GcodeSuite::M406() { * M407: Get measured filament diameter on serial output */ void GcodeSuite::M407() { - SERIAL_ECHOLNPAIR("Filament dia (measured mm):", filament_width_meas); + SERIAL_ECHOLNPAIR("Filament dia (measured mm):", filwidth.measured_mm); } #endif // FILAMENT_WIDTH_SENSOR diff --git a/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp b/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp index c08067fb6b..b7ccbfec71 100644 --- a/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp +++ b/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp @@ -622,14 +622,9 @@ void MarlinUI::draw_status_message(const bool blink) { // Alternate Status message and Filament display if (ELAPSED(millis(), next_filament_display)) { lcd_put_u8str_P(PSTR("Dia ")); - lcd_put_u8str(ftostr12ns(filament_width_meas)); + lcd_put_u8str(ftostr12ns(filwidth.measured_mm)); lcd_put_u8str_P(PSTR(" V")); - lcd_put_u8str(i16tostr3(100.0 * ( - parser.volumetric_enabled - ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] - : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] - ) - )); + lcd_put_u8str(i16tostr3(planner.volumetric_percent(parser.volumetric_enabled))); lcd_put_wchar('%'); return; } diff --git a/Marlin/src/lcd/dogm/status_screen_DOGM.cpp b/Marlin/src/lcd/dogm/status_screen_DOGM.cpp index 9477bc7263..e9023515cf 100644 --- a/Marlin/src/lcd/dogm/status_screen_DOGM.cpp +++ b/Marlin/src/lcd/dogm/status_screen_DOGM.cpp @@ -349,13 +349,8 @@ void MarlinUI::draw_status_screen() { strcpy(ystring, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS]))); strcpy(zstring, ftostr52sp( LOGICAL_Z_POSITION(current_position[Z_AXIS]))); #if ENABLED(FILAMENT_LCD_DISPLAY) - strcpy(wstring, ftostr12ns(filament_width_meas)); - strcpy(mstring, i16tostr3(100.0 * ( - parser.volumetric_enabled - ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] - : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] - ) - )); + strcpy(wstring, ftostr12ns(filwidth.measured_mm)); + strcpy(mstring, i16tostr3(planner.volumetric_percent(parser.volumetric_enabled))); #endif } diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 55e8462861..d01acd8426 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1328,14 +1328,14 @@ void Planner::check_axes_activity() { * into a volumetric multiplier. Conversion differs when using * linear extrusion vs volumetric extrusion. */ - void Planner::calculate_volumetric_for_width_sensor(const int8_t encoded_ratio) { + void Planner::apply_filament_width_sensor(const int8_t encoded_ratio) { // Reconstitute the nominal/measured ratio const float nom_meas_ratio = 1 + 0.01f * encoded_ratio, ratio_2 = sq(nom_meas_ratio); volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] = parser.volumetric_enabled - ? ratio_2 / CIRCLE_AREA(filament_width_nominal * 0.5f) // Volumetric uses a true volumetric multiplier - : ratio_2; // Linear squares the ratio, which scales the volume + ? ratio_2 / CIRCLE_AREA(filwidth.nominal_mm * 0.5f) // Volumetric uses a true volumetric multiplier + : ratio_2; // Linear squares the ratio, which scales the volume refresh_e_factor(FILAMENT_SENSOR_EXTRUDER_NUM); } @@ -2069,37 +2069,8 @@ bool Planner::_populate_block(block_t * const block, bool split_move, block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0 #if ENABLED(FILAMENT_WIDTH_SENSOR) - static float filwidth_e_count = 0, filwidth_delay_dist = 0; - - //FMM update ring buffer used for delay with filament measurements - if (extruder == FILAMENT_SENSOR_EXTRUDER_NUM && filwidth_delay_index[1] >= 0) { //only for extruder with filament sensor and if ring buffer is initialized - - constexpr int MMD_CM = MAX_MEASUREMENT_DELAY + 1, MMD_MM = MMD_CM * 10; - - // increment counters with next move in e axis - filwidth_e_count += delta_mm[E_AXIS]; - filwidth_delay_dist += delta_mm[E_AXIS]; - - // Only get new measurements on forward E movement - if (!UNEAR_ZERO(filwidth_e_count)) { - - // Loop the delay distance counter (modulus by the mm length) - while (filwidth_delay_dist >= MMD_MM) filwidth_delay_dist -= MMD_MM; - - // Convert into an index into the measurement array - filwidth_delay_index[0] = int8_t(filwidth_delay_dist * 0.1f); - - // If the index has changed (must have gone forward)... - if (filwidth_delay_index[0] != filwidth_delay_index[1]) { - filwidth_e_count = 0; // Reset the E movement counter - const int8_t meas_sample = thermalManager.widthFil_to_size_ratio(); - do { - filwidth_delay_index[1] = (filwidth_delay_index[1] + 1) % MMD_CM; // The next unused slot - measurement_delay[filwidth_delay_index[1]] = meas_sample; // Store the measurement - } while (filwidth_delay_index[0] != filwidth_delay_index[1]); // More slots to fill? - } - } - } + if (extruder == FILAMENT_SENSOR_EXTRUDER_NUM) // Only for extruder with filament sensor + filwidth.advance_e(delta_mm[E_AXIS]); #endif // Calculate and limit speed in mm/sec for each axis diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 2f9d52698b..2de0336251 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -375,7 +375,14 @@ class Planner { static void calculate_volumetric_multipliers(); #if ENABLED(FILAMENT_WIDTH_SENSOR) - void calculate_volumetric_for_width_sensor(const int8_t encoded_ratio); + void apply_filament_width_sensor(const int8_t encoded_ratio); + + static inline float volumetric_percent(const bool vol) { + return 100.0f * (vol + ? volumetric_area_nominal / volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] + : volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] + ); + } #endif #if DISABLED(NO_VOLUMETRICS) diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 2f64652879..6f46a86ef5 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -301,10 +301,6 @@ volatile bool Temperature::temp_meas_ready = false; millis_t Temperature::preheat_end_time[HOTENDS] = { 0 }; #endif -#if ENABLED(FILAMENT_WIDTH_SENSOR) - int8_t Temperature::meas_shift_index; // Index of a delayed sample in buffer -#endif - #if HAS_AUTO_FAN millis_t Temperature::next_auto_fan_check_ms = 0; #endif @@ -314,10 +310,6 @@ volatile bool Temperature::temp_meas_ready = false; Temperature::soft_pwm_count_fan[FAN_COUNT]; #endif -#if ENABLED(FILAMENT_WIDTH_SENSOR) - uint16_t Temperature::current_raw_filwidth = 0; // Measured filament diameter - one extruder only -#endif - #if ENABLED(PROBING_HEATERS_OFF) bool Temperature::paused; #endif @@ -1082,16 +1074,11 @@ void Temperature::manage_heater() { #if ENABLED(FILAMENT_WIDTH_SENSOR) /** - * Filament Width Sensor dynamically sets the volumetric multiplier - * based on a delayed measurement of the filament diameter. + * Dynamically set the volumetric multiplier based + * on the delayed Filament Width measurement. */ - if (filament_sensor) { - meas_shift_index = filwidth_delay_index[0] - meas_delay_cm; - if (meas_shift_index < 0) meas_shift_index += MAX_MEASUREMENT_DELAY + 1; //loop around buffer if needed - LIMIT(meas_shift_index, 0, MAX_MEASUREMENT_DELAY); - planner.calculate_volumetric_for_width_sensor(measurement_delay[meas_shift_index]); - } - #endif // FILAMENT_WIDTH_SENSOR + filwidth.update_volumetric(); + #endif #if HAS_HEATED_BED @@ -1526,7 +1513,7 @@ void Temperature::updateTemperaturesFromRawValues() { redundant_temperature = analog_to_celsius_hotend(redundant_temperature_raw, 1); #endif #if ENABLED(FILAMENT_WIDTH_SENSOR) - filament_width_meas = analog_to_mm_fil_width(); + filwidth.update_measured_mm(); #endif #if ENABLED(USE_WATCHDOG) @@ -1537,30 +1524,6 @@ void Temperature::updateTemperaturesFromRawValues() { temp_meas_ready = false; } - -#if ENABLED(FILAMENT_WIDTH_SENSOR) - - // Convert raw Filament Width to millimeters - float Temperature::analog_to_mm_fil_width() { - return current_raw_filwidth * 5.0f * (1.0f / 16383.0f); - } - - /** - * Convert Filament Width (mm) to a simple ratio - * and reduce to an 8 bit value. - * - * A nominal width of 1.75 and measured width of 1.73 - * gives (100 * 1.75 / 1.73) for a ratio of 101 and - * a return value of 1. - */ - int8_t Temperature::widthFil_to_size_ratio() { - if (ABS(filament_width_nominal - filament_width_meas) <= FILWIDTH_ERROR_MARGIN) - return int(100.0f * filament_width_nominal / filament_width_meas) - 100; - return 0; - } - -#endif - #if MAX6675_SEPARATE_SPI SPIclass max6675_spi; #endif @@ -2241,10 +2204,6 @@ void Temperature::set_current_temp_raw() { temp_meas_ready = true; } -#if ENABLED(FILAMENT_WIDTH_SENSOR) - uint32_t raw_filwidth_value; // = 0 -#endif - void Temperature::readings_ready() { // Update the raw values if they've been read. Else we could be updating them during reading. @@ -2252,7 +2211,7 @@ void Temperature::readings_ready() { // Filament Sensor - can be read any time since IIR filtering is used #if ENABLED(FILAMENT_WIDTH_SENSOR) - current_raw_filwidth = raw_filwidth_value >> 10; // Divide to get to 0-16384 range since we used 1/128 IIR filter approach + filwidth.reading_ready(); #endif #if HOTENDS @@ -2781,10 +2740,8 @@ void Temperature::isr() { case Measure_FILWIDTH: if (!HAL_ADC_READY()) next_sensor_state = adc_sensor_state; // redo this state - else if (HAL_READ_ADC() > 102) { // Make sure ADC is reading > 0.5 volts, otherwise don't read. - raw_filwidth_value -= raw_filwidth_value >> 7; // Subtract 1/128th of the raw_filwidth_value - raw_filwidth_value += uint32_t(HAL_READ_ADC()) << 7; // Add new ADC reading, scaled by 128 - } + else + filwidth.accumulate(HAL_READ_ADC()); break; #endif diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index 22b1cd6c98..7419c124d2 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -392,18 +392,10 @@ class Temperature { static millis_t preheat_end_time[HOTENDS]; #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - static int8_t meas_shift_index; // Index of a delayed sample in buffer - #endif - #if HAS_AUTO_FAN static millis_t next_auto_fan_check_ms; #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - static uint16_t current_raw_filwidth; // Measured filament diameter - one extruder only - #endif - #if ENABLED(PROBING_HEATERS_OFF) static bool paused; #endif @@ -570,12 +562,6 @@ class Temperature { #define is_preheating(n) (false) #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - static float analog_to_mm_fil_width(); // Convert raw Filament Width to millimeters - static int8_t widthFil_to_size_ratio(); // Convert Filament Width (mm) to an extrusion ratio - #endif - - //high level conversion routines, for use outside of temperature.cpp //inline so that there is no performance decrease. //deg=degreeCelsius