altos/micropeak-v2.0: Don't run flight code when plugged in
[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         ao_storage_setup();
175
176         /* Give the person a second to get their finger out of the way */
177         ao_delay(AO_MS_TO_TICKS(1000));
178
179         ao_pips();
180
181         ao_log_micro_restore();
182         ao_compute_height();
183         ao_report_altitude();
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(&ao_on_battery);
197 }
198
199 static void
200 ao_show_bat(void)
201 {
202         printf("battery: %u\n", ao_battery_voltage());
203 }
204
205 static struct ao_cmds mp_cmd[] = {
206         { ao_show_bat, "b\0Show battery voltage" },
207         { 0 }
208 };
209
210 static void
211 ao_hsi_init(void)
212 {
213         uint32_t        cfgr;
214
215         /* Disable all interrupts */
216         stm_rcc.cir = 0;
217
218         /* Enable prefetch */
219         stm_flash.acr |= (1 << STM_FLASH_ACR_PRFTBE);
220
221         /* Enable power interface clock */
222         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
223
224         /* HCLK to 48MHz -> AHB prescaler = /1 */
225         cfgr = stm_rcc.cfgr;
226         cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
227         cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
228         stm_rcc.cfgr = cfgr;
229         while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
230                (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
231                 ao_arch_nop();
232
233         /* APB Prescaler = AO_APB_PRESCALER */
234         cfgr = stm_rcc.cfgr;
235         cfgr &= ~(STM_RCC_CFGR_PPRE_MASK << STM_RCC_CFGR_PPRE);
236         cfgr |= (AO_RCC_CFGR_PPRE_DIV << STM_RCC_CFGR_PPRE);
237         stm_rcc.cfgr = cfgr;
238
239         /* Clear reset flags */
240         stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
241 }
242
243 void
244 main(void)
245 {
246         if (ao_battery_voltage() < 320)
247                 ao_on_battery = 1;
248
249         /* Leave the system running on the HSI if we're on battery */
250         if (!ao_on_battery)
251                 ao_clock_init();
252         else
253                 ao_hsi_init();
254
255         ao_led_init();
256         ao_task_init();
257         ao_timer_init();
258         ao_serial_init();
259         stm_moder_set(&stm_gpioa, 2, STM_MODER_OUTPUT);
260         ao_dma_init();
261         ao_spi_init();
262         ao_exti_init();
263
264         ao_ms5607_init();
265         ao_storage_init();
266
267         if (ao_on_battery) {
268                 /* On battery power, run the flight code */
269                 ao_add_task(&mp_task, ao_micropeak, "micropeak");
270         } else {
271                 /* otherwise, turn on USB and run the command processor */
272                 ao_usb_init();
273                 ao_cmd_init();
274                 ao_cmd_register(mp_cmd);
275         }
276         ao_start_scheduler();
277 }