#ifndef __AXIS_HPP #define __AXIS_HPP class Axis; #include "encoder.hpp" #include "acim_estimator.hpp" #include "sensorless_estimator.hpp" #include "controller.hpp" #include "open_loop_controller.hpp" #include "trapTraj.hpp" #include "endstop.hpp" #include "mechanical_brake.hpp" #include "low_level.h" #include "utils.hpp" #include "task_timer.hpp" #include class Axis : public ODriveIntf::AxisIntf { public: struct LockinConfig_t { float current = 10.0f; // [A] float ramp_time = 0.4f; // [s] float ramp_distance = 1 * M_PI; // [rad] float accel = 20.0f; // [rad/s^2] float vel = 40.0f; // [rad/s] float finish_distance = 100.0f; // [rad] bool finish_on_vel = false; bool finish_on_distance = false; bool finish_on_enc_idx = false; }; struct TaskTimes { TaskTimer thermistor_update; TaskTimer encoder_update; TaskTimer sensorless_estimator_update; TaskTimer endstop_update; TaskTimer can_heartbeat; TaskTimer controller_update; TaskTimer open_loop_controller_update; TaskTimer acim_estimator_update; TaskTimer motor_update; TaskTimer current_controller_update; TaskTimer dc_calib; TaskTimer current_sense; TaskTimer pwm_update; }; static LockinConfig_t default_calibration(); static LockinConfig_t default_sensorless(); static LockinConfig_t default_lockin(); struct CANConfig_t { uint32_t node_id = 0; bool is_extended = false; uint32_t heartbeat_rate_ms = 100; uint32_t encoder_rate_ms = 10; }; struct Config_t { bool startup_motor_calibration = false; //decode_step_dir_pins(); } void set_dir_gpio_pin(uint16_t value) { dir_gpio_pin = value; parent->decode_step_dir_pins(); } }; struct Homing_t { bool is_homed = false; }; struct CAN_t { uint32_t last_heartbeat = 0; uint32_t last_encoder = 0; }; Axis(int axis_num, uint16_t default_step_gpio_pin, uint16_t default_dir_gpio_pin, osPriority thread_priority, Encoder& encoder, SensorlessEstimator& sensorless_estimator, Controller& controller, Motor& motor, TrapezoidalTrajectory& trap, Endstop& min_endstop, Endstop& max_endstop, MechanicalBrake& mechanical_brake); bool apply_config(); void clear_config(); void start_thread(); bool wait_for_control_iteration(); void step_cb(); void set_step_dir_active(bool enable); void decode_step_dir_pins(); bool do_checks(uint32_t timestamp); void watchdog_feed(); bool watchdog_check(); // True if there are no errors bool inline check_for_errors() { return error_ == ERROR_NONE; } bool start_closed_loop_control(); bool stop_closed_loop_control(); bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, std::function loop_cb = {} ); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); constexpr uint32_t get_watchdog_reset() { return static_cast(std::clamp(config_.watchdog_timeout, 0, UINT32_MAX / (current_meas_hz + 1)) * current_meas_hz); } void run_state_machine_loop(); // hardware config int axis_num_; uint16_t default_step_gpio_pin_; uint16_t default_dir_gpio_pin_; osPriority thread_priority_; Config_t config_; Encoder& encoder_; AcimEstimator acim_estimator_; SensorlessEstimator& sensorless_estimator_; Controller& controller_; OpenLoopController open_loop_controller_; Motor& motor_; TrapezoidalTrajectory& trap_traj_; Endstop& min_endstop_; Endstop& max_endstop_; MechanicalBrake& mechanical_brake_; TaskTimes task_times_; osThreadId thread_id_ = 0; const uint32_t stack_size_ = 2048; // Bytes volatile bool thread_id_valid_ = false; // variables exposed on protocol Error error_ = ERROR_NONE; bool step_dir_active_ = false; // auto enabled after calibration, based on config.enable_step_dir int64_t steps_ = 0; // Steps counted at interface uint32_t last_drv_fault_ = 0; // updated from config in constructor, and on protocol hook Stm32Gpio step_gpio_; Stm32Gpio dir_gpio_; AxisState requested_state_ = AXIS_STATE_STARTUP_SEQUENCE; std::array task_chain_ = { AXIS_STATE_UNDEFINED }; AxisState& current_state_ = task_chain_.front(); Homing_t homing_; CAN_t can_; // watchdog uint32_t watchdog_current_value_= 0; }; #endif /* __AXIS_HPP */