blob: a6f2da4abae6c47e85b05565f3afc827aeeb6c1e [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>
5#include <vector>
6
John Park33858a32018-09-28 23:05:48 -07007#include "aos/macros.h"
Brian Silvermanff7b3472015-01-26 17:53:04 -05008
Brian Silvermanb5b46ca2016-03-13 01:14:17 -05009#include "frc971/wpilib/dma.h"
10
Parker Schuhd3b7a8872018-02-19 16:42:27 -080011#include "frc971/wpilib/ahal/AnalogInput.h"
12#include "frc971/wpilib/ahal/DigitalInput.h"
13#include "frc971/wpilib/ahal/Encoder.h"
14#include "frc971/wpilib/ahal/Utility.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
48 double last_width() const { return last_width_; }
49
50 private:
51 void UpdateFromSample(const DMASample & /*sample*/) override;
52 void UpdatePolledValue() override {}
53
54 void PollFromSample(const DMASample & /*sample*/) override {}
55 void AddToDMA(DMA *dma) override {
56 dma->Add(input_);
57 dma->SetExternalTrigger(input_, true, true);
58 }
59
Parker Schuhd3b7a8872018-02-19 16:42:27 -080060 frc::DigitalInput *input_ = nullptr;
Austin Schuh8e5950d2018-03-21 20:29:40 -070061
62 // The last DMA reading we got.
63 DMASample prev_sample_;
64 // Whether or not we actually have anything in prev_sample_.
65 bool have_prev_sample_ = false;
66
67 double last_width_ = ::std::numeric_limits<double>::quiet_NaN();
68
69 DISALLOW_COPY_AND_ASSIGN(DMAPulseWidthReader);
70};
71
Brian Silvermanff7b3472015-01-26 17:53:04 -050072// Counts edges on a sensor using DMA data and latches encoder values
73// corresponding to those edges.
74class DMAEdgeCounter : public DMASampleHandlerInterface {
75 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -080076 DMAEdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman03a00cf2015-08-29 19:26:54 -070077 : encoder_(encoder), input_(input) {}
Brianef030df2017-03-05 15:06:04 -080078 DMAEdgeCounter() = default;
79
Parker Schuhd3b7a8872018-02-19 16:42:27 -080080 void set_encoder(frc::Encoder *encoder) { encoder_ = encoder; }
81 void set_input(frc::DigitalInput *input) { input_ = input; }
Brian Silvermanff7b3472015-01-26 17:53:04 -050082
Brian Silverman552350b2015-08-02 18:23:34 -070083 int positive_count() const { return pos_edge_count_; }
84 int negative_count() const { return neg_edge_count_; }
85 int last_positive_encoder_value() const { return pos_last_encoder_; }
86 int last_negative_encoder_value() const { return neg_last_encoder_; }
Brian Silvermanff7b3472015-01-26 17:53:04 -050087
88 // Returns the value of the sensor in the last-read DMA sample.
Brian Silverman552350b2015-08-02 18:23:34 -070089 bool last_value() const { return ExtractValue(prev_sample_); }
Brian Silvermanff7b3472015-01-26 17:53:04 -050090 // Returns the most recent polled value of the sensor.
91 bool polled_value() const { return polled_value_; }
92 // Returns the most recent polled reading from the encoder.
93 int polled_encoder() const { return polled_encoder_; }
94
95 private:
Brian Silverman552350b2015-08-02 18:23:34 -070096 void UpdateFromSample(const DMASample &sample) override;
97 void UpdatePolledValue() override {
Austin Schuhf83da152017-03-05 22:28:45 -080098 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -070099 polled_value_ = input_->Get();
100 polled_encoder_ = encoder_->GetRaw();
101 }
102 void PollFromSample(const DMASample &sample) override {
Austin Schuhf83da152017-03-05 22:28:45 -0800103 previous_polled_value_ = polled_value_;
Brian Silverman552350b2015-08-02 18:23:34 -0700104 polled_value_ = ExtractValue(sample);
105 polled_encoder_ = sample.GetRaw(encoder_);
106 }
107 void AddToDMA(DMA *dma) override {
108 dma->Add(encoder_);
109 dma->Add(input_);
Brian Silverman03a00cf2015-08-29 19:26:54 -0700110 dma->SetExternalTrigger(input_, true, true);
Brian Silverman552350b2015-08-02 18:23:34 -0700111 }
112
113 bool ExtractValue(const DMASample &sample) const;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500114
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800115 frc::Encoder *encoder_ = nullptr;
116 frc::DigitalInput *input_ = nullptr;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500117
118 // The last DMA reading we got.
119 DMASample prev_sample_;
120 // Whether or not we actually have anything in prev_sample_.
121 bool have_prev_sample_ = false;
122
123 // Values related to the positive edge.
124 int pos_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500125 int pos_last_encoder_ = 0;
126
127 // Values related to the negative edge.
128 int neg_edge_count_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500129 int neg_last_encoder_ = 0;
130
131 bool polled_value_ = false;
Austin Schuhf83da152017-03-05 22:28:45 -0800132 bool previous_polled_value_ = false;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500133 int polled_encoder_ = 0;
134
135 DISALLOW_COPY_AND_ASSIGN(DMAEdgeCounter);
136};
137
Brian Silverman552350b2015-08-02 18:23:34 -0700138// Reads a hall effect in sync with DMA samples.
139class DMADigitalReader : public DMASampleHandlerInterface {
140 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800141 DMADigitalReader(frc::DigitalInput *input) : input_(input) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700142
143 bool value() const { return value_; }
144
145 private:
146 void UpdateFromSample(const DMASample & /*sample*/) override {}
147 void UpdatePolledValue() override { value_ = input_->Get(); }
148 void PollFromSample(const DMASample &sample) override {
Brian Silverman03a00cf2015-08-29 19:26:54 -0700149 value_ = sample.Get(input_);
Brian Silverman552350b2015-08-02 18:23:34 -0700150 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800151 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Brian Silverman552350b2015-08-02 18:23:34 -0700152
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800153 frc::DigitalInput *const input_;
Brian Silverman552350b2015-08-02 18:23:34 -0700154
155 bool value_;
156
157 DISALLOW_COPY_AND_ASSIGN(DMADigitalReader);
158};
159
Austin Schuh89f1e092015-11-22 22:30:39 -0800160// Reads an analog sensor in sync with DMA samples.
161class DMAAnalogReader : public DMASampleHandlerInterface {
162 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800163 DMAAnalogReader(frc::AnalogInput *input) : input_(input) {}
Austin Schuh89f1e092015-11-22 22:30:39 -0800164
165 double value() const { return value_; }
166
167 private:
168 void UpdateFromSample(const DMASample & /*sample*/) override {}
169 void UpdatePolledValue() override { value_ = input_->GetVoltage(); }
170 void PollFromSample(const DMASample &sample) override {
171 value_ = sample.GetVoltage(input_);
172 }
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800173 void AddToDMA(DMA *dma) override { dma->Add(input_); }
Austin Schuh89f1e092015-11-22 22:30:39 -0800174
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800175 frc::AnalogInput *const input_;
Austin Schuh89f1e092015-11-22 22:30:39 -0800176
177 double value_;
178
179 DISALLOW_COPY_AND_ASSIGN(DMAAnalogReader);
180};
181
Brian Silvermanff7b3472015-01-26 17:53:04 -0500182// This class handles updating the sampled data on multiple
183// DMASampleHandlerInterfaces. The caller should create an instance and then
184// periodically call RunIteration, retrieving whatever data from the
185// DMASampleHandlerInterfaces after each iteration.
186class DMASynchronizer {
187 public:
188 DMASynchronizer(::std::unique_ptr<DMA> dma) : dma_(::std::move(dma)) {}
189
190 // Adds a new handler to this object. This method must not be called after
191 // Start().
192 void Add(DMASampleHandlerInterface *handler) {
193 handler->AddToDMA(dma_.get());
194 handlers_.emplace_back(handler);
195 }
196
197 // Actually starts watching for DMA samples.
198 // Add may not be called any more after this.
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800199 void Start() { dma_->Start(1024); }
Brian Silvermanff7b3472015-01-26 17:53:04 -0500200
201 // Updates all sensor values.
202 void RunIteration() {
203 SampleSensors();
204 CheckDMA();
205 }
206
207 private:
208 // Reads the state of all the sensors and records it as the polled values of
209 // all the inputs.
210 void SampleSensors() {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800211 sample_time_ = frc::GetFPGATime();
Brian Silvermanff7b3472015-01-26 17:53:04 -0500212 for (auto &c : handlers_) {
213 c->UpdatePolledValue();
214 }
215 }
216
217 // Gets called by the DMA handler to update edge counts.
218 void CheckDMA();
219
220 const ::std::unique_ptr<DMA> dma_;
221 ::std::vector<DMASampleHandlerInterface *> handlers_;
222
223 // The time at which we most recently read the sensor values.
Austin Schuhf853bde2017-11-23 13:23:27 -0800224 int64_t sample_time_ = 0;
Brian Silvermanff7b3472015-01-26 17:53:04 -0500225
226 DISALLOW_COPY_AND_ASSIGN(DMASynchronizer);
227};
228
229} // namespace wpilib
230} // namespace frc971
231
232#endif // FRC971_WPILIB_DMA_EDGE_COUNTING_H_