Skip to content

Commit c03c57d

Browse files
committed
Merge branch 'develop'
2 parents 4e9943d + fe21be6 commit c03c57d

7 files changed

Lines changed: 245 additions & 27 deletions

File tree

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"m5stack/M5Utility": ">=0.0.10",
1515
"m5stack/M5HAL": "*"
1616
},
17-
"version": "0.4.2",
17+
"version": "0.4.3",
1818
"frameworks": [
1919
"arduino"
2020
],

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=M5UnitUnified
2-
version=0.4.2
2+
version=0.4.3
33
author=M5Stack
44
maintainer=M5Stack
55
sentence=M5UnitUnified is a library for unified handling of various M5 units products. (Alpha version)

src/M5UnitComponent.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,11 @@ bool Component::readAnalogRX(uint16_t& v)
341341
return adapter()->readAnalogRX(v) == m5::hal::error::error_t::OK;
342342
}
343343

344+
bool Component::readAnalogMilliVoltsRX(uint32_t& mv)
345+
{
346+
return adapter()->readAnalogMilliVoltsRX(mv) == m5::hal::error::error_t::OK;
347+
}
348+
344349
bool Component::pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us)
345350
{
346351
return adapter()->pulseInRX(duration, state, timeout_us) == m5::hal::error::error_t::OK;
@@ -371,6 +376,11 @@ bool Component::readAnalogTX(uint16_t& v)
371376
return adapter()->readAnalogTX(v) == m5::hal::error::error_t::OK;
372377
}
373378

379+
bool Component::readAnalogMilliVoltsTX(uint32_t& mv)
380+
{
381+
return adapter()->readAnalogMilliVoltsTX(mv) == m5::hal::error::error_t::OK;
382+
}
383+
374384
bool Component::pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us)
375385
{
376386
return adapter()->pulseInTX(duration, state, timeout_us) == m5::hal::error::error_t::OK;

src/M5UnitComponent.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,15 @@ class Component {
448448
bool readDigitalRX(bool& high);
449449
bool writeAnalogRX(const uint16_t v);
450450
bool readAnalogRX(uint16_t& v);
451+
bool readAnalogMilliVoltsRX(uint32_t& mv);
451452
bool pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us = 1000000);
452453

453454
bool pinModeTX(const gpio::Mode m);
454455
bool writeDigitalTX(const bool high);
455456
bool readDigitalTX(bool& high);
456457
bool writeAnalogTX(const uint16_t v);
457458
bool readAnalogTX(uint16_t& v);
459+
bool readAnalogMilliVoltsTX(uint32_t& mv);
458460
bool pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us = 1000000);
459461
///@endcond
460462

src/m5_unit_component/adapter_base.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ class Adapter {
8484
{
8585
return m5::hal::error::error_t::UNKNOWN_ERROR;
8686
}
87+
virtual m5::hal::error::error_t readAnalogMilliVoltsRX(uint32_t&)
88+
{
89+
return m5::hal::error::error_t::UNKNOWN_ERROR;
90+
}
8791
virtual m5::hal::error::error_t pulseInRX(uint32_t&, const int, const uint32_t)
8892
{
8993
return m5::hal::error::error_t::UNKNOWN_ERROR;
@@ -109,6 +113,10 @@ class Adapter {
109113
{
110114
return m5::hal::error::error_t::UNKNOWN_ERROR;
111115
}
116+
virtual m5::hal::error::error_t readAnalogMilliVoltsTX(uint32_t&)
117+
{
118+
return m5::hal::error::error_t::UNKNOWN_ERROR;
119+
}
112120
virtual m5::hal::error::error_t pulseInTX(uint32_t&, const int, const uint32_t)
113121
{
114122
return m5::hal::error::error_t::UNKNOWN_ERROR;
@@ -199,6 +207,10 @@ class Adapter {
199207
{
200208
return _impl->readAnalogRX(v);
201209
}
210+
inline m5::hal::error::error_t readAnalogMilliVoltsRX(uint32_t& mv)
211+
{
212+
return _impl->readAnalogMilliVoltsRX(mv);
213+
}
202214
inline m5::hal::error::error_t pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us)
203215
{
204216
return _impl->pulseInRX(duration, state, timeout_us);
@@ -224,6 +236,10 @@ class Adapter {
224236
{
225237
return _impl->readAnalogTX(v);
226238
}
239+
inline m5::hal::error::error_t readAnalogMilliVoltsTX(uint32_t& mv)
240+
{
241+
return _impl->readAnalogMilliVoltsTX(mv);
242+
}
227243
inline m5::hal::error::error_t pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us)
228244
{
229245
return _impl->pulseInTX(duration, state, timeout_us);

src/m5_unit_component/adapter_gpio.cpp

Lines changed: 194 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
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"
159163
constexpr 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"
202207
constexpr 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"
226232
constexpr 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"
236242
constexpr 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"
246253
constexpr int8_t gpio_to_adc_table[] = {
247254
/* 0 */ -1,
248255
/* 1 */ -1,
@@ -330,6 +337,70 @@ int gpio_to_adc12(const int8_t pin)
330337
namespace m5 {
331338
namespace 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+
333404
namespace gpio {
334405

335406
uint8_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+
474643
m5::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

Comments
 (0)