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