blob: dd48f2df445c9969e743e3e345a7a40c479f64b7 [file] [log] [blame]
Brian Silverman70ec7192015-01-26 17:52:40 -05001#ifndef FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_
2#define FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_
3
Brian Silverman70ec7192015-01-26 17:52:40 -05004#include <atomic>
Parker Schuhd3b7a8872018-02-19 16:42:27 -08005#include <memory>
Brian Silverman70ec7192015-01-26 17:52:40 -05006#include <thread>
7#include <vector>
8
Austin Schuha0c41ba2020-09-10 22:59:14 -07009#include "aos/logging/logging.h"
John Park33858a32018-09-28 23:05:48 -070010#include "aos/macros.h"
Austin Schuha0c41ba2020-09-10 22:59:14 -070011#include "aos/stl_mutex/stl_mutex.h"
Brian Silverman70ec7192015-01-26 17:52:40 -050012
Parker Schuhd3b7a8872018-02-19 16:42:27 -080013#include "frc971/wpilib/ahal/DigitalInput.h"
14#include "frc971/wpilib/ahal/Encoder.h"
Brian Silverman70ec7192015-01-26 17:52:40 -050015
16namespace frc971 {
17namespace wpilib {
18
19class InterruptSynchronizer;
20
21// Handles interrupts arriving from a single source.
22//
23// Instances of subclasses should be passed to InterruptSynchronizer::Add to use
24// them. All methods which are called with the lock held should avoid taking too
25// long because they directly contribute to interrupt-handling latency for all
26// InterruptHandlers in the same InterruptSynchronizer.
27//
28// Each instance handles any important sensor reading which must happen right
29// after an interrupt triggers from a single source. It is also important that
30// some sensors are sampled after any pending interrupts have been processed.
31// This is handled using a per-InterruptSynchronizer mutex. Each
32// InterruptHandler records that it has received an interrupt, locks the mutex,
33// and then updates a "shadow" state. The InterruptSynchronizer then triggers
34// making this "shadow" state visible after making sure no more interrupts have
35// arrived while holding the mutex.
36class InterruptHandler {
37 public:
38 virtual ~InterruptHandler() {}
39
40 // Stops the thread which actually does the sampling and waits for it to
41 // finish.
42 void Quit() {
43 run_ = false;
44 thread_.join();
45 }
46
47 // Starts the thread running.
48 // set_priority and set_mutex must be called first.
49 void Start() {
Austin Schuhf257f3c2019-10-27 21:00:43 -070050 AOS_CHECK_NE(nullptr, mutex_);
51 AOS_CHECK_NE(0, priority_);
Brian Silverman70ec7192015-01-26 17:52:40 -050052 thread_ = ::std::thread(::std::ref(*this));
53 }
54
55 // Polls the current values and saves them to the "shadow" output.
56 // Called while the lock is held.
57 virtual void GatherPolledValue() = 0;
58
59 // Actually outputs the "shadow" state collected during the most recent
60 // GatherPolledValue.
61 // Called while the lock is held.
62 virtual void CommitValue() = 0;
63
64 // Saves the current interrupt count to be compared when
65 // interrupt_count_changed() is called.
66 void save_interrupt_count() { saved_interrupt_count_ = interrupt_count_; }
67 // Returns whether or not the interrupt count has changed since
68 // save_interrupt_count() was last called.
69 bool interrupt_count_changed() const {
70 return saved_interrupt_count_ != interrupt_count_;
71 }
72
73 // Sets the priority the thread will run at.
74 // This must be called before Start.
75 void set_priority(int priority) { priority_ = priority; }
76
77 // Sets the mutex to use for synchronizing readings.
78 // This must be called before Start.
79 void set_mutex(::aos::stl_mutex *mutex) { mutex_ = mutex; }
80
81 // Waits for interrupts, locks the mutex, and updates the internal state.
82 // Should only be called by the (internal) ::std::thread.
83 virtual void operator()() = 0;
84
85 protected:
86 // Indicates that another interrupt has been received (not handled yet).
87 void interrupt_received() { ++interrupt_count_; }
88
89 int priority() const { return priority_; }
90
91 ::aos::stl_mutex *mutex() { return mutex_; }
92
93 // Returns true if the thread should continue running.
94 bool should_run() const { return run_; }
95
96 private:
97 ::std::atomic<int> interrupt_count_{0};
98 int saved_interrupt_count_;
99
100 ::std::atomic<bool> run_{true};
101 ::std::thread thread_;
102
103 int priority_ = 0;
104 ::aos::stl_mutex *mutex_ = nullptr;
105};
106
107// Latches the value of an encoder on rising and falling edges of a digital
108// input.
109class EdgeCounter : public InterruptHandler {
110 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800111 EdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input)
Brian Silverman70ec7192015-01-26 17:52:40 -0500112 : encoder_(encoder), input_(input) {}
113
114 // Returns the current interrupt edge counts and encoder values.
115 int positive_interrupt_count() const {
116 return output_.positive_interrupt_count;
117 }
118 int negative_interrupt_count() const {
119 return output_.negative_interrupt_count;
120 }
121 int32_t last_positive_encoder_value() const {
122 return output_.last_positive_encoder_value;
123 }
124 int32_t last_negative_encoder_value() const {
125 return output_.last_negative_encoder_value;
126 }
127 // Returns the current polled value.
128 bool polled_value() const { return output_.polled_value; }
129
130 private:
131 struct OutputValues {
132 bool polled_value = false;
133 int positive_interrupt_count = 0, negative_interrupt_count = 0;
134 int32_t last_positive_encoder_value = 0, last_negative_encoder_value = 0;
135 };
136
137 void GatherPolledValue() override;
138 void CommitValue() override { output_ = shadow_values_; }
139 void operator()() override;
140
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800141 frc::Encoder *encoder_;
142 frc::DigitalInput *input_;
Brian Silverman70ec7192015-01-26 17:52:40 -0500143
144 // The following variables represent the current "shadow" state.
145 bool current_value_ = false;
146 bool last_miss_match_ = true;
147 OutputValues shadow_values_;
148
149 // The actual output values.
150 OutputValues output_;
151
152 DISALLOW_COPY_AND_ASSIGN(EdgeCounter);
153};
154
Brian Silverman552350b2015-08-02 18:23:34 -0700155// Synchronizes reading an encoder with interrupt handling.
156class InterruptSynchronizedEncoder : public InterruptHandler {
157 public:
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800158 InterruptSynchronizedEncoder(frc::Encoder *encoder) : encoder_(encoder) {}
Brian Silverman552350b2015-08-02 18:23:34 -0700159
160 int32_t get() const { return output_; }
161
162 private:
163 void GatherPolledValue() override { shadow_ = encoder_->GetRaw(); }
164 void CommitValue() override { output_ = shadow_; }
165 void operator()() override {}
166
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800167 frc::Encoder *const encoder_;
Brian Silverman552350b2015-08-02 18:23:34 -0700168
169 int32_t shadow_, output_;
170};
171
Brian Silverman70ec7192015-01-26 17:52:40 -0500172// Synchronizes interrupts with poll-based sampling on multiple
173// InterruptHandlers.
174//
175// See InterruptHandler for an overview of the logic.
176//
177// Usage is to create an instance, call Add 1 or more times, call Start, and
178// then call RunIteration during normal sensor sampling. After RunIteration
179// returns, the output values from the various InterruptHandlers can be
180// retrieved.
181class InterruptSynchronizer {
182 public:
183 InterruptSynchronizer(int interrupt_priority)
184 : interrupt_priority_(interrupt_priority) {}
185
Brian Silverman552350b2015-08-02 18:23:34 -0700186 void Add(InterruptHandler *handler) {
Brian Silverman70ec7192015-01-26 17:52:40 -0500187 handler->set_mutex(&mutex_);
188 handler->set_priority(interrupt_priority_);
Brian Silverman552350b2015-08-02 18:23:34 -0700189 handlers_.emplace_back(handler);
Brian Silverman70ec7192015-01-26 17:52:40 -0500190 }
191
192 void Start() {
193 for (auto &c : handlers_) {
194 c->Start();
195 }
196 }
197
198 // Updates all of the counts and makes sure everything is synchronized.
199 // IMPORTANT: This will usually only take 120uS but WILL occasionally take
200 // longer, so be careful about letting that jitter get into control loops.
201 void RunIteration();
202
203 // Asks all of the InterruptHandlers to stop and waits until they have done
204 // so.
205 void Quit() {
206 for (auto &c : handlers_) {
207 c->Quit();
208 }
209 }
210
211 private:
212 // Starts a sampling iteration. See RunIteration for usage.
213 // Returns true if we are ready to go or false if we already need to retry.
214 bool TryStartIteration();
215
216 // Attempts to finish a sampling iteration. See RunIteration for usage.
217 // Returns true if the iteration succeeded, and false otherwise.
218 bool TryFinishingIteration();
219
220 const int interrupt_priority_;
221
222 // The mutex used to synchronize all the sampling.
223 ::aos::stl_mutex mutex_;
224
Brian Silverman552350b2015-08-02 18:23:34 -0700225 ::std::vector<InterruptHandler *> handlers_;
Brian Silverman70ec7192015-01-26 17:52:40 -0500226
227 DISALLOW_COPY_AND_ASSIGN(InterruptSynchronizer);
228};
229
230} // namespace wpilib
231} // namespace frc971
232
233#endif // FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_