altos/micropeak-v2.0: expose log and config commands over USB
[fw/altos] / src / micropeak-v2.0 / ao_micropeak.c
1 /*
2  * Copyright © 2017 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_exti.h>
21 #include <ao_micropeak.h>
22 #include <ao_report_micro.h>
23 #include <ao_log_micro.h>
24 #include <ao_storage.h>
25
26 static struct ao_ms5607_value   value;
27
28 alt_t           ground_alt, max_alt;
29 alt_t           ao_max_height;
30
31 void
32 ao_pa_get(void)
33 {
34         ao_ms5607_sample(&ao_ms5607_current);
35         ao_ms5607_convert(&ao_ms5607_current, &value);
36         pa = value.pres;
37 }
38
39 static void
40 ao_compute_height(void)
41 {
42         ground_alt = ao_pa_to_altitude(pa_ground);
43         max_alt = ao_pa_to_altitude(pa_min);
44         ao_max_height = max_alt - ground_alt;
45 }
46
47 static void
48 ao_pips(void)
49 {
50         uint8_t i;
51         for (i = 0; i < 10; i++) {
52                 ao_led_toggle(AO_LED_REPORT);
53                 ao_delay(AO_MS_TO_TICKS(80));
54         }
55         ao_delay(AO_MS_TO_TICKS(200));
56 }
57
58 void
59 ao_delay_until(uint16_t target) {
60         int16_t delay = target - ao_time();
61         if (delay > 0) {
62                 ao_sleep_for(ao_delay_until, delay);
63         }
64 }
65
66 static struct ao_task mp_task;
67
68 static void
69 ao_battery_disable(void)
70 {
71         /* Disable */
72         if (stm_adc.cr & (1 << STM_ADC_CR_ADEN)) {
73                 stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
74                 while (stm_adc.cr & (1 << STM_ADC_CR_ADDIS))
75                         ;
76         }
77
78         /* Turn off everything */
79         stm_adc.cr &= ~((1 << STM_ADC_CR_ADCAL) |
80                         (1 << STM_ADC_CR_ADSTP) |
81                         (1 << STM_ADC_CR_ADSTART) |
82                         (1 << STM_ADC_CR_ADEN));
83 }
84
85 static void
86 ao_battery_init(void)
87 {
88         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADCEN);
89
90         ao_battery_disable();
91
92         /* Configure */
93         stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) |                             /* analog watchdog channel 0 */
94                          (0 << STM_ADC_CFGR1_AWDEN) |                             /* Disable analog watchdog */
95                          (0 << STM_ADC_CFGR1_AWDSGL) |                            /* analog watchdog on all channels */
96                          (0 << STM_ADC_CFGR1_DISCEN) |                            /* Not discontinuous mode. All channels converted with one trigger */
97                          (0 << STM_ADC_CFGR1_AUTOOFF) |                           /* Leave ADC running */
98                          (1 << STM_ADC_CFGR1_WAIT) |                              /* Wait for data to be read before next conversion */
99                          (0 << STM_ADC_CFGR1_CONT) |                              /* only one set of conversions per trigger */
100                          (1 << STM_ADC_CFGR1_OVRMOD) |                            /* overwrite on overrun */
101                          (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) |   /* SW trigger */
102                          (0 << STM_ADC_CFGR1_ALIGN) |                             /* Align to LSB */
103                          (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) |            /* 12 bit resolution */
104                          (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) |    /* scan 0 .. n */
105                          (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
106                          (1 << STM_ADC_CFGR1_DMAEN));                             /* enable DMA */
107
108         /* Set the clock */
109         stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
110
111         /* Shortest sample time */
112         stm_adc.smpr = STM_ADC_SMPR_SMP_71_5 << STM_ADC_SMPR_SMP;
113
114         /* Select Vref */
115         stm_adc.chselr = 1 << 17;
116
117         stm_adc.ccr = ((0 << STM_ADC_CCR_VBATEN) |
118                        (0 << STM_ADC_CCR_TSEN) |
119                        (1 << STM_ADC_CCR_VREFEN));
120
121         /* Calibrate */
122         stm_adc.cr |= (1 << STM_ADC_CR_ADCAL);
123         while ((stm_adc.cr & (1 << STM_ADC_CR_ADCAL)) != 0)
124                 ;
125
126         /* Enable */
127         stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
128         while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
129                 ;
130
131         /* Clear any stale status bits */
132         stm_adc.isr = 0;
133
134         /* Turn on syscfg */
135         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
136 }
137
138 static void
139 ao_battery_fini(void)
140 {
141         /* Disable */
142         ao_battery_disable();
143
144         /* Power down */
145         stm_rcc.apb2enr &= ~(1 << STM_RCC_APB2ENR_ADCEN);
146 }
147
148 static uint16_t
149 ao_battery_voltage(void)
150 {
151         uint16_t        vrefint;
152
153         ao_battery_init();
154
155         stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
156
157         while ((stm_adc.isr & (1 << STM_ADC_ISR_EOC)) == 0)
158                 ao_arch_nop();
159
160         vrefint = stm_adc.dr;
161
162         ao_battery_fini();
163
164         return 330 * stm_cal.vrefint_cal / vrefint;
165 }
166
167
168 uint8_t ao_on_battery;
169
170 static void
171 ao_micropeak(void)
172 {
173         ao_ms5607_setup();
174
175         /* Give the person a second to get their finger out of the way */
176         ao_delay(AO_MS_TO_TICKS(1000));
177
178         ao_log_micro_restore();
179         ao_compute_height();
180         ao_report_altitude();
181
182         ao_pips();
183
184         ao_log_micro_dump();
185
186 #if BOOST_DELAY
187         ao_delay(BOOST_DELAY);
188 #endif
189
190         ao_microflight();
191
192         ao_log_micro_save();
193         ao_compute_height();
194         ao_report_altitude();
195
196         ao_sleep_mode();
197         ao_sleep(&ao_on_battery);
198 }
199
200 static void
201 ao_show_bat(void)
202 {
203         printf("battery: %u\n", ao_battery_voltage());
204 }
205
206 uint8_t
207 ao_log_present(void)
208 {
209         uint16_t        n_samples;
210
211         ao_eeprom_read(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples));
212
213         return n_samples != 0xffff;
214 }
215
216 static void
217 ao_log_list(void)
218 {
219         if (ao_log_present())
220                 printf ("flight %d start %x end %x\n",
221                         1,
222                         0, MAX_LOG_OFFSET >> 8);
223         printf ("done\n");
224 }
225
226 static void
227 ao_log_delete(void)
228 {
229         int16_t cmd_flight = 1;
230
231         ao_cmd_white();
232         if (ao_cmd_lex_c == '-') {
233                 cmd_flight = -1;
234                 ao_cmd_lex();
235         }
236         cmd_flight *= ao_cmd_decimal();
237         if (ao_cmd_status != ao_cmd_success)
238                 return;
239
240         /* Look for the flight log matching the requested flight */
241         if (cmd_flight == 1 && ao_log_present()) {
242                 uint32_t        pos;
243                 for (pos = 0; pos < ao_storage_log_max; pos += ao_storage_block)
244                         ao_storage_erase(pos);
245                 puts("Erased");
246                 return;
247         }
248         printf("No such flight: %d\n", cmd_flight);
249 }
250
251 static struct ao_cmds mp_cmd[] = {
252         { ao_log_list,  "l\0List logs" },
253         { ao_log_delete,        "d <flight-number>\0Delete flight" },
254         { ao_show_bat, "b\0Show battery voltage" },
255         { 0 }
256 };
257
258 static void
259 ao_hsi_init(void)
260 {
261         uint32_t        cfgr;
262
263         /* Disable all interrupts */
264         stm_rcc.cir = 0;
265
266         /* Enable prefetch */
267         stm_flash.acr |= (1 << STM_FLASH_ACR_PRFTBE);
268
269         /* Enable power interface clock */
270         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
271
272         /* HCLK to 48MHz -> AHB prescaler = /1 */
273         cfgr = stm_rcc.cfgr;
274         cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
275         cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
276         stm_rcc.cfgr = cfgr;
277         while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
278                (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
279                 ao_arch_nop();
280
281         /* APB Prescaler = AO_APB_PRESCALER */
282         cfgr = stm_rcc.cfgr;
283         cfgr &= ~(STM_RCC_CFGR_PPRE_MASK << STM_RCC_CFGR_PPRE);
284         cfgr |= (AO_RCC_CFGR_PPRE_DIV << STM_RCC_CFGR_PPRE);
285         stm_rcc.cfgr = cfgr;
286
287         /* Clear reset flags */
288         stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
289 }
290
291 void
292 main(void)
293 {
294         int i;
295
296         for (i = 0; i < 100000; i++)
297                 ao_arch_nop();
298
299         if (ao_battery_voltage() < 320)
300                 ao_on_battery = 1;
301
302         /* Leave the system running on the HSI if we're on battery */
303         if (!ao_on_battery)
304                 ao_clock_init();
305         else
306                 ao_hsi_init();
307
308         ao_led_init();
309         ao_task_init();
310         ao_timer_init();
311         ao_serial_init();
312         stm_moder_set(&stm_gpioa, 2, STM_MODER_OUTPUT);
313         ao_dma_init();
314         ao_spi_init();
315         ao_exti_init();
316
317         ao_storage_setup();
318
319         ao_ms5607_init();
320         ao_storage_init();
321
322         if (ao_on_battery) {
323                 /* On battery power, run the flight code */
324                 ao_add_task(&mp_task, ao_micropeak, "micropeak");
325         } else {
326                 /* otherwise, turn on USB and run the command processor */
327                 ao_usb_init();
328                 ao_cmd_init();
329                 ao_cmd_register(mp_cmd);
330                 ao_config_init();
331         }
332         ao_start_scheduler();
333 }