aos/common/control_loop/Timing=>aos/common/util/phased_loop
diff --git a/aos/common/util/phased_loop.cc b/aos/common/util/phased_loop.cc
new file mode 100644
index 0000000..4b9dcdb
--- /dev/null
+++ b/aos/common/util/phased_loop.cc
@@ -0,0 +1,20 @@
+#include "aos/common/util/phased_loop.h"
+
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace time {
+
+void PhasedLoopXMS(int ms, int offset) {
+  // TODO(brians): Tests!
+  const Time frequency = Time::InMS(ms);
+  SleepUntil((Time::Now() / static_cast<int32_t>(frequency.ToNSec())) *
+             static_cast<int32_t>(frequency.ToNSec()) +
+             frequency + Time::InUS(offset));
+}
+
+}  // namespace timing
+}  // namespace aos
diff --git a/aos/common/util/phased_loop.h b/aos/common/util/phased_loop.h
new file mode 100644
index 0000000..d231449
--- /dev/null
+++ b/aos/common/util/phased_loop.h
@@ -0,0 +1,19 @@
+#ifndef AOS_COMMON_UTIL_PHASED_LOOP_H_
+#define AOS_COMMON_UTIL_PHASED_LOOP_H_
+
+#include <time.h>
+#include <string>
+
+namespace aos {
+namespace time {
+
+// Will not be accurate if ms isn't a factor of 1000.
+// offset is in us.
+void PhasedLoopXMS(int ms, int offset);
+// offset is in us.
+inline void PhasedLoop10MS(int offset) { PhasedLoopXMS(10, offset); }
+
+}  // namespace time
+}  // namespace aos
+
+#endif  // AOS_COMMON_UTIL_PHASED_LOOP_H_
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index 6dd2aa2..682db82 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -8,6 +8,17 @@
       ],
     },
     {
+      'target_name': 'phased_loop',
+      'type': 'static_library',
+      'sources': [
+        'phased_loop.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/common.gyp:time',
+      ],
+    },
+    {
       'target_name': 'log_interval',
       'type': 'static_library',
       'sources': [