deecfb79422b9c2980507a0a00e9eb3344b64842
[fw/altos] / src / drivers / ao_quadrature.c
1 /*
2  * Copyright © 2012 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  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_quadrature.h>
21 #include <ao_exti.h>
22 #include <ao_fast_timer.h>
23 #include <ao_event.h>
24
25 __xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
26 static uint8_t  ao_quadrature_state[AO_QUADRATURE_COUNT];
27
28 struct ao_debounce {
29         uint8_t state;
30         uint8_t count;
31 };
32
33 static struct ao_debounce ao_debounce_state[AO_QUADRATURE_COUNT][2];
34
35 #define port(q) AO_QUADRATURE_ ## q ## _PORT
36 #define bita(q) AO_QUADRATURE_ ## q ## _A
37 #define bitb(q) AO_QUADRATURE_ ## q ## _B
38 #define pina(q) AO_QUADRATURE_ ## q ## _A ## _PIN
39 #define pinb(q) AO_QUADRATURE_ ## q ## _B ## _PIN
40 #define isr(q)  ao_quadrature_isr_ ## q
41
42 #define DEBOUNCE        10
43
44 static uint8_t
45 ao_debounce(uint8_t cur, struct ao_debounce *debounce)
46 {
47         if (cur == debounce->state)
48                 debounce->count = 0;
49         else {
50                 if (++debounce->count == DEBOUNCE) {
51                         debounce->state = cur;
52                         debounce->count = 0;
53                 }
54         }
55         return debounce->state;
56 }
57
58 static uint16_t
59 ao_quadrature_read(struct stm_gpio *gpio, uint8_t pin_a, uint8_t pin_b, struct ao_debounce debounce_state[2]) {
60         uint16_t        v = ~stm_gpio_get_all(gpio);
61         uint8_t         a = (v >> pin_a) & 1;
62         uint8_t         b = (v >> pin_b) & 1;
63
64         a = ao_debounce(a, &debounce_state[0]);
65         b = ao_debounce(b, &debounce_state[1]);
66
67         return a | (b << 1);
68 }
69
70 #define _ao_quadrature_get(q)   ao_quadrature_read(port(q), bita(q), bitb(q), ao_debounce_state[q])
71
72 static void
73 _ao_quadrature_queue(uint8_t q, int8_t step)
74 {
75         ao_quadrature_count[q] += step;
76 #if AO_EVENT
77         ao_event_put_isr(AO_EVENT_QUADRATURE, q, step);
78 #endif
79         ao_wakeup(&ao_quadrature_count[q]);
80 }
81
82 static void
83 _ao_quadrature_set(uint8_t q, uint8_t new) {
84         uint8_t old = ao_quadrature_state[q];
85
86         if (old != new && new == 0) {
87                 if (old & 2)
88                         _ao_quadrature_queue(q, 1);
89                 else if (old & 1)
90                         _ao_quadrature_queue(q, -1);
91         }
92         ao_quadrature_state[q] = new;
93 }
94
95 static void
96 ao_quadrature_isr(void)
97 {
98 #if AO_QUADRATURE_COUNT > 0
99         _ao_quadrature_set(0, _ao_quadrature_get(0));
100 #endif
101 #if AO_QUADRATURE_COUNT > 1
102         _ao_quadrature_set(1, _ao_quadrature_get(1));
103 #endif
104 }
105
106 int32_t
107 ao_quadrature_poll(uint8_t q)
108 {
109         int32_t ret;
110         ao_arch_critical(ret = ao_quadrature_count[q];);
111         return ret;
112 }
113
114 int32_t
115 ao_quadrature_wait(uint8_t q)
116 {
117         ao_sleep(&ao_quadrature_count[q]);
118         return ao_quadrature_poll(q);
119 }
120
121 static void
122 ao_quadrature_test(void)
123 {
124         uint8_t q;
125         int32_t c;
126         uint8_t s;
127
128         ao_cmd_decimal();
129         q = ao_cmd_lex_i;
130         if (q >= AO_QUADRATURE_COUNT) {
131                 ao_cmd_status = ao_cmd_syntax_error;
132                 return;
133         }
134
135         c = -10000;
136         s = 0;
137         while (ao_quadrature_count[q] != 10) {
138                 if (ao_quadrature_count[q] != c ||
139                     ao_quadrature_state[q] != s) {
140                         c = ao_quadrature_count[q];
141                         s = ao_quadrature_state[q];
142                         printf ("count %3d state %2x\n", c, s);
143                         flush();
144                 }
145         }
146 #if 0
147         for (;;) {
148                 int32_t c;
149                 flush();
150                 c = ao_quadrature_wait(q);
151                 printf ("new count %6d\n", c);
152                 if (c == 100)
153                         break;
154         }
155 #endif
156 }
157
158 static const struct ao_cmds ao_quadrature_cmds[] = {
159         { ao_quadrature_test,   "q <unit>\0Test quadrature" },
160         { 0, NULL }
161 };
162
163 #define init(q) do {                                    \
164                 ao_enable_input(port(q), bita(q), 0);   \
165                 ao_enable_input(port(q), bitb(q), 0);   \
166         } while (0)
167
168 void
169 ao_quadrature_init(void)
170 {
171 #if AO_QUADRATURE_COUNT > 0
172         init(0);
173 #endif
174 #if AO_QUADRATURE_COUNT > 1
175         init(1);
176 #endif
177         ao_fast_timer_init();
178         ao_fast_timer_on(ao_quadrature_isr);
179         ao_cmd_register(&ao_quadrature_cmds[0]);
180 }