-
Notifications
You must be signed in to change notification settings - Fork 7.4k
/
esp32-hal-adc.c
692 lines (614 loc) · 23.1 KB
/
esp32-hal-adc.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-adc.h"
#if SOC_ADC_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali_scheme.h"
// ESP32-C2 does not define those two for some reason
#ifndef SOC_ADC_DIGI_RESULT_BYTES
#define SOC_ADC_DIGI_RESULT_BYTES (4)
#endif
#ifndef SOC_ADC_DIGI_DATA_BYTES_PER_CONV
#define SOC_ADC_DIGI_DATA_BYTES_PER_CONV (4)
#endif
static uint8_t __analogAttenuation = ADC_11db;
static uint8_t __analogWidth = SOC_ADC_RTC_MAX_BITWIDTH;
static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH;
typedef struct {
voidFuncPtr fn;
void *arg;
} interrupt_config_t;
typedef struct {
adc_oneshot_unit_handle_t adc_oneshot_handle;
adc_continuous_handle_t adc_continuous_handle;
interrupt_config_t adc_interrupt_handle;
adc_cali_handle_t adc_cali_handle;
uint32_t buffer_size;
uint32_t conversion_frame_size;
} adc_handle_t;
adc_handle_t adc_handle[SOC_ADC_PERIPH_NUM];
static bool adcDetachBus(void *pin) {
adc_channel_t adc_channel;
adc_unit_t adc_unit;
uint8_t used_channels = 0;
adc_oneshot_io_to_channel((int)(pin - 1), &adc_unit, &adc_channel);
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
used_channels++;
}
}
if (used_channels == 1) { //only 1 channel is used
esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit].adc_oneshot_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_oneshot_handle = NULL;
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#elif !defined(CONFIG_IDF_TARGET_ESP32H2)
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#endif
}
adc_handle[adc_unit].adc_cali_handle = NULL;
}
return true;
}
esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, int8_t pin) {
esp_err_t err = ESP_OK;
adc_oneshot_chan_cfg_t config = {
.bitwidth = width,
.atten = (atten & 3),
};
if (pin == -1) { //Reconfigure all used analog pins/channels
for (int adc_unit = 0; adc_unit < SOC_ADC_PERIPH_NUM; adc_unit++) {
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
}
}
//ADC calibration reconfig only if all channels are updated
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d cali handle", adc_unit);
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_delete_scheme_curve_fitting failed with error: %d", err);
return err;
}
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = atten,
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d curve cali handle", adc_unit);
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err);
return err;
}
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d line cali handle", adc_unit);
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_delete_scheme_line_fitting failed with error: %d", err);
return err;
}
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = atten,
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d line cali handle", adc_unit);
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err);
return err;
}
#endif
}
}
}
//make it default for next channels
__analogWidth = width;
__analogAttenuation = atten;
} else { //Reconfigure single channel
if (perimanGetPinBusType(pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
adc_channel_t channel;
adc_unit_t adc_unit;
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return err;
}
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
} else {
log_e("Pin is not configured as analog channel");
}
}
return ESP_OK;
}
static inline uint16_t mapResolution(uint16_t value) {
uint8_t from = __analogWidth;
if (from == __analogReturnedWidth) {
return value;
}
if (from > __analogReturnedWidth) {
return value >> (from - __analogReturnedWidth);
}
return value << (__analogReturnedWidth - from);
}
void __analogSetAttenuation(adc_attenuation_t attenuation) {
if (__analogChannelConfig(__analogWidth, attenuation, -1) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
#if CONFIG_IDF_TARGET_ESP32
void __analogSetWidth(uint8_t bits) {
if (bits < SOC_ADC_RTC_MIN_BITWIDTH) {
bits = SOC_ADC_RTC_MIN_BITWIDTH;
} else if (bits > SOC_ADC_RTC_MAX_BITWIDTH) {
bits = SOC_ADC_RTC_MAX_BITWIDTH;
}
if (__analogChannelConfig(bits, __analogAttenuation, -1) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
#endif
esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit) {
esp_err_t err = ESP_OK;
if (adc_handle[adc_unit].adc_oneshot_handle == NULL) {
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = adc_unit,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit].adc_oneshot_handle);
if (err != ESP_OK) {
log_e("adc_oneshot_new_unit failed with error: %d", err);
return err;
}
}
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_ONESHOT, adcDetachBus);
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT, (void *)(pin + 1), adc_unit, channel)) {
adcDetachBus((void *)(pin + 1));
return err;
}
adc_oneshot_chan_cfg_t config = {
.bitwidth = __analogWidth,
.atten = __analogAttenuation,
};
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
return ESP_OK;
}
void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) {
if (__analogChannelConfig(__analogWidth, attenuation, pin) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
void __analogReadResolution(uint8_t bits) {
if (!bits || bits > 16) {
return;
}
__analogReturnedWidth = bits;
#if CONFIG_IDF_TARGET_ESP32
__analogSetWidth(bits); // hardware analog resolution from 9 to 12
#endif
}
uint16_t __analogRead(uint8_t pin) {
int value = 0;
adc_channel_t channel;
adc_unit_t adc_unit;
esp_err_t err = ESP_OK;
err = adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return value;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
log_d("Calling __analogInit! pin = %d", pin);
err = __analogInit(pin, channel, adc_unit);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return value;
}
}
adc_oneshot_read(adc_handle[adc_unit].adc_oneshot_handle, channel, &value);
return mapResolution(value);
}
uint32_t __analogReadMilliVolts(uint8_t pin) {
int value = 0;
adc_channel_t channel;
adc_unit_t adc_unit;
esp_err_t err = ESP_OK;
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return value;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
err = __analogInit(pin, channel, adc_unit);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return value;
}
}
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = __analogAttenuation,
.bitwidth = __analogWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __analogWidth,
.atten = __analogAttenuation,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#endif
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_x failed!");
return value;
}
}
err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit].adc_oneshot_handle, adc_handle[adc_unit].adc_cali_handle, channel, &value);
if (err != ESP_OK) {
log_e("adc_oneshot_get_calibrated_result failed!");
return 0;
}
return value;
}
extern uint16_t analogRead(uint8_t pin) __attribute__((weak, alias("__analogRead")));
extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__((weak, alias("__analogReadMilliVolts")));
extern void analogReadResolution(uint8_t bits) __attribute__((weak, alias("__analogReadResolution")));
extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetAttenuation")));
extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetPinAttenuation")));
#if CONFIG_IDF_TARGET_ESP32
extern void analogSetWidth(uint8_t bits) __attribute__((weak, alias("__analogSetWidth")));
#endif
/*
* ADC Continuous mode
*/
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type1.data)
#else
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type2.data)
#endif
static uint8_t __adcContinuousAtten = ADC_11db;
static uint8_t __adcContinuousWidth = SOC_ADC_DIGI_MAX_BITWIDTH;
static uint8_t used_adc_channels = 0;
adc_continuous_data_t *adc_result = NULL;
static bool adcContinuousDetachBus(void *adc_unit_number) {
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;
if (adc_handle[adc_unit].adc_continuous_handle == NULL) {
return true;
} else {
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_continuous_handle = NULL;
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#elif !defined(CONFIG_IDF_TARGET_ESP32H2)
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#endif
}
adc_handle[adc_unit].adc_cali_handle = NULL;
//set all used pins to INIT state
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
if (!perimanClearPinBus(io_pin)) {
return false;
}
}
}
}
return true;
}
bool IRAM_ATTR adcFnWrapper(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *args) {
interrupt_config_t *isr = (interrupt_config_t *)args;
//Check if edata->size matches conversion_frame_size, else just return from ISR
if (edata->size == adc_handle[0].conversion_frame_size) {
if (isr->fn) {
if (isr->arg) {
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
}
return false;
}
esp_err_t __analogContinuousInit(adc_channel_t *channel, uint8_t channel_num, adc_unit_t adc_unit, uint32_t sampling_freq_hz) {
//Create new ADC continuous handle
adc_continuous_handle_cfg_t adc_config = {
.max_store_buf_size = adc_handle[adc_unit].buffer_size,
.conv_frame_size = adc_handle[adc_unit].conversion_frame_size,
};
esp_err_t err = adc_continuous_new_handle(&adc_config, &adc_handle[adc_unit].adc_continuous_handle);
if (err != ESP_OK) {
log_e("adc_continuous_new_handle failed with error: %d", err);
return ESP_FAIL;
}
//Configure adc pins
adc_continuous_config_t dig_cfg = {
.sample_freq_hz = sampling_freq_hz,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_OUTPUT_TYPE,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
dig_cfg.pattern_num = channel_num;
for (int i = 0; i < channel_num; i++) {
adc_pattern[i].atten = __adcContinuousAtten;
adc_pattern[i].channel = channel[i];
adc_pattern[i].unit = ADC_UNIT_1;
adc_pattern[i].bit_width = __adcContinuousWidth;
}
dig_cfg.adc_pattern = adc_pattern;
err = adc_continuous_config(adc_handle[adc_unit].adc_continuous_handle, &dig_cfg);
if (err != ESP_OK) {
log_e("adc_continuous_config failed with error: %d", err);
return ESP_FAIL;
}
used_adc_channels = channel_num;
return ESP_OK;
}
bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)) {
adc_channel_t channel[pins_count];
adc_unit_t adc_unit = ADC_UNIT_1;
esp_err_t err = ESP_OK;
//Convert pins to channels and check if all are ADC1s unit
for (int i = 0; i < pins_count; i++) {
err = adc_continuous_io_to_channel(pins[i], &adc_unit, &channel[i]);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pins[i]);
return false;
}
if (adc_unit != 0) {
log_e("Only ADC1 pins are supported in continuous mode!");
return false;
}
}
//Check if Oneshot and Continuous handle exists
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
log_e("ADC%d is running in oneshot mode. Aborting.", adc_unit + 1);
return false;
}
if (adc_handle[adc_unit].adc_continuous_handle != NULL) {
log_e("ADC%d continuous is already initialized. To reconfigure call analogContinuousDeinit() first.", adc_unit + 1);
return false;
}
//Check sampling frequency
if ((sampling_freq_hz < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || (sampling_freq_hz > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)) {
log_e("Sampling frequency is out of range. Supported sampling frequencies are %d - %d", SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
return false;
}
//Set periman deinit function and reset all pins to init state.
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcContinuousDetachBus);
for (int j = 0; j < pins_count; j++) {
if (!perimanClearPinBus(pins[j])) {
return false;
}
}
//Set conversion frame and buffer size (conversion frame must be in multiples of SOC_ADC_DIGI_DATA_BYTES_PER_CONV)
adc_handle[adc_unit].conversion_frame_size = conversions_per_pin * pins_count * SOC_ADC_DIGI_RESULT_BYTES;
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
uint8_t calc_multiple = adc_handle[adc_unit].conversion_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV;
if (calc_multiple != 0) {
adc_handle[adc_unit].conversion_frame_size = (adc_handle[adc_unit].conversion_frame_size + calc_multiple);
}
#endif
adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;
//Conversion frame size buffer cant be bigger than 4092 bytes
if (adc_handle[adc_unit].conversion_frame_size > 4092) {
log_e("Buffers are too big. Please set lower conversions per pin.");
return false;
}
//Initialize continuous handle and pins
err = __analogContinuousInit(channel, sizeof(channel) / sizeof(adc_channel_t), adc_unit, sampling_freq_hz);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return false;
}
//Setup callbacks for complete event
adc_continuous_evt_cbs_t cbs = {
.on_conv_done = adcFnWrapper,
//.on_pool_ovf can be used in future
};
adc_handle[adc_unit].adc_interrupt_handle.fn = (voidFuncPtr)userFunc;
err = adc_continuous_register_event_callbacks(adc_handle[adc_unit].adc_continuous_handle, &cbs, &adc_handle[adc_unit].adc_interrupt_handle);
if (err != ESP_OK) {
log_e("adc_continuous_register_event_callbacks failed!");
return false;
}
//Allocate and prepare result structure for adc readings
adc_result = malloc(pins_count * sizeof(adc_continuous_data_t));
for (int k = 0; k < pins_count; k++) {
adc_result[k].pin = pins[k];
adc_result[k].channel = channel[k];
}
//Initialize ADC calibration handle
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = __adcContinuousAtten,
.bitwidth = __adcContinuousWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif !defined(CONFIG_IDF_TARGET_ESP32H2) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __adcContinuousWidth,
.atten = __adcContinuousAtten,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#endif
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_x failed!");
return false;
}
}
for (int k = 0; k < pins_count; k++) {
if (!perimanSetPinBus(pins[k], ESP32_BUS_TYPE_ADC_CONT, (void *)(adc_unit + 1), adc_unit, channel[k])) {
log_e("perimanSetPinBus to ADC Continuous failed!");
adcContinuousDetachBus((void *)(adc_unit + 1));
return false;
}
}
return true;
}
bool analogContinuousRead(adc_continuous_data_t **buffer, uint32_t timeout_ms) {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
uint32_t bytes_read = 0;
uint32_t read_raw[used_adc_channels];
uint32_t read_count[used_adc_channels];
uint8_t adc_read[adc_handle[ADC_UNIT_1].conversion_frame_size];
memset(adc_read, 0xcc, sizeof(adc_read));
memset(read_raw, 0, sizeof(read_raw));
memset(read_count, 0, sizeof(read_count));
esp_err_t err = adc_continuous_read(adc_handle[ADC_UNIT_1].adc_continuous_handle, adc_read, adc_handle[0].conversion_frame_size, &bytes_read, timeout_ms);
if (err != ESP_OK) {
if (err == ESP_ERR_TIMEOUT) {
log_e("Reading data failed: No data, increase timeout");
} else {
log_e("Reading data failed with error: %X", err);
}
*buffer = NULL;
return false;
}
for (int i = 0; i < bytes_read; i += SOC_ADC_DIGI_RESULT_BYTES) {
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&adc_read[i];
uint32_t chan_num = ADC_GET_CHANNEL(p);
uint32_t data = ADC_GET_DATA(p);
/* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */
if (chan_num >= SOC_ADC_CHANNEL_NUM(0)) {
log_e("Invalid data [%d_%d]", chan_num, data);
*buffer = NULL;
return false;
}
if (data >= (1 << SOC_ADC_DIGI_MAX_BITWIDTH)) {
data = 0;
log_e("Invalid data");
}
for (int j = 0; j < used_adc_channels; j++) {
if (adc_result[j].channel == chan_num) {
read_raw[j] += data;
read_count[j] += 1;
break;
}
}
}
for (int j = 0; j < used_adc_channels; j++) {
if (read_count[j] != 0) {
adc_result[j].avg_read_raw = read_raw[j] / read_count[j];
adc_cali_raw_to_voltage(adc_handle[ADC_UNIT_1].adc_cali_handle, adc_result[j].avg_read_raw, &adc_result[j].avg_read_mvolts);
} else {
log_w("No data read for pin %d", adc_result[j].pin);
}
}
*buffer = adc_result;
return true;
} else {
log_e("ADC Continuous is not initialized!");
return false;
}
}
bool analogContinuousStart() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
if (adc_continuous_start(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousStop() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
if (adc_continuous_stop(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousDeinit() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
esp_err_t err = adc_continuous_deinit(adc_handle[ADC_UNIT_1].adc_continuous_handle);
if (err != ESP_OK) {
return false;
}
free(adc_result);
adc_handle[ADC_UNIT_1].adc_continuous_handle = NULL;
} else {
log_i("ADC Continuous was not initialized");
}
return true;
}
void analogContinuousSetAtten(adc_attenuation_t attenuation) {
__adcContinuousAtten = attenuation;
}
void analogContinuousSetWidth(uint8_t bits) {
if ((bits < SOC_ADC_DIGI_MIN_BITWIDTH) || (bits > SOC_ADC_DIGI_MAX_BITWIDTH)) {
log_e("Selected width cannot be set. Range is from %d to %d", SOC_ADC_DIGI_MIN_BITWIDTH, SOC_ADC_DIGI_MAX_BITWIDTH);
return;
}
__adcContinuousWidth = bits;
}
#endif