altos/stm32l0: Add adc and flash drivers
[fw/altos] / src / stm32l0 / ao_adc_stm32l0.c
1 /*
2  * Copyright © 2020 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_adc_stm32l0.h>
21
22 void
23 ao_adc_init(void)
24 {
25         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADCEN);
26
27         /* Configure */
28         stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) |                             /* analog watchdog channel 0 */
29                          (0 << STM_ADC_CFGR1_AWDEN) |                             /* Disable analog watchdog */
30                          (0 << STM_ADC_CFGR1_AWDSGL) |                            /* analog watchdog on all channels */
31                          (0 << STM_ADC_CFGR1_DISCEN) |                            /* Not discontinuous mode. All channels converted with one trigger */
32                          (0 << STM_ADC_CFGR1_AUTOOFF) |                           /* Leave ADC running */
33                          (1 << STM_ADC_CFGR1_WAIT) |                              /* Wait for data to be read before next conversion */
34                          (0 << STM_ADC_CFGR1_CONT) |                              /* only one set of conversions per trigger */
35                          (1 << STM_ADC_CFGR1_OVRMOD) |                            /* overwrite on overrun */
36                          (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) |   /* SW trigger */
37                          (0 << STM_ADC_CFGR1_ALIGN) |                             /* Align to LSB */
38                          (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) |            /* 12 bit resolution */
39                          (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) |    /* scan 0 .. n */
40                          (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
41                          (0 << STM_ADC_CFGR1_DMAEN));                             /* disable DMA */
42
43         /* Set the clock */
44         stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
45
46         /* Shortest sample time */
47         stm_adc.smpr = STM_ADC_SMPR_SMP_71_5 << STM_ADC_SMPR_SMP;
48
49 #define AO_ADC_LFMEN    (AO_SYSCLK < 3500000)
50
51         stm_adc.ccr = ((AO_ADC_LFMEN << STM_ADC_CCR_LFMEN) |
52                        (0 << STM_ADC_CCR_VLCDEN) |
53                        (0 << STM_ADC_CCR_TSEN) |
54                        (0 << STM_ADC_CCR_VREFEN));
55
56         /* Calibrate. This also enables the ADC vreg */
57         stm_adc.cr |= (1 << STM_ADC_CR_ADCAL);
58         while ((stm_adc.cr & (1 << STM_ADC_CR_ADCAL)) != 0)
59                 ;
60
61         /* Enable */
62         stm_adc.isr = (1 << STM_ADC_ISR_ADRDY); /* Clear ADRDY bit */
63         stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
64         while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
65                 ;
66 }
67
68 static void
69 ao_adc_shutdown(void)
70 {
71         /* Disable ADC */
72         stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
73         while ((stm_adc.cr & (1 << STM_ADC_CR_ADEN)) != 0)
74                 ;
75
76         /* Clear ADRDY bit */
77         stm_adc.isr = (1 << STM_ADC_ISR_ADRDY);
78
79         /* Disable ADC vreg */
80         stm_adc.cr &= ~(1 << STM_ADC_CR_ADVREGEN);
81
82         /* Disable ADC clocks */
83         stm_rcc.apb2enr &= ~(1 << STM_RCC_APB2ENR_ADCEN);
84 }
85
86 uint16_t
87 ao_adc_read_vref(void)
88 {
89         uint16_t value;
90
91         ao_adc_init();
92
93         /* Turn on voltage reference */
94         stm_adc.ccr |= (1 << STM_ADC_CCR_VREFEN);
95
96         /* Select VREF */
97         stm_adc.chselr = (1 << STM_ADC_CHSEL_VREF);
98
99         /* Start conversion */
100         stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
101
102         /* Wait for conversion complete */
103         while ((stm_adc.isr & (1 << STM_ADC_ISR_EOC)) == 0)
104                 ;
105
106         /* Fetch result */
107         value = stm_adc.dr;
108
109         ao_adc_shutdown();
110         return value;
111 }