blob: 4c5e604e0783908cd25cea0631648d3d9085c357 [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
James Kuszmaul998d3032018-09-08 15:41:41 -070027 virtual void Reset() = 0;
28
Brian Silverman19ea60f2018-01-03 21:43:15 -080029 // Scales a current reading from ADC units to amps.
30 //
31 // Note that this doesn't apply any offset. The common offset will be
32 // automatically removed as part of the balancing process.
33 virtual float scale_current_reading(float reading) const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070034
Brian Silverman19ea60f2018-01-03 21:43:15 -080035 virtual int mechanical_counts_per_revolution() const = 0;
36 virtual int electrical_counts_per_revolution() const = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070037
38 // raw_currents are in amps for each phase.
39 // theta is in electrical counts, which will be less than
40 // counts_per_revolution().
James Kuszmaul998d3032018-09-08 15:41:41 -070041 virtual ::std::array<float, 3> DoIteration(const float raw_currents[3],
42 uint32_t theta,
43 const float command_current) = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -070044
45 virtual int16_t Debug(uint32_t theta) = 0;
46
47 virtual float estimated_velocity() const = 0;
James Kuszmaul998d3032018-09-08 15:41:41 -070048 virtual int16_t i_goal(size_t ii) 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 Silverman19ea60f2018-01-03 21:43:15 -080064 void set_debug_tty(teensy::AcmTty *debug_tty) { debug_tty_ = debug_tty; }
65 void set_deadtime_compensation(int deadtime_compensation) {
66 deadtime_compensation_ = deadtime_compensation;
67 }
68 void set_switching_divisor(int switching_divisor) {
69 switching_divisor_ = switching_divisor;
70 }
Brian Silverman6260c092018-01-14 15:21:36 -080071 void set_encoder_offset(int32_t encoder_offset) {
Brian Silverman19ea60f2018-01-03 21:43:15 -080072 encoder_offset_ = encoder_offset;
Brian Silverman6260c092018-01-14 15:21:36 -080073 last_wrapped_encoder_reading_ = wrapped_encoder();
74 }
75 int32_t encoder_offset() const { return encoder_offset_; }
76
77 void set_encoder_calibration_offset(int encoder_offset) {
78 encoder_calibration_offset_ = encoder_offset;
Brian Silverman19ea60f2018-01-03 21:43:15 -080079 // Add mechanical_counts_per_revolution to the offset so that when we mod
80 // below, we are guaranteed to be > 0 regardless of the encoder multiplier.
81 // % isn't well-defined with negative numbers.
Brian Silverman6260c092018-01-14 15:21:36 -080082 while (encoder_calibration_offset_ <
83 controls_->mechanical_counts_per_revolution()) {
84 encoder_calibration_offset_ +=
85 controls_->mechanical_counts_per_revolution();
Brian Silverman19ea60f2018-01-03 21:43:15 -080086 }
87 }
88 void set_encoder_multiplier(int encoder_multiplier) {
89 encoder_multiplier_ = encoder_multiplier;
90 }
91
Brian Silverman6260c092018-01-14 15:21:36 -080092 int32_t absolute_encoder(uint32_t wrapped_encoder_reading) {
93 const uint32_t counts_per_revolution =
94 controls_->mechanical_counts_per_revolution();
95 const uint32_t wrap_down = counts_per_revolution / 4;
96 const uint32_t wrap_up = wrap_down * 3;
97 if (last_wrapped_encoder_reading_ > wrap_up &&
98 wrapped_encoder_reading < wrap_down) {
99 encoder_offset_ += counts_per_revolution;
100 } else if (last_wrapped_encoder_reading_ < wrap_down &&
101 wrapped_encoder_reading > wrap_up) {
102 encoder_offset_ -= counts_per_revolution;
103 }
104
105 last_wrapped_encoder_reading_ = wrapped_encoder_reading;
106
107 return static_cast<int32_t>(wrapped_encoder_reading) + encoder_offset_;
108 }
109
Brian Silverman19ea60f2018-01-03 21:43:15 -0800110 int encoder() {
111 return encoder_multiplier_ * encoder_ftm_->CNT;
112 }
113 uint32_t wrapped_encoder() {
Brian Silverman6260c092018-01-14 15:21:36 -0800114 return (encoder() + encoder_calibration_offset_) %
Brian Silverman19ea60f2018-01-03 21:43:15 -0800115 controls_->mechanical_counts_per_revolution();
116 }
117
Brian Silverman8d3816a2017-07-03 18:52:15 -0700118 // Sets up everything but doesn't actually start the timers.
119 //
120 // This assumes the global time base configuration happens outside so the
121 // timers for both motors (if applicable) are synced up.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700122 void Init();
123
Brian Silverman8d3816a2017-07-03 18:52:15 -0700124 // Starts the timers.
Brian Silverman19ea60f2018-01-03 21:43:15 -0800125 //
126 // If the global time base is in use, it must be activated after this.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700127 void Start();
128
Brian Silverman19ea60f2018-01-03 21:43:15 -0800129 void HandleInterrupt(const BalancedReadings &readings,
130 uint32_t captured_wrapped_encoder);
131
132 void SetGoalCurrent(float goal_current) {
133 DisableInterrupts disable_interrupts;
134 goal_current_ = goal_current;
135 last_current_set_time_ = micros();
136 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700137
Brian Silverman19ea60f2018-01-03 21:43:15 -0800138 inline int counts_per_cycle() const {
139 return BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY / switching_divisor_;
140 }
141
James Kuszmaul998d3032018-09-08 15:41:41 -0700142 inline uint16_t get_switching_points_cycles(size_t ii) const {
143 return static_cast<uint16_t>(switching_points_ratio_[ii] *
144 counts_per_cycle());
145 }
146
147 inline float estimated_velocity() const {
148 return controls_->estimated_velocity();
149 }
150
151 inline int16_t i_goal(size_t ii) const {
152 return controls_->i_goal(ii);
153 }
154
155 inline int16_t goal_current() const {
156 return goal_current_;
157 }
158
159 private:
Brian Silverman19ea60f2018-01-03 21:43:15 -0800160 uint32_t CalculateOnTime(uint32_t width) const;
161 uint32_t CalculateOffTime(uint32_t width) const;
162
Brian Silverman8d3816a2017-07-03 18:52:15 -0700163 bool flip_time_offset_ = false;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800164 int deadtime_compensation_ = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700165
166 BigFTM *const pwm_ftm_;
167 LittleFTM *const encoder_ftm_;
168 MotorControls *const controls_;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800169 const ::std::array<volatile uint32_t *, 3> output_registers_;
James Kuszmaul998d3032018-09-08 15:41:41 -0700170 ::std::array<float, 3> switching_points_ratio_;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800171
172 float goal_current_ = 0;
173 uint32_t last_current_set_time_ = 0;
174 int switching_divisor_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800175 int encoder_calibration_offset_ = 0;
176 int32_t encoder_offset_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800177 int encoder_multiplier_ = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800178 uint32_t last_wrapped_encoder_reading_ = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800179
180 teensy::AcmTty *debug_tty_ = nullptr;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700181};
182
Brian Silvermana96c1a42018-05-12 12:11:31 -0700183} // namespace motors
Brian Silverman8d3816a2017-07-03 18:52:15 -0700184} // namespace frc971
185
186#endif // MOTORS_MOTOR_H_