parent
925201d77a
commit
f0a042c1b6
|
|
@ -39,6 +39,9 @@ bool FeedToFinda::Step() {
|
||||||
mm::motion.PlanMove(-600, 0, 0, 4000, 0, 0); //@@TODO constants
|
mm::motion.PlanMove(-600, 0, 0, 4000, 0, 0); //@@TODO constants
|
||||||
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on
|
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on
|
||||||
state = Failed;
|
state = Failed;
|
||||||
|
// @@TODO - shall we disengage the idler?
|
||||||
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::off);
|
||||||
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::red, ml::blink0);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case UnloadBackToPTFE:
|
case UnloadBackToPTFE:
|
||||||
|
|
@ -52,6 +55,7 @@ bool FeedToFinda::Step() {
|
||||||
state = OK;
|
state = OK;
|
||||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::on);
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::on);
|
||||||
}
|
}
|
||||||
|
// @@TODO FINDA must be reported as OFF again as we are pulling the filament from it - is this correct?
|
||||||
return false;
|
return false;
|
||||||
case OK:
|
case OK:
|
||||||
case Failed:
|
case Failed:
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ struct FeedToFinda {
|
||||||
/// @param feedPhaseLimited
|
/// @param feedPhaseLimited
|
||||||
/// * true feed phase is limited, doesn't react on button press
|
/// * true feed phase is limited, doesn't react on button press
|
||||||
/// * false feed phase is unlimited, can be interrupted by any button press after blanking time
|
/// * false feed phase is unlimited, can be interrupted by any button press after blanking time
|
||||||
|
/// Beware: the function returns immediately without actually doing anything if the FINDA is "pressed", i.e. the filament is already at the FINDA
|
||||||
void Reset(bool feedPhaseLimited);
|
void Reset(bool feedPhaseLimited);
|
||||||
|
|
||||||
/// @returns true if the state machine finished its job, false otherwise
|
/// @returns true if the state machine finished its job, false otherwise
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,14 @@ namespace motion {
|
||||||
|
|
||||||
Motion motion;
|
Motion motion;
|
||||||
|
|
||||||
|
void Motion::InitAxis(Axis axis) {}
|
||||||
|
|
||||||
|
void Motion::DisableAxis(Axis axis) {}
|
||||||
|
|
||||||
|
bool Motion::StallGuard(Axis axis) { return false; }
|
||||||
|
|
||||||
|
void Motion::ClearStallGuardFlag(Axis axis) {}
|
||||||
|
|
||||||
void Motion::PlanMove(uint16_t pulley, uint16_t idler, uint16_t selector, uint16_t feedrate, uint16_t starting_speed, uint16_t ending_speed) {}
|
void Motion::PlanMove(uint16_t pulley, uint16_t idler, uint16_t selector, uint16_t feedrate, uint16_t starting_speed, uint16_t ending_speed) {}
|
||||||
|
|
||||||
void Motion::Home(Axis axis, bool direction) {}
|
void Motion::Home(Axis axis, bool direction) {}
|
||||||
|
|
@ -14,6 +22,10 @@ void Motion::SetMode(MotorMode mode) {}
|
||||||
|
|
||||||
void Motion::Step() {}
|
void Motion::Step() {}
|
||||||
|
|
||||||
|
bool Motion::QueueEmpty() const { return false; }
|
||||||
|
|
||||||
|
void Motion::AbortPlannedMoves() {}
|
||||||
|
|
||||||
void ISR() {}
|
void ISR() {}
|
||||||
|
|
||||||
//@@TODO check the directions
|
//@@TODO check the directions
|
||||||
|
|
|
||||||
|
|
@ -63,16 +63,16 @@ class Motion {
|
||||||
public:
|
public:
|
||||||
/// Init axis driver - @@TODO this should be probably hidden somewhere deeper ... something should manage the axes and their state
|
/// Init axis driver - @@TODO this should be probably hidden somewhere deeper ... something should manage the axes and their state
|
||||||
/// especially when the TMC may get randomly reset (deinited)
|
/// especially when the TMC may get randomly reset (deinited)
|
||||||
void InitAxis(Axis axis) {}
|
void InitAxis(Axis axis);
|
||||||
|
|
||||||
/// Disable axis motor
|
/// Disable axis motor
|
||||||
void DisableAxis(Axis axis) {}
|
void DisableAxis(Axis axis);
|
||||||
|
|
||||||
/// @returns true if a stall guard event occurred recently on the axis
|
/// @returns true if a stall guard event occurred recently on the axis
|
||||||
bool StallGuard(Axis axis) { return false; }
|
bool StallGuard(Axis axis);
|
||||||
|
|
||||||
/// clear stall guard flag reported on an axis
|
/// clear stall guard flag reported on an axis
|
||||||
void ClearStallGuardFlag(Axis axis) {}
|
void ClearStallGuardFlag(Axis axis);
|
||||||
|
|
||||||
/// Enqueue move of a specific motor/axis into planner buffer
|
/// Enqueue move of a specific motor/axis into planner buffer
|
||||||
/// @param pulley, idler, selector - target coords
|
/// @param pulley, idler, selector - target coords
|
||||||
|
|
@ -90,10 +90,10 @@ public:
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
/// @returns true if all planned moves have been finished
|
/// @returns true if all planned moves have been finished
|
||||||
bool QueueEmpty() const { return false; }
|
bool QueueEmpty() const;
|
||||||
|
|
||||||
/// stop whatever moves are being done
|
/// stop whatever moves are being done
|
||||||
void AbortPlannedMoves() {}
|
void AbortPlannedMoves();
|
||||||
|
|
||||||
/// probably higher-level operations knowing the semantic meaning of axes
|
/// probably higher-level operations knowing the semantic meaning of axes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
add_subdirectory(cut_filament)
|
add_subdirectory(cut_filament)
|
||||||
|
|
||||||
# add_subdirectory(feed_to_finda)
|
add_subdirectory(feed_to_finda)
|
||||||
|
|
||||||
# add_subdirectory(feed_to_bondtech)
|
# add_subdirectory(feed_to_bondtech)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ add_executable(
|
||||||
../../../../src/modules/globals.cpp
|
../../../../src/modules/globals.cpp
|
||||||
../../../../src/modules/idler.cpp
|
../../../../src/modules/idler.cpp
|
||||||
../../../../src/modules/leds.cpp
|
../../../../src/modules/leds.cpp
|
||||||
../../../../src/modules/motion.cpp
|
|
||||||
../../../../src/modules/permanent_storage.cpp
|
../../../../src/modules/permanent_storage.cpp
|
||||||
../../../../src/modules/selector.cpp
|
../../../../src/modules/selector.cpp
|
||||||
../../modules/stubs/stub_adc.cpp
|
../../modules/stubs/stub_adc.cpp
|
||||||
../../modules/stubs/stub_eeprom.cpp
|
../../modules/stubs/stub_eeprom.cpp
|
||||||
../../modules/stubs/stub_shr16.cpp
|
../../modules/stubs/stub_shr16.cpp
|
||||||
../stubs/main_loop_stub.cpp
|
../stubs/main_loop_stub.cpp
|
||||||
|
../stubs/stub_motion.cpp
|
||||||
test_cut_filament.cpp
|
test_cut_filament.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,71 @@
|
||||||
#include "../../../../src/modules/globals.h"
|
#include "../../../../src/modules/globals.h"
|
||||||
#include "../../../../src/modules/idler.h"
|
#include "../../../../src/modules/idler.h"
|
||||||
#include "../../../../src/modules/leds.h"
|
#include "../../../../src/modules/leds.h"
|
||||||
|
#include "../../../../src/modules/motion.h"
|
||||||
#include "../../../../src/modules/permanent_storage.h"
|
#include "../../../../src/modules/permanent_storage.h"
|
||||||
#include "../../../../src/modules/selector.h"
|
#include "../../../../src/modules/selector.h"
|
||||||
|
|
||||||
#include "../../../../src/logic/cut_filament.h"
|
#include "../../../../src/logic/cut_filament.h"
|
||||||
|
|
||||||
#include "../../modules/stubs/stub_adc.h"
|
#include "../../modules/stubs/stub_adc.h"
|
||||||
|
|
||||||
#include "../stubs/main_loop_stub.h"
|
#include "../stubs/main_loop_stub.h"
|
||||||
|
#include "../stubs/stub_motion.h"
|
||||||
|
|
||||||
using Catch::Matchers::Equals;
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
TEST_CASE("cut_filament::basic1", "[cut_filament]") {
|
template <typename COND>
|
||||||
|
bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
|
||||||
|
while (cond() && --maxLoops) {
|
||||||
|
main_loop();
|
||||||
|
}
|
||||||
|
return maxLoops > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("cut_filament::cut0", "[cut_filament]") {
|
||||||
using namespace logic;
|
using namespace logic;
|
||||||
CutFilament cf;
|
CutFilament cf;
|
||||||
currentCommand = &cf;
|
|
||||||
main_loop();
|
main_loop();
|
||||||
|
|
||||||
|
// let's assume we have the filament NOT loaded
|
||||||
|
modules::globals::globals.SetFilamentLoaded(false);
|
||||||
|
|
||||||
|
// restart the automaton
|
||||||
|
currentCommand = &cf;
|
||||||
|
cf.Reset(0);
|
||||||
|
|
||||||
|
// it should have instructed the selector and idler to move to slot 1
|
||||||
|
// check if the idler and selector have the right command
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == 0); // @@TODO constants
|
||||||
|
|
||||||
|
// now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::SelectingFilamentSlot; }, 5000));
|
||||||
|
|
||||||
|
// idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step
|
||||||
|
REQUIRE(cf.State() == ProgressCode::FeedingToFinda);
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::FeedingToFinda; }, 5000));
|
||||||
|
|
||||||
|
// filament fed into FINDA, cutting...
|
||||||
|
REQUIRE(cf.State() == ProgressCode::PreparingBlade);
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::EngagingIdler; }, 5000));
|
||||||
|
|
||||||
|
// the idler should be at the active slot @@TODO
|
||||||
|
REQUIRE(cf.State() == ProgressCode::PushingFilament);
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::PushingFilament; }, 5000));
|
||||||
|
|
||||||
|
// filament pushed - performing cut
|
||||||
|
REQUIRE(cf.State() == ProgressCode::PerformingCut);
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::PerformingCut; }, 5000));
|
||||||
|
|
||||||
|
// returning selector
|
||||||
|
REQUIRE(cf.State() == ProgressCode::ReturningSelector);
|
||||||
|
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::ReturningSelector; }, 5000));
|
||||||
|
|
||||||
|
// the next states are still @@TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// comments:
|
||||||
|
// The tricky part of the whole state machine are the edge cases - filament not loaded, stall guards etc.
|
||||||
|
// ... all the external influence we can get on the real HW
|
||||||
|
// But the good news is we can simulate them all in the unit test and thus ensure proper handling
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# define the test executable
|
||||||
|
add_executable(
|
||||||
|
feed_to_finda_tests
|
||||||
|
../../../../src/logic/feed_to_finda.cpp
|
||||||
|
../../../../src/modules/buttons.cpp
|
||||||
|
../../../../src/modules/debouncer.cpp
|
||||||
|
../../../../src/modules/finda.cpp
|
||||||
|
../../../../src/modules/fsensor.cpp
|
||||||
|
../../../../src/modules/globals.cpp
|
||||||
|
../../../../src/modules/idler.cpp
|
||||||
|
../../../../src/modules/leds.cpp
|
||||||
|
../../../../src/modules/permanent_storage.cpp
|
||||||
|
../../../../src/modules/selector.cpp
|
||||||
|
../../modules/stubs/stub_adc.cpp
|
||||||
|
../../modules/stubs/stub_eeprom.cpp
|
||||||
|
../../modules/stubs/stub_shr16.cpp
|
||||||
|
../stubs/main_loop_stub.cpp
|
||||||
|
../stubs/stub_motion.cpp
|
||||||
|
test_feed_to_finda.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# define required search paths
|
||||||
|
target_include_directories(
|
||||||
|
feed_to_finda_tests PUBLIC ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/src/hal
|
||||||
|
${CMAKE_SOURCE_DIR}/src/logic
|
||||||
|
)
|
||||||
|
|
||||||
|
# tell build system about the test case
|
||||||
|
add_catch_test(feed_to_finda_tests)
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
#include "../../../../src/modules/buttons.h"
|
||||||
|
#include "../../../../src/modules/finda.h"
|
||||||
|
#include "../../../../src/modules/fsensor.h"
|
||||||
|
#include "../../../../src/modules/globals.h"
|
||||||
|
#include "../../../../src/modules/idler.h"
|
||||||
|
#include "../../../../src/modules/leds.h"
|
||||||
|
#include "../../../../src/modules/motion.h"
|
||||||
|
#include "../../../../src/modules/permanent_storage.h"
|
||||||
|
#include "../../../../src/modules/selector.h"
|
||||||
|
|
||||||
|
#include "../../../../src/logic/feed_to_finda.h"
|
||||||
|
|
||||||
|
#include "../../modules/stubs/stub_adc.h"
|
||||||
|
|
||||||
|
#include "../stubs/main_loop_stub.h"
|
||||||
|
#include "../stubs/stub_motion.h"
|
||||||
|
|
||||||
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
|
template <typename COND>
|
||||||
|
bool WhileCondition(logic::FeedToFinda &ff, COND cond, uint32_t maxLoops = 5000) {
|
||||||
|
while (cond() && --maxLoops) {
|
||||||
|
main_loop();
|
||||||
|
ff.Step();
|
||||||
|
}
|
||||||
|
return maxLoops > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
|
||||||
|
using namespace logic;
|
||||||
|
FeedToFinda ff;
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
// let's assume we have the filament NOT loaded and active slot 0
|
||||||
|
modules::globals::globals.SetFilamentLoaded(false);
|
||||||
|
modules::globals::globals.SetActiveSlot(0);
|
||||||
|
|
||||||
|
// restart the automaton
|
||||||
|
ff.Reset(false);
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::EngagingIdler);
|
||||||
|
|
||||||
|
// it should have instructed the selector and idler to move to slot 1
|
||||||
|
// check if the idler and selector have the right command
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == 0); // @@TODO constants
|
||||||
|
|
||||||
|
// engaging idler
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
ff,
|
||||||
|
[&]() { return !modules::idler::idler.Engaged(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// idler engaged, we'll start pushing filament
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::PushingFilament);
|
||||||
|
// at least at the beginning the LED should shine green (it should be blinking, but this mode has been already verified in the LED's unit test)
|
||||||
|
REQUIRE(modules::leds::leds.LedOn(modules::globals::globals.ActiveSlot(), modules::leds::Color::green));
|
||||||
|
|
||||||
|
// now let the filament be pushed into the FINDA - do 500 steps without triggering the condition
|
||||||
|
// and then let the simulated ADC channel 1 create a FINDA switch
|
||||||
|
hal::adc::TADCData switchFindaOn({ 0, 600, 700, 800, 900 });
|
||||||
|
hal::adc::ReinitADC(1, std::move(switchFindaOn), 100);
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
ff,
|
||||||
|
[&]() { return ff.State() == FeedToFinda::PushingFilament; },
|
||||||
|
1500));
|
||||||
|
// From now on the FINDA is reported as ON
|
||||||
|
|
||||||
|
// unloading back to PTFE
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::UnloadBackToPTFE);
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
ff,
|
||||||
|
[&]() { return ff.State() == FeedToFinda::UnloadBackToPTFE; },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// disengaging idler
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::DisengagingIdler);
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
ff,
|
||||||
|
[&]() { return modules::idler::idler.Engaged(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// state machine finished ok, the green LED should be on
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::OK);
|
||||||
|
REQUIRE(modules::leds::leds.LedOn(modules::globals::globals.ActiveSlot(), modules::leds::Color::green));
|
||||||
|
|
||||||
|
REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
|
||||||
|
using namespace logic;
|
||||||
|
FeedToFinda ff;
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
// let's assume we have the filament NOT loaded and active slot 0
|
||||||
|
modules::globals::globals.SetFilamentLoaded(false);
|
||||||
|
modules::globals::globals.SetActiveSlot(0);
|
||||||
|
|
||||||
|
// restart the automaton - we want the limited version of the feed
|
||||||
|
ff.Reset(true);
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::EngagingIdler);
|
||||||
|
|
||||||
|
// it should have instructed the selector and idler to move to slot 1
|
||||||
|
// check if the idler and selector have the right command
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
|
||||||
|
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == 0); // @@TODO constants
|
||||||
|
|
||||||
|
// engaging idler
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
ff,
|
||||||
|
[&]() { return !modules::idler::idler.Engaged(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// idler engaged, we'll start pushing filament
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::PushingFilament);
|
||||||
|
// at least at the beginning the LED should shine green (it should be blinking, but this mode has been already verified in the LED's unit test)
|
||||||
|
REQUIRE(modules::leds::leds.LedOn(modules::globals::globals.ActiveSlot(), modules::leds::Color::green));
|
||||||
|
|
||||||
|
// now let the filament be pushed into the FINDA - but we make sure the FINDA doesn't trigger at all
|
||||||
|
hal::adc::TADCData switchFindaOff({ 0 });
|
||||||
|
hal::adc::ReinitADC(1, std::move(switchFindaOff), 100);
|
||||||
|
REQUIRE(!WhileCondition(
|
||||||
|
ff, // boo, this formatting is UGLY!
|
||||||
|
[&]() { return ff.State() == FeedToFinda::PushingFilament; },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// the FINDA didn't trigger, we should be in the Failed state
|
||||||
|
REQUIRE(ff.State() == FeedToFinda::Failed);
|
||||||
|
|
||||||
|
REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
|
||||||
|
}
|
||||||
|
|
@ -13,10 +13,11 @@ logic::CommandBase *currentCommand = nullptr;
|
||||||
// just like in the real FW, step all the known automata
|
// just like in the real FW, step all the known automata
|
||||||
void main_loop() {
|
void main_loop() {
|
||||||
modules::buttons::buttons.Step(hal::adc::ReadADC(0));
|
modules::buttons::buttons.Step(hal::adc::ReadADC(0));
|
||||||
modules::leds::leds.Step(0);
|
modules::leds::leds.Step(1);
|
||||||
modules::finda::finda.Step(0);
|
modules::finda::finda.Step(1);
|
||||||
modules::fsensor::fsensor.Step(0);
|
modules::fsensor::fsensor.Step(1);
|
||||||
modules::idler::idler.Step();
|
modules::idler::idler.Step();
|
||||||
modules::selector::selector.Step();
|
modules::selector::selector.Step();
|
||||||
|
if (currentCommand)
|
||||||
currentCommand->Step();
|
currentCommand->Step();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "motion.h"
|
||||||
|
#include "stub_motion.h"
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
namespace motion {
|
||||||
|
|
||||||
|
Motion motion;
|
||||||
|
AxisSim axes[3];
|
||||||
|
|
||||||
|
void Motion::InitAxis(Axis axis) {
|
||||||
|
axes[axis].enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::DisableAxis(Axis axis) {
|
||||||
|
axes[axis].enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Motion::StallGuard(Axis axis) {
|
||||||
|
return axes[axis].stallGuard;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::ClearStallGuardFlag(Axis axis) {
|
||||||
|
axes[axis].stallGuard = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::PlanMove(uint16_t pulley, uint16_t idler, uint16_t selector, uint16_t feedrate, uint16_t starting_speed, uint16_t ending_speed) {
|
||||||
|
axes[Pulley].targetPos = axes[Pulley].pos + pulley;
|
||||||
|
axes[Idler].targetPos = axes[Idler].pos + pulley;
|
||||||
|
axes[Selector].targetPos = axes[Selector].pos + pulley;
|
||||||
|
// speeds and feedrates are not simulated yet
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::Home(Axis axis, bool direction) {
|
||||||
|
axes[Pulley].homed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::SetMode(MotorMode mode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::Step() {
|
||||||
|
for (uint8_t i = 0; i < 3; ++i) {
|
||||||
|
if (axes[i].pos != axes[i].targetPos) {
|
||||||
|
int8_t dirInc = (axes[i].pos < axes[i].targetPos) ? 1 : -1;
|
||||||
|
axes[i].pos += dirInc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Motion::QueueEmpty() const {
|
||||||
|
for (uint8_t i = 0; i < 3; ++i) {
|
||||||
|
if (axes[i].pos != axes[i].targetPos)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Motion::AbortPlannedMoves() {
|
||||||
|
for (uint8_t i = 0; i < 3; ++i) {
|
||||||
|
axes[i].targetPos = axes[i].pos; // leave the axis where it was at the time of abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// probably higher-level operations knowing the semantic meaning of axes
|
||||||
|
|
||||||
|
} // namespace motion
|
||||||
|
} // namespace modules
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
namespace motion {
|
||||||
|
|
||||||
|
struct AxisSim {
|
||||||
|
uint32_t pos;
|
||||||
|
uint32_t targetPos;
|
||||||
|
bool enabled;
|
||||||
|
bool homed;
|
||||||
|
bool stallGuard;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern AxisSim axes[3];
|
||||||
|
|
||||||
|
} // namespace motion
|
||||||
|
} // namespace modules
|
||||||
|
|
@ -34,7 +34,7 @@ bool Step_Basic_One_Button(hal::adc::TADCData &&d, uint8_t testedButtonIndex) {
|
||||||
|
|
||||||
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
||||||
constexpr uint8_t oversampleFactor = 100;
|
constexpr uint8_t oversampleFactor = 100;
|
||||||
hal::adc::ReinitADC(std::move(d), oversampleFactor);
|
hal::adc::ReinitADC(0, std::move(d), oversampleFactor);
|
||||||
|
|
||||||
uint8_t otherButton1 = 1, otherButton2 = 2;
|
uint8_t otherButton1 = 1, otherButton2 = 2;
|
||||||
switch (testedButtonIndex) {
|
switch (testedButtonIndex) {
|
||||||
|
|
@ -76,7 +76,7 @@ TEST_CASE("buttons::Step-basic-button-one-after-other", "[buttons]") {
|
||||||
|
|
||||||
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
||||||
constexpr uint8_t oversampleFactor = 100;
|
constexpr uint8_t oversampleFactor = 100;
|
||||||
hal::adc::ReinitADC(std::move(d), oversampleFactor);
|
hal::adc::ReinitADC(0, std::move(d), oversampleFactor);
|
||||||
|
|
||||||
CHECK(Step_Basic_One_Button_Test(b, oversampleFactor, 0, 1, 2));
|
CHECK(Step_Basic_One_Button_Test(b, oversampleFactor, 0, 1, 2));
|
||||||
CHECK(Step_Basic_One_Button_Test(b, oversampleFactor, 1, 0, 2));
|
CHECK(Step_Basic_One_Button_Test(b, oversampleFactor, 1, 0, 2));
|
||||||
|
|
@ -92,7 +92,7 @@ TEST_CASE("buttons::Step-debounce-one-button", "[buttons]") {
|
||||||
|
|
||||||
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button
|
||||||
constexpr uint8_t oversampleFactor = 25;
|
constexpr uint8_t oversampleFactor = 25;
|
||||||
hal::adc::ReinitADC(std::move(d), oversampleFactor);
|
hal::adc::ReinitADC(0, std::move(d), oversampleFactor);
|
||||||
|
|
||||||
Buttons b;
|
Buttons b;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,27 +5,27 @@
|
||||||
namespace hal {
|
namespace hal {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
static TADCData values2Return;
|
static TADCData values2Return[2];
|
||||||
static TADCData::const_iterator rdptr = values2Return.cbegin();
|
static TADCData::const_iterator rdptr[2] = { values2Return[0].cbegin(), values2Return[1].cbegin() };
|
||||||
static uint8_t oversampleFactor = 1;
|
static uint8_t oversampleFactor = 1;
|
||||||
static uint8_t oversample = 1; ///< current count of oversampled values returned from the ADC - will get filled with oversampleFactor once it reaches zero
|
static uint8_t oversample = 1; ///< current count of oversampled values returned from the ADC - will get filled with oversampleFactor once it reaches zero
|
||||||
|
|
||||||
void ReinitADC(TADCData &&d, uint8_t ovsmpl) {
|
void ReinitADC(uint8_t channel, TADCData &&d, uint8_t ovsmpl) {
|
||||||
values2Return = std::move(d);
|
values2Return[channel] = std::move(d);
|
||||||
oversampleFactor = ovsmpl;
|
oversampleFactor = ovsmpl;
|
||||||
oversample = ovsmpl;
|
oversample = ovsmpl;
|
||||||
rdptr = values2Return.cbegin();
|
rdptr[channel] = values2Return[channel].cbegin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ADC access routines
|
/// ADC access routines
|
||||||
uint16_t ReadADC(uint8_t /*adc*/) {
|
uint16_t ReadADC(uint8_t adc) {
|
||||||
if (!oversample) {
|
if (!oversample) {
|
||||||
++rdptr;
|
++rdptr[adc];
|
||||||
oversample = oversampleFactor;
|
oversample = oversampleFactor;
|
||||||
} else {
|
} else {
|
||||||
--oversample;
|
--oversample;
|
||||||
}
|
}
|
||||||
return rdptr != values2Return.end() ? *rdptr : 1023;
|
return rdptr[adc] != values2Return[adc].end() ? *rdptr[adc] : values2Return[adc].back();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace adc {
|
||||||
|
|
||||||
using TADCData = std::vector<uint16_t>;
|
using TADCData = std::vector<uint16_t>;
|
||||||
|
|
||||||
void ReinitADC(TADCData &&d, uint8_t ovsmpl);
|
void ReinitADC(uint8_t channel, TADCData &&d, uint8_t ovsmpl);
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
} // namespace hal
|
} // namespace hal
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue