blob: eda77f053c8b53915eb99d00da7536368d246b39 [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
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080017namespace frc971::wpilib {
Brian Silvermanff7b3472015-01-26 17:53:04 -050018
19// Generic interface for classes that do something with DMA samples and also
20// poll current sensor values.
21class DMASampleHandlerInterface {
22 public:
23 virtual ~DMASampleHandlerInterface() {}
24
25 // Updates values based on a new DMA sample.
26 virtual void UpdateFromSample(const DMASample &sample) = 0;
27
28 // Polls the current values and saves them for later reference.
29 virtual void UpdatePolledValue() = 0;
30
31 // Fills in the "polled" values from sample.
32 // This is only called when a DMA event happens right as we're polling values.
33 virtual void PollFromSample(const DMASample &sample) = 0;
34
35 // Adds readings and triggers appropriate to this object to dma.
36 virtual void AddToDMA(DMA *dma) = 0;
37};
38
Austin Schuh8e5950d2018-03-21 20:29:40 -070039// TODO(brian): Timeout old data.
40class DMAPulseWidthReader : public DMASampleHandlerInterface {
41 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080042 DMAPulseWidthReader(frc::DigitalInput *input) : input_(input) {}
Austin Schuh8e5950d2018-03-21 20:29:40 -070043 DMAPulseWidthReader() = default;
44
Parker Schuhd3b7a8872018-02-19 16:42:27 -080045 void set_input(frc::DigitalInput *input) { input_ = input; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070046
milind-u6b672f82023-02-24 17:36:27 -080047 // Last pulse width in seconds
Austin Schuh8e5950d2018-03-21 20:29:40 -070048 double last_width() const { return last_width_; }
milind-u3a7f9212023-02-24 20:46:59 -080049 double last_period() const { return last_period_; }
Austin Schuh8e5950d2018-03-21 20:29:40 -070050
51 private:
52 void UpdateFromSample(const DMASample & /*sample*/) override;
milind-ua2e612b2023-02-24 18:00:59 -080053 void UpdatePolledValue() override;
Austin Schuh8e5950d2018-03-21 20:29:40 -070054
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;
milind-u6b672f82023-02-24 17:36:27 -080067 // Last time the reading switched to high
68 uint64_t high_time_ = 0;
milind-ua2e612b2023-02-24 18:00:59 -080069 // Number of times we've been polled without an update
70 size_t poll_count_ = 0;
Austin Schuh8e5950d2018-03-21 20:29:40 -070071
72 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
milind-u3a7f9212023-02-24 20:46:59 -080073 double last_period_ = ::std::numeric_limits<double>::quiet_NaN();
Austin Schuh8e5950d2018-03-21 20:29:40 -070074
75 DISALLOW_COPY_AND_ASSIGN(DMAPulseWidthReader);
76};
77
Ravago Jones128a50e2021-09-18 15:19:39 -070078// Takes two digital inputs and times the difference between the first one going
Austin Schuh6c053ef2021-09-26 14:32:16 -070079// low and the second one going low.
Ravago Jones128a50e2021-09-18 15:19:39 -070080class DMAPulseSeparationReader : public DMASampleHandlerInterface {
81 public:
82 DMAPulseSeparationReader(frc::DigitalInput *input_one,
83 frc::DigitalInput *input_two)
84 : input_one_(input_one), input_two_(input_two) {}
85 DMAPulseSeparationReader() = default;
86
87 void set_input_one(frc::DigitalInput *input) { input_one_ = input; }
88 void set_input_two(frc::DigitalInput *input) { input_two_ = input; }
89
90 double last_width() const { return last_width_; }
91 double pulses_detected() const { return pulses_detected_; }
92
93 private:
94 void UpdateFromSample(const DMASample & /*sample*/) override;
95 void UpdatePolledValue() override {}
96
97 void PollFromSample(const DMASample & /*sample*/) override {}
98 void AddToDMA(DMA *dma) override {
99 dma->Add(input_one_);
100 dma->SetExternalTrigger(input_one_, true, true);
101 dma->Add(input_two_);
Austin Schuh6c053ef2021-09-26 14:32:16 -0700102 dma->SetExternalTrigger(input_two_, false, true);
Ravago Jones128a50e2021-09-18 15:19:39 -0700103 }
104
105 static constexpr double kSampleTimeoutSeconds = 0.1;
106
107 frc::DigitalInput *input_one_ = nullptr;
108 frc::DigitalInput *input_two_ = nullptr;
109
110 // The last DMA reading we got.
111 DMASample prev_sample_;
112 // Whether or not we actually have anything in prev_sample_.
113 bool have_prev_sample_ = false;
114
Austin Schuh6c053ef2021-09-26 14:32:16 -0700115 // the time when the input one went low.
Ravago Jones128a50e2021-09-18 15:19:39 -0700116 std::optional<double> input_one_time_;
117
118 int pulses_detected_ = 0;
119
120 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
121
122 DISALLOW_COPY_AND_ASSIGN(DMAPulseSeparationReader);
123};
124
Brian Silvermanff7b3472015-01-26 17:53:04 -0500125// Counts edges on a sensor using DMA data and latches encoder values
126// corresponding to those edges.
127class DMAEdgeCounter : public DMASampleHandlerInterface {
128 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800129 DMAEdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman03a00cf2015-08-29 19:26:54 -0700130 : encoder_(encoder), input_(input) {}
Brianef030df2017-03-05 15:06:04 -0800131 DMAEdgeCounter() = default;
132
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800133 void set_encoder(frc::Encoder *encoder) { encoder_ = encoder; }
134 void set_input(frc::DigitalInput *input) { input_ = input; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500135
Brian Silverman552350b2015-08-02 18:23:34 -0700136 int positive_count() const { return pos_edge_count_; }
137 int negative_count() const { return neg_edge_count_; }
138 int last_positive_encoder_value() const { return pos_last_encoder_; }
139 int last_negative_encoder_value() const { return neg_last_encoder_; }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500140
141 // Returns the value of the sensor in the last-read DMA sample.
Brian Silverman552350b2015-08-02 18:23:34 -0700142 bool last_value() const { return ExtractValue(prev_sample_); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500143 // Returns the most recent polled value of the sensor.
144 bool polled_value() const { return polled_value_; }
145 // Returns the most recent polled reading from the encoder.
146 int polled_encoder() const { return polled_encoder_; }
147
148 private:
Brian Silverman552350b2015-08-02 18:23:34 -0700149 void UpdateFromSample(const DMASample &sample) override;
150 void UpdatePolledValue() override {
Austin Schuhf83da152017-03-05 22:28:45 -0800151 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700152 polled_value_ = input_->Get();
153 polled_encoder_ = encoder_->GetRaw();
154 }
155 void PollFromSample(const DMASample &sample) override {
Austin Schuhf83da152017-03-05 22:28:45 -0800156 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700157 polled_value_ = ExtractValue(sample);
158 polled_encoder_ = sample.GetRaw(encoder_);
159 }
160 void AddToDMA(DMA *dma) override {
161 dma->Add(encoder_);
162 dma->Add(input_);
Brian Silverman03a00cf2015-08-29 19:26:54 -0700163 dma->SetExternalTrigger(input_, true, true);
Brian Silverman552350b2015-08-02 18:23:34 -0700164 }
165
166 bool ExtractValue(const DMASample &sample) const;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500167
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800168 frc::Encoder *encoder_ = nullptr;
169 frc::DigitalInput *input_ = nullptr;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500170
171 // The last DMA reading we got.
172 DMASample prev_sample_;
173 // Whether or not we actually have anything in prev_sample_.
174 bool have_prev_sample_ = false;
175
176 // Values related to the positive edge.
177 int pos_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500178 int pos_last_encoder_ = 0;
179
180 // Values related to the negative edge.
181 int neg_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500182 int neg_last_encoder_ = 0;
183
184 bool polled_value_ = false;
Austin Schuhf83da152017-03-05 22:28:45 -0800185 bool previous_polled_value_ = false;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500186 int polled_encoder_ = 0;
187
188 DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
189};
190
Brian Silverman552350b2015-08-02 18:23:34 -0700191// Reads a hall effect in sync with DMA samples.
192class DMADigitalReader : public DMASampleHandlerInterface {
193 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800194 DMADigitalReader(frc::DigitalInput *input) : input_(input) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700195
196 bool value() const { return value_; }
197
198 private:
199 void UpdateFromSample(const DMASample & /*sample*/) override {}
200 void UpdatePolledValue() override { value_ = input_->Get(); }
201 void PollFromSample(const DMASample &sample) override {
Brian Silverman03a00cf2015-08-29 19:26:54 -0700202 value_ = sample.Get(input_);
Brian Silverman552350b2015-08-02 18:23:34 -0700203 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800204 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Brian Silverman552350b2015-08-02 18:23:34 -0700205
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800206 frc::DigitalInput *const input_;
Brian Silverman552350b2015-08-02 18:23:34 -0700207
208 bool value_;
209
210 DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
211};
212
Austin Schuh89f1e092015-11-22 22:30:39 -0800213// Reads an analog sensor in sync with DMA samples.
214class DMAAnalogReader : public DMASampleHandlerInterface {
215 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800216 DMAAnalogReader(frc::AnalogInput *input) : input_(input) {}
Austin Schuh89f1e092015-11-22 22:30:39 -0800217
218 double value() const { return value_; }
219
220 private:
221 void UpdateFromSample(const DMASample & /*sample*/) override {}
222 void UpdatePolledValue() override { value_ = input_->GetVoltage(); }
223 void PollFromSample(const DMASample &sample) override {
224 value_ = sample.GetVoltage(input_);
225 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800226 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Austin Schuh89f1e092015-11-22 22:30:39 -0800227
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800228 frc::AnalogInput *const input_;
Austin Schuh89f1e092015-11-22 22:30:39 -0800229
230 double value_;
231
232 DISALLOW_COPY_AND_ASSIGN(DMAAnalogReader);
233};
234
Brian Silvermanff7b3472015-01-26 17:53:04 -0500235// This class handles updating the sampled data on multiple
236// DMASampleHandlerInterfaces. The caller should create an instance and then
237// periodically call RunIteration, retrieving whatever data from the
238// DMASampleHandlerInterfaces after each iteration.
239class DMASynchronizer {
240 public:
241 DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
242
243 // Adds a new handler to this object. This method must not be called after
244 // Start().
245 void Add(DMASampleHandlerInterface *handler) {
246 handler->AddToDMA(dma_.get());
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700247 handlers_.push_back(handler);
Brian Silvermanff7b3472015-01-26 17:53:04 -0500248 }
249
250 // Actually starts watching for DMA samples.
251 // Add may not be called any more after this.
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800252 void Start() { dma_->Start(1024); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500253
254 // Updates all sensor values.
255 void RunIteration() {
256 SampleSensors();
257 CheckDMA();
258 }
259
260 private:
261 // Reads the state of all the sensors and records it as the polled values of
262 // all the inputs.
263 void SampleSensors() {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800264 sample_time_ = frc::GetFPGATime();
Brian Silvermanff7b3472015-01-26 17:53:04 -0500265 for (auto &c : handlers_) {
266 c->UpdatePolledValue();
267 }
268 }
269
270 // Gets called by the DMA handler to update edge counts.
271 void CheckDMA();
272
273 const ::std::unique_ptr<DMA> dma_;
Austin Schuhb1aaefe2021-10-12 22:05:44 -0700274 aos::SizedArray<DMASampleHandlerInterface *, 4> handlers_;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500275
276 // The time at which we most recently read the sensor values.
Austin Schuhf853bde2017-11-23 13:23:27 -0800277 int64_t sample_time_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500278
279 DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
280};
281
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800282} // namespace frc971::wpilib
Brian Silvermanff7b3472015-01-26 17:53:04 -0500283
284#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_