c0242b91d5d77a106c1013e9854ba3cd83c49f19
[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 static void
168 ao_log_erase(void)
169 {
170         ao_storage_erase(0, ao_storage_log_max);
171 }
172
173 uint8_t ao_on_battery;
174
175 static void
176 ao_micropeak(void)
177 {
178         ao_ms5607_setup();
179
180         /* Give the person a second to get their finger out of the way */
181         ao_delay(AO_MS_TO_TICKS(1000));
182
183         ao_log_micro_restore();
184         ao_compute_height();
185         ao_report_altitude();
186
187         ao_pips();
188
189         ao_log_micro_dump();
190
191 #if BOOST_DELAY
192         ao_delay(BOOST_DELAY);
193 #endif
194         ao_log_erase();
195
196         ao_microflight();
197
198         ao_log_micro_save();
199         ao_compute_height();
200         ao_report_altitude();
201
202         ao_sleep_mode();
203         ao_sleep(&ao_on_battery);
204 }
205
206 static void
207 ao_show_bat(void)
208 {
209         printf("battery: %u\n", ao_battery_voltage());
210 }
211
212 uint8_t
213 ao_log_present(void)
214 {
215         uint16_t        n_samples;
216
217         ao_eeprom_read(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples));
218
219         return n_samples != 0xffff;
220 }
221
222 static void
223 ao_log_list(void)
224 {
225         if (ao_log_present())
226                 printf ("flight %d start %x end %x\n",
227                         1,
228                         0, MAX_LOG_OFFSET >> 8);
229         printf ("done\n");
230 }
231
232 static void
233 ao_log_delete(void)
234 {
235         int16_t cmd_flight = 1;
236
237         ao_cmd_white();
238         if (ao_cmd_lex_c == '-') {
239                 cmd_flight = -1;
240                 ao_cmd_lex();
241         }
242         cmd_flight *= ao_cmd_decimal();
243         if (ao_cmd_status != ao_cmd_success)
244                 return;
245
246         /* Look for the flight log matching the requested flight */
247         if (cmd_flight == 1 && ao_log_present()) {
248                 ao_log_erase();
249                 puts("Erased");
250                 return;
251         }
252         printf("No such flight: %d\n", cmd_flight);
253 }
254
255 static struct ao_cmds mp_cmd[] = {
256         { ao_log_list,  "l\0List logs" },
257         { ao_log_delete,        "d <flight-number>\0Delete flight" },
258         { ao_show_bat, "b\0Show battery voltage" },
259         { 0 }
260 };
261
262 static void
263 ao_hsi_init(void)
264 {
265         uint32_t        cfgr;
266
267         /* Disable all interrupts */
268         stm_rcc.cir = 0;
269
270         /* Enable prefetch */
271         stm_flash.acr |= (1 << STM_FLASH_ACR_PRFTBE);
272
273         /* HCLK to 48MHz -> AHB prescaler = /1 */
274         cfgr = stm_rcc.cfgr;
275         cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
276         cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
277         stm_rcc.cfgr = cfgr;
278         while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
279                (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
280                 ao_arch_nop();
281
282         /* APB Prescaler = AO_APB_PRESCALER */
283         cfgr = stm_rcc.cfgr;
284         cfgr &= ~(STM_RCC_CFGR_PPRE_MASK << STM_RCC_CFGR_PPRE);
285         cfgr |= (AO_RCC_CFGR_PPRE_DIV << STM_RCC_CFGR_PPRE);
286         stm_rcc.cfgr = cfgr;
287
288         /* Clear reset flags */
289         stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
290 }
291
292 int
293 main(void)
294 {
295         int i;
296
297         for (i = 0; i < 100000; i++)
298                 ao_arch_nop();
299
300         if (ao_battery_voltage() < 320)
301                 ao_on_battery = 1;
302
303         /* Leave the system running on the HSI if we're on battery */
304         if (!ao_on_battery)
305                 ao_clock_init();
306         else
307                 ao_hsi_init();
308
309         ao_led_init();
310         ao_task_init();
311         ao_timer_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         /* Let FLITF clock turn off in sleep mode */
323         stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_FLITFEN);
324
325         /* Le SRAM clock turn off in sleep mode */
326         stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_SRAMEN);
327
328         if (ao_on_battery) {
329                 /* On battery power, run the flight code */
330                 ao_add_task(&mp_task, ao_micropeak, "micropeak");
331         } else {
332                 /* otherwise, turn on USB and run the command processor */
333                 ao_usb_init();
334                 ao_cmd_init();
335                 ao_cmd_register(mp_cmd);
336                 ao_config_init();
337         }
338         ao_start_scheduler();
339 }