rc_servos: enable bypass mode by default
[protos/rc_servos.git] / main.c
1 #include <aversive.h>
2
3 struct servo {
4         uint8_t bit;
5         uint16_t command;
6 };
7
8 static struct servo servo_table[] = {
9         {
10                 .bit = 0,
11                 .command = 0,
12         },
13         {
14                 .bit = 1,
15                 .command = 512,
16         },
17         {
18                 .bit = 2,
19                 .command = 1023,
20         },
21 };
22 #define NB_SERVO (sizeof(servo_table)/sizeof(*servo_table))
23
24 static volatile uint8_t bypass;
25 static volatile uint8_t done;
26 static volatile uint8_t portval;
27 static volatile uint8_t rxidx;
28
29 #define BYPASS_ENABLE 14
30 #define BYPASS_DISABLE 15
31
32 /*
33  * SPI protocol:
34  *
35  * A command is stored on 2 bytes. The first one has its msb to 0, and the
36  * second one to 1. The first received byte contains the command number, and the
37  * msb of the servo value. The second byte contains the lsb of the servo value.
38  *
39  * Commands 0 to NB_SERVO are to set the value of servo.
40  * Command 14 is to enable bypass mode.
41  * Command 15 is to disable bypass mode.
42  */
43 static volatile union {
44         uint8_t u8;
45         struct {
46                 /* inverted: little endian */
47                 uint8_t val_msb:3;
48                 uint8_t cmd_num:4;
49                 uint8_t zero:1;
50         };
51 } byte0;
52
53 static volatile union {
54         uint8_t u8;
55         struct {
56                 /* inverted: little endian */
57                 uint8_t val_lsb:7;
58                 uint8_t one:1;
59         };
60 } byte1;
61
62 SIGNAL(TIMER1_COMPA_vect)
63 {
64         PORTC = portval;
65         TIMSK1 &= ~_BV(OCIE1A);
66         done = 1;
67 }
68
69 static void poll_spi(void)
70 {
71         uint8_t c;
72
73         /* reception complete ? */
74         if (!(SPSR & (1<<SPIF)))
75                 return;
76
77         c = SPDR;
78         if ((rxidx == 0) && (c & 0x80)) {
79                 rxidx = 0;
80                 return; /* drop */
81         }
82         if ((rxidx == 1) && ((c & 0x80) == 0)) {
83                 rxidx = 0;
84                 return; /* drop */
85         }
86
87         if (rxidx == 0) {
88                 byte0.u8 = c;
89         }
90         else {
91                 uint16_t val;
92
93                 byte1.u8 = c;
94
95                 /* process command */
96
97                 if (byte0.cmd_num < NB_SERVO) {
98                         val = (uint16_t)byte0.val_msb << 7;
99                         val += byte1.val_lsb;
100                         servo_table[byte0.cmd_num].command = val;
101                 }
102                 else if (byte0.cmd_num == BYPASS_ENABLE) {
103                         bypass = 1;
104                 }
105                 else if (byte0.cmd_num == BYPASS_DISABLE) {
106                         bypass = 0;
107                 }
108         }
109
110         rxidx ^= 1;
111 }
112
113 static void load_timer_at(uint16_t t)
114 {
115         OCR1A = t;
116         TIMSK1 |= _BV(OCIE1A);
117 }
118
119 static void do_one_servo(struct servo *s)
120 {
121         uint16_t t;
122
123         /* set bit */
124         done = 0;
125         //portval = PORTC | (1 << s->bit);
126         portval = (1 << s->bit);
127         t = TCNT1;
128         load_timer_at(t + 150);
129         while (done == 0)
130                 poll_spi();
131
132         /* reset bit */
133         done = 0;
134         portval = 0;
135         //portval = PORTC & (~(1 << s->bit));
136         load_timer_at(t + 150 + 8000 + s->command * 8);
137         while (done == 0)
138                 poll_spi();
139 }
140
141 int main(void)
142 {
143         uint8_t i;
144         uint8_t t, diff;
145
146         /* LED */
147         DDRB = 0x20;
148
149         /* servo outputs */
150         DDRC = 0x7;
151
152         /* start timer1 at clk/1 (8Mhz) */
153         TCNT1 = 0;
154         TCCR1B = _BV(CS10);
155
156         /* start timer0 at clk/1024 (~8Khz) */
157         TCNT0 = 0;
158         TCCR0B = _BV(CS02) | _BV(CS00);
159
160         /* enable spi (don't set unused MISO as output) */
161         SPCR = _BV(SPE);
162
163         sei();
164
165         bypass = 1;
166         while (1) {
167                 t = TCNT0;
168                 for (i = 0; i < NB_SERVO; i++) {
169                         do_one_servo(&servo_table[i]);
170                 }
171                 /* wait 20 ms */
172                 while (1) {
173                         diff = TCNT0 - t;
174                         if (diff >= 160)
175                                 break;
176                         poll_spi();
177                 }
178                 /* bypass mode */
179                 while (bypass == 1) {
180                         PORTC = PORTB;
181                 }
182         }
183
184         return 0;
185 }