add dma-based edge-counting code

Change-Id: I4414b716357effcfbcabdec2a1a19b57d1f344e6
diff --git a/frc971/wpilib/dma_edge_counting.cc b/frc971/wpilib/dma_edge_counting.cc
new file mode 100644
index 0000000..4b0349c
--- /dev/null
+++ b/frc971/wpilib/dma_edge_counting.cc
@@ -0,0 +1,65 @@
+#include "frc971/wpilib/dma_edge_counting.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace frc971 {
+namespace wpilib {
+
+bool DMAEdgeCounter::ExtractValue(const DMASample &sample) {
+  if (inverted_) {
+    return !sample.Get(input_);
+  } else {
+    return sample.Get(input_);
+  }
+}
+
+void DMAEdgeCounter::UpdateFromSample(const DMASample &sample) {
+  if (!have_prev_sample_) {
+    have_prev_sample_ = true;
+  } else {
+    if (!ExtractValue(prev_sample_) && ExtractValue(sample)) {
+      pos_edge_count_++;
+      pos_edge_time_ = sample.GetTimestamp();
+      pos_last_encoder_ = sample.GetRaw(encoder_);
+    } else if (ExtractValue(prev_sample_) && !ExtractValue(sample)) {
+      neg_edge_count_++;
+      neg_edge_time_ = sample.GetTimestamp();
+      neg_last_encoder_ = sample.GetRaw(encoder_);
+    }
+  }
+
+  prev_sample_ = sample;
+}
+
+void DMASynchronizer::CheckDMA() {
+  DMASample current_sample;
+
+  size_t remaining = 0;
+  while (true) {
+    switch (dma_->Read(&current_sample, 0, &remaining)) {
+      case DMA::STATUS_OK:
+        for (auto &c : handlers_) {
+          c->UpdateFromSample(current_sample);
+        }
+
+        if (remaining == 0) {
+          if (sample_time_ < current_sample.GetTimestamp()) {
+            // If the latest DMA sample happened after we started polling, then
+            // just use the values from it because they're more recent.
+            for (auto &c : handlers_) {
+              c->PollFromSample(current_sample);
+            }
+          }
+          return;
+        }
+      case DMA::STATUS_TIMEOUT:
+        return;
+      case DMA::STATUS_ERROR:
+        LOG(WARNING, "DMA read failed\n");
+        break;
+    }
+  }
+}
+
+}  // namespace wpilib
+}  // namespace frc971