blob: a5eac240d151f60759745319618c8b6e185588d1 [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"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080010#include "frc971/wpilib/ahal/AnalogInput.h"
11#include "frc971/wpilib/ahal/Counter.h"
12#include "frc971/wpilib/ahal/DigitalSource.h"
13#include "frc971/wpilib/ahal/Encoder.h"
Brian Silvermanb5b46ca2016-03-13 01:14:17 -050014#include "frc971/wpilib/dma.h"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080015#include "frc971/wpilib/dma_edge_counting.h"
Brian Silverman4da58072015-01-26 20:18:52 -050016
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080017namespace frc971::wpilib {
Brian Silverman4da58072015-01-26 20:18:52 -050018
Brian Silverman7cce2d32017-02-19 21:48:48 -080019// Latches values from an encoder on positive edges from another input using
20// DMA.
21class DMAEncoder : public DMASampleHandlerInterface {
Brian Silverman4da58072015-01-26 20:18:52 -050022 public:
Brian Silverman7cce2d32017-02-19 21:48:48 -080023 DMAEncoder() {}
Brian Silverman4da58072015-01-26 20:18:52 -050024
Parker Schuhd3b7a8872018-02-19 16:42:27 -080025 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
Brian Silverman4da58072015-01-26 20:18:52 -050026 encoder_ = ::std::move(encoder);
27 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080028 frc::Encoder *encoder() const { return encoder_.get(); }
Brian Silverman4da58072015-01-26 20:18:52 -050029
Parker Schuhd3b7a8872018-02-19 16:42:27 -080030 void set_index(::std::unique_ptr<frc::DigitalSource> index) {
Brian Silverman4da58072015-01-26 20:18:52 -050031 index_ = ::std::move(index);
32 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080033 frc::DigitalSource *index() const { return index_.get(); }
Brian Silverman4da58072015-01-26 20:18:52 -050034
Brian Silverman4da58072015-01-26 20:18:52 -050035 // Returns the most recent polled value of the encoder.
36 uint32_t polled_encoder_value() const { return polled_encoder_value_; }
Brian Silverman4da58072015-01-26 20:18:52 -050037
38 // Returns the number of poseges that have happened on the index input.
39 uint32_t index_posedge_count() const { return index_posedge_count_; }
40 // Returns the value of the encoder at the last index posedge.
41 int32_t last_encoder_value() const { return last_encoder_value_; }
Brian Silverman7cce2d32017-02-19 21:48:48 -080042
43 void UpdateFromSample(const DMASample &sample) override {
44 DoUpdateFromSample(sample);
Brian Silverman4da58072015-01-26 20:18:52 -050045 }
46
Brian Silverman7cce2d32017-02-19 21:48:48 -080047 void PollFromSample(const DMASample &sample) override {
Brian Silverman4da58072015-01-26 20:18:52 -050048 polled_encoder_value_ = sample.GetRaw(encoder_.get());
Brian Silverman4da58072015-01-26 20:18:52 -050049 }
50
Brian Silverman7cce2d32017-02-19 21:48:48 -080051 void UpdatePolledValue() override {
Brian Silverman4da58072015-01-26 20:18:52 -050052 polled_encoder_value_ = encoder_->GetRaw();
Brian Silverman4da58072015-01-26 20:18:52 -050053 }
54
Brian Silverman7cce2d32017-02-19 21:48:48 -080055 void AddToDMA(DMA *dma) override {
Brian Silverman4da58072015-01-26 20:18:52 -050056 dma->Add(encoder_.get());
57 dma->Add(index_.get());
Austin Schuhc6cc4102015-02-15 23:19:53 -080058 dma->SetExternalTrigger(index_.get(), true, true);
Brian Silverman4da58072015-01-26 20:18:52 -050059 }
60
Brian Silverman7cce2d32017-02-19 21:48:48 -080061 protected:
62 // The same as UpdateFromSample except also returns true if this sample is a
63 // new edge on the index.
64 bool DoUpdateFromSample(const DMASample &sample);
65
Brian Silverman4da58072015-01-26 20:18:52 -050066 private:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080067 ::std::unique_ptr<frc::Encoder> encoder_;
68 ::std::unique_ptr<frc::DigitalSource> index_;
Brian Silverman4da58072015-01-26 20:18:52 -050069
70 int32_t polled_encoder_value_ = 0;
Brian Silverman4da58072015-01-26 20:18:52 -050071
72 int32_t last_encoder_value_ = 0;
Brian Silverman4da58072015-01-26 20:18:52 -050073
74 uint32_t index_posedge_count_ = 0;
75
76 // Whether or not it was triggered in the last sample.
77 bool index_last_value_ = false;
78
Brian Silverman7cce2d32017-02-19 21:48:48 -080079 DISALLOW_COPY_AND_ASSIGN(DMAEncoder);
80};
81
82// Latches values from an encoder and potentiometer on positive edges from
83// another input using DMA.
84class DMAEncoderAndPotentiometer : public DMAEncoder {
85 public:
86 DMAEncoderAndPotentiometer() {}
87
Parker Schuhd3b7a8872018-02-19 16:42:27 -080088 void set_potentiometer(::std::unique_ptr<frc::AnalogInput> potentiometer) {
Brian Silverman7cce2d32017-02-19 21:48:48 -080089 potentiometer_ = ::std::move(potentiometer);
90 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -080091 frc::AnalogInput *potentiometer() const { return potentiometer_.get(); }
Brian Silverman7cce2d32017-02-19 21:48:48 -080092
93 // Returns the most recent polled voltage of the potentiometer.
94 float polled_potentiometer_voltage() const {
95 return polled_potentiometer_voltage_;
96 }
97
98 // Returns the voltage of the potentiometer at the last index posedge.
99 float last_potentiometer_voltage() const {
100 return last_potentiometer_voltage_;
101 }
102
103 void UpdateFromSample(const DMASample &sample) override {
104 if (DMAEncoder::DoUpdateFromSample(sample)) {
105 last_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
106 }
107 }
108
109 void PollFromSample(const DMASample &sample) override {
110 polled_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
111 DMAEncoder::PollFromSample(sample);
112 }
113
114 void UpdatePolledValue() override {
115 polled_potentiometer_voltage_ = potentiometer_->GetVoltage();
116 DMAEncoder::UpdatePolledValue();
117 }
118
119 void AddToDMA(DMA *dma) override {
120 dma->Add(potentiometer_.get());
121 DMAEncoder::AddToDMA(dma);
122 }
123
124 private:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800125 ::std::unique_ptr<frc::AnalogInput> potentiometer_;
Brian Silverman7cce2d32017-02-19 21:48:48 -0800126
127 float polled_potentiometer_voltage_ = 0.0f;
128
129 float last_potentiometer_voltage_ = 0.0f;
130
Brian Silverman4da58072015-01-26 20:18:52 -0500131 DISALLOW_COPY_AND_ASSIGN(DMAEncoderAndPotentiometer);
132};
133
Austin Schuh2a3e0632018-02-19 16:24:49 -0800134// Class to read duty cycle of an input. This is tuned for the CTRE encoder's
135// absolute position output.
136class DutyCycleReader {
137 public:
138 // Configure the reader to use the provided digital input.
139 void set_input(::std::unique_ptr<::frc::DigitalInput> input) {
140 high_counter_.reset(new ::frc::Counter(input.get()));
141 high_counter_->SetMaxPeriod(kMaxPeriod);
142 high_counter_->SetSemiPeriodMode(true);
143
144 period_length_counter_.reset(new ::frc::Counter(input.get()));
145 period_length_counter_->SetMaxPeriod(kMaxPeriod);
146 period_length_counter_->SetUpSourceEdge(true, false);
147
148 input_ = ::std::move(input);
149 }
150
151 // Returns the last duty cycle or nan if the signal is stale.
152 double Read() const {
153 const double high_time = high_counter_->GetPeriod();
154 const double period_length = period_length_counter_->GetPeriod();
155 if (!::std::isfinite(high_time) || !::std::isfinite(period_length)) {
156 return ::std::numeric_limits<double>::quiet_NaN();
157 }
158 return high_time / period_length;
159 }
160
161 private:
162 static constexpr ::std::chrono::nanoseconds kNominalPeriod =
163 ::std::chrono::microseconds(4096);
164 static constexpr double kMaxPeriod =
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700165 ::aos::time::DurationInSeconds(kNominalPeriod * 2);
Austin Schuh2a3e0632018-02-19 16:24:49 -0800166
167 ::std::unique_ptr<::frc::Counter> high_counter_, period_length_counter_;
168 ::std::unique_ptr<::frc::DigitalInput> input_;
169};
170
171// Class to hold a CTRE encoder with absolute angle pwm and potentiometer pair.
172class AbsoluteEncoderAndPotentiometer {
173 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800174 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800175 duty_cycle_.set_input(::std::move(input));
176 }
177
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800178 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800179 encoder_ = ::std::move(encoder);
180 }
181
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800182 void set_potentiometer(::std::unique_ptr<frc::AnalogInput> potentiometer) {
Austin Schuh2a3e0632018-02-19 16:24:49 -0800183 potentiometer_ = ::std::move(potentiometer);
184 }
185
186 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
187 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
188 double ReadPotentiometerVoltage() const {
189 return potentiometer_->GetVoltage();
190 }
191
192 private:
193 DutyCycleReader duty_cycle_;
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800194 ::std::unique_ptr<frc::Encoder> encoder_;
195 ::std::unique_ptr<frc::AnalogInput> potentiometer_;
Austin Schuh2a3e0632018-02-19 16:24:49 -0800196};
197
Ravago Jones937587c2020-12-26 17:21:09 -0800198// Class to hold two CTRE encoders, one with both index pulses and absolute
199// angle pwm, and another that can only turn once and only reports absolute
200// angle pwm.
201class AbsoluteAndAbsoluteEncoder {
202 public:
203 void set_single_turn_absolute_pwm(
204 ::std::unique_ptr<frc::DigitalInput> input) {
205 single_turn_duty_cycle_.set_input(::std::move(input));
206 }
207
208 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
209 duty_cycle_.set_input(::std::move(input));
210 }
211
212 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
213 encoder_ = ::std::move(encoder);
214 }
215
216 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
217 double ReadSingleTurnAbsoluteEncoder() const {
218 return single_turn_duty_cycle_.Read();
219 }
220 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
221
222 private:
223 DutyCycleReader duty_cycle_;
224 DutyCycleReader single_turn_duty_cycle_;
225 ::std::unique_ptr<frc::Encoder> encoder_;
226};
227
Sabina Davis8d8ac0a2019-02-06 23:51:07 -0800228class AbsoluteEncoder {
229 public:
230 void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
231 duty_cycle_.set_input(::std::move(input));
232 }
233
234 void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
235 encoder_ = ::std::move(encoder);
236 }
237
238 double ReadAbsoluteEncoder() const { return duty_cycle_.Read(); }
239 int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
240
241 private:
242 DutyCycleReader duty_cycle_;
243 ::std::unique_ptr<frc::Encoder> encoder_;
244};
245
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800246} // namespace frc971::wpilib
Brian Silverman4da58072015-01-26 20:18:52 -0500247
248#endif // FRC971_ENCODER_AND_POTENTIOMETER_H_