blob: f51318d535a14e59b2516f6a880cc7ec1e2395f9 [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
Austin Schuhb1aaefe2021-10-12 22:05:44 -07008#include "aos/containers/sized_array.h"
milind-u6b672f82023-02-24 17:36:27 -08009#include "aos/macros.h"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080010#include "frc971/wpilib/ahal/AnalogInput.h"
11#include "frc971/wpilib/ahal/DigitalInput.h"
12#include "frc971/wpilib/ahal/Encoder.h"
milind-u6b672f82023-02-24 17:36:27 -080013#include "frc971/wpilib/ahal/Utility.h"
14#include "frc971/wpilib/dma.h"
Austin Schuh5c25ab72015-11-01 13:17:11 -080015#undef ERROR
Brian Silvermanff7b3472015-01-26 17:53:04 -050016
17namespace frc971 {
18namespace wpilib {
19
20// Generic interface for classes that do something with DMA samples and also
21// poll current sensor values.
22class DMASampleHandlerInterface {
23 public:
24 virtual ~DMASampleHandlerInterface() {}
25
26 // Updates values based on a new DMA sample.
27 virtual void UpdateFromSample(const DMASample &sample) = 0;
28
29 // Polls the current values and saves them for later reference.
30 virtual void UpdatePolledValue() = 0;
31
32 // Fills in the "polled" values from sample.
33 // This is only called when a DMA event happens right as we're polling values.
34 virtual void PollFromSample(const DMASample &sample) = 0;
35
36 // Adds readings and triggers appropriate to this object to dma.
37 virtual void AddToDMA(DMA *dma) = 0;
38};
39
Austin Schuh8e5950d2018-03-21 20:29:40 -070040// TODO(brian): Timeout old data.
41class DMAPulseWidthReader : public DMASampleHandlerInterface {
42 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080043 DMAPulseWidthReader(frc::DigitalInput *input) : input_(input) {}
Austin Schuh8e5950d2018-03-21 20:29:40 -070044 DMAPulseWidthReader() = default;
45
Parker Schuhd3b7a8872018-02-19 16:42:27 -080046 void set_input(frc::DigitalInput *input) { input_ = input; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070047
milind-u6b672f82023-02-24 17:36:27 -080048 // Last pulse width in seconds
Austin Schuh8e5950d2018-03-21 20:29:40 -070049 double last_width() const { return last_width_; }
milind-u3a7f9212023-02-24 20:46:59 -080050 double last_period() const { return last_period_; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070051
52 private:
53 void UpdateFromSample(const DMASample & /*sample*/) override;
milind-ua2e612b2023-02-24 18:00:59 -080054 void UpdatePolledValue() override;
Austin Schuh8e5950d2018-03-21 20:29:40 -070055
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;
milind-u6b672f82023-02-24 17:36:27 -080068 // Last time the reading switched to high
69 uint64_t high_time_ = 0;
milind-ua2e612b2023-02-24 18:00:59 -080070 // Number of times we've been polled without an update
71 size_t poll_count_ = 0;
Austin Schuh8e5950d2018-03-21 20:29:40 -070072
73 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
milind-u3a7f9212023-02-24 20:46:59 -080074 double last_period_ = ::std::numeric_limits<double>::quiet_NaN();
Austin Schuh8e5950d2018-03-21 20:29:40 -070075
76 DISALLOW_COPY_AND_ASSIGN(DMAPulseWidthReader);
77};
78
Ravago Jones128a50e2021-09-18 15:19:39 -070079// Takes two digital inputs and times the difference between the first one going
Austin Schuh6c053ef2021-09-26 14:32:16 -070080// low and the second one going low.
Ravago Jones128a50e2021-09-18 15:19:39 -070081class DMAPulseSeparationReader : public DMASampleHandlerInterface {
82 public:
83 DMAPulseSeparationReader(frc::DigitalInput *input_one,
84 frc::DigitalInput *input_two)
85 : input_one_(input_one), input_two_(input_two) {}
86 DMAPulseSeparationReader() = default;
87
88 void set_input_one(frc::DigitalInput *input) { input_one_ = input; }
89 void set_input_two(frc::DigitalInput *input) { input_two_ = input; }
90
91 double last_width() const { return last_width_; }
92 double pulses_detected() const { return pulses_detected_; }
93
94 private:
95 void UpdateFromSample(const DMASample & /*sample*/) override;
96 void UpdatePolledValue() override {}
97
98 void PollFromSample(const DMASample & /*sample*/) override {}
99 void AddToDMA(DMA *dma) override {
100 dma->Add(input_one_);
101 dma->SetExternalTrigger(input_one_, true, true);
102 dma->Add(input_two_);
Austin Schuh6c053ef2021-09-26 14:32:16 -0700103 dma->SetExternalTrigger(input_two_, false, true);
Ravago Jones128a50e2021-09-18 15:19:39 -0700104 }
105
106 static constexpr double kSampleTimeoutSeconds = 0.1;
107
108 frc::DigitalInput *input_one_ = nullptr;
109 frc::DigitalInput *input_two_ = nullptr;
110
111 // The last DMA reading we got.
112 DMASample prev_sample_;
113 // Whether or not we actually have anything in prev_sample_.
114 bool have_prev_sample_ = false;
115
Austin Schuh6c053ef2021-09-26 14:32:16 -0700116 // the time when the input one went low.
Ravago Jones128a50e2021-09-18 15:19:39 -0700117 std::optional<double> input_one_time_;
118
119 int pulses_detected_ = 0;
120
121 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
122
123 DISALLOW_COPY_AND_ASSIGN(DMAPulseSeparationReader);
124};
125
Brian Silvermanff7b3472015-01-26 17:53:04 -0500126// Counts edges on a sensor using DMA data and latches encoder values
127// corresponding to those edges.
128class DMAEdgeCounter : public DMASampleHandlerInterface {
129 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800130 DMAEdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman03a00cf2015-08-29 19:26:54 -0700131 : encoder_(encoder), input_(input) {}
Brianef030df2017-03-05 15:06:04 -0800132 DMAEdgeCounter() = default;
133
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800134 void set_encoder(frc::Encoder *encoder) { encoder_ = encoder; }
135 void set_input(frc::DigitalInput *input) { input_ = input; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500136
Brian Silverman552350b2015-08-02 18:23:34 -0700137 int positive_count() const { return pos_edge_count_; }
138 int negative_count() const { return neg_edge_count_; }
139 int last_positive_encoder_value() const { return pos_last_encoder_; }
140 int last_negative_encoder_value() const { return neg_last_encoder_; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500141
142 // Returns the value of the sensor in the last-read DMA sample.
Brian Silverman552350b2015-08-02 18:23:34 -0700143 bool last_value() const { return ExtractValue(prev_sample_); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500144 // Returns the most recent polled value of the sensor.
145 bool polled_value() const { return polled_value_; }
146 // Returns the most recent polled reading from the encoder.
147 int polled_encoder() const { return polled_encoder_; }
148
149 private:
Brian Silverman552350b2015-08-02 18:23:34 -0700150 void UpdateFromSample(const DMASample &sample) override;
151 void UpdatePolledValue() override {
Austin Schuhf83da152017-03-05 22:28:45 -0800152 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700153 polled_value_ = input_->Get();
154 polled_encoder_ = encoder_->GetRaw();
155 }
156 void PollFromSample(const DMASample &sample) override {
Austin Schuhf83da152017-03-05 22:28:45 -0800157 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700158 polled_value_ = ExtractValue(sample);
159 polled_encoder_ = sample.GetRaw(encoder_);
160 }
161 void AddToDMA(DMA *dma) override {
162 dma->Add(encoder_);
163 dma->Add(input_);
Brian Silverman03a00cf2015-08-29 19:26:54 -0700164 dma->SetExternalTrigger(input_, true, true);
Brian Silverman552350b2015-08-02 18:23:34 -0700165 }
166
167 bool ExtractValue(const DMASample &sample) const;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500168
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800169 frc::Encoder *encoder_ = nullptr;
170 frc::DigitalInput *input_ = nullptr;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500171
172 // The last DMA reading we got.
173 DMASample prev_sample_;
174 // Whether or not we actually have anything in prev_sample_.
175 bool have_prev_sample_ = false;
176
177 // Values related to the positive edge.
178 int pos_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500179 int pos_last_encoder_ = 0;
180
181 // Values related to the negative edge.
182 int neg_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500183 int neg_last_encoder_ = 0;
184
185 bool polled_value_ = false;
Austin Schuhf83da152017-03-05 22:28:45 -0800186 bool previous_polled_value_ = false;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500187 int polled_encoder_ = 0;
188
189 DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
190};
191
Brian Silverman552350b2015-08-02 18:23:34 -0700192// Reads a hall effect in sync with DMA samples.
193class DMADigitalReader : public DMASampleHandlerInterface {
194 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800195 DMADigitalReader(frc::DigitalInput *input) : input_(input) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700196
197 bool value() const { return value_; }
198
199 private:
200 void UpdateFromSample(const DMASample & /*sample*/) override {}
201 void UpdatePolledValue() override { value_ = input_->Get(); }
202 void PollFromSample(const DMASample &sample) override {
Brian Silverman03a00cf2015-08-29 19:26:54 -0700203 value_ = sample.Get(input_);
Brian Silverman552350b2015-08-02 18:23:34 -0700204 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800205 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Brian Silverman552350b2015-08-02 18:23:34 -0700206
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800207 frc::DigitalInput *const input_;
Brian Silverman552350b2015-08-02 18:23:34 -0700208
209 bool value_;
210
211 DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
212};
213
Austin Schuh89f1e092015-11-22 22:30:39 -0800214// Reads an analog sensor in sync with DMA samples.
215class DMAAnalogReader : public DMASampleHandlerInterface {
216 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800217 DMAAnalogReader(frc::AnalogInput *input) : input_(input) {}
Austin Schuh89f1e092015-11-22 22:30:39 -0800218
219 double value() const { return value_; }
220
221 private:
222 void UpdateFromSample(const DMASample & /*sample*/) override {}
223 void UpdatePolledValue() override { value_ = input_->GetVoltage(); }
224 void PollFromSample(const DMASample &sample) override {
225 value_ = sample.GetVoltage(input_);
226 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800227 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Austin Schuh89f1e092015-11-22 22:30:39 -0800228
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800229 frc::AnalogInput *const input_;
Austin Schuh89f1e092015-11-22 22:30:39 -0800230
231 double value_;
232
233 DISALLOW_COPY_AND_ASSIGN(DMAAnalogReader);
234};
235
Brian Silvermanff7b3472015-01-26 17:53:04 -0500236// This class handles updating the sampled data on multiple
237// DMASampleHandlerInterfaces. The caller should create an instance and then
238// periodically call RunIteration, retrieving whatever data from the
239// DMASampleHandlerInterfaces after each iteration.
240class DMASynchronizer {
241 public:
242 DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
243
244 // Adds a new handler to this object. This method must not be called after
245 // Start().
246 void Add(DMASampleHandlerInterface *handler) {
247 handler->AddToDMA(dma_.get());
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700248 handlers_.push_back(handler);
Brian Silvermanff7b3472015-01-26 17:53:04 -0500249 }
250
251 // Actually starts watching for DMA samples.
252 // Add may not be called any more after this.
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800253 void Start() { dma_->Start(1024); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500254
255 // Updates all sensor values.
256 void RunIteration() {
257 SampleSensors();
258 CheckDMA();
259 }
260
261 private:
262 // Reads the state of all the sensors and records it as the polled values of
263 // all the inputs.
264 void SampleSensors() {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800265 sample_time_ = frc::GetFPGATime();
Brian Silvermanff7b3472015-01-26 17:53:04 -0500266 for (auto &c : handlers_) {
267 c->UpdatePolledValue();
268 }
269 }
270
271 // Gets called by the DMA handler to update edge counts.
272 void CheckDMA();
273
274 const ::std::unique_ptr<DMA> dma_;
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700275 aos::SizedArray<DMASampleHandlerInterface *, 4> handlers_;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500276
277 // The time at which we most recently read the sensor values.
Austin Schuhf853bde2017-11-23 13:23:27 -0800278 int64_t sample_time_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500279
280 DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
281};
282
283} // namespace wpilib
284} // namespace frc971
285
286#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_