blob: 20f60fc66b9b9f03841c402c494cd8789cdcb967 [file] [log] [blame]
Brian Silverman8d3816a2017-07-03 18:52:15 -07001#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 Silverman4787a6e2018-10-06 16:00:54 -070010#include "motors/core/time.h"
Brian Silverman8d3816a2017-07-03 18:52:15 -070011#include "motors/peripheral/adc.h"
Brian Silverman19ea60f2018-01-03 21:43:15 -080012#include "motors/peripheral/configuration.h"
Brian Silverman4787a6e2018-10-06 16:00:54 -070013#include "motors/print/print.h"
Brian Silverman8d3816a2017-07-03 18:52:15 -070014#include "motors/util.h"
15
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080016namespace frc971::motors {
Brian Silverman8d3816a2017-07-03 18:52:15 -070017
18class MotorControls {
19 public:
20 MotorControls() = default;
21 virtual ~MotorControls() = default;
22
23 MotorControls(const MotorControls &) = delete;
24 void operator=(const MotorControls &) = delete;
25
James Kuszmaul998d3032018-09-08 15:41:41 -070026 virtual void Reset() = 0;
27
Brian Silverman19ea60f2018-01-03 21:43:15 -080028 // 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 Silverman8d3816a2017-07-03 18:52:15 -070033
Brian Silverman19ea60f2018-01-03 21:43:15 -080034 virtual int mechanical_counts_per_revolution() const = 0;
35 virtual int electrical_counts_per_revolution() const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070036
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 Kuszmaul998d3032018-09-08 15:41:41 -070040 virtual ::std::array<float, 3> DoIteration(const float raw_currents[3],
41 uint32_t theta,
42 const float command_current) = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070043
44 virtual int16_t Debug(uint32_t theta) = 0;
45
46 virtual float estimated_velocity() const = 0;
James Kuszmaul998d3032018-09-08 15:41:41 -070047 virtual int16_t i_goal(size_t ii) const = 0;
James Kuszmaul521eb652018-10-17 19:09:33 -070048 virtual float overall_measured_current() const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070049};
50
51// Controls a single motor.
Brian Silverman19ea60f2018-01-03 21:43:15 -080052class Motor final {
Brian Silverman8d3816a2017-07-03 18:52:15 -070053 public:
54 // pwm_ftm is used to drive the PWM outputs.
55 // encoder_ftm is used for reading the encoder.
Brian Silverman19ea60f2018-01-03 21:43:15 -080056 Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls,
57 const ::std::array<volatile uint32_t *, 3> &output_registers);
Brian Silverman8d3816a2017-07-03 18:52:15 -070058
59 Motor(const Motor &) = delete;
60 void operator=(const Motor &) = delete;
61
James Kuszmaul998d3032018-09-08 15:41:41 -070062 void Reset() { controls_->Reset(); }
63
Brian Silverman4787a6e2018-10-06 16:00:54 -070064 void set_printing_implementation(
65 PrintingImplementation *printing_implementation) {
66 printing_implementation_ = printing_implementation;
67 }
Brian Silverman19ea60f2018-01-03 21:43:15 -080068 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 Silverman6260c092018-01-14 15:21:36 -080074 void set_encoder_offset(int32_t encoder_offset) {
Brian Silverman19ea60f2018-01-03 21:43:15 -080075 encoder_offset_ = encoder_offset;
Brian Silverman6260c092018-01-14 15:21:36 -080076 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 Silverman19ea60f2018-01-03 21:43:15 -080082 // 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 Silverman6260c092018-01-14 15:21:36 -080085 while (encoder_calibration_offset_ <
86 controls_->mechanical_counts_per_revolution()) {
87 encoder_calibration_offset_ +=
88 controls_->mechanical_counts_per_revolution();
Brian Silverman19ea60f2018-01-03 21:43:15 -080089 }
90 }
91 void set_encoder_multiplier(int encoder_multiplier) {
92 encoder_multiplier_ = encoder_multiplier;
93 }
94
Brian Silverman6260c092018-01-14 15:21:36 -080095 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 Schrader790cb542023-07-05 21:06:52 -0700113 int encoder() { return encoder_multiplier_ * encoder_ftm_->CNT; }
Brian Silverman19ea60f2018-01-03 21:43:15 -0800114 uint32_t wrapped_encoder() {
Brian Silverman6260c092018-01-14 15:21:36 -0800115 return (encoder() + encoder_calibration_offset_) %
Brian Silverman19ea60f2018-01-03 21:43:15 -0800116 controls_->mechanical_counts_per_revolution();
117 }
118
Brian Silverman8d3816a2017-07-03 18:52:15 -0700119 // 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 Silverman8d3816a2017-07-03 18:52:15 -0700123 void Init();
124
Brian Silverman8d3816a2017-07-03 18:52:15 -0700125 // Starts the timers.
Brian Silverman19ea60f2018-01-03 21:43:15 -0800126 //
127 // If the global time base is in use, it must be activated after this.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700128 void Start();
129
Austin Schuh54c8c842019-04-07 13:54:23 -0700130 void CurrentInterrupt(const BalancedReadings &readings,
131 uint32_t captured_wrapped_encoder);
Brian Silverman19ea60f2018-01-03 21:43:15 -0800132
Austin Schuh6bf82752019-04-07 13:55:01 -0700133 // Runs each phase at a fixed duty cycle.
Austin Schuhefe72932019-04-13 00:03:19 -0700134 void CycleFixedPhaseInterupt(int period = 80);
Austin Schuh6bf82752019-04-07 13:55:01 -0700135
Brian Silverman19ea60f2018-01-03 21:43:15 -0800136 void SetGoalCurrent(float goal_current) {
137 DisableInterrupts disable_interrupts;
138 goal_current_ = goal_current;
139 last_current_set_time_ = micros();
140 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700141
Brian Silverman19ea60f2018-01-03 21:43:15 -0800142 inline int counts_per_cycle() const {
143 return BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY / switching_divisor_;
144 }
145
James Kuszmaul998d3032018-09-08 15:41:41 -0700146 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 Schrader790cb542023-07-05 21:06:52 -0700155 inline int16_t i_goal(size_t ii) const { return controls_->i_goal(ii); }
James Kuszmaul998d3032018-09-08 15:41:41 -0700156
Philipp Schrader790cb542023-07-05 21:06:52 -0700157 inline int16_t goal_current() const { return goal_current_; }
James Kuszmaul998d3032018-09-08 15:41:41 -0700158
James Kuszmaul521eb652018-10-17 19:09:33 -0700159 inline float overall_measured_current() const {
160 return controls_->overall_measured_current();
161 }
162
Austin Schuh5b0e6b62019-04-07 14:23:37 -0700163 ::std::array<volatile uint32_t *, 3> output_registers() const {
164 return output_registers_;
165 }
166
James Kuszmaul998d3032018-09-08 15:41:41 -0700167 private:
Brian Silverman19ea60f2018-01-03 21:43:15 -0800168 uint32_t CalculateOnTime(uint32_t width) const;
169 uint32_t CalculateOffTime(uint32_t width) const;
170
Brian Silverman8d3816a2017-07-03 18:52:15 -0700171 bool flip_time_offset_ = false;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800172 int deadtime_compensation_ = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700173
174 BigFTM *const pwm_ftm_;
175 LittleFTM *const encoder_ftm_;
176 MotorControls *const controls_;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800177 const ::std::array<volatile uint32_t *, 3> output_registers_;
James Kuszmaul998d3032018-09-08 15:41:41 -0700178 ::std::array<float, 3> switching_points_ratio_;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800179
180 float goal_current_ = 0;
181 uint32_t last_current_set_time_ = 0;
182 int switching_divisor_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800183 int encoder_calibration_offset_ = 0;
184 int32_t encoder_offset_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800185 int encoder_multiplier_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800186 uint32_t last_wrapped_encoder_reading_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800187
Brian Silverman4787a6e2018-10-06 16:00:54 -0700188 PrintingImplementation *printing_implementation_ = nullptr;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700189};
190
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800191} // namespace frc971::motors
Brian Silverman8d3816a2017-07-03 18:52:15 -0700192
193#endif // MOTORS_MOTOR_H_