1515#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
1616#pragma message "Using RMT v2,Oneshot"
1717#include < esp_adc/adc_oneshot.h>
18+ #include < esp_adc/adc_cali.h>
19+ #include < esp_adc/adc_cali_scheme.h>
1820#else
1921#pragma message "Using RMT v1"
2022#include < driver/adc.h>
23+ #include < esp_adc_cal.h>
2124#endif
2225
2326// ADC_ATTEN_DB_12 was introduced in ESP-IDF v4.4.7 / v5.1.3
@@ -156,6 +159,7 @@ constexpr gpio_config_t gpio_cfg_table[] = {
156159};
157160
158161#if CONFIG_IDF_TARGET_ESP32
162+ #pragma message "ADC table: ESP32"
159163constexpr int8_t gpio_to_adc_table[] = {
160164 /* 0 */ 11 , // ADC2_CHANNEL_1
161165 /* 1 */ -1 ,
@@ -199,6 +203,7 @@ constexpr int8_t gpio_to_adc_table[] = {
199203 /* 39 */ 3 // ADC1_CHANNEL_3
200204};
201205#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
206+ #pragma message "ADC table: ESP32-S2/S3"
202207constexpr int8_t gpio_to_adc_table[] = {
203208 /* 0 */ -1 ,
204209 /* 1 */ 0 , // ADC1_CHANNEL_0
@@ -223,6 +228,7 @@ constexpr int8_t gpio_to_adc_table[] = {
223228 /* 20 */ 19 , // ADC2_CHANNEL_9
224229};
225230#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
231+ #pragma message "ADC table: ESP32-C3/C2"
226232constexpr int8_t gpio_to_adc_table[] = {
227233 /* 0 */ 0 , // ADC1_CHANNEL_0
228234 /* 1 */ 1 , // ADC1_CHANNEL_1
@@ -231,8 +237,8 @@ constexpr int8_t gpio_to_adc_table[] = {
231237 /* 4 */ 4 , // ADC1_CHANNEL_4
232238 /* 5 */ 10 , // ADC2_CHANNEL_0
233239};
234- #elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || \
235- CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
240+ #elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
241+ # pragma message "ADC table: ESP32-C6/H2/C5/C61"
236242constexpr int8_t gpio_to_adc_table[] = {
237243 /* 0 */ 0 , // ADC1_CHANNEL_0
238244 /* 1 */ 1 , // ADC1_CHANNEL_1
@@ -243,6 +249,7 @@ constexpr int8_t gpio_to_adc_table[] = {
243249 /* 6 */ 6 , // ADC1_CHANNEL_6
244250};
245251#elif CONFIG_IDF_TARGET_ESP32P4
252+ #pragma message "ADC table: ESP32-P4"
246253constexpr int8_t gpio_to_adc_table[] = {
247254 /* 0 */ -1 ,
248255 /* 1 */ -1 ,
@@ -330,6 +337,70 @@ int gpio_to_adc12(const int8_t pin)
330337namespace m5 {
331338namespace unit {
332339
340+ AdapterGPIOBase::GPIOImpl::~GPIOImpl ()
341+ {
342+ release_adc_resources ();
343+ }
344+
345+ void AdapterGPIOBase::GPIOImpl::release_adc_resources ()
346+ {
347+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
348+ if (_cali_handle) {
349+ auto cali = static_cast <adc_cali_handle_t >(_cali_handle);
350+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
351+ adc_cali_delete_scheme_curve_fitting (cali);
352+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
353+ adc_cali_delete_scheme_line_fitting (cali);
354+ #endif
355+ _cali_handle = nullptr ;
356+ _cached_cali_channel = -1 ;
357+ }
358+ if (_adc_handle) {
359+ adc_oneshot_del_unit (static_cast <adc_oneshot_unit_handle_t >(_adc_handle));
360+ _adc_handle = nullptr ;
361+ _cached_adc_unit = -1 ;
362+ }
363+ #endif
364+ }
365+
366+ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::ensure_adc_handle (const gpio_num_t pin)
367+ {
368+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
369+ const auto ch = gpio_to_adc_channel (pin);
370+ if (ch < 0 ) {
371+ return m5::hal::error::error_t ::INVALID_ARGUMENT ;
372+ }
373+ int8_t needed_unit = (ch < 10 ) ? 0 : 1 ;
374+
375+ if (_adc_handle && _cached_adc_unit == needed_unit) {
376+ return m5::hal::error::error_t ::OK ;
377+ }
378+
379+ // ADC unit changed — release everything and recreate
380+ release_adc_resources ();
381+
382+ adc_unit_t unit = (needed_unit == 0 ) ? ADC_UNIT_1 : ADC_UNIT_2 ;
383+ adc_oneshot_unit_handle_t handle{};
384+ #if defined(CONFIG_IDF_TARGET_ESP32C6)
385+ adc_oneshot_unit_init_cfg_t init_config = {
386+ .unit_id = unit, .clk_src = ADC_DIGI_CLK_SRC_DEFAULT , .ulp_mode = ADC_ULP_MODE_DISABLE };
387+ #else
388+ adc_oneshot_unit_init_cfg_t init_config = {
389+ .unit_id = unit, .clk_src = ADC_RTC_CLK_SRC_DEFAULT , .ulp_mode = ADC_ULP_MODE_DISABLE };
390+ #endif
391+
392+ if (adc_oneshot_new_unit (&init_config, &handle) != ESP_OK ) {
393+ return m5::hal::error::error_t ::UNKNOWN_ERROR ;
394+ }
395+
396+ _adc_handle = handle;
397+ _cached_adc_unit = needed_unit;
398+ return m5::hal::error::error_t ::OK ;
399+ #else
400+ return m5::hal::error::error_t ::OK ;
401+ #endif
402+ }
403+
333404namespace gpio {
334405
335406uint8_t calculate_rmt_clk_div (uint32_t apb_freq_hz, uint32_t tick_ns)
@@ -416,37 +487,28 @@ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog(uint16_t& value,
416487
417488#if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
418489 // ESP-IDF 5.x
419- adc_unit_t unit = (ch < 10 ) ? ADC_UNIT_1 : ADC_UNIT_2 ;
420- adc_channel_t channel = static_cast <adc_channel_t >((ch < 10 ) ? ch : (ch - 10 ));
421-
422- adc_oneshot_unit_handle_t adc_handle{};
423- #if defined(CONFIG_IDF_TARGET_ESP32C6)
424- adc_oneshot_unit_init_cfg_t init_config = {
425- .unit_id = unit, .clk_src = ADC_DIGI_CLK_SRC_DEFAULT , .ulp_mode = ADC_ULP_MODE_DISABLE };
426- #else
427- adc_oneshot_unit_init_cfg_t init_config = {
428- .unit_id = unit, .clk_src = ADC_RTC_CLK_SRC_DEFAULT , .ulp_mode = ADC_ULP_MODE_DISABLE };
429- #endif
430-
431- if (adc_oneshot_new_unit (&init_config, &adc_handle) != ESP_OK ) {
432- return m5::hal::error::error_t ::UNKNOWN_ERROR ;
490+ auto err = ensure_adc_handle (pin);
491+ if (err != m5::hal::error::error_t ::OK ) {
492+ return err;
433493 }
434494
495+ auto adc_handle = static_cast <adc_oneshot_unit_handle_t >(_adc_handle);
496+ adc_channel_t channel = static_cast <adc_channel_t >((ch < 10 ) ? ch : (ch - 10 ));
497+
435498 adc_oneshot_chan_cfg_t chan_config = {
436499 .atten = M5_ADC_ATTEN_DB , // 0~3.3V
437500 .bitwidth = ADC_BITWIDTH_DEFAULT // 12bit
438501 };
439502
440- auto ret = m5::hal::error::error_t ::UNKNOWN_ERROR ;
441- if (adc_oneshot_config_channel (adc_handle, channel, &chan_config) == ESP_OK ) {
442- int raw{};
443- if (adc_oneshot_read (adc_handle, channel, &raw) == ESP_OK ) {
444- value = static_cast <uint16_t >(raw);
445- ret = m5::hal::error::error_t ::OK ;
446- }
503+ if (adc_oneshot_config_channel (adc_handle, channel, &chan_config) != ESP_OK ) {
504+ return m5::hal::error::error_t ::UNKNOWN_ERROR ;
505+ }
506+ int raw{};
507+ if (adc_oneshot_read (adc_handle, channel, &raw) != ESP_OK ) {
508+ return m5::hal::error::error_t ::UNKNOWN_ERROR ;
447509 }
448- adc_oneshot_del_unit (adc_handle );
449- return ret ;
510+ value = static_cast < uint16_t >(raw );
511+ return m5::hal::error:: error_t :: OK ;
450512
451513#else
452514 // ESP-IDF 4.x
@@ -471,6 +533,113 @@ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog(uint16_t& value,
471533#endif
472534}
473535
536+ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog_millivolts (uint32_t & millivolts, const gpio_num_t pin)
537+ {
538+ millivolts = 0 ;
539+
540+ const auto ch = gpio_to_adc_channel (pin);
541+ if (ch < 0 ) {
542+ return m5::hal::error::error_t ::INVALID_ARGUMENT ;
543+ }
544+ #if !defined(SOC_ADC_PERIPH_NUM) || SOC_ADC_PERIPH_NUM <= 1
545+ if (ch >= 10 ) {
546+ M5_LIB_LOGE (" Not support ADC2" );
547+ return m5::hal::error::error_t ::NOT_IMPLEMENTED ;
548+ }
549+ #endif
550+
551+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
552+ // ESP-IDF 5.x: Use adc_cali for calibrated millivolt reading
553+ auto err = ensure_adc_handle (pin);
554+ if (err != m5::hal::error::error_t ::OK ) {
555+ return err;
556+ }
557+
558+ auto adc_handle = static_cast <adc_oneshot_unit_handle_t >(_adc_handle);
559+ adc_channel_t channel = static_cast <adc_channel_t >((ch < 10 ) ? ch : (ch - 10 ));
560+
561+ adc_oneshot_chan_cfg_t chan_config = {
562+ .atten = M5_ADC_ATTEN_DB , // 0~3.3V
563+ .bitwidth = ADC_BITWIDTH_DEFAULT // 12bit
564+ };
565+
566+ if (adc_oneshot_config_channel (adc_handle, channel, &chan_config) != ESP_OK ) {
567+ return m5::hal::error::error_t ::UNKNOWN_ERROR ;
568+ }
569+
570+ // Ensure calibration handle for this channel
571+ if (_cached_cali_channel != ch) {
572+ // Release old cali handle if any
573+ if (_cali_handle) {
574+ auto old_cali = static_cast <adc_cali_handle_t >(_cali_handle);
575+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
576+ adc_cali_delete_scheme_curve_fitting (old_cali);
577+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
578+ adc_cali_delete_scheme_line_fitting (old_cali);
579+ #endif
580+ _cali_handle = nullptr ;
581+ }
582+
583+ adc_unit_t unit = (_cached_adc_unit == 0 ) ? ADC_UNIT_1 : ADC_UNIT_2 ;
584+ adc_cali_handle_t cali_handle{};
585+ bool cali_ok{};
586+
587+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
588+ adc_cali_curve_fitting_config_t cali_config = {
589+ .unit_id = unit,
590+ .chan = channel,
591+ .atten = M5_ADC_ATTEN_DB ,
592+ .bitwidth = ADC_BITWIDTH_DEFAULT ,
593+ };
594+ cali_ok = (adc_cali_create_scheme_curve_fitting (&cali_config, &cali_handle) == ESP_OK );
595+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
596+ adc_cali_line_fitting_config_t cali_config = {
597+ .unit_id = unit,
598+ .atten = M5_ADC_ATTEN_DB ,
599+ .bitwidth = ADC_BITWIDTH_DEFAULT ,
600+ };
601+ cali_ok = (adc_cali_create_scheme_line_fitting (&cali_config, &cali_handle) == ESP_OK );
602+ #endif
603+
604+ if (cali_ok) {
605+ _cali_handle = cali_handle;
606+ _cached_cali_channel = ch;
607+ }
608+ }
609+
610+ int raw{};
611+ if (adc_oneshot_read (adc_handle, channel, &raw) != ESP_OK ) {
612+ return m5::hal::error::error_t ::UNKNOWN_ERROR ;
613+ }
614+
615+ if (_cali_handle) {
616+ int mv{};
617+ if (adc_cali_raw_to_voltage (static_cast <adc_cali_handle_t >(_cali_handle), raw, &mv) == ESP_OK ) {
618+ millivolts = static_cast <uint32_t >(mv);
619+ return m5::hal::error::error_t ::OK ;
620+ }
621+ }
622+
623+ // Fallback: uncalibrated estimate
624+ millivolts = static_cast <uint32_t >(raw * 3100 / 4095 );
625+ return m5::hal::error::error_t ::OK ;
626+
627+ #else
628+ // ESP-IDF 4.x: Use esp_adc_cal for calibrated millivolt reading
629+ uint16_t raw{};
630+ auto err = read_analog (raw, pin);
631+ if (err != m5::hal::error::error_t ::OK ) {
632+ return err;
633+ }
634+
635+ adc_unit_t unit = (ch < 10 ) ? ADC_UNIT_1 : ADC_UNIT_2 ;
636+ esp_adc_cal_characteristics_t chars{};
637+ esp_adc_cal_characterize (unit, M5_ADC_ATTEN_DB , ADC_WIDTH_BIT_12 , 1100 , &chars);
638+ millivolts = esp_adc_cal_raw_to_voltage (raw, &chars);
639+ return m5::hal::error::error_t ::OK ;
640+ #endif
641+ }
642+
474643m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::pulse_in (uint32_t & duration, const gpio_num_t pin, const int state,
475644 const uint32_t timeout_us)
476645{
0 commit comments