Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 1 | #ifndef FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_ |
| 2 | #define FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_ |
| 3 | |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 4 | #include <atomic> |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 5 | #include <memory> |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 6 | #include <thread> |
| 7 | #include <vector> |
| 8 | |
Austin Schuh | a0c41ba | 2020-09-10 22:59:14 -0700 | [diff] [blame] | 9 | #include "aos/logging/logging.h" |
John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 10 | #include "aos/macros.h" |
Austin Schuh | a0c41ba | 2020-09-10 22:59:14 -0700 | [diff] [blame] | 11 | #include "aos/stl_mutex/stl_mutex.h" |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 12 | #include "frc971/wpilib/ahal/DigitalInput.h" |
| 13 | #include "frc971/wpilib/ahal/Encoder.h" |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 14 | |
Stephan Pleines | d99b1ee | 2024-02-02 20:56:44 -0800 | [diff] [blame] | 15 | namespace frc971::wpilib { |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 16 | |
| 17 | class 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. |
| 34 | class 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 Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 48 | AOS_CHECK_NE(nullptr, mutex_); |
| 49 | AOS_CHECK_NE(0, priority_); |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 50 | 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. |
| 107 | class EdgeCounter : public InterruptHandler { |
| 108 | public: |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 109 | EdgeCounter(frc::Encoder *encoder, frc::DigitalInput *input) |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 110 | : 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 Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 139 | frc::Encoder *encoder_; |
| 140 | frc::DigitalInput *input_; |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 141 | |
| 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 Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 153 | // Synchronizes reading an encoder with interrupt handling. |
| 154 | class InterruptSynchronizedEncoder : public InterruptHandler { |
| 155 | public: |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 156 | InterruptSynchronizedEncoder(frc::Encoder *encoder) : encoder_(encoder) {} |
Brian Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 157 | |
| 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 Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 165 | frc::Encoder *const encoder_; |
Brian Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 166 | |
| 167 | int32_t shadow_, output_; |
| 168 | }; |
| 169 | |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 170 | // 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. |
| 179 | class InterruptSynchronizer { |
| 180 | public: |
| 181 | InterruptSynchronizer(int interrupt_priority) |
| 182 | : interrupt_priority_(interrupt_priority) {} |
| 183 | |
Brian Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 184 | void Add(InterruptHandler *handler) { |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 185 | handler->set_mutex(&mutex_); |
| 186 | handler->set_priority(interrupt_priority_); |
Brian Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 187 | handlers_.emplace_back(handler); |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 188 | } |
| 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 Silverman | 552350b | 2015-08-02 18:23:34 -0700 | [diff] [blame] | 223 | ::std::vector<InterruptHandler *> handlers_; |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 224 | |
| 225 | DISALLOW_COPY_AND_ASSIGN(InterruptSynchronizer); |
| 226 | }; |
| 227 | |
Stephan Pleines | d99b1ee | 2024-02-02 20:56:44 -0800 | [diff] [blame] | 228 | } // namespace frc971::wpilib |
Brian Silverman | 70ec719 | 2015-01-26 17:52:40 -0500 | [diff] [blame] | 229 | |
| 230 | #endif // FRC971_WPILIB_INTERRUPT_EDGE_COUNTING_H_ |