altos/test: Adjust CRC error rate after FEC fix
[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 int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
26 #ifndef AO_QUADRATURE_SINGLE_CODE
27 static int8_t ao_quadrature_step[AO_QUADRATURE_COUNT];
28 #endif
29
30 static uint8_t  ao_quadrature_state[AO_QUADRATURE_COUNT];
31
32 struct ao_debounce {
33         uint8_t state;
34         uint8_t count;
35 };
36
37 static struct ao_debounce ao_debounce_state[AO_QUADRATURE_COUNT][2];
38
39 #define port(q) AO_QUADRATURE_ ## q ## _PORT
40 #define bita(q) AO_QUADRATURE_ ## q ## _A
41 #define bitb(q) AO_QUADRATURE_ ## q ## _B
42 #define pina(q) AO_QUADRATURE_ ## q ## _A ## _PIN
43 #define pinb(q) AO_QUADRATURE_ ## q ## _B ## _PIN
44 #define isr(q)  ao_quadrature_isr_ ## q
45
46 #ifndef AO_QUADRATURE_DEBOUNCE
47 #error must define AO_QUADRATURE_DEBOUNCE
48 #endif
49
50 static uint8_t
51 ao_debounce(uint8_t cur, struct ao_debounce *debounce)
52 {
53 #if AO_QUADRATURE_DEBOUNCE > 0
54         if (debounce->count > 0) {
55                 debounce->count--;
56         } else if (cur != debounce->state) {
57                 debounce->state = cur;
58                 debounce->count = AO_QUADRATURE_DEBOUNCE;
59         }
60         return debounce->state;
61 #else
62         (void) debounce;
63         return cur;
64 #endif
65 }
66
67 static uint16_t
68 ao_quadrature_read(struct stm_gpio *gpio, uint8_t pin_a, uint8_t pin_b, struct ao_debounce debounce_state[2]) {
69         uint16_t        v = ~stm_gpio_get_all(gpio);
70         uint8_t         a = (v >> pin_a) & 1;
71         uint8_t         b = (v >> pin_b) & 1;
72
73         a = ao_debounce(a, &debounce_state[0]);
74         b = ao_debounce(b, &debounce_state[1]);
75
76         return (uint16_t) (a | (b << 1));
77 }
78
79 #define _ao_quadrature_get(q)   ao_quadrature_read(port(q), bita(q), bitb(q), ao_debounce_state[q])
80
81 static void
82 _ao_quadrature_step(uint8_t q, int8_t step)
83 {
84 #ifndef AO_QUADRATURE_SINGLE_CODE
85         ao_quadrature_step[q] += step;
86         if (ao_quadrature_state[q] != 0)
87                 return;
88         if (ao_quadrature_step[q] >= 4) {
89                 ao_quadrature_step[q] = 0;
90                 step = 1;
91         } else if (ao_quadrature_step[q] <= -4) {
92                 ao_quadrature_step[q] = 0;
93                 step = -1;
94         } else
95                 return;
96 #endif
97         ao_quadrature_count[q] += step;
98 #if AO_EVENT
99         ao_event_put_isr(AO_EVENT_QUADRATURE, q, step);
100 #endif
101         ao_wakeup(&ao_quadrature_count[q]);
102 }
103
104 static const struct {
105         uint8_t prev, next;
106 } ao_quadrature_steps[4] = {
107         [0] { .prev = 2, .next = 1 },
108         [1] { .prev = 0, .next = 3 },
109         [3] { .prev = 1, .next = 2 },
110         [2] { .prev = 3, .next = 0 },
111 };
112
113 static void
114 _ao_quadrature_set(uint8_t q, uint8_t new)
115 {
116         uint8_t old;
117
118         ao_arch_block_interrupts();
119         old = ao_quadrature_state[q];
120         ao_quadrature_state[q] = new;
121         ao_arch_release_interrupts();
122
123         if (new == ao_quadrature_steps[old].next)
124                 _ao_quadrature_step(q, 1);
125         else if (new == ao_quadrature_steps[old].prev)
126                 _ao_quadrature_step(q, -1);
127 }
128
129 static void
130 ao_quadrature_isr(void)
131 {
132 #if AO_QUADRATURE_COUNT > 0
133         _ao_quadrature_set(0, (uint8_t) _ao_quadrature_get(0));
134 #endif
135 #if AO_QUADRATURE_COUNT > 1
136         _ao_quadrature_set(1, (uint8_t) _ao_quadrature_get(1));
137 #endif
138 }
139
140 static void
141 _ao_quadrature_start_one(uint8_t q, uint8_t new)
142 {
143         ao_arch_block_interrupts();
144         ao_quadrature_state[q] = new;
145         ao_arch_release_interrupts();
146 }
147
148 int32_t
149 ao_quadrature_poll(uint8_t q)
150 {
151         int32_t ret;
152         ao_arch_critical(ret = ao_quadrature_count[q];);
153         return ret;
154 }
155
156 int32_t
157 ao_quadrature_wait(uint8_t q)
158 {
159         ao_sleep(&ao_quadrature_count[q]);
160         return ao_quadrature_poll(q);
161 }
162
163 static void
164 ao_quadrature_test(void)
165 {
166         uint32_t q;
167         int32_t c;
168         uint8_t s;
169 #ifndef AO_QUADRATURE_SINGLE_CODE
170         int8_t t = 0;
171 #endif
172
173         q = ao_cmd_decimal();
174         if (q >= AO_QUADRATURE_COUNT)
175                 ao_cmd_status = ao_cmd_syntax_error;
176         if (ao_cmd_status != ao_cmd_success)
177                 return;
178
179         c = -10000;
180         s = 0;
181         while (ao_quadrature_count[q] != 10) {
182                 if (ao_quadrature_count[q] != c ||
183 #ifndef AO_QUADRATURE_SINGLE_CODE
184                     ao_quadrature_step[q] != t ||
185 #endif
186                     ao_quadrature_state[q] != s)
187                 {
188                         c = ao_quadrature_count[q];
189                         s = ao_quadrature_state[q];
190 #ifndef AO_QUADRATURE_SINGLE_CODE
191                         t = ao_quadrature_step[q];
192                         printf("step %3d ", t);
193 #endif
194                         printf ("count %3ld state %2x\n", (long) c, s);
195                         flush();
196                 }
197         }
198 #if 0
199         for (;;) {
200                 int32_t c;
201                 flush();
202                 c = ao_quadrature_wait(q);
203                 printf ("new count %6d\n", c);
204                 if (c == 100)
205                         break;
206         }
207 #endif
208 }
209
210 static const struct ao_cmds ao_quadrature_cmds[] = {
211         { ao_quadrature_test,   "q <unit>\0Test quadrature" },
212         { 0, NULL }
213 };
214
215 #define init(q) do {                                                    \
216                 ao_enable_input(port(q), bita(q), 0);                   \
217                 ao_enable_input(port(q), bitb(q), 0);                   \
218                 _ao_quadrature_start_one(q, (uint8_t) _ao_quadrature_get(q)); \
219         } while (0)
220
221 void
222 ao_quadrature_init(void)
223 {
224 #if AO_QUADRATURE_COUNT > 0
225         init(0);
226 #endif
227 #if AO_QUADRATURE_COUNT > 1
228         init(1);
229 #endif
230         ao_fast_timer_init();
231         ao_fast_timer_on(ao_quadrature_isr);
232         ao_cmd_register(&ao_quadrature_cmds[0]);
233 }