blob: 2d49ecb917f06ab4dfbb5618f58426b07f5cc668 [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
Austin Schuhb1aaefe2021-10-12 22:05:44 -070010#include "aos/containers/sized_array.h"
Austin Schuh9950f682021-11-06 15:27:58 -070011#include "frc971/wpilib/ahal/Utility.h"
Brian Silvermanb5b46ca2016-03-13 01:14:17 -050012#include "frc971/wpilib/dma.h"
13
Parker Schuhd3b7a8872018-02-19 16:42:27 -080014#include "frc971/wpilib/ahal/AnalogInput.h"
15#include "frc971/wpilib/ahal/DigitalInput.h"
16#include "frc971/wpilib/ahal/Encoder.h"
Austin Schuh5c25ab72015-11-01 13:17:11 -080017#undef ERROR
Brian Silvermanff7b3472015-01-26 17:53:04 -050018
19namespace frc971 {
20namespace wpilib {
21
22// Generic interface for classes that do something with DMA samples and also
23// poll current sensor values.
24class DMASampleHandlerInterface {
25 public:
26 virtual ~DMASampleHandlerInterface() {}
27
28 // Updates values based on a new DMA sample.
29 virtual void UpdateFromSample(const DMASample &sample) = 0;
30
31 // Polls the current values and saves them for later reference.
32 virtual void UpdatePolledValue() = 0;
33
34 // Fills in the "polled" values from sample.
35 // This is only called when a DMA event happens right as we're polling values.
36 virtual void PollFromSample(const DMASample &sample) = 0;
37
38 // Adds readings and triggers appropriate to this object to dma.
39 virtual void AddToDMA(DMA *dma) = 0;
40};
41
Austin Schuh8e5950d2018-03-21 20:29:40 -070042// TODO(brian): Timeout old data.
43class DMAPulseWidthReader : public DMASampleHandlerInterface {
44 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080045 DMAPulseWidthReader(frc::DigitalInput *input) : input_(input) {}
Austin Schuh8e5950d2018-03-21 20:29:40 -070046 DMAPulseWidthReader() = default;
47
Parker Schuhd3b7a8872018-02-19 16:42:27 -080048 void set_input(frc::DigitalInput *input) { input_ = input; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070049
50 double last_width() const { return last_width_; }
51
52 private:
53 void UpdateFromSample(const DMASample & /*sample*/) override;
54 void UpdatePolledValue() override {}
55
56 void PollFromSample(const DMASample & /*sample*/) override {}
57 void AddToDMA(DMA *dma) override {
58 dma->Add(input_);
59 dma->SetExternalTrigger(input_, true, true);
60 }
61
Parker Schuhd3b7a8872018-02-19 16:42:27 -080062 frc::DigitalInput *input_ = nullptr;
Austin Schuh8e5950d2018-03-21 20:29:40 -070063
64 // The last DMA reading we got.
65 DMASample prev_sample_;
66 // Whether or not we actually have anything in prev_sample_.
67 bool have_prev_sample_ = false;
68
69 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
70
71 DISALLOW_COPY_AND_ASSIGN(DMAPulseWidthReader);
72};
73
Ravago Jones128a50e2021-09-18 15:19:39 -070074// Takes two digital inputs and times the difference between the first one going
Austin Schuh6c053ef2021-09-26 14:32:16 -070075// low and the second one going low.
Ravago Jones128a50e2021-09-18 15:19:39 -070076class DMAPulseSeparationReader : public DMASampleHandlerInterface {
77 public:
78 DMAPulseSeparationReader(frc::DigitalInput *input_one,
79 frc::DigitalInput *input_two)
80 : input_one_(input_one), input_two_(input_two) {}
81 DMAPulseSeparationReader() = default;
82
83 void set_input_one(frc::DigitalInput *input) { input_one_ = input; }
84 void set_input_two(frc::DigitalInput *input) { input_two_ = input; }
85
86 double last_width() const { return last_width_; }
87 double pulses_detected() const { return pulses_detected_; }
88
89 private:
90 void UpdateFromSample(const DMASample & /*sample*/) override;
91 void UpdatePolledValue() override {}
92
93 void PollFromSample(const DMASample & /*sample*/) override {}
94 void AddToDMA(DMA *dma) override {
95 dma->Add(input_one_);
96 dma->SetExternalTrigger(input_one_, true, true);
97 dma->Add(input_two_);
Austin Schuh6c053ef2021-09-26 14:32:16 -070098 dma->SetExternalTrigger(input_two_, false, true);
Ravago Jones128a50e2021-09-18 15:19:39 -070099 }
100
101 static constexpr double kSampleTimeoutSeconds = 0.1;
102
103 frc::DigitalInput *input_one_ = nullptr;
104 frc::DigitalInput *input_two_ = nullptr;
105
106 // The last DMA reading we got.
107 DMASample prev_sample_;
108 // Whether or not we actually have anything in prev_sample_.
109 bool have_prev_sample_ = false;
110
Austin Schuh6c053ef2021-09-26 14:32:16 -0700111 // the time when the input one went low.
Ravago Jones128a50e2021-09-18 15:19:39 -0700112 std::optional<double> input_one_time_;
113
114 int pulses_detected_ = 0;
115
116 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
117
118 DISALLOW_COPY_AND_ASSIGN(DMAPulseSeparationReader);
119};
120
Brian Silvermanff7b3472015-01-26 17:53:04 -0500121// Counts edges on a sensor using DMA data and latches encoder values
122// corresponding to those edges.
123class DMAEdgeCounter : public DMASampleHandlerInterface {
124 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800125 DMAEdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman03a00cf2015-08-29 19:26:54 -0700126 : encoder_(encoder), input_(input) {}
Brianef030df2017-03-05 15:06:04 -0800127 DMAEdgeCounter() = default;
128
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800129 void set_encoder(frc::Encoder *encoder) { encoder_ = encoder; }
130 void set_input(frc::DigitalInput *input) { input_ = input; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500131
Brian Silverman552350b2015-08-02 18:23:34 -0700132 int positive_count() const { return pos_edge_count_; }
133 int negative_count() const { return neg_edge_count_; }
134 int last_positive_encoder_value() const { return pos_last_encoder_; }
135 int last_negative_encoder_value() const { return neg_last_encoder_; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500136
137 // Returns the value of the sensor in the last-read DMA sample.
Brian Silverman552350b2015-08-02 18:23:34 -0700138 bool last_value() const { return ExtractValue(prev_sample_); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500139 // Returns the most recent polled value of the sensor.
140 bool polled_value() const { return polled_value_; }
141 // Returns the most recent polled reading from the encoder.
142 int polled_encoder() const { return polled_encoder_; }
143
144 private:
Brian Silverman552350b2015-08-02 18:23:34 -0700145 void UpdateFromSample(const DMASample &sample) override;
146 void UpdatePolledValue() override {
Austin Schuhf83da152017-03-05 22:28:45 -0800147 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700148 polled_value_ = input_->Get();
149 polled_encoder_ = encoder_->GetRaw();
150 }
151 void PollFromSample(const DMASample &sample) override {
Austin Schuhf83da152017-03-05 22:28:45 -0800152 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700153 polled_value_ = ExtractValue(sample);
154 polled_encoder_ = sample.GetRaw(encoder_);
155 }
156 void AddToDMA(DMA *dma) override {
157 dma->Add(encoder_);
158 dma->Add(input_);
Brian Silverman03a00cf2015-08-29 19:26:54 -0700159 dma->SetExternalTrigger(input_, true, true);
Brian Silverman552350b2015-08-02 18:23:34 -0700160 }
161
162 bool ExtractValue(const DMASample &sample) const;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500163
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800164 frc::Encoder *encoder_ = nullptr;
165 frc::DigitalInput *input_ = nullptr;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500166
167 // The last DMA reading we got.
168 DMASample prev_sample_;
169 // Whether or not we actually have anything in prev_sample_.
170 bool have_prev_sample_ = false;
171
172 // Values related to the positive edge.
173 int pos_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500174 int pos_last_encoder_ = 0;
175
176 // Values related to the negative edge.
177 int neg_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500178 int neg_last_encoder_ = 0;
179
180 bool polled_value_ = false;
Austin Schuhf83da152017-03-05 22:28:45 -0800181 bool previous_polled_value_ = false;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500182 int polled_encoder_ = 0;
183
184 DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
185};
186
Brian Silverman552350b2015-08-02 18:23:34 -0700187// Reads a hall effect in sync with DMA samples.
188class DMADigitalReader : public DMASampleHandlerInterface {
189 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800190 DMADigitalReader(frc::DigitalInput *input) : input_(input) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700191
192 bool value() const { return value_; }
193
194 private:
195 void UpdateFromSample(const DMASample & /*sample*/) override {}
196 void UpdatePolledValue() override { value_ = input_->Get(); }
197 void PollFromSample(const DMASample &sample) override {
Brian Silverman03a00cf2015-08-29 19:26:54 -0700198 value_ = sample.Get(input_);
Brian Silverman552350b2015-08-02 18:23:34 -0700199 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800200 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Brian Silverman552350b2015-08-02 18:23:34 -0700201
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800202 frc::DigitalInput *const input_;
Brian Silverman552350b2015-08-02 18:23:34 -0700203
204 bool value_;
205
206 DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
207};
208
Austin Schuh89f1e092015-11-22 22:30:39 -0800209// Reads an analog sensor in sync with DMA samples.
210class DMAAnalogReader : public DMASampleHandlerInterface {
211 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800212 DMAAnalogReader(frc::AnalogInput *input) : input_(input) {}
Austin Schuh89f1e092015-11-22 22:30:39 -0800213
214 double value() const { return value_; }
215
216 private:
217 void UpdateFromSample(const DMASample & /*sample*/) override {}
218 void UpdatePolledValue() override { value_ = input_->GetVoltage(); }
219 void PollFromSample(const DMASample &sample) override {
220 value_ = sample.GetVoltage(input_);
221 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800222 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Austin Schuh89f1e092015-11-22 22:30:39 -0800223
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800224 frc::AnalogInput *const input_;
Austin Schuh89f1e092015-11-22 22:30:39 -0800225
226 double value_;
227
228 DISALLOW_COPY_AND_ASSIGN(DMAAnalogReader);
229};
230
Brian Silvermanff7b3472015-01-26 17:53:04 -0500231// This class handles updating the sampled data on multiple
232// DMASampleHandlerInterfaces. The caller should create an instance and then
233// periodically call RunIteration, retrieving whatever data from the
234// DMASampleHandlerInterfaces after each iteration.
235class DMASynchronizer {
236 public:
237 DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
238
239 // Adds a new handler to this object. This method must not be called after
240 // Start().
241 void Add(DMASampleHandlerInterface *handler) {
242 handler->AddToDMA(dma_.get());
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700243 handlers_.push_back(handler);
Brian Silvermanff7b3472015-01-26 17:53:04 -0500244 }
245
246 // Actually starts watching for DMA samples.
247 // Add may not be called any more after this.
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800248 void Start() { dma_->Start(1024); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500249
250 // Updates all sensor values.
251 void RunIteration() {
252 SampleSensors();
253 CheckDMA();
254 }
255
256 private:
257 // Reads the state of all the sensors and records it as the polled values of
258 // all the inputs.
259 void SampleSensors() {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800260 sample_time_ = frc::GetFPGATime();
Brian Silvermanff7b3472015-01-26 17:53:04 -0500261 for (auto &c : handlers_) {
262 c->UpdatePolledValue();
263 }
264 }
265
266 // Gets called by the DMA handler to update edge counts.
267 void CheckDMA();
268
269 const ::std::unique_ptr<DMA> dma_;
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700270 aos::SizedArray<DMASampleHandlerInterface *, 4> handlers_;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500271
272 // The time at which we most recently read the sensor values.
Austin Schuhf853bde2017-11-23 13:23:27 -0800273 int64_t sample_time_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500274
275 DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
276};
277
278} // namespace wpilib
279} // namespace frc971
280
281#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_