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