blob: bd76d7c423ff15b92c9e2eef6663522238f2b8f3 [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
156// Synchronizes interrupts with poll-based sampling on multiple
157// InterruptHandlers.
158//
159// See InterruptHandler for an overview of the logic.
160//
161// Usage is to create an instance, call Add 1 or more times, call Start, and
162// then call RunIteration during normal sensor sampling. After RunIteration
163// returns, the output values from the various InterruptHandlers can be
164// retrieved.
165class InterruptSynchronizer {
166 public:
167 InterruptSynchronizer(int interrupt_priority)
168 : interrupt_priority_(interrupt_priority) {}
169
170 void Add(::std::unique_ptr<InterruptHandler> handler) {
171 handler->set_mutex(&mutex_);
172 handler->set_priority(interrupt_priority_);
173 handlers_.emplace_back(::std::move(handler));
174 }
175
176 void Start() {
177 for (auto &c : handlers_) {
178 c->Start();
179 }
180 }
181
182 // Updates all of the counts and makes sure everything is synchronized.
183 // IMPORTANT: This will usually only take 120uS but WILL occasionally take
184 // longer, so be careful about letting that jitter get into control loops.
185 void RunIteration();
186
187 // Asks all of the InterruptHandlers to stop and waits until they have done
188 // so.
189 void Quit() {
190 for (auto &c : handlers_) {
191 c->Quit();
192 }
193 }
194
195 private:
196 // Starts a sampling iteration. See RunIteration for usage.
197 // Returns true if we are ready to go or false if we already need to retry.
198 bool TryStartIteration();
199
200 // Attempts to finish a sampling iteration. See RunIteration for usage.
201 // Returns true if the iteration succeeded, and false otherwise.
202 bool TryFinishingIteration();
203
204 const int interrupt_priority_;
205
206 // The mutex used to synchronize all the sampling.
207 ::aos::stl_mutex mutex_;
208
209 ::std::vector<::std::unique_ptr<InterruptHandler>> handlers_;
210
211 DISALLOW_COPY_AND_ASSIGN(InterruptSynchronizer);
212};
213
214} // namespace wpilib
215} // namespace frc971
216
217#endif // FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_