+#ifndef AO_QUADRATURE_DEBOUNCE
+#define AO_QUADRATURE_DEBOUNCE 30
+#endif
+
+static uint8_t
+ao_debounce(uint8_t cur, struct ao_debounce *debounce)
+{
+#if AO_QUADRATURE_DEBOUNCE > 0
+ if (debounce->count > 0) {
+ debounce->count--;
+ } else if (cur != debounce->state) {
+ debounce->state = cur;
+ debounce->count = AO_QUADRATURE_DEBOUNCE;
+ }
+ return debounce->state;
+#else
+ (void) debounce;
+ return cur;
+#endif
+}
+
+static uint16_t
+ao_quadrature_read(struct stm_gpio *gpio, uint8_t pin_a, uint8_t pin_b, struct ao_debounce debounce_state[2]) {
+ uint16_t v = ~stm_gpio_get_all(gpio);
+ uint8_t a = (v >> pin_a) & 1;
+ uint8_t b = (v >> pin_b) & 1;
+
+ a = ao_debounce(a, &debounce_state[0]);
+ b = ao_debounce(b, &debounce_state[1]);
+
+ return a | (b << 1);
+}
+
+#define _ao_quadrature_get(q) ao_quadrature_read(port(q), bita(q), bitb(q), ao_debounce_state[q])
+
+static void
+_ao_quadrature_queue(uint8_t q, int8_t step)
+{
+ ao_quadrature_count[q] += step;
+#if AO_EVENT
+ ao_event_put_isr(AO_EVENT_QUADRATURE, q, step);
+#endif
+ ao_wakeup(&ao_quadrature_count[q]);
+}
+
+#if AO_QUADRATURE_SINGLE_CODE
+struct ao_quadrature_step {
+ uint8_t inc;
+ uint8_t dec;
+};
+
+static struct ao_quadrature_step ao_quadrature_steps[4] = {
+ [0] = { .inc = 1, .dec = 2 },
+ [1] = { .inc = 3, .dec = 0 },
+ [3] = { .inc = 2, .dec = 1 },
+ [2] = { .inc = 0, .dec = 3 },
+};
+#endif
+
+static void
+_ao_quadrature_set(uint8_t q, uint8_t new) {
+ uint8_t old = ao_quadrature_state[q];
+
+#ifdef AO_QUADRATURE_SINGLE_CODE
+ if (new == ao_quadrature_steps[old].inc) {
+ _ao_quadrature_queue(q, 1);
+ } else if (new == ao_quadrature_steps[old].dec) {
+ _ao_quadrature_queue(q, -1);
+ }
+#else
+ if (old != new && new == 0) {
+ if (old == 2)
+ _ao_quadrature_queue(q, 1);
+ else if (old == 1)
+ _ao_quadrature_queue(q, -1);
+ }
+#endif
+ ao_quadrature_state[q] = new;
+}