Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 1 | #ifndef MOTORS_MOTOR_H_ |
| 2 | #define MOTORS_MOTOR_H_ |
| 3 | |
| 4 | #include <limits.h> |
| 5 | |
| 6 | #include <array> |
| 7 | |
| 8 | #include "motors/algorithms.h" |
| 9 | #include "motors/core/kinetis.h" |
Brian Silverman | 4787a6e | 2018-10-06 16:00:54 -0700 | [diff] [blame] | 10 | #include "motors/core/time.h" |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 11 | #include "motors/peripheral/adc.h" |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 12 | #include "motors/peripheral/configuration.h" |
Brian Silverman | 4787a6e | 2018-10-06 16:00:54 -0700 | [diff] [blame] | 13 | #include "motors/print/print.h" |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 14 | #include "motors/util.h" |
| 15 | |
Stephan Pleines | d99b1ee | 2024-02-02 20:56:44 -0800 | [diff] [blame] | 16 | namespace frc971::motors { |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 17 | |
| 18 | class MotorControls { |
| 19 | public: |
| 20 | MotorControls() = default; |
| 21 | virtual ~MotorControls() = default; |
| 22 | |
| 23 | MotorControls(const MotorControls &) = delete; |
| 24 | void operator=(const MotorControls &) = delete; |
| 25 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 26 | virtual void Reset() = 0; |
| 27 | |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 28 | // Scales a current reading from ADC units to amps. |
| 29 | // |
| 30 | // Note that this doesn't apply any offset. The common offset will be |
| 31 | // automatically removed as part of the balancing process. |
| 32 | virtual float scale_current_reading(float reading) const = 0; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 33 | |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 34 | virtual int mechanical_counts_per_revolution() const = 0; |
| 35 | virtual int electrical_counts_per_revolution() const = 0; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 36 | |
| 37 | // raw_currents are in amps for each phase. |
| 38 | // theta is in electrical counts, which will be less than |
| 39 | // counts_per_revolution(). |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 40 | virtual ::std::array<float, 3> DoIteration(const float raw_currents[3], |
| 41 | uint32_t theta, |
| 42 | const float command_current) = 0; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 43 | |
| 44 | virtual int16_t Debug(uint32_t theta) = 0; |
| 45 | |
| 46 | virtual float estimated_velocity() const = 0; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 47 | virtual int16_t i_goal(size_t ii) const = 0; |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 48 | virtual float overall_measured_current() const = 0; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 49 | }; |
| 50 | |
| 51 | // Controls a single motor. |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 52 | class Motor final { |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 53 | public: |
| 54 | // pwm_ftm is used to drive the PWM outputs. |
| 55 | // encoder_ftm is used for reading the encoder. |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 56 | Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls, |
| 57 | const ::std::array<volatile uint32_t *, 3> &output_registers); |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 58 | |
| 59 | Motor(const Motor &) = delete; |
| 60 | void operator=(const Motor &) = delete; |
| 61 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 62 | void Reset() { controls_->Reset(); } |
| 63 | |
Brian Silverman | 4787a6e | 2018-10-06 16:00:54 -0700 | [diff] [blame] | 64 | void set_printing_implementation( |
| 65 | PrintingImplementation *printing_implementation) { |
| 66 | printing_implementation_ = printing_implementation; |
| 67 | } |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 68 | void set_deadtime_compensation(int deadtime_compensation) { |
| 69 | deadtime_compensation_ = deadtime_compensation; |
| 70 | } |
| 71 | void set_switching_divisor(int switching_divisor) { |
| 72 | switching_divisor_ = switching_divisor; |
| 73 | } |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 74 | void set_encoder_offset(int32_t encoder_offset) { |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 75 | encoder_offset_ = encoder_offset; |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 76 | last_wrapped_encoder_reading_ = wrapped_encoder(); |
| 77 | } |
| 78 | int32_t encoder_offset() const { return encoder_offset_; } |
| 79 | |
| 80 | void set_encoder_calibration_offset(int encoder_offset) { |
| 81 | encoder_calibration_offset_ = encoder_offset; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 82 | // Add mechanical_counts_per_revolution to the offset so that when we mod |
| 83 | // below, we are guaranteed to be > 0 regardless of the encoder multiplier. |
| 84 | // % isn't well-defined with negative numbers. |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 85 | while (encoder_calibration_offset_ < |
| 86 | controls_->mechanical_counts_per_revolution()) { |
| 87 | encoder_calibration_offset_ += |
| 88 | controls_->mechanical_counts_per_revolution(); |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 89 | } |
| 90 | } |
| 91 | void set_encoder_multiplier(int encoder_multiplier) { |
| 92 | encoder_multiplier_ = encoder_multiplier; |
| 93 | } |
| 94 | |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 95 | int32_t absolute_encoder(uint32_t wrapped_encoder_reading) { |
| 96 | const uint32_t counts_per_revolution = |
| 97 | controls_->mechanical_counts_per_revolution(); |
| 98 | const uint32_t wrap_down = counts_per_revolution / 4; |
| 99 | const uint32_t wrap_up = wrap_down * 3; |
| 100 | if (last_wrapped_encoder_reading_ > wrap_up && |
| 101 | wrapped_encoder_reading < wrap_down) { |
| 102 | encoder_offset_ += counts_per_revolution; |
| 103 | } else if (last_wrapped_encoder_reading_ < wrap_down && |
| 104 | wrapped_encoder_reading > wrap_up) { |
| 105 | encoder_offset_ -= counts_per_revolution; |
| 106 | } |
| 107 | |
| 108 | last_wrapped_encoder_reading_ = wrapped_encoder_reading; |
| 109 | |
| 110 | return static_cast<int32_t>(wrapped_encoder_reading) + encoder_offset_; |
| 111 | } |
| 112 | |
Philipp Schrader | 790cb54 | 2023-07-05 21:06:52 -0700 | [diff] [blame] | 113 | int encoder() { return encoder_multiplier_ * encoder_ftm_->CNT; } |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 114 | uint32_t wrapped_encoder() { |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 115 | return (encoder() + encoder_calibration_offset_) % |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 116 | controls_->mechanical_counts_per_revolution(); |
| 117 | } |
| 118 | |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 119 | // Sets up everything but doesn't actually start the timers. |
| 120 | // |
| 121 | // This assumes the global time base configuration happens outside so the |
| 122 | // timers for both motors (if applicable) are synced up. |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 123 | void Init(); |
| 124 | |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 125 | // Starts the timers. |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 126 | // |
| 127 | // If the global time base is in use, it must be activated after this. |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 128 | void Start(); |
| 129 | |
Austin Schuh | 54c8c84 | 2019-04-07 13:54:23 -0700 | [diff] [blame] | 130 | void CurrentInterrupt(const BalancedReadings &readings, |
| 131 | uint32_t captured_wrapped_encoder); |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 132 | |
Austin Schuh | 6bf8275 | 2019-04-07 13:55:01 -0700 | [diff] [blame] | 133 | // Runs each phase at a fixed duty cycle. |
Austin Schuh | efe7293 | 2019-04-13 00:03:19 -0700 | [diff] [blame] | 134 | void CycleFixedPhaseInterupt(int period = 80); |
Austin Schuh | 6bf8275 | 2019-04-07 13:55:01 -0700 | [diff] [blame] | 135 | |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 136 | void SetGoalCurrent(float goal_current) { |
| 137 | DisableInterrupts disable_interrupts; |
| 138 | goal_current_ = goal_current; |
| 139 | last_current_set_time_ = micros(); |
| 140 | } |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 141 | |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 142 | inline int counts_per_cycle() const { |
| 143 | return BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY / switching_divisor_; |
| 144 | } |
| 145 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 146 | inline uint16_t get_switching_points_cycles(size_t ii) const { |
| 147 | return static_cast<uint16_t>(switching_points_ratio_[ii] * |
| 148 | counts_per_cycle()); |
| 149 | } |
| 150 | |
| 151 | inline float estimated_velocity() const { |
| 152 | return controls_->estimated_velocity(); |
| 153 | } |
| 154 | |
Philipp Schrader | 790cb54 | 2023-07-05 21:06:52 -0700 | [diff] [blame] | 155 | inline int16_t i_goal(size_t ii) const { return controls_->i_goal(ii); } |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 156 | |
Philipp Schrader | 790cb54 | 2023-07-05 21:06:52 -0700 | [diff] [blame] | 157 | inline int16_t goal_current() const { return goal_current_; } |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 158 | |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 159 | inline float overall_measured_current() const { |
| 160 | return controls_->overall_measured_current(); |
| 161 | } |
| 162 | |
Austin Schuh | 5b0e6b6 | 2019-04-07 14:23:37 -0700 | [diff] [blame] | 163 | ::std::array<volatile uint32_t *, 3> output_registers() const { |
| 164 | return output_registers_; |
| 165 | } |
| 166 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 167 | private: |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 168 | uint32_t CalculateOnTime(uint32_t width) const; |
| 169 | uint32_t CalculateOffTime(uint32_t width) const; |
| 170 | |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 171 | bool flip_time_offset_ = false; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 172 | int deadtime_compensation_ = 0; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 173 | |
| 174 | BigFTM *const pwm_ftm_; |
| 175 | LittleFTM *const encoder_ftm_; |
| 176 | MotorControls *const controls_; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 177 | const ::std::array<volatile uint32_t *, 3> output_registers_; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 178 | ::std::array<float, 3> switching_points_ratio_; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 179 | |
| 180 | float goal_current_ = 0; |
| 181 | uint32_t last_current_set_time_ = 0; |
| 182 | int switching_divisor_ = 1; |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 183 | int encoder_calibration_offset_ = 0; |
| 184 | int32_t encoder_offset_ = 0; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 185 | int encoder_multiplier_ = 1; |
Brian Silverman | 6260c09 | 2018-01-14 15:21:36 -0800 | [diff] [blame] | 186 | uint32_t last_wrapped_encoder_reading_ = 0; |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 187 | |
Brian Silverman | 4787a6e | 2018-10-06 16:00:54 -0700 | [diff] [blame] | 188 | PrintingImplementation *printing_implementation_ = nullptr; |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 189 | }; |
| 190 | |
Stephan Pleines | d99b1ee | 2024-02-02 20:56:44 -0800 | [diff] [blame] | 191 | } // namespace frc971::motors |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 192 | |
| 193 | #endif // MOTORS_MOTOR_H_ |