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
@@ -330,6 +333,70 @@ int gpio_to_adc12(const int8_t pin)
330333namespace m5 {
331334namespace unit {
332335
336+ AdapterGPIOBase::GPIOImpl::~GPIOImpl ()
337+ {
338+ release_adc_resources ();
339+ }
340+
341+ void AdapterGPIOBase::GPIOImpl::release_adc_resources ()
342+ {
343+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
344+ if (_cali_handle) {
345+ auto cali = static_cast <adc_cali_handle_t >(_cali_handle);
346+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
347+ adc_cali_delete_scheme_curve_fitting (cali);
348+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
349+ adc_cali_delete_scheme_line_fitting (cali);
350+ #endif
351+ _cali_handle = nullptr ;
352+ _cached_cali_channel = -1 ;
353+ }
354+ if (_adc_handle) {
355+ adc_oneshot_del_unit (static_cast <adc_oneshot_unit_handle_t >(_adc_handle));
356+ _adc_handle = nullptr ;
357+ _cached_adc_unit = -1 ;
358+ }
359+ #endif
360+ }
361+
362+ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::ensure_adc_handle (const gpio_num_t pin)
363+ {
364+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
365+ const auto ch = gpio_to_adc_channel (pin);
366+ if (ch < 0 ) {
367+ return m5::hal::error::error_t ::INVALID_ARGUMENT;
368+ }
369+ int8_t needed_unit = (ch < 10 ) ? 0 : 1 ;
370+
371+ if (_adc_handle && _cached_adc_unit == needed_unit) {
372+ return m5::hal::error::error_t ::OK;
373+ }
374+
375+ // ADC unit changed — release everything and recreate
376+ release_adc_resources ();
377+
378+ adc_unit_t unit = (needed_unit == 0 ) ? ADC_UNIT_1 : ADC_UNIT_2;
379+ adc_oneshot_unit_handle_t handle{};
380+ #if defined(CONFIG_IDF_TARGET_ESP32C6)
381+ adc_oneshot_unit_init_cfg_t init_config = {
382+ .unit_id = unit, .clk_src = ADC_DIGI_CLK_SRC_DEFAULT, .ulp_mode = ADC_ULP_MODE_DISABLE};
383+ #else
384+ adc_oneshot_unit_init_cfg_t init_config = {
385+ .unit_id = unit, .clk_src = ADC_RTC_CLK_SRC_DEFAULT, .ulp_mode = ADC_ULP_MODE_DISABLE};
386+ #endif
387+
388+ if (adc_oneshot_new_unit (&init_config, &handle) != ESP_OK) {
389+ return m5::hal::error::error_t ::UNKNOWN_ERROR;
390+ }
391+
392+ _adc_handle = handle;
393+ _cached_adc_unit = needed_unit;
394+ return m5::hal::error::error_t ::OK;
395+ #else
396+ return m5::hal::error::error_t ::OK;
397+ #endif
398+ }
399+
333400namespace gpio {
334401
335402uint8_t calculate_rmt_clk_div (uint32_t apb_freq_hz, uint32_t tick_ns)
@@ -416,37 +483,28 @@ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog(uint16_t& value,
416483
417484#if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
418485 // 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;
486+ auto err = ensure_adc_handle (pin);
487+ if (err != m5::hal::error::error_t ::OK) {
488+ return err;
433489 }
434490
491+ auto adc_handle = static_cast <adc_oneshot_unit_handle_t >(_adc_handle);
492+ adc_channel_t channel = static_cast <adc_channel_t >((ch < 10 ) ? ch : (ch - 10 ));
493+
435494 adc_oneshot_chan_cfg_t chan_config = {
436495 .atten = M5_ADC_ATTEN_DB, // 0~3.3V
437496 .bitwidth = ADC_BITWIDTH_DEFAULT // 12bit
438497 };
439498
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- }
499+ if (adc_oneshot_config_channel (adc_handle, channel, &chan_config) != ESP_OK) {
500+ return m5::hal::error::error_t ::UNKNOWN_ERROR;
501+ }
502+ int raw{};
503+ if (adc_oneshot_read (adc_handle, channel, &raw) != ESP_OK) {
504+ return m5::hal::error::error_t ::UNKNOWN_ERROR;
447505 }
448- adc_oneshot_del_unit (adc_handle );
449- return ret ;
506+ value = static_cast < uint16_t >(raw );
507+ return m5::hal::error:: error_t ::OK ;
450508
451509#else
452510 // ESP-IDF 4.x
@@ -471,6 +529,113 @@ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog(uint16_t& value,
471529#endif
472530}
473531
532+ m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog_millivolts (uint32_t & millivolts, const gpio_num_t pin)
533+ {
534+ millivolts = 0 ;
535+
536+ const auto ch = gpio_to_adc_channel (pin);
537+ if (ch < 0 ) {
538+ return m5::hal::error::error_t ::INVALID_ARGUMENT;
539+ }
540+ #if !defined(SOC_ADC_PERIPH_NUM) || SOC_ADC_PERIPH_NUM <= 1
541+ if (ch >= 10 ) {
542+ M5_LIB_LOGE (" Not support ADC2" );
543+ return m5::hal::error::error_t ::NOT_IMPLEMENTED;
544+ }
545+ #endif
546+
547+ #if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
548+ // ESP-IDF 5.x: Use adc_cali for calibrated millivolt reading
549+ auto err = ensure_adc_handle (pin);
550+ if (err != m5::hal::error::error_t ::OK) {
551+ return err;
552+ }
553+
554+ auto adc_handle = static_cast <adc_oneshot_unit_handle_t >(_adc_handle);
555+ adc_channel_t channel = static_cast <adc_channel_t >((ch < 10 ) ? ch : (ch - 10 ));
556+
557+ adc_oneshot_chan_cfg_t chan_config = {
558+ .atten = M5_ADC_ATTEN_DB, // 0~3.3V
559+ .bitwidth = ADC_BITWIDTH_DEFAULT // 12bit
560+ };
561+
562+ if (adc_oneshot_config_channel (adc_handle, channel, &chan_config) != ESP_OK) {
563+ return m5::hal::error::error_t ::UNKNOWN_ERROR;
564+ }
565+
566+ // Ensure calibration handle for this channel
567+ if (_cached_cali_channel != ch) {
568+ // Release old cali handle if any
569+ if (_cali_handle) {
570+ auto old_cali = static_cast <adc_cali_handle_t >(_cali_handle);
571+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
572+ adc_cali_delete_scheme_curve_fitting (old_cali);
573+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
574+ adc_cali_delete_scheme_line_fitting (old_cali);
575+ #endif
576+ _cali_handle = nullptr ;
577+ }
578+
579+ adc_unit_t unit = (_cached_adc_unit == 0 ) ? ADC_UNIT_1 : ADC_UNIT_2;
580+ adc_cali_handle_t cali_handle{};
581+ bool cali_ok{};
582+
583+ #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
584+ adc_cali_curve_fitting_config_t cali_config = {
585+ .unit_id = unit,
586+ .chan = channel,
587+ .atten = M5_ADC_ATTEN_DB,
588+ .bitwidth = ADC_BITWIDTH_DEFAULT,
589+ };
590+ cali_ok = (adc_cali_create_scheme_curve_fitting (&cali_config, &cali_handle) == ESP_OK);
591+ #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
592+ adc_cali_line_fitting_config_t cali_config = {
593+ .unit_id = unit,
594+ .atten = M5_ADC_ATTEN_DB,
595+ .bitwidth = ADC_BITWIDTH_DEFAULT,
596+ };
597+ cali_ok = (adc_cali_create_scheme_line_fitting (&cali_config, &cali_handle) == ESP_OK);
598+ #endif
599+
600+ if (cali_ok) {
601+ _cali_handle = cali_handle;
602+ _cached_cali_channel = ch;
603+ }
604+ }
605+
606+ int raw{};
607+ if (adc_oneshot_read (adc_handle, channel, &raw) != ESP_OK) {
608+ return m5::hal::error::error_t ::UNKNOWN_ERROR;
609+ }
610+
611+ if (_cali_handle) {
612+ int mv{};
613+ if (adc_cali_raw_to_voltage (static_cast <adc_cali_handle_t >(_cali_handle), raw, &mv) == ESP_OK) {
614+ millivolts = static_cast <uint32_t >(mv);
615+ return m5::hal::error::error_t ::OK;
616+ }
617+ }
618+
619+ // Fallback: uncalibrated estimate
620+ millivolts = static_cast <uint32_t >(raw * 3100 / 4095 );
621+ return m5::hal::error::error_t ::OK;
622+
623+ #else
624+ // ESP-IDF 4.x: Use esp_adc_cal for calibrated millivolt reading
625+ uint16_t raw{};
626+ auto err = read_analog (raw, pin);
627+ if (err != m5::hal::error::error_t ::OK) {
628+ return err;
629+ }
630+
631+ adc_unit_t unit = (ch < 10 ) ? ADC_UNIT_1 : ADC_UNIT_2;
632+ esp_adc_cal_characteristics_t chars{};
633+ esp_adc_cal_characterize (unit, M5_ADC_ATTEN_DB, ADC_WIDTH_BIT_12, 1100 , &chars);
634+ millivolts = esp_adc_cal_raw_to_voltage (raw, &chars);
635+ return m5::hal::error::error_t ::OK;
636+ #endif
637+ }
638+
474639m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::pulse_in (uint32_t & duration, const gpio_num_t pin, const int state,
475640 const uint32_t timeout_us)
476641{
0 commit comments