blob: d8fe47620e1456a2595a8870de20ef352c71a802 [file] [log] [blame]
Brian Silvermanff7b3472015-01-26 17:53:04 -05001#ifndef FRC971_WPILIB_DMA_EDGE_COUNTING_H_
2#define FRC971_WPILIB_DMA_EDGE_COUNTING_H_
3
4#include <memory>
Ravago Jones128a50e2021-09-18 15:19:39 -07005#include <optional>
Brian Silvermanff7b3472015-01-26 17:53:04 -05006#include <vector>
7
John Park33858a32018-09-28 23:05:48 -07008#include "aos/macros.h"
Brian Silvermanff7b3472015-01-26 17:53:04 -05009
Brian Silvermanb5b46ca2016-03-13 01:14:17 -050010#include "frc971/wpilib/dma.h"
11
Parker Schuhd3b7a8872018-02-19 16:42:27 -080012#include "frc971/wpilib/ahal/AnalogInput.h"
13#include "frc971/wpilib/ahal/DigitalInput.h"
14#include "frc971/wpilib/ahal/Encoder.h"
15#include "frc971/wpilib/ahal/Utility.h"
Austin Schuh5c25ab72015-11-01 13:17:11 -080016#undef ERROR
Brian Silvermanff7b3472015-01-26 17:53:04 -050017
18namespace frc971 {
19namespace wpilib {
20
21// Generic interface for classes that do something with DMA samples and also
22// poll current sensor values.
23class DMASampleHandlerInterface {
24 public:
25 virtual ~DMASampleHandlerInterface() {}
26
27 // Updates values based on a new DMA sample.
28 virtual void UpdateFromSample(const DMASample &sample) = 0;
29
30 // Polls the current values and saves them for later reference.
31 virtual void UpdatePolledValue() = 0;
32
33 // Fills in the "polled" values from sample.
34 // This is only called when a DMA event happens right as we're polling values.
35 virtual void PollFromSample(const DMASample &sample) = 0;
36
37 // Adds readings and triggers appropriate to this object to dma.
38 virtual void AddToDMA(DMA *dma) = 0;
39};
40
Austin Schuh8e5950d2018-03-21 20:29:40 -070041// TODO(brian): Timeout old data.
42class DMAPulseWidthReader : public DMASampleHandlerInterface {
43 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080044 DMAPulseWidthReader(frc::DigitalInput *input) : input_(input) {}
Austin Schuh8e5950d2018-03-21 20:29:40 -070045 DMAPulseWidthReader() = default;
46
Parker Schuhd3b7a8872018-02-19 16:42:27 -080047 void set_input(frc::DigitalInput *input) { input_ = input; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070048
49 double last_width() const { return last_width_; }
50
51 private:
52 void UpdateFromSample(const DMASample & /*sample*/) override;
53 void UpdatePolledValue() override {}
54
55 void PollFromSample(const DMASample & /*sample*/) override {}
56 void AddToDMA(DMA *dma) override {
57 dma->Add(input_);
58 dma->SetExternalTrigger(input_, true, true);
59 }
60
Parker Schuhd3b7a8872018-02-19 16:42:27 -080061 frc::DigitalInput *input_ = nullptr;
Austin Schuh8e5950d2018-03-21 20:29:40 -070062
63 // The last DMA reading we got.
64 DMASample prev_sample_;
65 // Whether or not we actually have anything in prev_sample_.
66 bool have_prev_sample_ = false;
67
68 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
69
70 DISALLOW_COPY_AND_ASSIGN(DMAPulseWidthReader);
71};
72
Ravago Jones128a50e2021-09-18 15:19:39 -070073// Takes two digital inputs and times the difference between the first one going
Austin Schuh6c053ef2021-09-26 14:32:16 -070074// low and the second one going low.
Ravago Jones128a50e2021-09-18 15:19:39 -070075class DMAPulseSeparationReader : public DMASampleHandlerInterface {
76 public:
77 DMAPulseSeparationReader(frc::DigitalInput *input_one,
78 frc::DigitalInput *input_two)
79 : input_one_(input_one), input_two_(input_two) {}
80 DMAPulseSeparationReader() = default;
81
82 void set_input_one(frc::DigitalInput *input) { input_one_ = input; }
83 void set_input_two(frc::DigitalInput *input) { input_two_ = input; }
84
85 double last_width() const { return last_width_; }
86 double pulses_detected() const { return pulses_detected_; }
87
88 private:
89 void UpdateFromSample(const DMASample & /*sample*/) override;
90 void UpdatePolledValue() override {}
91
92 void PollFromSample(const DMASample & /*sample*/) override {}
93 void AddToDMA(DMA *dma) override {
94 dma->Add(input_one_);
95 dma->SetExternalTrigger(input_one_, true, true);
96 dma->Add(input_two_);
Austin Schuh6c053ef2021-09-26 14:32:16 -070097 dma->SetExternalTrigger(input_two_, false, true);
Ravago Jones128a50e2021-09-18 15:19:39 -070098 }
99
100 static constexpr double kSampleTimeoutSeconds = 0.1;
101
102 frc::DigitalInput *input_one_ = nullptr;
103 frc::DigitalInput *input_two_ = nullptr;
104
105 // The last DMA reading we got.
106 DMASample prev_sample_;
107 // Whether or not we actually have anything in prev_sample_.
108 bool have_prev_sample_ = false;
109
Austin Schuh6c053ef2021-09-26 14:32:16 -0700110 // the time when the input one went low.
Ravago Jones128a50e2021-09-18 15:19:39 -0700111 std::optional<double> input_one_time_;
112
113 int pulses_detected_ = 0;
114
115 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
116
117 DISALLOW_COPY_AND_ASSIGN(DMAPulseSeparationReader);
118};
119
Brian Silvermanff7b3472015-01-26 17:53:04 -0500120// Counts edges on a sensor using DMA data and latches encoder values
121// corresponding to those edges.
122class DMAEdgeCounter : public DMASampleHandlerInterface {
123 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800124 DMAEdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman03a00cf2015-08-29 19:26:54 -0700125 : encoder_(encoder), input_(input) {}
Brianef030df2017-03-05 15:06:04 -0800126 DMAEdgeCounter() = default;
127
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800128 void set_encoder(frc::Encoder *encoder) { encoder_ = encoder; }
129 void set_input(frc::DigitalInput *input) { input_ = input; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500130
Brian Silverman552350b2015-08-02 18:23:34 -0700131 int positive_count() const { return pos_edge_count_; }
132 int negative_count() const { return neg_edge_count_; }
133 int last_positive_encoder_value() const { return pos_last_encoder_; }
134 int last_negative_encoder_value() const { return neg_last_encoder_; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500135
136 // Returns the value of the sensor in the last-read DMA sample.
Brian Silverman552350b2015-08-02 18:23:34 -0700137 bool last_value() const { return ExtractValue(prev_sample_); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500138 // Returns the most recent polled value of the sensor.
139 bool polled_value() const { return polled_value_; }
140 // Returns the most recent polled reading from the encoder.
141 int polled_encoder() const { return polled_encoder_; }
142
143 private:
Brian Silverman552350b2015-08-02 18:23:34 -0700144 void UpdateFromSample(const DMASample &sample) override;
145 void UpdatePolledValue() override {
Austin Schuhf83da152017-03-05 22:28:45 -0800146 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700147 polled_value_ = input_->Get();
148 polled_encoder_ = encoder_->GetRaw();
149 }
150 void PollFromSample(const DMASample &sample) override {
Austin Schuhf83da152017-03-05 22:28:45 -0800151 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700152 polled_value_ = ExtractValue(sample);
153 polled_encoder_ = sample.GetRaw(encoder_);
154 }
155 void AddToDMA(DMA *dma) override {
156 dma->Add(encoder_);
157 dma->Add(input_);
Brian Silverman03a00cf2015-08-29 19:26:54 -0700158 dma->SetExternalTrigger(input_, true, true);
Brian Silverman552350b2015-08-02 18:23:34 -0700159 }
160
161 bool ExtractValue(const DMASample &sample) const;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500162
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800163 frc::Encoder *encoder_ = nullptr;
164 frc::DigitalInput *input_ = nullptr;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500165
166 // The last DMA reading we got.
167 DMASample prev_sample_;
168 // Whether or not we actually have anything in prev_sample_.
169 bool have_prev_sample_ = false;
170
171 // Values related to the positive edge.
172 int pos_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500173 int pos_last_encoder_ = 0;
174
175 // Values related to the negative edge.
176 int neg_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500177 int neg_last_encoder_ = 0;
178
179 bool polled_value_ = false;
Austin Schuhf83da152017-03-05 22:28:45 -0800180 bool previous_polled_value_ = false;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500181 int polled_encoder_ = 0;
182
183 DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
184};
185
Brian Silverman552350b2015-08-02 18:23:34 -0700186// Reads a hall effect in sync with DMA samples.
187class DMADigitalReader : public DMASampleHandlerInterface {
188 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800189 DMADigitalReader(frc::DigitalInput *input) : input_(input) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700190
191 bool value() const { return value_; }
192
193 private:
194 void UpdateFromSample(const DMASample & /*sample*/) override {}
195 void UpdatePolledValue() override { value_ = input_->Get(); }
196 void PollFromSample(const DMASample &sample) override {
Brian Silverman03a00cf2015-08-29 19:26:54 -0700197 value_ = sample.Get(input_);
Brian Silverman552350b2015-08-02 18:23:34 -0700198 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800199 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Brian Silverman552350b2015-08-02 18:23:34 -0700200
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800201 frc::DigitalInput *const input_;
Brian Silverman552350b2015-08-02 18:23:34 -0700202
203 bool value_;
204
205 DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
206};
207
Austin Schuh89f1e092015-11-22 22:30:39 -0800208// Reads an analog sensor in sync with DMA samples.
209class DMAAnalogReader : public DMASampleHandlerInterface {
210 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800211 DMAAnalogReader(frc::AnalogInput *input) : input_(input) {}
Austin Schuh89f1e092015-11-22 22:30:39 -0800212
213 double value() const { return value_; }
214
215 private:
216 void UpdateFromSample(const DMASample & /*sample*/) override {}
217 void UpdatePolledValue() override { value_ = input_->GetVoltage(); }
218 void PollFromSample(const DMASample &sample) override {
219 value_ = sample.GetVoltage(input_);
220 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800221 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Austin Schuh89f1e092015-11-22 22:30:39 -0800222
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800223 frc::AnalogInput *const input_;
Austin Schuh89f1e092015-11-22 22:30:39 -0800224
225 double value_;
226
227 DISALLOW_COPY_AND_ASSIGN(DMAAnalogReader);
228};
229
Brian Silvermanff7b3472015-01-26 17:53:04 -0500230// This class handles updating the sampled data on multiple
231// DMASampleHandlerInterfaces. The caller should create an instance and then
232// periodically call RunIteration, retrieving whatever data from the
233// DMASampleHandlerInterfaces after each iteration.
234class DMASynchronizer {
235 public:
236 DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
237
238 // Adds a new handler to this object. This method must not be called after
239 // Start().
240 void Add(DMASampleHandlerInterface *handler) {
241 handler->AddToDMA(dma_.get());
242 handlers_.emplace_back(handler);
243 }
244
245 // Actually starts watching for DMA samples.
246 // Add may not be called any more after this.
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800247 void Start() { dma_->Start(1024); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500248
249 // Updates all sensor values.
250 void RunIteration() {
251 SampleSensors();
252 CheckDMA();
253 }
254
255 private:
256 // Reads the state of all the sensors and records it as the polled values of
257 // all the inputs.
258 void SampleSensors() {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800259 sample_time_ = frc::GetFPGATime();
Brian Silvermanff7b3472015-01-26 17:53:04 -0500260 for (auto &c : handlers_) {
261 c->UpdatePolledValue();
262 }
263 }
264
265 // Gets called by the DMA handler to update edge counts.
266 void CheckDMA();
267
268 const ::std::unique_ptr<DMA> dma_;
269 ::std::vector<DMASampleHandlerInterface *> handlers_;
270
271 // The time at which we most recently read the sensor values.
Austin Schuhf853bde2017-11-23 13:23:27 -0800272 int64_t sample_time_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500273
274 DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
275};
276
277} // namespace wpilib
278} // namespace frc971
279
280#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_