altos/telefireone-v1.0: Track ao_led_init API change
[fw/altos] / src / drivers / ao_matrix.c
1 /*
2  * Copyright © 2017 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include <ao.h>
16 #include <ao_matrix.h>
17 #include <ao_event.h>
18 #include <ao_exti.h>
19
20 #define row_port(q)     AO_MATRIX_ROW_ ## q ## _PORT
21 #define row_bit(q)      AO_MATRIX_ROW_ ## q ## _PIN
22 #define row_pin(q)      AO_MATRIX_ROW_ ## q ## _PIN
23
24 #define col_port(q)     AO_MATRIX_COL_ ## q ## _PORT
25 #define col_bit(q)      AO_MATRIX_COL_ ## q ## _PIN
26 #define col_pin(q)      AO_MATRIX_COL_ ## q ## _PIN
27
28 static void
29 _ao_matrix_drive_row(uint8_t row, uint8_t val)
30 {
31         switch (row) {
32 #define drive(n) case n: ao_gpio_set(row_port(n), row_bit(n), row_pin(n), val); break
33                 drive(0);
34 #if AO_MATRIX_ROWS > 1
35                 drive(1);
36 #endif
37 #if AO_MATRIX_ROWS > 2
38                 drive(2);
39 #endif
40 #if AO_MATRIX_ROWS > 3
41                 drive(3);
42 #endif
43 #if AO_MATRIX_ROWS > 4
44                 drive(4);
45 #endif
46 #if AO_MATRIX_ROWS > 5
47                 drive(5);
48 #endif
49 #if AO_MATRIX_ROWS > 6
50                 drive(6);
51 #endif
52 #if AO_MATRIX_ROWS > 7
53                 drive(7);
54 #endif
55         }
56 }
57
58 static uint8_t
59 _ao_matrix_read_cols(void)
60 {
61         uint8_t v = 0;
62 #define read(n) (v |= ao_gpio_get(col_port(n), col_bit(n), col_pin(n)) << n)
63
64         read(0);
65 #if AO_MATRIX_ROWS > 1
66         read(1);
67 #endif
68 #if AO_MATRIX_ROWS > 2
69         read(2);
70 #endif
71 #if AO_MATRIX_ROWS > 3
72         read(3);
73 #endif
74 #if AO_MATRIX_ROWS > 4
75         read(4);
76 #endif
77 #if AO_MATRIX_ROWS > 5
78         read(5);
79 #endif
80 #if AO_MATRIX_ROWS > 6
81         read(6);
82 #endif
83 #if AO_MATRIX_ROWS > 7
84         read(7);
85 #endif
86         return v;
87 }
88
89 static uint8_t
90 _ao_matrix_read(uint8_t row) {
91         uint8_t state;
92         _ao_matrix_drive_row(row, 0);
93         state = _ao_matrix_read_cols();
94         _ao_matrix_drive_row(row, 1);
95         return state;
96 }
97
98 #define AO_MATRIX_DEBOUNCE_INTERVAL     AO_MS_TO_TICKS(50)
99
100 static uint8_t ao_matrix_keymap[AO_MATRIX_ROWS][AO_MATRIX_COLS] = AO_MATRIX_KEYCODES;
101
102 static uint8_t          ao_matrix_state[AO_MATRIX_ROWS];
103 static AO_TICK_TYPE     ao_matrix_tick[AO_MATRIX_ROWS];
104
105 static void
106 _ao_matrix_poll_one(uint8_t row) {
107         uint8_t state = _ao_matrix_read(row);
108
109         if (state != ao_matrix_state[row]) {
110                 AO_TICK_TYPE    now = ao_time();
111
112                 if ((now - ao_matrix_tick[row]) >= AO_MATRIX_DEBOUNCE_INTERVAL) {
113                         uint8_t col;
114                         uint8_t changes = state ^ ao_matrix_state[row];
115
116                         for (col = 0; col < AO_MATRIX_COLS; col++) {
117                                 if (changes & (1 << col)) {
118                                         ao_event_put_isr(AO_EVENT_KEY,
119                                                          ao_matrix_keymap[row][col],
120                                                          ((state >> col) & 1) == 0);
121                                 }
122                         }
123                         ao_matrix_state[row] = state;
124                 }
125                 ao_matrix_tick[row] = now;
126         }
127 }
128
129 void
130 ao_matrix_poll(void)
131 {
132         uint8_t row;
133
134         for (row = 0; row < AO_MATRIX_ROWS; row++)
135                 _ao_matrix_poll_one(row);
136 }
137
138 #define init_row(b) do {                                                \
139                 ao_enable_output(row_port(b), row_bit(b), row_pin(v), 1); \
140                 ao_gpio_set_output_mode(row_port(b), row_bit(b), row_pin(b), AO_OUTPUT_OPEN_DRAIN); \
141         } while (0)
142
143 #define init_col(b) do { \
144                 ao_enable_input(col_port(b), col_bit(b), AO_EXTI_MODE_PULL_UP); \
145         } while(0)
146
147 void
148 ao_matrix_init(void)
149 {
150         uint8_t row;
151
152         init_row(0);
153 #if AO_MATRIX_ROWS > 1
154         init_row(1);
155 #endif
156 #if AO_MATRIX_ROWS > 2
157         init_row(2);
158 #endif
159 #if AO_MATRIX_ROWS > 3
160         init_row(3);
161 #endif
162 #if AO_MATRIX_ROWS > 4
163         init_row(4);
164 #endif
165 #if AO_MATRIX_ROWS > 5
166         init_row(5);
167 #endif
168 #if AO_MATRIX_ROWS > 6
169         init_row(6);
170 #endif
171 #if AO_MATRIX_ROWS > 7
172         init_row(7);
173 #endif
174
175         init_col(0);
176 #if AO_MATRIX_COLS > 1
177         init_col(1);
178 #endif
179 #if AO_MATRIX_COLS > 2
180         init_col(2);
181 #endif
182 #if AO_MATRIX_COLS > 3
183         init_col(3);
184 #endif
185 #if AO_MATRIX_COLS > 4
186         init_col(4);
187 #endif
188 #if AO_MATRIX_COLS > 5
189         init_col(5);
190 #endif
191 #if AO_MATRIX_COLS > 6
192         init_col(6);
193 #endif
194 #if AO_MATRIX_COLS > 7
195         init_col(7);
196 #endif
197         for (row = 0; row < AO_MATRIX_ROWS; row++) {
198                 ao_matrix_state[row] = _ao_matrix_read(row);
199                 ao_matrix_tick[row] = ao_time();
200         }
201 }