blob: f657d13f76a7e8909870dfe37a82a0a618400b02 [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"
10#include "motors/peripheral/adc.h"
Brian Silverman19ea60f2018-01-03 21:43:15 -080011#include "motors/peripheral/configuration.h"
Brian Silverman8d3816a2017-07-03 18:52:15 -070012#include "motors/util.h"
Brian Silverman19ea60f2018-01-03 21:43:15 -080013#include "motors/usb/cdc.h"
14#include "motors/core/time.h"
Brian Silverman8d3816a2017-07-03 18:52:15 -070015
16namespace frc971 {
Brian Silvermana96c1a42018-05-12 12:11:31 -070017namespace motors {
Brian Silverman8d3816a2017-07-03 18:52:15 -070018
19class MotorControls {
20 public:
21 MotorControls() = default;
22 virtual ~MotorControls() = default;
23
24 MotorControls(const MotorControls &) = delete;
25 void operator=(const MotorControls &) = delete;
26
Brian Silverman19ea60f2018-01-03 21:43:15 -080027 // Scales a current reading from ADC units to amps.
28 //
29 // Note that this doesn't apply any offset. The common offset will be
30 // automatically removed as part of the balancing process.
31 virtual float scale_current_reading(float reading) const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070032
Brian Silverman19ea60f2018-01-03 21:43:15 -080033 virtual int mechanical_counts_per_revolution() const = 0;
34 virtual int electrical_counts_per_revolution() const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070035
36 // raw_currents are in amps for each phase.
37 // theta is in electrical counts, which will be less than
38 // counts_per_revolution().
39 virtual ::std::array<uint32_t, 3> DoIteration(
40 const float raw_currents[3], uint32_t theta,
41 const float command_current) = 0;
42
43 virtual int16_t Debug(uint32_t theta) = 0;
44
45 virtual float estimated_velocity() const = 0;
46};
47
48// Controls a single motor.
Brian Silverman19ea60f2018-01-03 21:43:15 -080049class Motor final {
Brian Silverman8d3816a2017-07-03 18:52:15 -070050 public:
51 // pwm_ftm is used to drive the PWM outputs.
52 // encoder_ftm is used for reading the encoder.
Brian Silverman19ea60f2018-01-03 21:43:15 -080053 Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls,
54 const ::std::array<volatile uint32_t *, 3> &output_registers);
Brian Silverman8d3816a2017-07-03 18:52:15 -070055
56 Motor(const Motor &) = delete;
57 void operator=(const Motor &) = delete;
58
Brian Silverman19ea60f2018-01-03 21:43:15 -080059 void set_debug_tty(teensy::AcmTty *debug_tty) { debug_tty_ = debug_tty; }
60 void set_deadtime_compensation(int deadtime_compensation) {
61 deadtime_compensation_ = deadtime_compensation;
62 }
63 void set_switching_divisor(int switching_divisor) {
64 switching_divisor_ = switching_divisor;
65 }
Brian Silverman6260c092018-01-14 15:21:36 -080066 void set_encoder_offset(int32_t encoder_offset) {
Brian Silverman19ea60f2018-01-03 21:43:15 -080067 encoder_offset_ = encoder_offset;
Brian Silverman6260c092018-01-14 15:21:36 -080068 last_wrapped_encoder_reading_ = wrapped_encoder();
69 }
70 int32_t encoder_offset() const { return encoder_offset_; }
71
72 void set_encoder_calibration_offset(int encoder_offset) {
73 encoder_calibration_offset_ = encoder_offset;
Brian Silverman19ea60f2018-01-03 21:43:15 -080074 // Add mechanical_counts_per_revolution to the offset so that when we mod
75 // below, we are guaranteed to be > 0 regardless of the encoder multiplier.
76 // % isn't well-defined with negative numbers.
Brian Silverman6260c092018-01-14 15:21:36 -080077 while (encoder_calibration_offset_ <
78 controls_->mechanical_counts_per_revolution()) {
79 encoder_calibration_offset_ +=
80 controls_->mechanical_counts_per_revolution();
Brian Silverman19ea60f2018-01-03 21:43:15 -080081 }
82 }
83 void set_encoder_multiplier(int encoder_multiplier) {
84 encoder_multiplier_ = encoder_multiplier;
85 }
86
Brian Silverman6260c092018-01-14 15:21:36 -080087 int32_t absolute_encoder(uint32_t wrapped_encoder_reading) {
88 const uint32_t counts_per_revolution =
89 controls_->mechanical_counts_per_revolution();
90 const uint32_t wrap_down = counts_per_revolution / 4;
91 const uint32_t wrap_up = wrap_down * 3;
92 if (last_wrapped_encoder_reading_ > wrap_up &&
93 wrapped_encoder_reading < wrap_down) {
94 encoder_offset_ += counts_per_revolution;
95 } else if (last_wrapped_encoder_reading_ < wrap_down &&
96 wrapped_encoder_reading > wrap_up) {
97 encoder_offset_ -= counts_per_revolution;
98 }
99
100 last_wrapped_encoder_reading_ = wrapped_encoder_reading;
101
102 return static_cast<int32_t>(wrapped_encoder_reading) + encoder_offset_;
103 }
104
Brian Silverman19ea60f2018-01-03 21:43:15 -0800105 int encoder() {
106 return encoder_multiplier_ * encoder_ftm_->CNT;
107 }
108 uint32_t wrapped_encoder() {
Brian Silverman6260c092018-01-14 15:21:36 -0800109 return (encoder() + encoder_calibration_offset_) %
Brian Silverman19ea60f2018-01-03 21:43:15 -0800110 controls_->mechanical_counts_per_revolution();
111 }
112
Brian Silverman8d3816a2017-07-03 18:52:15 -0700113 // Sets up everything but doesn't actually start the timers.
114 //
115 // This assumes the global time base configuration happens outside so the
116 // timers for both motors (if applicable) are synced up.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700117 void Init();
118
Brian Silverman8d3816a2017-07-03 18:52:15 -0700119 // Starts the timers.
Brian Silverman19ea60f2018-01-03 21:43:15 -0800120 //
121 // If the global time base is in use, it must be activated after this.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700122 void Start();
123
Brian Silverman19ea60f2018-01-03 21:43:15 -0800124 void HandleInterrupt(const BalancedReadings &readings,
125 uint32_t captured_wrapped_encoder);
126
127 void SetGoalCurrent(float goal_current) {
128 DisableInterrupts disable_interrupts;
129 goal_current_ = goal_current;
130 last_current_set_time_ = micros();
131 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700132
133 private:
134 // Represents the ADC reading which is closest to an edge.
135 struct CloseAdcReading {
136 // Adds a new reading to the readings to balance or pushes the previous
137 // closest one there and saves off this one.
138 //
139 // Returns true if it saves off the new reading.
140 bool MaybeUse(int new_distance, const MediumAdcReadings &adc_readings,
141 int phase, int sample, ReadingsToBalance *to_balance) {
142 if (new_distance < distance) {
143 if (distance != INT_MAX) {
144 to_balance->Add(index, value);
145 }
146 distance = new_distance;
147 value = adc_readings.motor_currents[phase][sample];
148 index = phase;
149 return true;
150 }
151 return false;
152 }
153
154 int distance = INT_MAX;
155 int32_t value = 0;
156 int index = 0;
157 };
158
Brian Silverman19ea60f2018-01-03 21:43:15 -0800159 inline int counts_per_cycle() const {
160 return BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY / switching_divisor_;
161 }
162
163 uint32_t CalculateOnTime(uint32_t width) const;
164 uint32_t CalculateOffTime(uint32_t width) const;
165
Brian Silverman8d3816a2017-07-03 18:52:15 -0700166 bool flip_time_offset_ = false;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800167 int deadtime_compensation_ = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700168
169 BigFTM *const pwm_ftm_;
170 LittleFTM *const encoder_ftm_;
171 MotorControls *const controls_;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800172 const ::std::array<volatile uint32_t *, 3> output_registers_;
173
174 float goal_current_ = 0;
175 uint32_t last_current_set_time_ = 0;
176 int switching_divisor_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800177 int encoder_calibration_offset_ = 0;
178 int32_t encoder_offset_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800179 int encoder_multiplier_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800180 uint32_t last_wrapped_encoder_reading_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800181
182 teensy::AcmTty *debug_tty_ = nullptr;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700183};
184
Brian Silvermana96c1a42018-05-12 12:11:31 -0700185} // namespace motors
Brian Silverman8d3816a2017-07-03 18:52:15 -0700186} // namespace frc971
187
188#endif // MOTORS_MOTOR_H_