blob: 50896204933637953cc161e457afa71bee786abe [file] [log] [blame]
Brian Silverman4da58072015-01-26 20:18:52 -05001#ifndef FRC971_ENCODER_AND_POTENTIOMETER_H_
2#define FRC971_ENCODER_AND_POTENTIOMETER_H_
3
4#include <atomic>
Parker Schuhd3b7a8872018-02-19 16:42:27 -08005#include <cmath>
Brian Silverman4da58072015-01-26 20:18:52 -05006#include <thread>
7
John Park33858a32018-09-28 23:05:48 -07008#include "aos/macros.h"
James Kuszmaul651fc3f2019-05-15 21:14:25 -07009#include "aos/time/time.h"
Brian Silverman4da58072015-01-26 20:18:52 -050010
Parker Schuhd3b7a8872018-02-19 16:42:27 -080011#include "frc971/wpilib/ahal/AnalogInput.h"
12#include "frc971/wpilib/ahal/Counter.h"
13#include "frc971/wpilib/ahal/DigitalSource.h"
14#include "frc971/wpilib/ahal/Encoder.h"
Brian Silverman4da58072015-01-26 20:18:52 -050015
Brian Silvermanb5b46ca2016-03-13 01:14:17 -050016#include "frc971/wpilib/dma.h"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080017#include "frc971/wpilib/dma_edge_counting.h"
Brian Silverman4da58072015-01-26 20:18:52 -050018
19namespace frc971 {
20namespace wpilib {
21
Brian Silverman7cce2d32017-02-19 21:48:48 -080022// Latches values from an encoder on positive edges from another input using
23// DMA.
24class DMAEncoder : public DMASampleHandlerInterface {
Brian Silverman4da58072015-01-26 20:18:52 -050025 public:
Brian Silverman7cce2d32017-02-19 21:48:48 -080026 DMAEncoder() {}
Brian Silverman4da58072015-01-26 20:18:52 -050027
Parker Schuhd3b7a8872018-02-19 16:42:27 -080028 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
Brian Silverman4da58072015-01-26 20:18:52 -050029 encoder_ = ::std::move(encoder);
30 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080031 frc::Encoder *encoder() const { return encoder_.get(); }
Brian Silverman4da58072015-01-26 20:18:52 -050032
Parker Schuhd3b7a8872018-02-19 16:42:27 -080033 void set_index(::std::unique_ptr<frc::DigitalSource> index) {
Brian Silverman4da58072015-01-26 20:18:52 -050034 index_ = ::std::move(index);
35 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080036 frc::DigitalSource *index() const { return index_.get(); }
Brian Silverman4da58072015-01-26 20:18:52 -050037
Brian Silverman4da58072015-01-26 20:18:52 -050038 // Returns the most recent polled value of the encoder.
39 uint32_t polled_encoder_value() const { return polled_encoder_value_; }
Brian Silverman4da58072015-01-26 20:18:52 -050040
41 // Returns the number of poseges that have happened on the index input.
42 uint32_t index_posedge_count() const { return index_posedge_count_; }
43 // Returns the value of the encoder at the last index posedge.
44 int32_t last_encoder_value() const { return last_encoder_value_; }
Brian Silverman7cce2d32017-02-19 21:48:48 -080045
46 void UpdateFromSample(const DMASample &sample) override {
47 DoUpdateFromSample(sample);
Brian Silverman4da58072015-01-26 20:18:52 -050048 }
49
Brian Silverman7cce2d32017-02-19 21:48:48 -080050 void PollFromSample(const DMASample &sample) override {
Brian Silverman4da58072015-01-26 20:18:52 -050051 polled_encoder_value_ = sample.GetRaw(encoder_.get());
Brian Silverman4da58072015-01-26 20:18:52 -050052 }
53
Brian Silverman7cce2d32017-02-19 21:48:48 -080054 void UpdatePolledValue() override {
Brian Silverman4da58072015-01-26 20:18:52 -050055 polled_encoder_value_ = encoder_->GetRaw();
Brian Silverman4da58072015-01-26 20:18:52 -050056 }
57
Brian Silverman7cce2d32017-02-19 21:48:48 -080058 void AddToDMA(DMA *dma) override {
Brian Silverman4da58072015-01-26 20:18:52 -050059 dma->Add(encoder_.get());
60 dma->Add(index_.get());
Austin Schuhc6cc4102015-02-15 23:19:53 -080061 dma->SetExternalTrigger(index_.get(), true, true);
Brian Silverman4da58072015-01-26 20:18:52 -050062 }
63
Brian Silverman7cce2d32017-02-19 21:48:48 -080064 protected:
65 // The same as UpdateFromSample except also returns true if this sample is a
66 // new edge on the index.
67 bool DoUpdateFromSample(const DMASample &sample);
68
Brian Silverman4da58072015-01-26 20:18:52 -050069 private:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080070 ::std::unique_ptr<frc::Encoder> encoder_;
71 ::std::unique_ptr<frc::DigitalSource> index_;
Brian Silverman4da58072015-01-26 20:18:52 -050072
73 int32_t polled_encoder_value_ = 0;
Brian Silverman4da58072015-01-26 20:18:52 -050074
75 int32_t last_encoder_value_ = 0;
Brian Silverman4da58072015-01-26 20:18:52 -050076
77 uint32_t index_posedge_count_ = 0;
78
79 // Whether or not it was triggered in the last sample.
80 bool index_last_value_ = false;
81
Brian Silverman7cce2d32017-02-19 21:48:48 -080082 DISALLOW_COPY_AND_ASSIGN(DMAEncoder);
83};
84
85// Latches values from an encoder and potentiometer on positive edges from
86// another input using DMA.
87class DMAEncoderAndPotentiometer : public DMAEncoder {
88 public:
89 DMAEncoderAndPotentiometer() {}
90
Parker Schuhd3b7a8872018-02-19 16:42:27 -080091 void set_potentiometer(::std::unique_ptr<frc::AnalogInput> potentiometer) {
Brian Silverman7cce2d32017-02-19 21:48:48 -080092 potentiometer_ = ::std::move(potentiometer);
93 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080094 frc::AnalogInput *potentiometer() const { return potentiometer_.get(); }
Brian Silverman7cce2d32017-02-19 21:48:48 -080095
96 // Returns the most recent polled voltage of the potentiometer.
97 float polled_potentiometer_voltage() const {
98 return polled_potentiometer_voltage_;
99 }
100
101 // Returns the voltage of the potentiometer at the last index posedge.
102 float last_potentiometer_voltage() const {
103 return last_potentiometer_voltage_;
104 }
105
106 void UpdateFromSample(const DMASample &sample) override {
107 if (DMAEncoder::DoUpdateFromSample(sample)) {
108 last_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
109 }
110 }
111
112 void PollFromSample(const DMASample &sample) override {
113 polled_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
114 DMAEncoder::PollFromSample(sample);
115 }
116
117 void UpdatePolledValue() override {
118 polled_potentiometer_voltage_ = potentiometer_->GetVoltage();
119 DMAEncoder::UpdatePolledValue();
120 }
121
122 void AddToDMA(DMA *dma) override {
123 dma->Add(potentiometer_.get());
124 DMAEncoder::AddToDMA(dma);
125 }
126
127 private:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800128 ::std::unique_ptr<frc::AnalogInput> potentiometer_;
Brian Silverman7cce2d32017-02-19 21:48:48 -0800129
130 float polled_potentiometer_voltage_ = 0.0f;
131
132 float last_potentiometer_voltage_ = 0.0f;
133
Brian Silverman4da58072015-01-26 20:18:52 -0500134 DISALLOW_COPY_AND_ASSIGN(DMAEncoderAndPotentiometer);
135};
136
Austin Schuh2a3e0632018-02-19 16:24:49 -0800137// Class to read duty cycle of an input. This is tuned for the CTRE encoder's
138// absolute position output.
139class DutyCycleReader {
140 public:
141 // Configure the reader to use the provided digital input.
142 void set_input(::std::unique_ptr<::frc::DigitalInput> input) {
143 high_counter_.reset(new ::frc::Counter(input.get()));
144 high_counter_->SetMaxPeriod(kMaxPeriod);
145 high_counter_->SetSemiPeriodMode(true);
146
147 period_length_counter_.reset(new ::frc::Counter(input.get()));
148 period_length_counter_->SetMaxPeriod(kMaxPeriod);
149 period_length_counter_->SetUpSourceEdge(true, false);
150
151 input_ = ::std::move(input);
152 }
153
154 // Returns the last duty cycle or nan if the signal is stale.
155 double Read() const {
156 const double high_time = high_counter_->GetPeriod();
157 const double period_length = period_length_counter_->GetPeriod();
158 if (!::std::isfinite(high_time) || !::std::isfinite(period_length)) {
159 return ::std::numeric_limits<double>::quiet_NaN();
160 }
161 return high_time / period_length;
162 }
163
164 private:
165 static constexpr ::std::chrono::nanoseconds kNominalPeriod =
166 ::std::chrono::microseconds(4096);
167 static constexpr double kMaxPeriod =
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700168 ::aos::time::DurationInSeconds(kNominalPeriod * 2);
Austin Schuh2a3e0632018-02-19 16:24:49 -0800169
170 ::std::unique_ptr<::frc::Counter> high_counter_, period_length_counter_;
171 ::std::unique_ptr<::frc::DigitalInput> input_;
172};
173
174// Class to hold a CTRE encoder with absolute angle pwm and potentiometer pair.
175class AbsoluteEncoderAndPotentiometer {
176 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800177 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800178 duty_cycle_.set_input(::std::move(input));
179 }
180
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800181 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800182 encoder_ = ::std::move(encoder);
183 }
184
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800185 void set_potentiometer(::std::unique_ptr<frc::AnalogInput> potentiometer) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800186 potentiometer_ = ::std::move(potentiometer);
187 }
188
189 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
190 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
191 double ReadPotentiometerVoltage() const {
192 return potentiometer_->GetVoltage();
193 }
194
195 private:
196 DutyCycleReader duty_cycle_;
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800197 ::std::unique_ptr<frc::Encoder> encoder_;
198 ::std::unique_ptr<frc::AnalogInput> potentiometer_;
Austin Schuh2a3e0632018-02-19 16:24:49 -0800199};
200
Ravago Jones937587c2020-12-26 17:21:09 -0800201// Class to hold two CTRE encoders, one with both index pulses and absolute
202// angle pwm, and another that can only turn once and only reports absolute
203// angle pwm.
204class AbsoluteAndAbsoluteEncoder {
205 public:
206 void set_single_turn_absolute_pwm(
207 ::std::unique_ptr<frc::DigitalInput> input) {
208 single_turn_duty_cycle_.set_input(::std::move(input));
209 }
210
211 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
212 duty_cycle_.set_input(::std::move(input));
213 }
214
215 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
216 encoder_ = ::std::move(encoder);
217 }
218
219 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
220 double ReadSingleTurnAbsoluteEncoder() const {
221 return single_turn_duty_cycle_.Read();
222 }
223 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
224
225 private:
226 DutyCycleReader duty_cycle_;
227 DutyCycleReader single_turn_duty_cycle_;
228 ::std::unique_ptr<frc::Encoder> encoder_;
229};
230
Sabina Davis8d8ac0a2019-02-06 23:51:07 -0800231class AbsoluteEncoder {
232 public:
233 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
234 duty_cycle_.set_input(::std::move(input));
235 }
236
237 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
238 encoder_ = ::std::move(encoder);
239 }
240
241 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
242 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
243
244 private:
245 DutyCycleReader duty_cycle_;
246 ::std::unique_ptr<frc::Encoder> encoder_;
247};
248
Brian Silverman4da58072015-01-26 20:18:52 -0500249} // namespace wpilib
250} // namespace frc971
251
252#endif // FRC971_ENCODER_AND_POTENTIOMETER_H_