/// @file tmc2130.h #pragma once #include "../config/config.h" #include "../hal/gpio.h" #include "../hal/shr16.h" #include "../hal/spi.h" namespace hal { /// TMC2130 interface /// There are multiple TMC2130 on our board, so there will be multiple /// instances of this class namespace tmc2130 { enum MotorMode : uint8_t { Stealth, Normal }; struct MotorParams { const hal::spi::SPI_TypeDef *const spi; uint8_t idx; ///< SHR16 index bool dirOn; ///< forward direction gpio::GPIO_pin csPin; ///< CS pin gpio::GPIO_pin stepPin; ///< step pin gpio::GPIO_pin sgPin; ///< stallguard pin config::MRes mRes; ///< microstep resolution int8_t sg_thrs; uint8_t axis; }; struct MotorCurrents { bool vSense; ///< VSense current scaling uint8_t iRun; ///< Running current uint8_t iHold; ///< Holding current }; struct __attribute__((packed)) ErrorFlags { uint8_t reset_flag : 1; ///< driver restarted uint8_t uv_cp : 1; ///< undervoltage on charge pump uint8_t s2g : 1; ///< short to ground uint8_t otpw : 1; ///< over temperature pre-warning uint8_t ot : 1; ///< over temperature hard inline constexpr ErrorFlags() : reset_flag(0) , uv_cp(0) , s2g(0) , otpw(0) , ot(0) {} inline bool Good() const { return reset_flag == 0 && uv_cp == 0 && s2g == 0 && otpw == 0 && ot == 0; } }; /// TMC2130 interface - instances of this class are hidden in modules::motion::Motion::AxisData class TMC2130 { public: /// TMC2130 register addresses enum class Registers : uint8_t { /// General Configuration Registers GCONF = 0x00, GSTAT = 0x01, IOIN = 0x04, /// Velocity Dependent Driver Feature Control Register Set IHOLD_IRUN = 0x10, TPOWERDOWN = 0x11, TSTEP = 0x12, TPWMTHRS = 0x13, TCOOLTHRS = 0x14, THIGH = 0x15, /// Motor Driver Registers MSCNT = 0x6A, CHOPCONF = 0x6C, COOLCONF = 0x6D, DRV_STATUS = 0x6F, PWMCONF = 0x70, }; /// Constructor TMC2130() = default; /// (re)initialization of the chip - please note this is necessary due to some HW flaws in the original MMU boards. /// And yes, the TMC may not get correctly initialized. /// @returns true if the TMC2130 was inited correctly bool Init(const MotorParams ¶ms, const MotorCurrents ¤ts, MotorMode mode); /// Set the current motor mode void SetMode(const MotorParams ¶ms, MotorMode mode); /// Set the current motor currents void SetCurrents(const MotorParams ¶ms, const MotorCurrents ¤ts); /// Set stallguard threshold void SetSGTHRS(const MotorParams ¶ms, uint8_t sgthrs); /// Return enabled state const bool Enabled() const { return enabled; } /// Enable/Disable the motor void SetEnabled(const MotorParams ¶ms, bool enabled); /// Set direction static inline void SetDir(const MotorParams ¶ms, bool dir) { hal::shr16::shr16.SetTMCDir(params.idx, dir ^ params.dirOn); } /// Step the motor static inline void Step(const MotorParams ¶ms) { gpio::TogglePin(params.stepPin); // assumes DEDGE } /// Return SG state static inline bool SampleDiag(const MotorParams ¶ms) { return gpio::ReadPin(params.sgPin) == gpio::Level::low; } inline bool Stalled() const { return sg_filter_counter == 0; } inline void ClearStallguard() { sg_filter_counter = sg_filter_threshold; } /// Should be called periodically from main loop. Maybe not all the time. Once every 10 ms is probably enough bool CheckForErrors(const MotorParams ¶ms); inline ErrorFlags GetErrorFlags() const { return errorFlags; } #ifdef UNITTEST /// A very brutal way of fiddling with the error flags from the outside of this class /// Intended only for the UNIT TESTS! inline void SetErrorFlags(ErrorFlags ef) { errorFlags = ef; } #endif /// Reads a driver register and updates the status flags uint32_t ReadRegister(const MotorParams ¶ms, Registers reg); /// Writes a driver register and updates the status flags void WriteRegister(const MotorParams ¶ms, Registers reg, uint32_t data); /// Used for polling the DIAG pin. Should be called from the stepper isr periodically when moving. void Isr(const MotorParams ¶ms); private: void _spi_tx_rx(const MotorParams ¶ms, uint8_t (&pData)[5]); void _handle_spi_status(const MotorParams ¶ms, uint8_t status); ErrorFlags errorFlags; bool enabled = false; uint8_t sg_filter_threshold; uint8_t sg_filter_counter = 0; }; } // namespace tmc2130 } // namespace hal