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