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