From 973db11bec737a94711f28ffa5d62f4afcb939ee Mon Sep 17 00:00:00 2001 From: VintagePC <53943260+vintagepc@users.noreply.github.com> Date: Tue, 13 Sep 2022 08:47:27 -0400 Subject: [PATCH] First pass/WIP --- src/hal/tmc2130.h | 5 ++ src/logic/CMakeLists.txt | 1 + src/logic/error_codes.h | 7 ++ src/logic/hw_sanity.cpp | 143 +++++++++++++++++++++++++++++++++++++ src/logic/hw_sanity.h | 49 +++++++++++++ src/logic/progress_codes.h | 9 +++ 6 files changed, 214 insertions(+) create mode 100644 src/logic/hw_sanity.cpp create mode 100644 src/logic/hw_sanity.h diff --git a/src/hal/tmc2130.h b/src/hal/tmc2130.h index 3592bd8..a7be1de 100644 --- a/src/hal/tmc2130.h +++ b/src/hal/tmc2130.h @@ -109,6 +109,11 @@ public: gpio::TogglePin(params.stepPin); // assumes DEDGE } + /// Set step to an explicit state + static inline void SetStep(const MotorParams ¶ms, bool state) { + gpio::WritePin(params.stepPin, (state ? gpio::Level::high : gpio::Level::low)); + } + /// Return SG state static inline bool SampleDiag(const MotorParams ¶ms) { return gpio::ReadPin(params.sgPin) == gpio::Level::low; diff --git a/src/logic/CMakeLists.txt b/src/logic/CMakeLists.txt index 295dcb0..215dc63 100644 --- a/src/logic/CMakeLists.txt +++ b/src/logic/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources( feed_to_bondtech.cpp feed_to_finda.cpp home.cpp + hw_sanity.cpp load_filament.cpp move_selector.cpp no_command.cpp diff --git a/src/logic/error_codes.h b/src/logic/error_codes.h index df7b3c9..646b643 100644 --- a/src/logic/error_codes.h +++ b/src/logic/error_codes.h @@ -70,6 +70,13 @@ enum class ErrorCode : uint_fast16_t { /// - E34240 All 3 TMC driver TMC_RESET = 0x8400, + /// TMC driver - IO pins are unreliable. While in theory it's recoverable, in practice it most likely + /// means your hardware is borked (we can't command the drivers reliably via STEP/EN/DIR due to electrical + /// issues or hardware fault. Possible "fixable" cause is undervoltage on the 5v logic line. + /// Unfixable possible cause: bad or cracked solder joints on the PCB, failed shift register, failed driver. + /// TODO: DRRacer - Separate codes per channel. + TMC_PINS_UNRELIABLE = 0x8600, + /// not enough current for the TMC, NOT RECOVERABLE /// - E34880 Pulley TMC driver /// - E34944 Selector TMC driver diff --git a/src/logic/hw_sanity.cpp b/src/logic/hw_sanity.cpp new file mode 100644 index 0000000..7153c4f --- /dev/null +++ b/src/logic/hw_sanity.cpp @@ -0,0 +1,143 @@ +/// @file hw_sanity.cpp +#include "hw_sanity.h" +#include "../modules/globals.h" +#include "../modules/motion.h" +#include "../modules/leds.h" +#include "../modules/timebase.h" +#include "config/axis.h" + +namespace logic { + +// Copy-pasta from command_base, they're inline +// so this shouldn't affect code size. +inline ErrorCode &operator|=(ErrorCode &a, ErrorCode b) { + return a = (ErrorCode)((uint16_t)a | (uint16_t)b); +} + +using Axis = config::Axis; +using TMC2130 = hal::tmc2130::TMC2130; + +static constexpr uint8_t LED_WAIT_MS = 50U; +static constexpr uint8_t TEST_PASSES = 6U; +static_assert(TEST_PASSES < 32); // Would overflow counters + +HWSanity hwSanity; + +uint8_t HWSanity::test_step = 0; +uint8_t HWSanity::fault_masks[] = { 0 }; +uint16_t HWSanity::wait_start = 0; +Axis HWSanity::axis; +ProgressCode HWSanity::next_state = ProgressCode::HWTestBegin; + +bool HWSanity::Reset(uint8_t param) { + state = ProgressCode::HWTestBegin; + error = ErrorCode::RUNNING; + axis = config::Axis::Idler; + fault_masks[0] = 0; + fault_masks[1] = 0; + fault_masks[2] = 0; + return true; +} + +enum pin_bits { + BIT_STEP = 0b001, + BIT_DIR = 0b010, + BIT_ENA = 0b100, +}; + +bool HWSanity::StepInner() { + switch (state) { + case ProgressCode::HWTestBegin: + //auto& driver = mm::motion.DriverForAxis(config::Axis::Pulley); + test_step = 0; + // Todo - set TOFF so the output bridge is disabled. + state = ProgressCode::HWTestIdler; + break; + case ProgressCode::HWTestIdler: + axis = config::Axis::Idler; + ml::leds.SetPairButOffOthers(5, ml::on, ml::off); + state = ProgressCode::HWTestExec; + next_state = ProgressCode::HWTestSelector; + break; + case ProgressCode::HWTestSelector: + axis = config::Axis::Selector; + ml::leds.SetPairButOffOthers(5, ml::off, ml::on); + state = ProgressCode::HWTestExec; + next_state = ProgressCode::HWTestPulley; + break; + case ProgressCode::HWTestPulley: + axis = config::Axis::Pulley; + ml::leds.SetPairButOffOthers(5, ml::on, ml::on); + state = ProgressCode::HWTestExec; + next_state = ProgressCode::HWTestCleanup; + break; + // The main test loop for a given axis. + case ProgressCode::HWTestDisplay: + // Hold for a few ms while we display the last step result. + if (!mt::timebase.Elapsed(wait_start, LED_WAIT_MS)) { + break; + } else { + state = ProgressCode::HWTestExec; + } + /* FALLTHRU */ + case ProgressCode::HWTestExec: { + auto driver = mm::motion.DriverForAxis(axis); + auto params = mm::axisParams[axis].params; + if (test_step < (TEST_PASSES * 8)) // 8 combos per axis + { + uint8_t set_state = test_step % 8; + // The order of the bits here is roughly the same as that of IOIN. + driver.SetStep(params, set_state & BIT_STEP); + driver.SetDir(params, set_state & BIT_DIR); + driver.SetEnabled(params, set_state & BIT_ENA); + uint16_t drv_ioin = driver.ReadRegister(params, TMC2130::Registers::IOIN); + // Compose IOIN to look like set_state. + drv_ioin = (drv_ioin & 0b11) | ((drv_ioin & 0b10000) >> 2); + uint8_t bit_errs = (drv_ioin ^ set_state); + // Set the LEDs. Note RED is index 0 in the enum, so we want the expression FALSE if there's an error. + ml::leds.SetMode(0, static_cast((bit_errs & BIT_STEP) == 0), ml::on); + ml::leds.SetMode(1, static_cast((bit_errs & BIT_DIR) == 0), ml::on); + ml::leds.SetMode(2, static_cast((bit_errs & BIT_ENA) == 0), ml::on); + // Capture the error for later. + fault_masks[axis] |= bit_errs; + // Enter the wait state: + wait_start = mt::timebase.Millis(); + state = ProgressCode::HWTestDisplay; + // Next iteration. + test_step++; + } else { + // This pass is complete. Move on to the next motor or cleanup. + test_step = 0; + state = next_state; + } + } break; + case ProgressCode::HWTestCleanup: + if (fault_masks[0] || fault_masks[1] || fault_masks[2]) { + // error, display it and return the code. + state = ProgressCode::ErrHwTestFailed; + error = ErrorCode::TMC_PINS_UNRELIABLE; + if (fault_masks[Axis::Idler]) { + error |= ErrorCode::TMC_IDLER_BIT; + } + if (fault_masks[Axis::Pulley]) { + error |= ErrorCode::TMC_PULLEY_BIT; + } + if (fault_masks[Axis::Selector]) { + error |= ErrorCode::TMC_SELECTOR_BIT; + } + return true; + } else { + //TODO: Re-enable TOFF here + FinishedOK(); + } + case ProgressCode::OK: + return true; + default: // we got into an unhandled state, better report it + state = ProgressCode::ERRInternal; + error = ErrorCode::INTERNAL; + return true; + } + return false; +} + +} // namespace logic diff --git a/src/logic/hw_sanity.h b/src/logic/hw_sanity.h new file mode 100644 index 0000000..bb8481f --- /dev/null +++ b/src/logic/hw_sanity.h @@ -0,0 +1,49 @@ +/// @file hw_sanity.h +#pragma once +#include +#include "command_base.h" +#include "config/axis.h" + +namespace logic { + +/// @brief Performs a sanity check of the hardware at reset/boot. Checks the following: +/// - TMC drivers using their IOIN registers (DIR/STEP/DRV_EN) +/// - ... +/// - Profit! + +class HWSanity : public CommandBase { +public: + inline HWSanity() + : CommandBase() {} + + /// Restart the automaton + bool Reset(uint8_t param) override; + + /// @returns true if the state machine finished its job, false otherwise. + /// LED indicators during the test execution: + /// Slots 1-3: Pin states for STEP, DIR, and ENA + /// Slot 4: Axis under test - G: Idler, R: Selector, RG: Pully. + /// Slot 5: G: Blinking to indicate test progression. R: Solid to indicate completed test w/ fault. + /// Indicators at test end (fault condition): + /// Slots 1-3 now indicate pin + /// - Off: No faults detected. + /// - G: STEP fault + /// - R: DIR fault + /// - RG: EN fault. + /// - Blinking R/G: Multiple fault, e.g both an EN fault together with STEP and/or DIR. + /// Slot 4: Reserved + /// Slot 5: R: Solid + bool StepInner() override; + +private: + static uint8_t test_step; + static config::Axis axis; + static uint8_t fault_masks[3]; + static ProgressCode next_state; + static uint16_t wait_start; +}; + +/// The one and only instance of hwSanity state machine in the FW +extern HWSanity hwSanity; + +} // namespace logic diff --git a/src/logic/progress_codes.h b/src/logic/progress_codes.h index 7dd2e19..5c6c814 100644 --- a/src/logic/progress_codes.h +++ b/src/logic/progress_codes.h @@ -41,5 +41,14 @@ enum class ProgressCode : uint_fast8_t { FeedingToFSensor, // P28 + HWTestBegin, // P29 + HWTestIdler, // P30 + HWTestSelector, // P31 + HWTestPulley, // P32 + HWTestCleanup, // P33 + HWTestExec, // P34 + HWTestDisplay, // P35 + ErrHwTestFailed, // P36 + Empty = 0xff // dummy empty state };