1 /*
2 * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <esp_types.h>
8 #include <sys/lock.h>
9 #include "sdkconfig.h"
10 #include "stdatomic.h"
11 #include "esp_log.h"
12 #include "esp_check.h"
13 #include "esp_heap_caps.h"
14 #include "freertos/FreeRTOS.h"
15 #include "driver/gpio.h"
16 #include "driver/rtc_io.h"
17 #include "esp_adc/adc_oneshot.h"
18 #include "esp_clk_tree.h"
19 #include "esp_private/adc_private.h"
20 #include "esp_private/adc_share_hw_ctrl.h"
21 #include "esp_private/sar_periph_ctrl.h"
22 #include "esp_private/esp_sleep_internal.h"
23 #include "hal/adc_types.h"
24 #include "hal/adc_oneshot_hal.h"
25 #include "hal/adc_ll.h"
26 #include "soc/adc_periph.h"
27
28
29 #if CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM
30 #define ADC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
31 #else
32 #define ADC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
33 #endif
34
35
36 extern portMUX_TYPE rtc_spinlock;
37 static const char *TAG = "adc_oneshot";
38
39
40 typedef struct adc_oneshot_unit_ctx_t {
41 adc_oneshot_hal_ctx_t hal;
42 uint32_t unit_id;
43 adc_ulp_mode_t ulp_mode;
44 } adc_oneshot_unit_ctx_t;
45
46 typedef struct adc_oneshot_ctx_t {
47 _lock_t mutex;
48 adc_oneshot_unit_ctx_t *units[SOC_ADC_PERIPH_NUM];
49 int apb_periph_ref_cnts; //For the chips that ADC oneshot mode using APB_SARADC periph
50 } adc_oneshot_ctx_t;
51
52
53 static adc_oneshot_ctx_t s_ctx; //ADC oneshot mode context
54 static atomic_bool s_adc_unit_claimed[SOC_ADC_PERIPH_NUM] = {ATOMIC_VAR_INIT(false),
55 #if (SOC_ADC_PERIPH_NUM >= 2)
56 ATOMIC_VAR_INIT(false)
57 #endif
58 };
59
60
61 static bool s_adc_unit_claim(adc_unit_t unit);
62 static bool s_adc_unit_free(adc_unit_t unit);
63 static esp_err_t s_adc_io_init(adc_unit_t unit, adc_channel_t channel);
64
65
adc_oneshot_io_to_channel(int io_num,adc_unit_t * unit_id,adc_channel_t * channel)66 esp_err_t adc_oneshot_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel)
67 {
68 return adc_io_to_channel(io_num, unit_id, channel);
69 }
70
adc_oneshot_channel_to_io(adc_unit_t unit_id,adc_channel_t channel,int * io_num)71 esp_err_t adc_oneshot_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num)
72 {
73 return adc_channel_to_io(unit_id, channel, io_num);
74 }
75
adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t * init_config,adc_oneshot_unit_handle_t * ret_unit)76 esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit)
77 {
78 esp_err_t ret = ESP_OK;
79 adc_oneshot_unit_ctx_t *unit = NULL;
80 ESP_GOTO_ON_FALSE(init_config && ret_unit, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument: null pointer");
81 ESP_GOTO_ON_FALSE(init_config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, err, TAG, "invalid unit");
82 #if CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_ADC_ONESHOT_FORCE_USE_ADC2_ON_C3
83 /**
84 * We only check this on ESP32C3, because other adc units are no longer supported on later chips
85 * If CONFIG_ADC_ONESHOT_FORCE_USE_ADC2_ON_C3 is enabled, we jump this check
86 */
87 ESP_GOTO_ON_FALSE(SOC_ADC_DIG_SUPPORTED_UNIT(init_config->unit_id), ESP_ERR_INVALID_ARG, err, TAG, "adc unit not supported");
88 #endif
89
90 unit = heap_caps_calloc(1, sizeof(adc_oneshot_unit_ctx_t), ADC_MEM_ALLOC_CAPS);
91 ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit");
92
93 bool success_claim = s_adc_unit_claim(init_config->unit_id);
94 ESP_GOTO_ON_FALSE(success_claim, ESP_ERR_NOT_FOUND, err, TAG, "adc%d is already in use", init_config->unit_id + 1);
95 _lock_acquire(&s_ctx.mutex);
96 s_ctx.units[init_config->unit_id] = unit;
97 _lock_release(&s_ctx.mutex);
98 unit->unit_id = init_config->unit_id;
99 unit->ulp_mode = init_config->ulp_mode;
100
101 adc_oneshot_clk_src_t clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
102 if (init_config->clk_src) {
103 clk_src = init_config->clk_src;
104 }
105 uint32_t clk_src_freq_hz = 0;
106 ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), err, TAG, "clock source not supported");
107
108 adc_oneshot_hal_cfg_t config = {
109 .unit = init_config->unit_id,
110 .work_mode = (init_config->ulp_mode == ADC_ULP_MODE_FSM) ? ADC_HAL_ULP_FSM_MODE : ADC_HAL_SINGLE_READ_MODE,
111 .clk_src = clk_src,
112 .clk_src_freq_hz = clk_src_freq_hz,
113 };
114 adc_oneshot_hal_init(&(unit->hal), &config);
115
116 #if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
117 //To enable the APB_SARADC periph if needed
118 _lock_acquire(&s_ctx.mutex);
119 s_ctx.apb_periph_ref_cnts++;
120 if (s_ctx.apb_periph_ref_cnts == 1) {
121 adc_apb_periph_claim();
122 }
123 _lock_release(&s_ctx.mutex);
124 #endif
125
126 if (init_config->ulp_mode == ADC_ULP_MODE_DISABLE) {
127 sar_periph_ctrl_adc_oneshot_power_acquire();
128 } else {
129 esp_sleep_enable_adc_tsens_monitor(true);
130 }
131
132 ESP_LOGD(TAG, "new adc unit%"PRId32" is created", unit->unit_id);
133 *ret_unit = unit;
134 return ESP_OK;
135
136 err:
137 if (unit) {
138 free(unit);
139 }
140 return ret;
141 }
142
adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle,adc_channel_t channel,const adc_oneshot_chan_cfg_t * config)143 esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config)
144 {
145 ESP_RETURN_ON_FALSE(handle && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
146 ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid attenuation");
147 ESP_RETURN_ON_FALSE(((config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
148 ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
149
150 s_adc_io_init(handle->unit_id, channel);
151
152 adc_oneshot_hal_ctx_t *hal = &(handle->hal);
153 adc_oneshot_hal_chan_cfg_t cfg = {
154 .atten = config->atten,
155 .bitwidth = (config->bitwidth == ADC_BITWIDTH_DEFAULT) ? SOC_ADC_RTC_MAX_BITWIDTH : config->bitwidth,
156 };
157 portENTER_CRITICAL(&rtc_spinlock);
158 adc_oneshot_hal_channel_config(hal, &cfg, channel);
159 if (handle->ulp_mode) {
160 adc_oneshot_hal_setup(hal, channel);
161 }
162 portEXIT_CRITICAL(&rtc_spinlock);
163
164 return ESP_OK;
165 }
166
adc_oneshot_read(adc_oneshot_unit_handle_t handle,adc_channel_t chan,int * out_raw)167 esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
168 {
169 ESP_RETURN_ON_FALSE(handle && out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
170 ESP_RETURN_ON_FALSE(chan < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
171
172 if (adc_lock_try_acquire(handle->unit_id) != ESP_OK) {
173 return ESP_ERR_TIMEOUT;
174 }
175 portENTER_CRITICAL(&rtc_spinlock);
176
177 adc_oneshot_hal_setup(&(handle->hal), chan);
178 #if SOC_ADC_CALIBRATION_V1_SUPPORTED
179 adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
180 adc_hal_calibration_init(handle->unit_id);
181 adc_set_hw_calibration_code(handle->unit_id, atten);
182 #endif // SOC_ADC_CALIBRATION_V1_SUPPORTED
183 bool valid = false;
184 valid = adc_oneshot_hal_convert(&(handle->hal), out_raw);
185
186 portEXIT_CRITICAL(&rtc_spinlock);
187 adc_lock_release(handle->unit_id);
188
189 return valid ? ESP_OK : ESP_ERR_TIMEOUT;
190 }
191
adc_oneshot_read_isr(adc_oneshot_unit_handle_t handle,adc_channel_t chan,int * out_raw)192 esp_err_t adc_oneshot_read_isr(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
193 {
194 ESP_RETURN_ON_FALSE_ISR(handle && out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
195 ESP_RETURN_ON_FALSE_ISR(out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
196 ESP_RETURN_ON_FALSE_ISR(chan < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
197
198 portENTER_CRITICAL_SAFE(&rtc_spinlock);
199
200 adc_oneshot_hal_setup(&(handle->hal), chan);
201 #if SOC_ADC_CALIBRATION_V1_SUPPORTED
202 adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
203 adc_hal_calibration_init(handle->unit_id);
204 adc_set_hw_calibration_code(handle->unit_id, atten);
205 #endif
206 adc_oneshot_hal_convert(&(handle->hal), out_raw);
207
208 portEXIT_CRITICAL_SAFE(&rtc_spinlock);
209
210 return ESP_OK;
211 }
212
adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle)213 esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle)
214 {
215 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
216 adc_ulp_mode_t ulp_mode = handle->ulp_mode;
217 bool success_free = s_adc_unit_free(handle->unit_id);
218 ESP_RETURN_ON_FALSE(success_free, ESP_ERR_NOT_FOUND, TAG, "adc%"PRId32" isn't in use", handle->unit_id + 1);
219
220 _lock_acquire(&s_ctx.mutex);
221 s_ctx.units[handle->unit_id] = NULL;
222 _lock_release(&s_ctx.mutex);
223
224 ESP_LOGD(TAG, "adc unit%"PRId32" is deleted", handle->unit_id);
225 free(handle);
226
227 if (ulp_mode == ADC_ULP_MODE_DISABLE) {
228 sar_periph_ctrl_adc_oneshot_power_release();
229 } else {
230 esp_sleep_enable_adc_tsens_monitor(false);
231 }
232
233 #if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
234 //To free the APB_SARADC periph if needed
235 _lock_acquire(&s_ctx.mutex);
236 s_ctx.apb_periph_ref_cnts--;
237 assert(s_ctx.apb_periph_ref_cnts >= 0);
238 if (s_ctx.apb_periph_ref_cnts == 0) {
239 adc_apb_periph_free();
240 }
241 _lock_release(&s_ctx.mutex);
242 #endif
243
244 return ESP_OK;
245 }
246
adc_oneshot_get_calibrated_result(adc_oneshot_unit_handle_t handle,adc_cali_handle_t cali_handle,adc_channel_t chan,int * cali_result)247 esp_err_t adc_oneshot_get_calibrated_result(adc_oneshot_unit_handle_t handle, adc_cali_handle_t cali_handle, adc_channel_t chan, int *cali_result)
248 {
249 int raw = 0;
250 ESP_RETURN_ON_ERROR(adc_oneshot_read(handle, chan, &raw), TAG, "adc oneshot read fail");
251 ESP_LOGD(TAG, "raw: 0d%d", raw);
252 ESP_RETURN_ON_ERROR(adc_cali_raw_to_voltage(cali_handle, raw, cali_result), TAG, "adc calibration fail");
253
254 return ESP_OK;
255 }
256
257 #define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel])
258
s_adc_io_init(adc_unit_t unit,adc_channel_t channel)259 static esp_err_t s_adc_io_init(adc_unit_t unit, adc_channel_t channel)
260 {
261 ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(unit), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
262
263 #if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
264
265 uint32_t io_num = ADC_GET_IO_NUM(unit, channel);
266 gpio_config_t cfg = {
267 .pin_bit_mask = BIT64(io_num),
268 .mode = GPIO_MODE_DISABLE,
269 .pull_up_en = GPIO_PULLUP_DISABLE,
270 .pull_down_en = GPIO_PULLDOWN_DISABLE,
271 .intr_type = GPIO_INTR_DISABLE,
272 };
273 ESP_RETURN_ON_ERROR(gpio_config(&cfg), TAG, "IO config fail");
274 #else
275 gpio_num_t io_num = ADC_GET_IO_NUM(unit, channel);
276 ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), TAG, "IO config fail");
277 ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_DISABLED), TAG, "IO config fail");
278 ESP_RETURN_ON_ERROR(rtc_gpio_pulldown_dis(io_num), TAG, "IO config fail");
279 ESP_RETURN_ON_ERROR(rtc_gpio_pullup_dis(io_num), TAG, "IO config fail");
280 #endif
281
282 return ESP_OK;
283 }
284
s_adc_unit_claim(adc_unit_t unit)285 static bool s_adc_unit_claim(adc_unit_t unit)
286 {
287 bool false_var = false;
288 return atomic_compare_exchange_strong(&s_adc_unit_claimed[unit], &false_var, true);
289 }
290
s_adc_unit_free(adc_unit_t unit)291 static bool s_adc_unit_free(adc_unit_t unit)
292 {
293 bool true_var = true;
294 return atomic_compare_exchange_strong(&s_adc_unit_claimed[unit], &true_var, false);
295 }
296