diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b079840..c0c2aacd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,9 @@ set(TEST_SOURCES Tests/tests.cpp Tests/Example_test.cpp Tests/Timer_test.cpp + Tests/AutoDeplete_test.cpp Firmware/Timer.cpp + Firmware/AutoDeplete.cpp ) add_executable(tests ${TEST_SOURCES}) target_include_directories(tests PRIVATE Tests) diff --git a/Firmware/AutoDeplete.cpp b/Firmware/AutoDeplete.cpp new file mode 100644 index 00000000..9c4340f0 --- /dev/null +++ b/Firmware/AutoDeplete.cpp @@ -0,0 +1,79 @@ +//! @file +//! @author: Marek Bel +//! @date Jan 3, 2019 + +#include "AutoDeplete.h" +#include "assert.h" + +//! @brief bit field marking depleted filaments +//! +//! binary 1 marks filament as depleted +//! Zero initialized value means, that no filament is depleted. +static uint8_t depleted; +static const uint8_t filamentCount = 5; + +//! @return binary 1 for all filaments +//! @par fCount number of filaments +static constexpr uint8_t allDepleted(uint8_t fCount) +{ + return fCount == 1 ? 1 : ((1 << (fCount - 1)) | allDepleted(fCount - 1)); +} + +//! @brief Is filament available for printing? +//! @par filament Filament number to be checked +//! @retval true Filament is available for printing. +//! @retval false Filament is not available for printing. +static bool loaded(uint8_t filament) +{ + if (depleted & (1 << filament)) return false; + return true; +} + +//! @brief Mark filament as not available for printing. +//! @par filament filament to be marked +void ad_markDepleted(uint8_t filament) +{ + assert(filament < filamentCount); + if (filament < filamentCount) + { + depleted |= 1 << filament; + } +} + +//! @brief Mark filament as available for printing. +//! @par filament filament to be marked +void ad_markLoaded(uint8_t filament) +{ + assert(filament < filamentCount); + if (filament < filamentCount) + { + depleted &= ~(1 << filament); + } +} + +//! @brief Get alternative filament, which is not depleted +//! @par filament filament +//! @return Filament, if it is depleted, returns next available, +//! if all filaments are depleted, returns filament function parameter. +uint8_t ad_getAlternative(uint8_t filament) +{ + assert(filament < filamentCount); + for (uint8_t i = 0; i + +void ad_markDepleted(uint8_t filament); +void ad_markLoaded(uint8_t filament); +uint8_t ad_getAlternative(uint8_t filament); +bool ad_allDepleted(); + +#endif /* AUTODEPLETE_H */ diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 49e20b69..3ff3ffea 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -78,6 +78,7 @@ #include #include "Dcodes.h" +#include "AutoDeplete.h" #ifdef SWSPI @@ -6944,6 +6945,10 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE)) } else { tmp_extruder = code_value(); + if (mmu_enabled && lcd_autoDepleteEnabled()) + { + tmp_extruder = ad_getAlternative(tmp_extruder); + } } st_synchronize(); snmm_filaments_used |= (1 << tmp_extruder); //for stop print diff --git a/Firmware/mmu.cpp b/Firmware/mmu.cpp index 6433eafb..4adde74a 100644 --- a/Firmware/mmu.cpp +++ b/Firmware/mmu.cpp @@ -1,4 +1,4 @@ -//mmu.cpp +//! @file #include "mmu.h" #include "planner.h" @@ -14,6 +14,7 @@ #include "printers.h" #include #include "io_atmega2560.h" +#include "AutoDeplete.h" #ifdef TMC2130 #include "tmc2130.h" @@ -320,8 +321,15 @@ void mmu_loop(void) if (!mmu_finda && CHECK_FINDA && fsensor_enabled) { fsensor_stop_and_save_print(); enquecommand_front_P(PSTR("FSENSOR_RECOVER")); //then recover - if (lcd_autoDepleteEnabled()) enquecommand_front_P(PSTR("M600 AUTO")); //save print and run M600 command - else enquecommand_front_P(PSTR("M600")); //save print and run M600 command + ad_markDepleted(mmu_extruder); + if (lcd_autoDepleteEnabled() && !ad_allDepleted()) + { + enquecommand_front_P(PSTR("M600 AUTO")); //save print and run M600 command + } + else + { + enquecommand_front_P(PSTR("M600")); //save print and run M600 command + } } mmu_state = 1; if (mmu_cmd == 0) @@ -417,16 +425,26 @@ int8_t mmu_set_filament_type(uint8_t extruder, uint8_t filament) return timeout?1:0; } +//! @brief Enqueue MMUv2 command +//! +//! Call manage_response() after enqueuing to process command. +//! If T command is enqueued, it disables current for extruder motor if TMC2130 driver present. +//! If T or L command is enqueued, it marks filament loaded in AutoDeplete module. void mmu_command(uint8_t cmd) { -#ifdef TMC2130 if ((cmd >= MMU_CMD_T0) && (cmd <= MMU_CMD_T4)) { //disable extruder motor +#ifdef TMC2130 tmc2130_set_pwr(E_AXIS, 0); - //printf_P(PSTR("E-axis disabled\n")); - } #endif //TMC2130 + //printf_P(PSTR("E-axis disabled\n")); + ad_markLoaded(cmd - MMU_CMD_T0); + } + if ((cmd >= MMU_CMD_L0) && (cmd <= MMU_CMD_L4)) + { + ad_markLoaded(cmd - MMU_CMD_L0); + } mmu_cmd = cmd; mmu_ready = false; @@ -748,7 +766,7 @@ void mmu_M600_load_filament(bool automatic) #endif //MMU_M600_SWITCH_EXTRUDER } else { - tmp_extruder = (tmp_extruder+1)%5; + tmp_extruder = ad_getAlternative(tmp_extruder); } lcd_update_enable(false); lcd_clear(); diff --git a/Firmware/mmu.h b/Firmware/mmu.h index 30f35599..1861be6d 100644 --- a/Firmware/mmu.h +++ b/Firmware/mmu.h @@ -1,4 +1,4 @@ -//mmu.h +//! @file #include diff --git a/Tests/AutoDeplete_test.cpp b/Tests/AutoDeplete_test.cpp new file mode 100644 index 00000000..fb2964bc --- /dev/null +++ b/Tests/AutoDeplete_test.cpp @@ -0,0 +1,146 @@ +/** + * @file + * @author Marek Bel + */ + +#include "catch.hpp" + +#include "../Firmware/AutoDeplete.h" + +TEST_CASE( "AutoDeplete test.", "[AutoDeplete]" ) +{ + CHECK(ad_allDepleted() == false); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 1); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + + ad_markDepleted(1); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markDepleted(3); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 4); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markDepleted(4); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 0); + CHECK(ad_getAlternative(4) == 0); + CHECK(ad_allDepleted() == false); + + ad_markDepleted(4); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 0); + CHECK(ad_getAlternative(4) == 0); + CHECK(ad_allDepleted() == false); + + ad_markDepleted(0); + + CHECK(ad_getAlternative(0) == 2); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 2); + CHECK(ad_getAlternative(4) == 2); + CHECK(ad_allDepleted() == false); + + ad_markDepleted(2); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 1); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == true); + + ad_markDepleted(2); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 1); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == true); + + ad_markLoaded(4); + + CHECK(ad_getAlternative(0) == 4); + CHECK(ad_getAlternative(1) == 4); + CHECK(ad_getAlternative(2) == 4); + CHECK(ad_getAlternative(3) == 4); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(0); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 4); + CHECK(ad_getAlternative(2) == 4); + CHECK(ad_getAlternative(3) == 4); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(3); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 3); + CHECK(ad_getAlternative(2) == 3); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(3); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 3); + CHECK(ad_getAlternative(2) == 3); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(2); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 2); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(1); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 1); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + + ad_markLoaded(1); + + CHECK(ad_getAlternative(0) == 0); + CHECK(ad_getAlternative(1) == 1); + CHECK(ad_getAlternative(2) == 2); + CHECK(ad_getAlternative(3) == 3); + CHECK(ad_getAlternative(4) == 4); + CHECK(ad_allDepleted() == false); + +}