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