altos: Get ao_pad.c working on telefire v0.1
[fw/altos] / src / drivers / ao_pad.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_pad.h>
20 #include <ao_74hc497.h>
21
22 __xdata uint8_t ao_pad_ignite;
23
24 #define ao_pad_igniter_status(c)        AO_PAD_IGNITER_STATUS_UNKNOWN
25 #define ao_pad_arm_status()             AO_PAD_ARM_STATUS_UNKNOWN
26
27 #if 0
28 #define PRINTD(...) printf(__VA_ARGS__)
29 #else
30 #define PRINTD(...) 
31 #endif
32
33 static void
34 ao_pad_run(void)
35 {
36         for (;;) {
37                 while (!ao_pad_ignite)
38                         ao_sleep(&ao_pad_ignite);
39                 /*
40                  * Actually set the pad bits
41                  */
42                 AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | ao_pad_ignite;
43                 while (ao_pad_ignite) {
44                         ao_pad_ignite = 0;
45                         ao_delay(AO_PAD_FIRE_TIME);
46                 }
47                 AO_PAD_PORT &= ~(AO_PAD_ALL_PINS);
48         }
49 }
50
51 static void
52 ao_pad_status(void)
53 {
54         uint8_t                 c;
55         uint8_t                 sample;
56         __pdata uint8_t                 prev = 0, cur = 0;
57         __pdata uint8_t                 beeping = 0;
58         __xdata struct ao_data  *packet;
59
60         sample = ao_data_head;
61         for (;;) {
62                 __pdata int16_t                 pyro;
63                 ao_arch_critical(
64                         while (sample == ao_data_head)
65                                 ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
66                         );
67
68                 packet = &ao_data_ring[sample];
69                 sample = ao_data_ring_next(sample);
70
71                 pyro = packet->adc.pyro;
72
73 #define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0))
74
75                 cur = 0;
76                 if (pyro > VOLTS_TO_PYRO(4)) {
77                         for (c = 0; c < AO_PAD_NUM; c++) {
78                                 int16_t         sense = packet->adc.sense[c];
79
80                                 if (sense >= pyro / 4 * 3)
81                                         cur |= AO_LED_CONTINUITY(c);
82                         }
83                 }
84                 if (cur != prev) {
85                         ao_led_set_mask(cur, AO_LED_CONTINUITY_MASK);
86                         prev = cur;
87                 }
88
89                 if (pyro > VOLTS_TO_PYRO(9) && sample == 0) {
90                         beeping = 1;
91                         ao_beep(AO_BEEP_HIGH);
92                 } else if (beeping) {
93                         beeping = 0;
94                         ao_beep(0);
95                 }
96         }
97 }
98
99 static __pdata uint8_t  ao_pad_armed;
100 static __pdata uint16_t ao_pad_arm_time;
101 static __pdata uint8_t  ao_pad_box;
102 static __xdata uint8_t  ao_pad_disabled;
103
104 void
105 ao_pad_disable(void)
106 {
107         if (!ao_pad_disabled) {
108                 ao_pad_disabled = 1;
109                 ao_radio_recv_abort();
110         }
111 }
112
113 void
114 ao_pad_enable(void)
115 {
116         ao_pad_disabled = 0;
117         ao_wakeup (&ao_pad_disabled);
118 }
119
120 static void
121 ao_pad(void)
122 {
123         static __xdata struct ao_pad_command    command;
124         static __xdata struct ao_pad_query      query;
125         int16_t time_difference;
126         uint8_t c;
127
128         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
129         ao_pad_box = ao_74hc497_read();
130         ao_led_set(0);
131         ao_led_on(AO_LED_POWER);
132         for (;;) {
133                 flush();
134                 while (ao_pad_disabled)
135                         ao_sleep(&ao_pad_disabled);
136                 if (ao_radio_cmac_recv(&command, sizeof (command), 0) != AO_RADIO_CMAC_OK)
137                         continue;
138                 
139                 PRINTD ("tick %d serial %d cmd %d channel %d\n",
140                         command.tick, command.serial, command.cmd, command.channel);
141
142                 switch (command.cmd) {
143                 case AO_LAUNCH_ARM:
144                         if (command.box != ao_pad_box) {
145                                 PRINTD ("box number mismatch\n");
146                                 break;
147                         }
148
149                         if (command.channels & ~(AO_PAD_ALL_PINS))
150                                 break;
151
152                         time_difference = command.tick - ao_time();
153                         PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
154                         if (time_difference < 0)
155                                 time_difference = -time_difference;
156                         if (time_difference > 10) {
157                                 PRINTD ("time difference too large %d\n", time_difference);
158                                 break;
159                         }
160                         PRINTD ("armed\n");
161                         ao_pad_armed = command.channels;
162                         ao_pad_arm_time = ao_time();
163
164                         /* fall through ... */
165
166                 case AO_LAUNCH_QUERY:
167                         if (command.box != ao_pad_box) {
168                                 PRINTD ("box number mismatch\n");
169                                 break;
170                         }
171
172                         query.tick = ao_time();
173                         query.box = ao_pad_box;
174                         query.channels = AO_PAD_ALL_PINS;
175                         query.armed = ao_pad_armed;
176                         query.arm_status = ao_pad_arm_status();
177                         for (c = 0; c < AO_PAD_NUM; c++)
178                                 query.igniter_status[c] = ao_pad_igniter_status(c);
179                         PRINTD ("query tick %d serial %d channel %d valid %d arm %d igniter %d\n",
180                                 query.tick, query.serial, query.channel, query.valid, query.arm_status,
181                                 query.igniter_status);
182                         ao_radio_cmac_send(&query, sizeof (query));
183                         break;
184                 case AO_LAUNCH_FIRE:
185                         if (!ao_pad_armed) {
186                                 PRINTD ("not armed\n");
187                                 break;
188                         }
189                         if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
190                                 PRINTD ("late pad arm_time %d time %d\n",
191                                         ao_pad_arm_time, ao_time());
192                                 break;
193                         }
194                         time_difference = command.tick - ao_time();
195                         if (time_difference < 0)
196                                 time_difference = -time_difference;
197                         if (time_difference > 10) {
198                                 PRINTD ("time different too large %d\n", time_difference);
199                                 break;
200                         }
201                         PRINTD ("ignite\n");
202                         ao_pad_ignite = ao_pad_armed;
203                         ao_wakeup(&ao_pad_ignite);
204                         break;
205                 }
206         }
207 }
208
209 void
210 ao_pad_test(void)
211 {
212 #if 0
213         switch (ao_igniter_status(ao_igniter_drogue)) {
214         case ao_igniter_ready:
215         case ao_igniter_active:
216                 printf ("Armed: ");
217                 switch (ao_igniter_status(ao_igniter_main)) {
218                 default:
219                         printf("unknown status\n");
220                         break;
221                 case ao_igniter_ready:
222                         printf("igniter good\n");
223                         break;
224                 case ao_igniter_open:
225                         printf("igniter bad\n");
226                         break;
227                 }
228                 break;
229         default:
230                 printf("Disarmed\n");
231         }
232 #endif
233 }
234
235 void
236 ao_pad_manual(void)
237 {
238         ao_cmd_white();
239         if (!ao_match_word("DoIt"))
240                 return;
241         ao_cmd_decimal();
242         if (ao_cmd_status != ao_cmd_success)
243                 return;
244         ao_pad_ignite = 1 << ao_cmd_lex_i;
245         ao_wakeup(&ao_pad_ignite);
246 }
247
248 static __xdata struct ao_task ao_pad_task;
249 static __xdata struct ao_task ao_pad_ignite_task;
250 static __xdata struct ao_task ao_pad_status_task;
251
252 __code struct ao_cmds ao_pad_cmds[] = {
253         { ao_pad_test,  "t\0Test pad continuity" },
254         { ao_pad_manual,        "i <key> <n>\0Fire igniter. <key> is doit with D&I" },
255         { 0, NULL }
256 };
257
258 void
259 ao_pad_init(void)
260 {
261 #if AO_PAD_NUM > 0
262         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
263 #endif
264 #if AO_PAD_NUM > 1
265         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
266 #endif
267 #if AO_PAD_NUM > 2
268         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
269 #endif
270 #if AO_PAD_NUM > 3
271         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
272 #endif
273         ao_cmd_register(&ao_pad_cmds[0]);
274         ao_add_task(&ao_pad_task, ao_pad, "pad listener");
275         ao_add_task(&ao_pad_ignite_task, ao_pad_run, "pad igniter");
276         ao_add_task(&ao_pad_status_task, ao_pad_status, "pad status");
277 }