static and tourel beacon
[aversive.git] / projects / microb2010 / tests / beacon_tsop / main.c
1 /*
2  *  Copyright Droids Corporation (2010)
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: main.c,v 1.8 2009-05-02 10:08:09 zer0 Exp $
20  *
21  */
22
23 #include <math.h>
24
25 #include <aversive.h>
26 #include <aversive/wait.h>
27
28 #include <uart.h>
29 #include <pid.h>
30 #include <pwm_ng.h>
31 #include <parse.h>
32 #include <rdline.h>
33 #include <vect_base.h>
34 #include <lines.h>
35 #include <circles.h>
36
37 #include "cmdline.h"
38 #include "uart_proto.h"
39 #include "trigo.h"
40 #include "main.h"
41
42 #define BOARD2010
43 //#define BOARD2006
44
45 #ifdef BOARD2010
46 #include "board2010.h"
47 #else
48 #include "board2006.h"
49 #endif
50
51 /******************* TSOP */
52
53 struct detected_frame {
54         uint16_t frame;
55         uint16_t ref_time;
56         uint16_t time;
57         uint16_t tick;
58 };
59
60 /* frame */
61 struct frame_status {
62         uint8_t led_cpt;
63         uint16_t ref_time;
64         uint16_t start_time;
65         uint16_t frame;
66         uint16_t mask;
67         uint16_t prev_time;
68         uint16_t time_long;
69         uint16_t time_short;
70         uint8_t prev_tsop;
71         uint8_t len;
72         uint8_t frame_len;
73         uint8_t val;
74 #define FRAME_RING_ORDER 4
75 #define FRAME_RING_SIZE  (1<<FRAME_RING_ORDER)
76 #define FRAME_RING_MASK  (FRAME_RING_SIZE-1)
77         uint8_t head;
78         uint8_t tail;
79         struct detected_frame ring[FRAME_RING_SIZE];
80 };
81
82 static struct frame_status static_beacon;
83 static struct frame_status opp_beacon;
84 static uint16_t tick = 0;
85
86 #define MIN_DIST 200.
87 #define MAX_DIST 3500.
88
89 /* in ticks (=CS_PERIOD), age before the entry is removed from ring */
90 #define MAX_CAP_AGE 30
91
92 /********************** CS */
93
94 /* 8ms, easier if it's a pow of 2 */
95 #define CS_PERIOD_US (8192)
96 #define CS_PERIOD ((uint16_t)(CS_PERIOD_US/4))
97 #define CPT_ICR_MAX (uint8_t)((1000000UL/(uint32_t)CS_PERIOD_US)) /* too slow = 1 tr/s */
98 #define CPT_ICR_MIN (uint8_t)((10000UL/(uint32_t)CS_PERIOD_US))   /* too fast = 100 tr/s */
99
100 /* in tr / 1000s */
101 #define CS_CONSIGN (20 * 1000L)
102
103 /* 5% tolerance to validate captures, period is in  */
104 #define TIM3_UNIT 250000000L
105 #define MOTOR_PERIOD_MIN ((uint32_t)((250000000L/CS_CONSIGN) * 0.95))
106 #define MOTOR_PERIOD_MAX ((uint32_t)((250000000L/CS_CONSIGN) * 1.05))
107
108 /* pwm for laser:
109  *  - clear on timer compare (CTC)
110  *  - Toggle OC0 on compare match
111  *  - prescaler = 1 */
112 #define LASER_ON() do { TCCR0 = _BV(WGM01) | _BV(COM00) | _BV(CS00); } while (0)
113 #define LASER_OFF() do { TCCR0 = 0; } while (0)
114
115 struct beacon_tsop beacon_tsop;
116 uint32_t cs_consign = CS_CONSIGN;
117
118 static uint32_t current_motor_period;
119
120 void debug_serial(void)
121 {
122 #if 0
123         while (1) {
124                 int16_t c;
125                 c = uart_recv_nowait(0);
126                 if (c != -1)
127                         printf("%c", (char)(c+1));
128                 LED1_ON();
129                 wait_ms(500);
130                 LED1_OFF();
131                 wait_ms(500);
132         }
133 #endif
134 }
135
136 void debug_tsop(void)
137 {
138 #if 0
139         while (1) {
140                 if (TSOP_READ())
141                         LED1_OFF();
142                 else {
143                         LED1_ON();
144                         wait_ms(500);
145                 }
146         }
147 #endif
148 }
149
150 /* val is 16 bits, including 4 bits-cksum in MSB, return 0xFFFF is
151  * cksum is wrong, or the 12 bits value on success. */
152 static uint16_t verify_cksum(uint16_t val)
153 {
154         uint16_t x, cksum;
155
156         x = (val & 0xfff);
157         /* add the four 4-bits blocks of val together */
158         cksum = val & 0xf;
159         val = val >> 4;
160         cksum += val & 0xf;
161         cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
162         val = val >> 4;
163         cksum += val & 0xf;
164         cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
165         val = val >> 4;
166         cksum += val & 0xf;
167         cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
168         if (cksum == 0xf)
169                 return x;
170         return 0xffff; /* wrong cksum */
171 }
172
173 static inline void decode_frame(struct frame_status *status,
174                                 uint16_t ref_time, uint16_t cur_time, uint8_t cur_tsop)
175 {
176         uint16_t diff_time = cur_time - status->prev_time;
177
178         /* first rising edge */
179         if (status->len == 0 && cur_tsop && diff_time > status->time_long) {
180                 status->len = 1;
181                 status->val = 1;
182                 status->frame = 0;
183                 status->start_time = cur_time;
184                 status->ref_time = ref_time;
185                 status->mask = 1;
186         }
187         /* any short pulse */
188         else if (status->len != 0 && diff_time < status->time_short) {
189                 if (status->len & 1) {
190                         if (status->val)
191                                 status->frame |= status->mask;
192                         status->mask <<= 1;
193                 }
194                 status->len ++;
195         }
196         /* any long pulse */
197         else if (status->len != 0 && diff_time < status->time_long) {
198                 status->val = !status->val;
199                 if (status->val)
200                         status->frame |= status->mask;
201                 status->mask <<= 1;
202                 status->len += 2;
203         }
204         /* error case, reset */
205         else {
206                 status->len = 0;
207         }
208
209         /* end of frame */
210         if (status->len == status->frame_len*2) {
211                 uint8_t tail_next = (status->tail+1) & FRAME_RING_MASK;
212                 uint16_t frame_mask;
213
214                 frame_mask = (1 << status->frame_len) - 1;
215
216                 if (tail_next != status->head) {
217                         status->ring[status->tail].frame = (status->frame & frame_mask);
218                         status->ring[status->tail].ref_time = status->ref_time;
219                         status->ring[status->tail].time = status->start_time;
220                         status->ring[status->tail].tick = tick;
221                         status->tail = tail_next;
222                         status->led_cpt ++;
223                 }
224                 status->len = 0;
225         }
226
227         status->prev_time = cur_time;
228         status->prev_tsop = cur_tsop;
229 }
230
231 /* decode frame */
232 SIGNAL(SIG_TSOP_STA) {
233         static uint8_t running = 0;
234
235         /* tsop status */
236         uint8_t cur_tsop;
237         uint16_t ref_time;
238         uint16_t cur_time;
239
240         ref_time = ICR3;
241         cur_time = TCNT3;
242         cur_tsop = TSOP_STA_READ();
243
244         /* avoid interruption stacking */
245         if (running)
246                 return;
247         running = 1;
248         sei();
249
250         if (cur_tsop)
251                 LED5_ON();
252         else
253                 LED5_OFF();
254
255         decode_frame(&static_beacon, ref_time, cur_time, cur_tsop);
256
257         running = 0;
258 }
259
260 /* decode frame */
261 SIGNAL(SIG_TSOP_OPP) {
262         static uint8_t running = 0;
263
264         /* tsop status */
265         uint8_t cur_tsop;
266         uint16_t ref_time;
267         uint16_t cur_time;
268
269         ref_time = ICR3;
270         cur_time = TCNT3;
271         cur_tsop = TSOP_OPP_READ();
272
273         /* avoid interruption stacking */
274         if (running)
275                 return;
276         running = 1;
277         sei();
278
279         if (cur_tsop)
280                 LED6_ON();
281         else
282                 LED6_OFF();
283
284         decode_frame(&opp_beacon, ref_time, cur_time, cur_tsop);
285
286         running = 0;
287 }
288
289 /* absolute value */
290 static inline int32_t AbS(int32_t x)
291 {
292         if (x > 0)
293                 return x;
294         else
295                 return -x;
296 }
297
298 /* Get the speed of motor (tr / 1000s)
299  * - icr_cpt is the number of CS period between 2 ICR updates
300  * - icr_diff is the difference of ICR values between the ICR updates
301  *   (modulo 65536 obviously) */
302 static inline int32_t get_speed(uint8_t icr_cpt, uint16_t icr_diff)
303 {
304         /* too slow (less than 1 tr/s) */
305         if (icr_cpt >= CPT_ICR_MAX) {
306                 current_motor_period = 250000;
307                 return 1000L;
308         }
309
310         /* too fast (more than 100 tr/s) */
311         if (icr_cpt <= CPT_ICR_MIN) {
312                 current_motor_period = 2500;
313                 return 100000L;
314         }
315
316         current_motor_period = icr_diff;
317         return TIM3_UNIT/icr_diff;
318 }
319
320 static int8_t check_sta_frame(uint16_t frame, uint16_t time)
321 {
322         int8_t beacon_id;
323         uint16_t cksum;
324
325         /* ignore bad cksum */
326         cksum = verify_cksum(frame);
327         if (cksum == 0xFFFF)
328                 goto fail;
329
330         beacon_id = frame & TSOP_STA_BEACON_ID_MASK;
331
332         if (beacon_id != TSOP_STA_BEACON_ID0 &&
333             beacon_id != TSOP_STA_BEACON_ID1)
334                 goto fail;
335
336         /* if motor speed is not good, skip values  */
337         if (current_motor_period < MOTOR_PERIOD_MIN)
338                 goto fail;
339         if (current_motor_period > MOTOR_PERIOD_MAX)
340                 goto fail;
341
342         return beacon_id;
343
344  fail:
345         /* display if needed */
346         if (beacon_tsop.debug_frame) {
347                 printf("STA ID=%d frame=%x time=%d, cksum=%x\r\n",
348                        beacon_id, frame, time, cksum);
349         }
350         return -1;
351 }
352
353
354 /* process the received frame ring */
355 static void process_sta_ring(struct frame_status *status)
356 {
357         uint8_t head, head_next;
358         uint16_t frame, frametick;
359         uint8_t found = 0;
360         int8_t beacon_id;
361
362         /* beacon 0 */
363         uint16_t data0, time0, ref_time0;
364         double angle0;
365         double dist0;
366
367         /* beacon 1 */
368         uint16_t data1, time1, ref_time1;
369         double angle1;
370         double dist1;
371
372         point_t pos;
373         double a;
374
375         /* remove too old captures from the ring */
376         while (status->head != status->tail) {
377                 head_next = (status->head+1) & FRAME_RING_MASK;
378                 frametick = status->ring[status->head].tick;
379                 if ((uint16_t)(tick - frametick) < MAX_CAP_AGE)
380                         break;
381                 status->head = head_next;
382         }
383
384         head = status->head;
385         /* after CS, check if we have a new frame in ring */
386         while (head != status->tail) {
387                 head_next = (head+1) & FRAME_RING_MASK;
388                 frame = status->ring[head].frame;
389
390                 beacon_id = check_sta_frame(frame, status->ring[head].time);
391                 if (beacon_id < 0) {
392                         head = head_next;
393                         continue;
394                 }
395
396                 if (beacon_id == TSOP_STA_BEACON_ID0) {
397                         found |= 0x1;
398                         data0 = (frame & TSOP_STA_FRAME_DATA_MASK) >> TSOP_STA_FRAME_DATA_SHIFT;
399                         time0 = status->ring[head].time;
400                         ref_time0 = status->ring[head].ref_time;
401                 }
402                 else if (beacon_id == TSOP_STA_BEACON_ID1) {
403                         found |= 0x2;
404                         data1 = (frame & TSOP_STA_FRAME_DATA_MASK) >> TSOP_STA_FRAME_DATA_SHIFT;
405                         time1 = status->ring[head].time;
406                         ref_time1 = status->ring[head].ref_time;
407                 }
408
409                 head = head_next;
410         }
411
412         /* if we didn't found beacon 0 and 1, return */
413         if (found != 0x3)
414                 return;
415
416         /* update ring head */
417         status->head = head;
418
419         /* beacon 0 */
420         dist0 = data0;
421         dist0 /= 512.;
422         dist0 *= (MAX_DIST-MIN_DIST);
423         dist0 += MIN_DIST;
424
425         time0 = time0 - ref_time0;
426         angle0 = (double)time0 / (double)current_motor_period;
427         if (angle0 > 1.)
428                 angle0 -= 1.;
429         if (angle0 > 1.)
430                 return; /* fail */
431         angle0 *= (2 * M_PI);
432         if (angle0 > M_PI)
433                 angle0 -= M_PI;
434
435         /* beacon 1 */
436         dist1 = data1;
437         dist1 /= 512.;
438         dist1 *= (MAX_DIST-MIN_DIST);
439         dist1 += MIN_DIST;
440
441         time1 = time1 - ref_time1;
442         angle1 = (double)time1 / (double)current_motor_period;
443         if (angle1 > 1.)
444                 angle1 -= 1.;
445         if (angle1 > 1.)
446                 return; /* fail */
447         angle1 *= (2 * M_PI);
448         if (angle0 > M_PI)
449                 angle0 -= M_PI;
450
451         /* display if needed */
452         if (beacon_tsop.debug_frame) {
453                 printf("STA ID=%d dist0=%2.2f angle0=%2.2f dist1=%2.2f angle1=%2.2f\r\n",
454                        beacon_id, dist0, angle0 * 180. / M_PI, dist1, angle1 * 180. / M_PI);
455         }
456
457         if (ad_to_posxya(&pos, &a, 0, &beacon0, &beacon1, angle0, dist0,
458                          angle1, dist1) < 0)
459                 return;
460
461         xmit_static((uint16_t)pos.x, (uint16_t)pos.y, (uint16_t)a);
462 }
463
464 static int8_t check_opp_frame(uint16_t frame, uint16_t time)
465 {
466         int8_t beacon_id = -1;
467         uint16_t cksum;
468
469         /* ignore bad cksum */
470         cksum = verify_cksum(frame);
471         if (cksum == 0xFFFF)
472                 goto fail;
473
474         beacon_id = frame & TSOP_OPP_BEACON_ID_MASK;
475         if (beacon_id != TSOP_OPP_BEACON_ID)
476                 goto fail;
477
478         /* if motor speed is not good, skip values  */
479         if (current_motor_period < MOTOR_PERIOD_MIN)
480                 goto fail;
481         if (current_motor_period > MOTOR_PERIOD_MAX)
482                 goto fail;
483
484         return beacon_id;
485  fail:
486         /* display if needed */
487         if (beacon_tsop.debug_frame) {
488                 printf("OPP ID=%d frame=%x time=%d cksum=%x d=%d\r\n",
489                        beacon_id, frame, time, cksum,
490                        (frame & TSOP_OPP_FRAME_DATA_MASK) >>
491                        TSOP_OPP_FRAME_DATA_SHIFT);
492         }
493         return -1;
494 }
495
496 /* process the received frame ring */
497 static void process_opp_ring(struct frame_status *status)
498 {
499         uint8_t head_next;
500         uint16_t frame;
501         uint8_t found = 0;
502         uint16_t data, time, ref_time;
503         double angle;
504         double dist;
505
506         /* after CS, check if we have a new frame in ring */
507         while (status->head != status->tail) {
508                 head_next = (status->head+1) & FRAME_RING_MASK;
509                 frame = status->ring[status->head].frame;
510
511                 if (check_opp_frame(frame, status->ring[status->head].time) < 0) {
512                         status->head = head_next;
513                         continue;
514                 }
515
516                 found = 1;
517                 data = (frame & TSOP_OPP_FRAME_DATA_MASK) >> TSOP_OPP_FRAME_DATA_SHIFT;
518                 time = status->ring[status->head].time;
519                 ref_time = status->ring[status->head].ref_time;
520
521                 status->head = head_next;
522         }
523
524         if (found == 0)
525                 return;
526
527         dist = data;
528         dist /= 512.;
529         dist *= (MAX_DIST-MIN_DIST);
530         dist += MIN_DIST;
531
532         time = time - ref_time;
533         angle = (double)time / (double)current_motor_period;
534         if (angle > 1.)
535                 angle -= 1.;
536         if (angle > 1.)
537                 return; /* fail */
538         angle *= 3600; /* angle in 1/10 deg */
539
540         /* display if needed */
541         if (beacon_tsop.debug_frame) {
542                 printf("OPP dist=%2.2f angle=%2.2f\r\n", dist, angle/10);
543         }
544         xmit_opp((uint16_t)dist, (uint16_t)angle);
545 }
546
547 int main(void)
548 {
549         uint16_t prev_cs = 0;
550         uint16_t prev_icr = 0;
551         uint16_t icr = 0;
552         uint16_t diff_icr = 0;
553         uint8_t cpt_icr = 0;
554         uint8_t cpt = 0;
555         int32_t speed = 0, out, err;
556         uint16_t tcnt3;
557         uint8_t x = 0; /* debug display counter */
558
559         opp_beacon.frame_len = TSOP_OPP_FRAME_LEN;
560         opp_beacon.time_long = TSOP_OPP_TIME_LONG;
561         opp_beacon.time_short = TSOP_OPP_TIME_SHORT;
562
563         static_beacon.frame_len = TSOP_STA_FRAME_LEN;
564         static_beacon.time_long = TSOP_STA_TIME_LONG;
565         static_beacon.time_short = TSOP_STA_TIME_SHORT;
566
567         /* LEDS */
568         LED_DDR_INIT();
569         DDRB |= 0x10; /* OC0 (laser pwm) */
570
571         /* PID init */
572         pid_init(&beacon_tsop.pid);
573         pid_set_gains(&beacon_tsop.pid, 700, 10, 0);
574         pid_set_maximums(&beacon_tsop.pid, 0, 200000, 4095);
575         pid_set_out_shift(&beacon_tsop.pid, 10);
576         pid_set_derivate_filter(&beacon_tsop.pid, 4);
577
578         uart_init();
579 #if CMDLINE_UART == 0
580         fdevopen(uart0_dev_send, uart0_dev_recv);
581 #elif CMDLINE_UART == 1
582         fdevopen(uart1_dev_send, uart1_dev_recv);
583 #endif
584
585         rdline_init(&beacon_tsop.rdl, write_char, valid_buffer, complete_buffer);
586         snprintf(beacon_tsop.prompt, sizeof(beacon_tsop.prompt), "beacon > ");
587         rdline_newline(&beacon_tsop.rdl, beacon_tsop.prompt);
588
589         debug_tsop();
590         debug_serial();
591
592         /* configure external interrupt for TSOP */
593         EICRx_TSOP |= _BV(ISCx0_TSOP_STA) | _BV(ISCx0_TSOP_OPP);
594         EIMSK |= _BV(INTx_TSOP_STA) | _BV(INTx_TSOP_OPP);
595
596         /* pwm for motor */
597         PWM_NG_TIMER_16BITS_INIT(1, TIMER_16_MODE_PWM_10,
598                                  TIMER1_PRESCALER_DIV_1);
599 #ifdef BOARD2010
600         PWM_NG_INIT16(&beacon_tsop.pwm_motor, 1, C, 10, 0, NULL, 0);
601 #else
602         PWM_NG_INIT16(&beacon_tsop.pwm_motor, 1, A, 10, 0, NULL, 0);
603 #endif
604
605         /* pwm for laser:
606          *  - clear on timer compare (CTC)
607          *  - Toggle OC0 on compare match
608          *  - prescaler = 1 */
609         TCCR0 = _BV(WGM01) | _BV(COM00) | _BV(CS00);
610         OCR0 = 18; /* f ~= 420 khz at 16 Mhz */
611
612         /* configure timer 3: CLK/64
613          * it is used as a reference time
614          * enable noise canceller for ICP3 */
615         TCCR3B = _BV(CS11) | _BV(CS10);
616
617         sei();
618
619         /* Control system will be done in main loop */
620         while (1) {
621
622                 /* process pending bytes on uart */
623                 cmdline_process();
624
625                 /* monitor the value of ICR (which is modified
626                  * automatically on TT rising edge). If the value
627                  * changed, process the time difference. */
628                 if (ETIFR & _BV(ICF3)) {
629                         cli();
630                         icr = ICR3;
631                         sei();
632                         ETIFR = _BV(ICF3);
633
634                         LED2_TOGGLE();
635                         diff_icr = (icr - prev_icr);
636                         cpt_icr = cpt;
637                         prev_icr = icr;
638                         cpt = 0;
639                         speed = get_speed(cpt_icr, diff_icr);
640                 }
641
642                 /* read time reference */
643                 cli();
644                 tcnt3 = TCNT3;
645                 sei();
646
647                 /* wait cs period */
648                 if (tcnt3 - prev_cs < CS_PERIOD)
649                         continue;
650
651                 /* CS LED */
652                 if (x & 0x80)
653                         LED1_ON();
654                 else
655                         LED1_OFF();
656                 x++;
657
658                 /* process CS... maybe we don't need to use
659                  * control_system_manager, just PID is enough */
660
661                 if (cpt == CPT_ICR_MAX)
662                         speed = 0;
663
664                 /* enabled laser when rotation speed if at least 5tr/s */
665                 if (1 || speed > 5000) /* XXX */
666                         LASER_ON();
667                 else
668                         LASER_OFF();
669
670                 err = cs_consign - speed;
671                 out = pid_do_filter(&beacon_tsop.pid, err);
672                 if (out < 0)
673                         out = 0;
674                 if (out > 3000)
675                         out = 3000;
676
677                 if (x == 0 && beacon_tsop.debug_speed)
678                         printf("%ld %ld %u %u / %u\r\n",
679                                speed, out, diff_icr, cpt_icr, cpt);
680
681                 pwm_ng_set(&beacon_tsop.pwm_motor, out);
682
683                 prev_cs = tcnt3;
684
685                 /* count the number of CS period between 2 ICR
686                  * captures */
687                 if (cpt < CPT_ICR_MAX)
688                         cpt ++;
689
690                 process_sta_ring(&static_beacon);
691                 process_opp_ring(&opp_beacon);
692                 cli();
693                 tick ++; /* global imprecise time reference */
694                 sei();
695         }
696
697         return 0;
698 }