#ifndef __ENCODER_HPP #define __ENCODER_HPP class Encoder; #include // needed for arm_math.h #include #include "utils.hpp" #include #include "component.hpp" class Encoder : public ODriveIntf::EncoderIntf { public: static constexpr uint32_t MODE_FLAG_ABS = 0x100; static constexpr std::array hall_edge_defaults = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f}; struct Config_t { Mode mode = MODE_INCREMENTAL; float calib_range = 0.02f; // Accuracy required to pass encoder cpr check float calib_scan_distance = 16.0f * M_PI; // rad electrical float calib_scan_omega = 4.0f * M_PI; // rad/s electrical float bandwidth = 1000.0f; int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase float phase_offset_float = 0.0f; // Sub-count phase alignment offset int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, float index_offset = 0.0f; bool use_index = false; bool pre_calibrated = false; // If true, this means the offset stored in // configuration is valid and does not need // be determined by run_offset_calibration. // In this case the encoder will enter ready // state as soon as the index is found. int32_t direction = 0; // direction with respect to motor bool use_index_offset = true; bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 uint8_t hall_polarity = 0; bool hall_polarity_calibrated = false; std::array hall_edge_phcnt = hall_edge_defaults; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; // custom setters Encoder* parent = nullptr; void set_use_index(bool value) { use_index = value; parent->set_idx_subscribe(); } void set_find_idx_on_lockin_only(bool value) { find_idx_on_lockin_only = value; parent->set_idx_subscribe(); } void set_abs_spi_cs_gpio_pin(uint16_t value) { abs_spi_cs_gpio_pin = value; parent->abs_spi_cs_pin_init(); } void set_pre_calibrated(bool value) { pre_calibrated = value; parent->check_pre_calibrated(); } void set_bandwidth(float value) { bandwidth = value; parent->update_pll_gains(); } }; Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio, Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio, Stm32SpiArbiter* spi_arbiter); bool apply_config(ODriveIntf::MotorIntf::MotorType motor_type); void setup(); void set_error(Error error); bool do_checks(); void enc_index_cb(); void set_idx_subscribe(bool override_enable = false); void update_pll_gains(); void check_pre_calibrated(); void set_linear_count(int32_t count); void set_circular_count(int32_t count, bool update_offset); bool calib_enc_offset(float voltage_magnitude); bool run_index_search(); bool run_direction_find(); bool run_hall_polarity_calibration(); bool run_hall_phase_calibration(); bool run_offset_calibration(); void sample_now(); bool read_sampled_gpio(Stm32Gpio gpio); void decode_hall_samples(); int32_t hall_model(float internal_pos); bool update(); TIM_HandleTypeDef* timer_; Stm32Gpio index_gpio_; Stm32Gpio hallA_gpio_; Stm32Gpio hallB_gpio_; Stm32Gpio hallC_gpio_; Stm32SpiArbiter* spi_arbiter_; Axis* axis_ = nullptr; // set by Axis constructor Config_t config_; Error error_ = ERROR_NONE; bool index_found_ = false; bool is_ready_ = false; int32_t shadow_count_ = 0; int32_t count_in_cpr_ = 0; float interpolation_ = 0.0f; OutputPort phase_ = 0.0f; // [rad] OutputPort phase_vel_ = 0.0f; // [rad/s] float pos_estimate_counts_ = 0.0f; // [count] float pos_cpr_counts_ = 0.0f; // [count] float delta_pos_cpr_counts_ = 0.0f; // [count] phase detector result for debug float vel_estimate_counts_ = 0.0f; // [count/s] float pll_kp_ = 0.0f; // [count/s / count] float pll_ki_ = 0.0f; // [(count/s^2) / count] float calib_scan_response_ = 0.0f; // debug report from offset calib int32_t pos_abs_ = 0; float spi_error_rate_ = 0.0f; OutputPort pos_estimate_ = 0.0f; // [turn] OutputPort vel_estimate_ = 0.0f; // [turn/s] OutputPort pos_circular_ = 0.0f; // [turn] bool pos_estimate_valid_ = false; bool vel_estimate_valid_ = false; int16_t tim_cnt_sample_ = 0; // static const constexpr GPIO_TypeDef* ports_to_sample[] = { GPIOA, GPIOB, GPIOC }; uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])]; // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC std::optional last_hall_cnt_ = std::nullopt; // Used to find hall edges for calibration bool calibrate_hall_phase_ = false; bool sample_hall_states_ = false; bool sample_hall_phase_ = false; std::array states_seen_count_; // for hall polarity calibration std::array hall_phase_calib_seen_count_; float sincos_sample_s_ = 0.0f; float sincos_sample_c_ = 0.0f; bool abs_spi_start_transaction(); void abs_spi_cb(bool success); void abs_spi_cs_pin_init(); bool abs_spi_pos_updated_ = false; Mode mode_ = MODE_INCREMENTAL; Stm32Gpio abs_spi_cs_gpio_; uint32_t abs_spi_cr1; uint32_t abs_spi_cr2; uint16_t abs_spi_dma_tx_[1] = {0xFFFF}; uint16_t abs_spi_dma_rx_[1]; Stm32SpiArbiter::SpiTask spi_task_; constexpr float getCoggingRatio(){ return 1.0f / 3600.0f; } }; #endif // __ENCODER_HPP