-
Notifications
You must be signed in to change notification settings - Fork 2k
/
adc_l0.c
169 lines (138 loc) · 4.05 KB
/
adc_l0.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
* Copyright (C) 2014-2017 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_stm32
* @ingroup drivers_periph_adc
* @{
*
* @file
* @brief Low-level ADC driver implementation
*
* @author Aurélien Fillau <aurelien.fillau@we-sens.com>
*
* @}
*/
#include "cpu.h"
#include "mutex.h"
#include "periph/adc.h"
/**
* @brief Allocate locks for all three available ADC device
*
* All STM32L0 CPUs we support so far only come with a single ADC device.
*/
static mutex_t lock = MUTEX_INIT;
static inline void prep(void)
{
mutex_lock(&lock);
periph_clk_en(APB2, RCC_APB2ENR_ADCEN);
}
static inline void done(void)
{
periph_clk_dis(APB2, RCC_APB2ENR_ADCEN);
mutex_unlock(&lock);
}
static void _enable_adc(void)
{
if ((ADC1->CR & ADC_CR_ADEN) != 0) {
ADC1->CR |= ADC_CR_ADDIS;
while(ADC1->CR & ADC_CR_ADEN) {} /* Wait for ADC disabled */
}
if ((ADC1->CR & ADC_CR_ADEN) == 0) {
/* Then, start a calibration */
ADC1->CR |= ADC_CR_ADCAL;
while(ADC1->CR & ADC_CR_ADCAL) {} /* Wait for the end of calibration */
}
/* Clear flag */
ADC1->ISR |= ADC_ISR_ADRDY;
/* enable device */
ADC1->CR = ADC_CR_ADVREGEN | ADC_CR_ADEN;
/* Wait for ADC to be ready */
while (!(ADC1->ISR & ADC_ISR_ADRDY)) {}
}
static void _disable_adc(void)
{
/* Disable ADC */
if ((ADC1->CR & ADC_CR_ADEN) != 0) {
ADC1->CR |= ADC_CR_ADDIS;
while(ADC1->CR & ADC_CR_ADEN) {} /* Wait for ADC disabled */
/* Disable Voltage regulator */
ADC1->CR = 0;
ADC1->ISR = 0;
}
}
int adc_init(adc_t line)
{
/* make sure the given line is valid */
if (line >= ADC_NUMOF) {
return -1;
}
/* lock and power on the device */
prep();
if ((adc_config[line].chan != 17) && (adc_config[line].chan != 18)) {
/* configure the pin */
gpio_init_analog(adc_config[line].pin);
}
/* no watchdog, no discontinuous mode, no auto off, single conv, no trigger,
* right align, 12bits, no dma, no wait */
ADC1->CFGR1 = 0;
/* no oversampling: Watch out, MSB (CKMODE) MUST not be changed while on
* (it is zero by default) */
ADC1->CFGR2 = 0;
/* activate VREF, and set prescaler to 4 (4Mhz clock)
* activate also temp sensor, so that it will be ready for temp measure */
ADC->CCR = ADC_CCR_VREFEN | ADC_CCR_TSEN | ADC_CCR_PRESC_1;
/* Sampling time selection: 7 => 160 clocks => 40µs @ 4MHz
* (must be 10+10 for ref start and sampling time) */
ADC1->SMPR |= ADC_SMPR_SMP;
/* clear previous flag */
ADC1->ISR |= ADC_ISR_EOC;
/* power off an release device for now */
done();
return 0;
}
int32_t adc_sample(adc_t line, adc_res_t res)
{
int sample;
/* check if resolution is applicable */
if ( (res != ADC_RES_6BIT) &&
(res != ADC_RES_8BIT) &&
(res != ADC_RES_10BIT) &&
(res != ADC_RES_12BIT)) {
return -1;
}
/* lock and power on the ADC device */
prep();
/* Enable ADC */
_enable_adc();
/* Reactivate VREFINT and temperature sensor if necessary */
if (adc_config[line].chan == 17) {
ADC->CCR |= ADC_CCR_VREFEN;
}
else if (adc_config[line].chan == 18) {
ADC->CCR |= ADC_CCR_TSEN;
}
/* else nothing */
/* set resolution and channel */
ADC1->CFGR1 &= ~ADC_CFGR1_RES;
ADC1->CFGR1 |= res & ADC_CFGR1_RES;
ADC1->CHSELR = (1 << adc_config[line].chan);
/* clear flag */
ADC1->ISR |= ADC_ISR_EOC;
/* start conversion and wait for results */
ADC1->CR |= ADC_CR_ADSTART;
while (!(ADC1->ISR & ADC_ISR_EOC)) {}
/* read result */
sample = (int)ADC1->DR;
/* Disable ADC */
_disable_adc();
/* Deactivate VREFINT and temperature sensor to save power */
ADC->CCR &= ~(ADC_CCR_VREFEN | ADC_CCR_TSEN);
/* unlock and power off device again */
done();
return sample;
}