ballboard blocking detection
[aversive.git] / projects / microb2010 / ballboard / sensor.c
1 /*
2  *  Copyright Droids Corporation (2009)
3  *  Olivier MATZ <zer0@droids-corp.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Revision : $Id: sensor.c,v 1.3 2009-05-27 20:04:07 zer0 Exp $
20  *
21  */
22
23 #include <stdlib.h>
24
25 #include <aversive.h>
26 #include <aversive/error.h>
27
28 #include <adc.h>
29 #include <scheduler.h>
30 #include <ax12.h>
31 #include <pwm_ng.h>
32
33 #include <pid.h>
34 #include <quadramp.h>
35 #include <control_system_manager.h>
36 #include <blocking_detection_manager.h>
37
38 #include <parse.h>
39 #include <rdline.h>
40
41 #include "main.h"
42 #include "sensor.h"
43 #include "../common/i2c_commands.h"
44
45 /************ ADC */
46
47 struct adc_infos {
48         uint16_t config;
49         int16_t value;
50         int16_t prev_val;
51         int16_t (*filter)(struct adc_infos *, int16_t);
52 };
53
54 /* reach 90% of the value in 4 samples */
55 int16_t rii_light(struct adc_infos *adc, int16_t val)
56 {
57         adc->prev_val = val + (int32_t)adc->prev_val / 2;
58         return adc->prev_val / 2;
59 }
60
61 /* reach 90% of the value in 8 samples */
62 int16_t rii_medium(struct adc_infos *adc, int16_t val)
63 {
64         adc->prev_val = val + ((int32_t)adc->prev_val * 3) / 4;
65         return adc->prev_val / 4;
66 }
67
68 /* reach 90% of the value in 16 samples */
69 int16_t rii_strong(struct adc_infos *adc, int16_t val)
70 {
71         adc->prev_val = val + ((int32_t)adc->prev_val * 7) / 8;
72         return adc->prev_val / 8;
73 }
74
75
76 #define ADC_CONF(x) ( ADC_REF_AVCC | ADC_MODE_INT | MUX_ADC##x )
77
78 /* define which ADC to poll, see in sensor.h */
79 static struct adc_infos adc_infos[ADC_MAX] = {
80
81         [ADC_CSENSE1] = { .config = ADC_CONF(0), .filter = rii_medium },
82         [ADC_CSENSE2] = { .config = ADC_CONF(1), .filter = rii_medium },
83         [ADC_CSENSE3] = { .config = ADC_CONF(2), .filter = rii_medium },
84         [ADC_CSENSE4] = { .config = ADC_CONF(3), .filter = rii_medium },
85
86         /* add adc on "cap" pins if needed */
87 /*      [ADC_CAP1] = { .config = ADC_CONF(10) }, */
88 /*      [ADC_CAP2] = { .config = ADC_CONF(11) }, */
89 /*      [ADC_CAP3] = { .config = ADC_CONF(12) }, */
90 /*      [ADC_CAP4] = { .config = ADC_CONF(13) }, */
91 };
92
93 static void adc_event(int16_t result);
94
95 /* called every 10 ms, see init below */
96 static void do_adc(void *dummy)
97 {
98         /* launch first conversion */
99         adc_launch(adc_infos[0].config);
100 }
101
102 static void adc_event(int16_t result)
103 {
104         static uint8_t i = 0;
105
106         /* filter value if needed */
107         if (adc_infos[i].filter)
108                 adc_infos[i].value = adc_infos[i].filter(&adc_infos[i],
109                                                          result);
110         else
111                 adc_infos[i].value = result;
112
113         i ++;
114         if (i >= ADC_MAX)
115                 i = 0;
116         else
117                 adc_launch(adc_infos[i].config);
118 }
119
120 int16_t sensor_get_adc(uint8_t i)
121 {
122         int16_t tmp;
123         uint8_t flags;
124
125         IRQ_LOCK(flags);
126         tmp = adc_infos[i].value;
127         IRQ_UNLOCK(flags);
128         return tmp;
129 }
130
131 /************ boolean sensors */
132
133
134 struct sensor_filter {
135         uint8_t filter;
136         uint8_t prev;
137         uint8_t thres_off;
138         uint8_t thres_on;
139         uint8_t cpt;
140         uint8_t invert;
141 };
142
143 /* pullup mapping:
144  * CAP 5,6,7,8
145  * voltage div mapping:
146  * CAP 1
147  */
148 static struct sensor_filter sensor_filter[SENSOR_MAX] = {
149         [S_HIGH_BARRIER] = { 20, 0, 1, 19, 0, 1 }, /* 0 */
150         [S_LOW_BARRIER] = { 50, 0, 1, 1, 0, 0 }, /* 1 */
151         [S_CAP3] = { 1, 0, 0, 1, 0, 0 }, /* 2 */
152         [S_CAP4] = { 1, 0, 0, 1, 0, 0 }, /* 3 */
153         [S_R_IR] = { 1, 0, 0, 1, 0, 0 }, /* 4 */
154         [S_R_US] = { 1, 0, 0, 1, 0, 1 }, /* 5 */
155         [S_L_US] = { 1, 0, 0, 1, 0, 1 }, /* 6 */
156         [S_L_IR] = { 1, 0, 0, 1, 0, 0 }, /* 7 */
157         [S_RESERVED1] = { 10, 0, 3, 7, 0, 0 }, /* 8 */
158         [S_RESERVED2] = { 10, 0, 3, 7, 0, 0 }, /* 9 */
159         [S_RESERVED3] = { 1, 0, 0, 1, 0, 0 }, /* 10 */
160         [S_RESERVED4] = { 1, 0, 0, 1, 0, 0 }, /* 11 */
161         [S_RESERVED5] = { 1, 0, 0, 1, 0, 0 }, /* 12 */
162         [S_RESERVED6] = { 1, 0, 0, 1, 0, 0 }, /* 13 */
163         [S_RESERVED7] = { 1, 0, 0, 1, 0, 0 }, /* 14 */
164         [S_RESERVED8] = { 1, 0, 0, 1, 0, 0 }, /* 15 */
165 };
166
167 /* value of filtered sensors */
168 static uint16_t sensor_filtered = 0;
169
170 /* sensor mapping :
171  * 0-3:  PORTK 2->5 (cap1 -> cap4) (adc10 -> adc13)
172  * 4-5:  PORTL 0->1 (cap5 -> cap6)
173  * 6-7:  PORTE 3->4 (cap7 -> cap8)
174  * 8-15: reserved
175  */
176
177 uint16_t sensor_get_all(void)
178 {
179         uint16_t tmp;
180         uint8_t flags;
181         IRQ_LOCK(flags);
182         tmp = sensor_filtered;
183         IRQ_UNLOCK(flags);
184         return tmp;
185 }
186
187 uint8_t sensor_get(uint8_t i)
188 {
189         uint16_t tmp = sensor_get_all();
190         return !!(tmp & _BV(i));
191 }
192
193 /* get the physical value of pins */
194 static uint16_t sensor_read(void)
195 {
196         uint16_t tmp = 0;
197         tmp |= (uint16_t)((PINK & (_BV(2)|_BV(3)|_BV(4)|_BV(5))) >> 2) << 0;
198         tmp |= (uint16_t)((PINL & (_BV(0)|_BV(1))) >> 0) << 4;
199         tmp |= (uint16_t)((PINE & (_BV(3)|_BV(4))) >> 3) << 6;
200         /* add reserved sensors here */
201         return tmp;
202 }
203
204 /* called every 10 ms, see init below */
205 static void do_boolean_sensors(void *dummy)
206 {
207         uint8_t i;
208         uint8_t flags;
209         uint16_t sensor = sensor_read();
210         uint16_t tmp = 0;
211
212         for (i=0; i<SENSOR_MAX; i++) {
213                 if ((1 << i) & sensor) {
214                         if (sensor_filter[i].cpt < sensor_filter[i].filter)
215                                 sensor_filter[i].cpt++;
216                         if (sensor_filter[i].cpt >= sensor_filter[i].thres_on)
217                                 sensor_filter[i].prev = 1;
218                 }
219                 else {
220                         if (sensor_filter[i].cpt > 0)
221                                 sensor_filter[i].cpt--;
222                         if (sensor_filter[i].cpt <= sensor_filter[i].thres_off)
223                                 sensor_filter[i].prev = 0;
224                 }
225
226                 if (sensor_filter[i].prev && !sensor_filter[i].invert) {
227                         tmp |= (1UL << i);
228                 }
229                 else if (!sensor_filter[i].prev && sensor_filter[i].invert) {
230                         tmp |= (1UL << i);
231                 }
232         }
233         IRQ_LOCK(flags);
234         sensor_filtered = tmp;
235         IRQ_UNLOCK(flags);
236 }
237
238 static volatile uint8_t lcob_seen = I2C_COB_NONE;
239 static volatile uint8_t rcob_seen = I2C_COB_NONE;
240
241 uint8_t cob_detect_left(void)
242 {
243         uint8_t flags;
244         uint8_t ret;
245         IRQ_LOCK(flags);
246         ret = lcob_seen;
247         lcob_seen = I2C_COB_NONE;
248         IRQ_UNLOCK(flags);
249         return ret;
250 }
251
252 uint8_t cob_detect_right(void)
253 {
254         uint8_t flags;
255         uint8_t ret;
256         IRQ_LOCK(flags);
257         ret = rcob_seen;
258         rcob_seen = I2C_COB_NONE;
259         IRQ_UNLOCK(flags);
260         return ret;
261 }
262
263 #define COB_MIN_DETECT 4
264 #define COB_MAX_DETECT 50
265 static void do_cob_detection(void)
266 {
267         uint8_t flags;
268         uint16_t tmp = sensor_get_all();
269         uint8_t l_us = !!(tmp & _BV(S_L_US));
270         uint8_t r_us = !!(tmp & _BV(S_R_US));
271         uint8_t l_ir = !!(tmp & _BV(S_L_IR));
272         uint8_t r_ir = !!(tmp & _BV(S_R_IR));
273         static uint8_t l_us_prev, r_us_prev, l_ir_prev, r_ir_prev;
274         static uint8_t l_cpt_on, l_cpt_off;
275         static uint8_t r_cpt_on, r_cpt_off;
276
277         /* rising edge on US */
278         if (l_us_prev == 0 && l_us == 1) {
279                 l_cpt_off = 0;
280                 l_cpt_on = 0;
281         }
282
283         /* us is on */
284         if (l_us) {
285                 if (l_ir && l_cpt_on < COB_MAX_DETECT)
286                         l_cpt_on ++;
287                 else if (l_cpt_off < COB_MAX_DETECT)
288                         l_cpt_off ++;
289         }
290
291         /* falling edge on US */
292         if (l_us_prev == 1 && l_us == 0) {
293                 /* detection should not be too short or too long */
294                 if ((l_cpt_off + l_cpt_on) < COB_MAX_DETECT &&
295                     (l_cpt_off + l_cpt_on) > COB_MIN_DETECT) {
296                         IRQ_LOCK(flags);
297                         if (l_cpt_on > l_cpt_off)
298                                 lcob_seen = I2C_COB_WHITE;
299                         else
300                                 lcob_seen = I2C_COB_BLACK;
301                         IRQ_UNLOCK(flags);
302                         if (l_cpt_on > l_cpt_off)
303                                 DEBUG(E_USER_SENSOR, "left white %d %d",
304                                       l_cpt_on, l_cpt_off);
305                         else
306                                 DEBUG(E_USER_SENSOR, "left black %d %d",
307                                       l_cpt_on, l_cpt_off);
308                 }
309         }
310
311
312         /* rising edge on US */
313         if (r_us_prev == 0 && r_us == 1) {
314                 r_cpt_off = 0;
315                 r_cpt_on = 0;
316         }
317
318         /* us is on */
319         if (r_us) {
320                 if (r_ir && r_cpt_on < COB_MAX_DETECT)
321                         r_cpt_on ++;
322                 else if (r_cpt_off < COB_MAX_DETECT)
323                         r_cpt_off ++;
324         }
325
326         /* falling edge on US */
327         if (r_us_prev == 1 && r_us == 0) {
328
329                 /* detection should not be too short or too long */
330                 if ((r_cpt_off + r_cpt_on) < COB_MAX_DETECT &&
331                     (r_cpt_off + r_cpt_on) > COB_MIN_DETECT) {
332                         IRQ_LOCK(flags);
333                         if (r_cpt_on > r_cpt_off)
334                                 rcob_seen = I2C_COB_WHITE;
335                         else
336                                 rcob_seen = I2C_COB_BLACK;
337                         IRQ_UNLOCK(flags);
338
339                         if (r_cpt_on > r_cpt_off)
340                                 DEBUG(E_USER_SENSOR, "right white %d %d",
341                                       r_cpt_on, r_cpt_off);
342                         else
343                                 DEBUG(E_USER_SENSOR, "right black %d %d",
344                                       r_cpt_on, r_cpt_off);
345                 }
346         }
347
348
349         l_us_prev = l_us;
350         r_us_prev = r_us;
351         l_ir_prev = l_ir;
352         r_ir_prev = r_ir;
353 }
354
355 /************ global sensor init */
356 #define BACKGROUND_ADC  0
357
358 /* called every 10 ms, see init below */
359 static void do_sensors(void *dummy)
360 {
361         if (BACKGROUND_ADC)
362                 do_adc(NULL);
363         do_boolean_sensors(NULL);
364         do_cob_detection();
365 }
366
367 void sensor_init(void)
368 {
369         adc_init();
370         if (BACKGROUND_ADC)
371                 adc_register_event(adc_event);
372         /* CS EVENT */
373         scheduler_add_periodical_event_priority(do_sensors, NULL,
374                                                 10000L / SCHEDULER_UNIT,
375                                                 ADC_PRIO);
376
377 }
378