altos: add button matrix driver
authorKeith Packard <keithp@keithp.com>
Mon, 3 Apr 2017 02:30:57 +0000 (19:30 -0700)
committerKeith Packard <keithp@keithp.com>
Mon, 3 Apr 2017 02:30:57 +0000 (19:30 -0700)
Scans the matrix once per clock tick queuing events for changed keys.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/drivers/ao_event.h
src/drivers/ao_matrix.c [new file with mode: 0644]
src/drivers/ao_matrix.h [new file with mode: 0644]
src/stm/ao_arch_funcs.h

index d1c69d81b2761d86106200af90c63f7f31ca6b12..d1df6eac4e59d7788721eb4a02814f093eb53dc9 100644 (file)
@@ -22,6 +22,7 @@
 #define AO_EVENT_NONE          0
 #define AO_EVENT_QUADRATURE    1
 #define AO_EVENT_BUTTON                2
+#define AO_EVENT_KEY           3
 
 struct ao_event {
        uint8_t         type;
diff --git a/src/drivers/ao_matrix.c b/src/drivers/ao_matrix.c
new file mode 100644 (file)
index 0000000..e0f8ba7
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_matrix.h>
+#include <ao_event.h>
+#include <ao_exti.h>
+
+#define row_port(q)    AO_MATRIX_ROW_ ## q ## _PORT
+#define row_bit(q)     AO_MATRIX_ROW_ ## q ## _PIN
+#define row_pin(q)     AO_MATRIX_ROW_ ## q ## _PIN
+
+#define col_port(q)    AO_MATRIX_COL_ ## q ## _PORT
+#define col_bit(q)     AO_MATRIX_COL_ ## q ## _PIN
+#define col_pin(q)     AO_MATRIX_COL_ ## q ## _PIN
+
+static void
+_ao_matrix_drive_row(uint8_t row, uint8_t val)
+{
+       switch (row) {
+#define drive(n) case n: ao_gpio_set(row_port(n), row_bit(n), row_pin(n), val); break
+               drive(0);
+#if AO_MATRIX_ROWS > 1
+               drive(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+               drive(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+               drive(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+               drive(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+               drive(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+               drive(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+               drive(7);
+#endif
+       }
+}
+
+static uint8_t
+_ao_matrix_read_cols(void)
+{
+       uint8_t v = 0;
+#define read(n)        (v |= ao_gpio_get(col_port(n), col_bit(n), col_pin(n)) << n)
+
+       read(0);
+#if AO_MATRIX_ROWS > 1
+       read(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+       read(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+       read(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+       read(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+       read(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+       read(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+       read(7);
+#endif
+       return v;
+}
+
+static uint8_t
+_ao_matrix_read(uint8_t row) {
+       uint8_t state;
+       _ao_matrix_drive_row(row, 1);
+       state = _ao_matrix_read_cols();
+       _ao_matrix_drive_row(row, 0);
+       return state;
+}
+
+#define AO_MATRIX_DEBOUNCE_INTERVAL    AO_MS_TO_TICKS(50)
+
+static uint8_t ao_matrix_keymap[AO_MATRIX_ROWS][AO_MATRIX_COLS] = AO_MATRIX_KEYCODES;
+
+static uint8_t         ao_matrix_state[AO_MATRIX_ROWS];
+static AO_TICK_TYPE    ao_matrix_tick[AO_MATRIX_ROWS];
+
+static void
+_ao_matrix_poll_one(uint8_t row) {
+       uint8_t state = _ao_matrix_read(row);
+
+       if (state != ao_matrix_state[row]) {
+               AO_TICK_TYPE    now = ao_time();
+
+               if ((now - ao_matrix_tick[row]) >= AO_MATRIX_DEBOUNCE_INTERVAL) {
+                       uint8_t col;
+                       uint8_t changes = state ^ ao_matrix_state[row];
+
+                       for (col = 0; col < AO_MATRIX_COLS; col++) {
+                               if (changes & (1 << col)) {
+                                       ao_event_put_isr(AO_EVENT_KEY,
+                                                        ao_matrix_keymap[row][col],
+                                                        ((state >> col) & 1) == 0);
+                               }
+                       }
+                       ao_matrix_state[row] = state;
+               }
+               ao_matrix_tick[row] = now;
+       }
+}
+
+void
+ao_matrix_poll(void)
+{
+       uint8_t row;
+
+       for (row = 0; row < AO_MATRIX_ROWS; row++)
+               _ao_matrix_poll_one(row);
+}
+
+#define init_row(b) do {                                               \
+               ao_enable_output(row_port(b), row_bit(b), row_pin(v), 1); \
+               ao_gpio_set_output_mode(row_port(b), row_bit(b), row_pin(b), AO_OUTPUT_OPEN_DRAIN); \
+       } while (0)
+
+#define init_col(b) do { \
+               ao_enable_input(col_port(b), col_bit(b), AO_EXTI_MODE_PULL_UP); \
+       } while(0)
+
+void
+ao_matrix_init(void)
+{
+       uint8_t row;
+
+       init_row(0);
+#if AO_MATRIX_ROWS > 1
+       init_row(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+       init_row(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+       init_row(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+       init_row(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+       init_row(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+       init_row(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+       init_row(7);
+#endif
+
+       init_col(0);
+#if AO_MATRIX_COLS > 1
+       init_col(1);
+#endif
+#if AO_MATRIX_COLS > 2
+       init_col(2);
+#endif
+#if AO_MATRIX_COLS > 3
+       init_col(3);
+#endif
+#if AO_MATRIX_COLS > 4
+       init_col(4);
+#endif
+#if AO_MATRIX_COLS > 5
+       init_col(5);
+#endif
+#if AO_MATRIX_COLS > 6
+       init_col(6);
+#endif
+#if AO_MATRIX_COLS > 7
+       init_col(7);
+#endif
+       for (row = 0; row < AO_MATRIX_ROWS; row++) {
+               ao_matrix_state[row] = _ao_matrix_read(row);
+               ao_matrix_tick[row] = ao_time();
+       }
+}
diff --git a/src/drivers/ao_matrix.h b/src/drivers/ao_matrix.h
new file mode 100644 (file)
index 0000000..ab5a1c5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_MATRIX_H_
+#define _AO_MATRIX_H_
+
+void
+ao_matrix_poll(void);
+
+void
+ao_matrix_init(void);
+
+#endif /* _AO_MATRIX_H_ */
index 88097406a3e104d835719dda073923299eb3e25b..b294c379ee7a330efe1161e683c4b660a1fbcc58 100644 (file)
@@ -211,6 +211,12 @@ ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t s
                stm_moder_set(port, bit, STM_MODER_OUTPUT);\
        } while (0)
 
+#define AO_OUTPUT_PUSH_PULL    STM_OTYPER_PUSH_PULL
+#define AO_OUTPUT_OPEN_DRAIN   STM_OTYPER_OPEN_DRAIN
+
+#define ao_gpio_set_output_mode(port,bit,pin,mode) \
+       stm_otyper_set(port, pin, mode)
+
 #define ao_gpio_set_mode(port,bit,mode) do {                           \
                if (mode == AO_EXTI_MODE_PULL_UP)                       \
                        stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP);    \
@@ -219,7 +225,7 @@ ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t s
                else                                                    \
                        stm_pupdr_set(port, bit, STM_PUPDR_NONE);       \
        } while (0)
-       
+
 #define ao_enable_input(port,bit,mode) do {                            \
                ao_enable_port(port);                                   \
                stm_moder_set(port, bit, STM_MODER_INPUT);              \