first commit

This commit is contained in:
stuce-bot 2025-06-30 20:47:33 +02:00
commit 5893b00dd2
1669 changed files with 1982740 additions and 0 deletions

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter.cpp
@brief Adapters to treat M5HAL and any connection in the same way
@note Currently handles Arduino directly, but will handle via M5HAL in the future
*/
#include "adapter.hpp"
#include <cassert>
#if defined(ARDUINO)
#include <Wire.h>
#endif
#include <M5HAL.hpp>
#include <M5Utility.hpp>
#include <soc/gpio_struct.h>
#include <soc/gpio_sig_map.h>
namespace m5 {
namespace unit {
// Adapter
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter.hpp
@brief Adapters to treat M5HAL and any connection in the same way
@note Currently handles Arduino directly, but will handle via M5HAL in the future
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_HPP
#define M5_UNIT_COMPONENT_ADAPTER_HPP
#include "adapter_base.hpp"
#include "adapter_i2c.hpp"
#include "identify_functions.hpp"
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include "adapter_gpio_v2.hpp"
#else
#include "adapter_gpio_v1.hpp"
#endif
#endif

View file

@ -0,0 +1,233 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_base.hpp
@brief Adapter base
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_BASE_HPP
#define M5_UNIT_COMPONENT_ADAPTER_BASE_HPP
#include <cstdint>
#include <cstddef>
#include <memory>
#include <M5HAL.hpp>
#include "types.hpp"
namespace m5 {
namespace unit {
/*!
@class m5::unit::Adapter
@brief Adapter base class to treat M5HAL and TwoWire,GPIO,Serial,SPI... in the same way
*/
class Adapter {
public:
enum class Type : uint8_t {
Unknown,
I2C,
GPIO,
UART,
SPI,
};
class Impl {
public:
Impl() = default;
virtual ~Impl()
{
}
// I2C R/W
virtual m5::hal::error::error_t readWithTransaction(uint8_t*, const size_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t*, const size_t, const uint32_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t, const uint8_t*, const size_t,
const uint32_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeWithTransaction(const uint16_t, const uint8_t*, const size_t,
const uint32_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t generalCall(const uint8_t*, const size_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
// GPIO R/W
virtual m5::hal::error::error_t pinModeRX(const gpio::Mode)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeDigitalRX(const bool)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t readDigitalRX(bool&)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeAnalogRX(const uint16_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t readAnalogRX(uint16_t&)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t pulseInRX(uint32_t&, const int, const uint32_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t pinModeTX(const gpio::Mode)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeDigitalTX(const bool)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t readDigitalTX(bool&)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t writeAnalogTX(const uint16_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t readAnalogTX(uint16_t&)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
virtual m5::hal::error::error_t pulseInTX(uint32_t&, const int, const uint32_t)
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
};
explicit Adapter() : _impl{new Impl()}
{
}
protected:
Adapter(const Type t, Impl* impl) : _type{t}, _impl{impl}
{
}
public:
Adapter(const Adapter&) = delete;
Adapter(Adapter&&) noexcept = default;
Adapter& operator=(const Adapter&) = delete;
Adapter& operator=(Adapter&&) noexcept = default;
virtual ~Adapter() = default;
inline Type type() const
{
return _type;
}
virtual Adapter* duplicate(const uint8_t /*addr*/)
{
return new Adapter();
}
// I2C R/W
inline m5::hal::error::error_t readWithTransaction(uint8_t* data, const size_t len)
{
return _impl->readWithTransaction(data, len);
}
inline m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len,
const uint32_t exparam = 1)
{
return _impl->writeWithTransaction(data, len, exparam);
}
inline m5::hal::error::error_t writeWithTransaction(const uint8_t reg, const uint8_t* data, const size_t len,
const uint32_t exparam = 1)
{
return _impl->writeWithTransaction(reg, data, len, exparam);
}
inline m5::hal::error::error_t writeWithTransaction(const uint16_t reg, const uint8_t* data, const size_t len,
const uint32_t exparam = 1)
{
return _impl->writeWithTransaction(reg, data, len, exparam);
}
inline m5::hal::error::error_t generalCall(const uint8_t* data, const size_t len)
{
return _impl->generalCall(data, len);
}
// GPIO R/W
inline m5::hal::error::error_t pinModeRX(const gpio::Mode m)
{
return _impl->pinModeRX(m);
}
inline m5::hal::error::error_t writeDigitalRX(const bool high)
{
return _impl->writeDigitalRX(high);
}
inline m5::hal::error::error_t readDigitalRX(bool& high)
{
return _impl->readDigitalRX(high);
}
inline m5::hal::error::error_t writeAnalogRX(const uint16_t v)
{
return _impl->writeAnalogRX(v);
}
inline m5::hal::error::error_t readAnalogRX(uint16_t& v)
{
return _impl->readAnalogRX(v);
}
inline m5::hal::error::error_t pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us)
{
return _impl->pulseInRX(duration, state, timeout_us);
}
inline m5::hal::error::error_t pinModeTX(const gpio::Mode m)
{
return _impl->pinModeTX(m);
}
inline m5::hal::error::error_t writeDigitalTX(const bool high)
{
return _impl->writeDigitalTX(high);
}
inline m5::hal::error::error_t readDigitalTX(bool& high)
{
return _impl->readDigitalTX(high);
}
inline m5::hal::error::error_t writeAnalogTX(const uint16_t v)
{
return _impl->writeAnalogTX(v);
}
inline m5::hal::error::error_t readAnalogTX(uint16_t& v)
{
return _impl->readAnalogTX(v);
}
inline m5::hal::error::error_t pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us)
{
return _impl->pulseInTX(duration, state, timeout_us);
}
private:
Type _type{Type::Unknown};
protected:
std::unique_ptr<Impl> _impl{};
};
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,507 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio.cpp
@brief Adapters to treat M5HAL and RMT in the same way
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#include "adapter_gpio.hpp"
#include <driver/gpio.h>
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#pragma message "Using RMT v2,Oneshot"
#include <esp_adc/adc_oneshot.h>
#else
#pragma message "Using RMT v1"
#include <driver/adc.h>
#endif
#if defined(SOC_DAC_SUPPORTED) && SOC_DAC_SUPPORTED
#pragma message "DAC supported"
#if __has_include(<driver/dac_common.h>)
#include <driver/dac_common.h>
#define USING_RMT_CHANNNE_T
#endif
#endif
#if !defined(USING_RMT_CHANNNE_T) && defined(ARDUINO)
#include <esp32-hal-dac.h>
#endif
#if SOC_ADC_SUPPORTED
#pragma message "ADC supported"
#else
#pragma message "ADC Not supported"
#endif
#include <esp_timer.h>
namespace {
constexpr gpio_config_t gpio_cfg_table[] = {
{
// Input
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// Output
.pin_bit_mask = 0,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// Pullup
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// InputPullup
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// Pulldown
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// InputPulldown
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// OpenDrain
.pin_bit_mask = 0,
.mode = GPIO_MODE_OUTPUT_OD,
.pull_up_en = GPIO_PULLUP_ENABLE, //
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// OutputOpenDrain,
.pin_bit_mask = 0,
.mode = GPIO_MODE_OUTPUT_OD,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
{
// Analog
.pin_bit_mask = 0,
.mode = GPIO_MODE_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
#if defined(CONFIG_IDF_TARGET_ESP32P4)
.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE,
#endif
},
};
#if CONFIG_IDF_TARGET_ESP32
constexpr int8_t gpio_to_adc_table[] = {
/* 0 */ 11, // ADC2_CHANNEL_1
/* 1 */ -1,
/* 2 */ 12, // ADC2_CHANNEL_2
/* 3 */ -1,
/* 4 */ 10, // ADC2_CHANNEL_0
/* 5 */ -1,
/* 6 */ -1,
/* 7 */ -1,
/* 8 */ -1,
/* 9 */ -1,
/* 10 */ -1,
/* 11 */ -1,
/* 12 */ 15, // ADC2_CHANNEL_5
/* 13 */ 14, // ADC2_CHANNEL_4
/* 14 */ 16, // ADC2_CHANNEL_6
/* 15 */ 13, // ADC2_CHANNEL_3
/* 16 */ -1,
/* 17 */ -1,
/* 18 */ -1,
/* 19 */ -1,
/* 20 */ -1,
/* 21 */ -1,
/* 22 */ -1,
/* 23 */ -1,
/* 24 */ -1,
/* 25 */ 18, // ADC2_CHANNEL_8
/* 26 */ 19, // ADC2_CHANNEL_9
/* 27 */ 17, // ADC2_CHANNEL_7
/* 28 */ -1,
/* 29 */ -1,
/* 30 */ -1,
/* 31 */ -1,
/* 32 */ 4, // ADC1_CHANNEL_4
/* 33 */ 5, // ADC1_CHANNEL_5
/* 34 */ 6, // ADC1_CHANNEL_6
/* 35 */ 7, // ADC1_CHANNEL_7
/* 36 */ 0, // ADC1_CHANNEL_0
/* 37 */ 1, // ADC1_CHANNEL_1
/* 38 */ 2, // ADC1_CHANNEL_2
/* 39 */ 3 // ADC1_CHANNEL_3
};
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
constexpr int8_t gpio_to_adc_table[] = {
/* 0 */ -1,
/* 1 */ 0, // ADC1_CHANNEL_0
/* 2 */ 1, // ADC1_CHANNEL_1
/* 3 */ 2, // ADC1_CHANNEL_2
/* 4 */ 3, // ADC1_CHANNEL_3
/* 5 */ 4, // ADC1_CHANNEL_4
/* 6 */ 5, // ADC1_CHANNEL_5
/* 7 */ 6, // ADC1_CHANNEL_6
/* 8 */ 7, // ADC1_CHANNEL_7
/* 9 */ 8, // ADC1_CHANNEL_8
/* 10 */ 9, // ADC1_CHANNEL_9
/* 11 */ 10, // ADC2_CHANNEL_0
/* 12 */ 11, // ADC2_CHANNEL_1
/* 13 */ 12, // ADC2_CHANNEL_2
/* 14 */ 13, // ADC2_CHANNEL_3
/* 15 */ 14, // ADC2_CHANNEL_4
/* 16 */ 15, // ADC2_CHANNEL_5
/* 17 */ 16, // ADC2_CHANNEL_6
/* 18 */ 17, // ADC2_CHANNEL_7
/* 19 */ 18, // ADC2_CHANNEL_8
/* 20 */ 19, // ADC2_CHANNEL_9
};
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
constexpr int8_t gpio_to_adc_table[] = {
/* 0 */ 0, // ADC1_CHANNEL_0
/* 1 */ 1, // ADC1_CHANNEL_1
/* 2 */ 2, // ADC1_CHANNEL_2
/* 3 */ 3, // ADC1_CHANNEL_3
/* 4 */ 4, // ADC1_CHANNEL_4
/* 5 */ 10, // ADC2_CHANNEL_0
};
#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || \
CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
constexpr int8_t gpio_to_adc_table[] = {
/* 0 */ 0, // ADC1_CHANNEL_0
/* 1 */ 1, // ADC1_CHANNEL_1
/* 2 */ 2, // ADC1_CHANNEL_2
/* 3 */ 3, // ADC1_CHANNEL_3
/* 4 */ 4, // ADC1_CHANNEL_4
/* 5 */ 5, // ADC1_CHANNEL_5
/* 6 */ 6, // ADC1_CHANNEL_6
};
#elif CONFIG_IDF_TARGET_ESP32P4
constexpr int8_t gpio_to_adc_table[] = {
/* 0 */ -1,
/* 1 */ -1,
/* 2 */ -1,
/* 3 */ -1,
/* 4 */ -1,
/* 5 */ -1,
/* 6 */ -1,
/* 7 */ -1,
/* 8 */ -1,
/* 9 */ -1,
/* 10 */ -1,
/* 11 */ -1,
/* 12 */ -1,
/* 13 */ -1,
/* 14 */ -1,
/* 15 */ -1,
/* 16 */ 0, // ADC1_CHANNEL_0
/* 17 */ 1, // ADC1_CHANNEL_1
/* 18 */ 2, // ADC1_CHANNEL_2
/* 19 */ 3, // ADC1_CHANNEL_3
/* 20 */ 4, // ADC1_CHANNEL_4
/* 21 */ 5, // ADC1_CHANNEL_5
/* 22 */ 6, // ADC1_CHANNEL_6
/* 23 */ 7, // ADC1_CHANNEL_7
/* 24 */ -1,
/* 25 */ -1,
/* 26 */ -1,
/* 27 */ -1,
/* 28 */ -1,
/* 29 */ -1,
/* 30 */ -1,
/* 31 */ -1,
/* 32 */ -1,
/* 33 */ -1,
/* 34 */ -1,
/* 35 */ -1,
/* 36 */ -1,
/* 37 */ -1,
/* 38 */ -1,
/* 39 */ -1,
/* 40 */ -1,
/* 41 */ -1,
/* 42 */ -1,
/* 43 */ -1,
/* 44 */ -1,
/* 45 */ -1,
/* 46 */ -1,
/* 47 */ -1,
/* 48 */ -1,
/* 49 */ 10, // ADC2_CHANNEL_0
/* 50 */ 11, // ADC2_CHANNEL_1
/* 51 */ 12, // ADC2_CHANNEL_2
/* 52 */ 13, // ADC2_CHANNEL_3
/* 53 */ 14, // ADC2_CHANNEL_4
/* 54 */ 15, // ADC2_CHANNEL_5
};
#else
#error Invalid target
#endif
// 0-9: ADC1 10-:ADC2 (ESP-IDF 4.x)
// 0-9, 10- : ADC (ESP-IDF 5.x)
int8_t gpio_to_adc_channel(const int8_t pin)
{
if (pin < 0 || pin >= m5::stl::size(gpio_to_adc_table)) {
return -1;
}
auto v = gpio_to_adc_table[pin];
return (v < 10) ? v : v - 10;
}
#if 0
// -1:ivalid 0:ADC1 1:ADC2
int gpio_to_adc12(const int8_t pin)
{
return (pin >= 0 && pin < m5::stl::size(gpio_to_adc_table))
? (gpio_to_adc_table[pin] < 0 ? -1 : (gpio_to_adc_table[pin] >= 10 ? 1 : 0))
: -1;
}
#endif
} // namespace
namespace m5 {
namespace unit {
namespace gpio {
uint8_t calculate_rmt_clk_div(uint32_t apb_freq_hz, uint32_t tick_ns)
{
if (tick_ns == 0) {
return 1;
}
uint64_t clk_div = (static_cast<uint64_t>(apb_freq_hz) * tick_ns + 500) / 1000000000UL;
clk_div = std::min<uint64_t>(255, std::max<uint64_t>(0, clk_div));
return static_cast<uint8_t>(clk_div);
}
uint32_t calculate_rmt_resolution_hz(uint32_t apb_freq_hz, uint32_t tick_ns)
{
if (tick_ns == 0) {
return apb_freq_hz;
}
uint64_t target_hz = 1000000000UL / tick_ns;
uint32_t clk_div = std::max<uint32_t>(1, (apb_freq_hz + target_hz / 2) / target_hz);
clk_div = std::min<uint32_t>(clk_div, 255U);
return apb_freq_hz / clk_div;
}
} // namespace gpio
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::pin_mode(const gpio_num_t pin, const gpio::Mode m)
{
if (m < gpio::Mode::RmtRX) {
gpio_config_t cfg = gpio_cfg_table[m5::stl::to_underlying(m)];
cfg.pin_bit_mask = 1ULL << pin;
gpio_config(&cfg);
return m5::hal::error::error_t::OK;
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::write_digital(const gpio_num_t pin, const bool high)
{
gpio_set_level(pin, high);
return m5::hal::error::error_t::OK;
}
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_digital(const gpio_num_t pin, bool& high)
{
high = true;
high = gpio_get_level(pin);
return m5::hal::error::error_t::OK;
}
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::write_analog(const gpio_num_t pin, const uint16_t value)
{
if (pin != 25 && pin != 26) {
// DAC output can 25 or 26
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
#if defined(USING_RMT_CHANNNE_T)
dac_channel_t ch = (pin == 25) ? DAC_CHANNEL_1 : DAC_CHANNEL_2;
dac_output_enable(ch);
dac_output_voltage(ch, static_cast<uint8_t>(value & 0xFF)); // 0〜255
return m5::hal::error::error_t::OK;
#else
analogWrite(pin, value & 0xFF);
return m5::hal::error::error_t::OK;
// return m5::hal::error::error_t::NOT_IMPLEMENTED;
#endif
}
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::read_analog(uint16_t& value, const gpio_num_t pin)
{
value = 0;
const auto ch = gpio_to_adc_channel(pin);
if (ch < 0) {
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
#if !defined(SOC_ADC_PERIPH_NUM) || SOC_ADC_PERIPH_NUM <= 1
if (ch >= 10) {
M5_LIB_LOGE("Not support ADC2");
return m5::hal::error::error_t::NOT_IMPLEMENTED;
}
#endif
#if defined(M5_UNIT_UNIFIED_USING_ADC_ONESHOT)
// ESP-IDF 5.x
adc_unit_t unit = (ch < 10) ? ADC_UNIT_1 : ADC_UNIT_2;
adc_channel_t channel = static_cast<adc_channel_t>((ch < 10) ? ch : (ch - 10));
adc_oneshot_unit_handle_t adc_handle{};
#if defined(CONFIG_IDF_TARGET_ESP32C6)
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = unit, .clk_src = ADC_DIGI_CLK_SRC_DEFAULT, .ulp_mode = ADC_ULP_MODE_DISABLE};
#else
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = unit, .clk_src = ADC_RTC_CLK_SRC_DEFAULT, .ulp_mode = ADC_ULP_MODE_DISABLE};
#endif
if (adc_oneshot_new_unit(&init_config, &adc_handle) != ESP_OK) {
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
adc_oneshot_chan_cfg_t chan_config = {
.atten = ADC_ATTEN_DB_12, // 0~3.3V
.bitwidth = ADC_BITWIDTH_DEFAULT // 12bit
};
auto ret = m5::hal::error::error_t::UNKNOWN_ERROR;
if (adc_oneshot_config_channel(adc_handle, channel, &chan_config) == ESP_OK) {
int raw{};
if (adc_oneshot_read(adc_handle, channel, &raw) == ESP_OK) {
value = static_cast<uint16_t>(raw);
ret = m5::hal::error::error_t::OK;
}
}
adc_oneshot_del_unit(adc_handle);
return ret;
#else
// ESP-IDF 4.x
// ADC2
if (ch >= 10) {
#if SOC_ADC_SUPPORTED && SOC_ADC_PERIPH_NUM > 1
adc2_channel_t channel = static_cast<adc2_channel_t>(ch - 10);
int v = 0;
if (adc2_get_raw(channel, ADC_WIDTH_BIT_12, &v) != ESP_OK) {
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
value = static_cast<uint16_t>(v);
return m5::hal::error::error_t::OK;
#endif
}
// ADC1
adc1_channel_t channel = static_cast<adc1_channel_t>(ch);
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(channel, ADC_ATTEN_DB_12);
value = static_cast<uint16_t>(adc1_get_raw(channel));
return m5::hal::error::error_t::OK;
#endif
}
m5::hal::error::error_t AdapterGPIOBase::GPIOImpl::pulse_in(uint32_t& duration, const gpio_num_t pin, const int state,
const uint32_t timeout_us = 30000)
{
duration = 0;
auto start = esp_timer_get_time();
auto now = start;
// Wait for any previous pulse to end
while (gpio_get_level(pin) == state) {
now = esp_timer_get_time();
if (now - start > timeout_us) {
return m5::hal::error::error_t::TIMEOUT_ERROR;
}
}
// Wait for the pulse to start
while (gpio_get_level(pin) != state) {
now = esp_timer_get_time();
if (now - start > timeout_us) {
return m5::hal::error::error_t::TIMEOUT_ERROR;
}
}
auto pulse_start = esp_timer_get_time();
// Wait for the pulse to end
while (gpio_get_level(pin) == state) {
now = esp_timer_get_time();
if (now - pulse_start > timeout_us) {
return m5::hal::error::error_t::TIMEOUT_ERROR;
}
}
auto pulse_end = esp_timer_get_time();
duration = static_cast<uint32_t>(pulse_end - pulse_start);
return m5::hal::error::error_t::OK;
}
AdapterGPIOBase::AdapterGPIOBase(GPIOImpl* impl) : Adapter(Adapter::Type::GPIO, impl)
{
}
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio.hpp
@brief Adapters to treat M5HAL and GPIO in the same way
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_GPIO_HPP
#define M5_UNIT_COMPONENT_ADAPTER_GPIO_HPP
#include "identify_functions.hpp"
#include "types.hpp"
#include "adapter_base.hpp"
namespace m5 {
namespace unit {
namespace gpio {
/*!
@brief Calculate clk_div from desired tick time (nanoseconds)
@param apb_freq_hz Current APB clock (Hz)
@param tick_ns Desired tick time (ns)
@return clk_div for RMT v1
*/
uint8_t calculate_rmt_clk_div(const uint32_t apb_freq_hz, const uint32_t tick_ns);
/*!
@brief Calculate resolution from desired tick time
@param apb_freq_hz Current APB clock (Hz)
@param tick_ns Desired tick time (ns)
@return resoution for RMT v2
*/
uint32_t calculate_rmt_resolution_hz(const uint32_t apb_freq_hz, const uint32_t tick_ns);
} // namespace gpio
// Base class for AdapterGPIO
class AdapterGPIOBase : public Adapter {
public:
class GPIOImpl : public Adapter::Impl {
public:
GPIOImpl() = default;
GPIOImpl(const int8_t rx_pin, const int8_t tx_pin) : _rx_pin{(gpio_num_t)rx_pin}, _tx_pin{(gpio_num_t)tx_pin}
{
}
inline gpio_num_t rx_pin() const
{
return _rx_pin;
}
inline gpio_num_t tx_pin() const
{
return _tx_pin;
}
inline gpio::adapter_config_t adapter_config() const
{
return _adapter_cfg;
}
inline virtual bool begin(const gpio::adapter_config_t& cfg)
{
return false;
}
//
inline virtual m5::hal::error::error_t pinModeRX(const gpio::Mode m) override
{
return pin_mode(rx_pin(), m);
}
inline virtual m5::hal::error::error_t writeDigitalRX(const bool high) override
{
return write_digital(rx_pin(), high);
}
inline virtual m5::hal::error::error_t readDigitalRX(bool& high) override
{
return read_digital(rx_pin(), high);
}
inline virtual m5::hal::error::error_t writeAnalogRX(const uint16_t v) override
{
return write_analog(rx_pin(), v);
}
inline virtual m5::hal::error::error_t readAnalogRX(uint16_t& v)
{
return read_analog(v, rx_pin());
}
inline virtual m5::hal::error::error_t pulseInRX(uint32_t& duration, const int state,
const uint32_t timeout_us) override
{
return pulse_in(duration, rx_pin(), state, timeout_us);
}
//
inline virtual m5::hal::error::error_t pinModeTX(const gpio::Mode m) override
{
return pin_mode(tx_pin(), m);
}
inline virtual m5::hal::error::error_t writeDigitalTX(const bool high) override
{
return write_digital(tx_pin(), high);
}
inline virtual m5::hal::error::error_t readDigitalTX(bool& high) override
{
return read_digital(tx_pin(), high);
}
inline virtual m5::hal::error::error_t writeAnalogTX(const uint16_t v) override
{
return write_analog(tx_pin(), v);
}
inline virtual m5::hal::error::error_t readAnalogTX(uint16_t& v)
{
return read_analog(v, tx_pin());
}
inline virtual m5::hal::error::error_t pulseInTX(uint32_t& duration, const int state,
const uint32_t timeout_us) override
{
return pulse_in(duration, tx_pin(), state, timeout_us);
}
protected:
m5::hal::error::error_t pin_mode(const gpio_num_t pin, const gpio::Mode m);
m5::hal::error::error_t write_digital(const gpio_num_t pin, const bool high);
m5::hal::error::error_t read_digital(const gpio_num_t pin, bool& high);
m5::hal::error::error_t write_analog(const gpio_num_t pin, const uint16_t value);
m5::hal::error::error_t read_analog(uint16_t& value, const gpio_num_t pin);
m5::hal::error::error_t pulse_in(uint32_t& duration, const gpio_num_t pin, const int state,
const uint32_t timeout_us);
protected:
gpio_num_t _rx_pin{(gpio_num_t)-1}, _tx_pin{(gpio_num_t)-1};
gpio::adapter_config_t _adapter_cfg{};
};
//
explicit AdapterGPIOBase(GPIOImpl* impl);
inline GPIOImpl* impl()
{
return static_cast<GPIOImpl*>(_impl.get());
}
inline const GPIOImpl* impl() const
{
return static_cast<GPIOImpl*>(_impl.get());
}
inline gpio_num_t rx_pin() const
{
return impl()->rx_pin();
}
inline gpio_num_t tx_pin() const
{
return impl()->tx_pin();
}
inline bool begin(const gpio::adapter_config_t& cfg)
{
return impl()->begin(cfg);
}
};
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,168 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio_v1.cpp
@brief Adapters to treat M5HAL and GPIO in the same way using RNT v1
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#include "adapter_gpio_v1.hpp"
#if !defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include <esp_clk.h>
using namespace m5::unit::gpio;
namespace {
uint32_t using_rmt_channel_bits{};
rmt_channel_t retrieve_available_rmt_channel()
{
for (int_fast8_t ch = 0; ch < RMT_CHANNEL_MAX; ++ch) {
if (((1U << ch) & using_rmt_channel_bits) == 0) {
return (rmt_channel_t)ch;
}
}
return RMT_CHANNEL_MAX;
}
bool declrare_use_rmt_channel(const int ch)
{
if (ch >= 0 && ch < RMT_CHANNEL_MAX && ((1U << ch) & using_rmt_channel_bits) == 0) {
using_rmt_channel_bits |= (1U << ch);
return true;
}
return false;
}
rmt_config_t to_rmt_config_tx(const adapter_config_t& cfg, const uint32_t apb_freq_hz)
{
rmt_config_t out{};
out.rmt_mode = RMT_MODE_TX;
out.mem_block_num = cfg.tx.mem_blocks;
out.clk_div = calculate_rmt_clk_div(apb_freq_hz, cfg.tx.tick_ns);
out.tx_config.carrier_en = false;
out.tx_config.idle_output_en = cfg.tx.idle_output;
out.tx_config.idle_level = cfg.tx.idle_level_high ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW;
out.tx_config.loop_en = cfg.tx.loop_enabled;
return out;
}
#if 0
rmt_config_t to_rmt_config_rx(const adapter_config_t& cfg, const uint32_t apb_freq_hz)
{
rmt_config_t out{};
out.rmt_mode = RMT_MODE_RX;
out.mem_block_num = cfg.tx.mem_blocks;
out.clk_div = calculate_rmt_clk_div(apb_freq_hz, cfg.rx.tick_ns);
out.rx_config.filter_en = true;
out.rx_config.filter_ticks_thresh = 30;
out.rx_config.idle_threshold = 300;
return out;
}
#endif
} // namespace
namespace m5 {
namespace unit {
class GPIOImplV1 : public AdapterGPIOBase::GPIOImpl {
public:
GPIOImplV1() : AdapterGPIOBase::GPIOImpl(-1, -1)
{
}
GPIOImplV1(const int8_t rx_pin, const int8_t tx_pin) : AdapterGPIOBase::GPIOImpl(rx_pin, tx_pin)
{
}
virtual bool begin(const gpio::adapter_config_t& cfg) override
{
_adapter_cfg = cfg;
if (_tx_config.clk_div || _rx_config.clk_div) {
M5_LIB_LOGD("Already begun");
return true;
}
// RMT TX
if (cfg.mode == gpio::Mode::RmtTX || cfg.mode == gpio::Mode::RmtRXTX) {
rmt_channel_t ch = retrieve_available_rmt_channel();
if (ch >= RMT_CHANNEL_MAX) {
M5_LIB_LOGE("RMT(v1) No room on channel");
return false;
}
declrare_use_rmt_channel(ch);
M5_LIB_LOGI("Retrive RMT(v1) %u", ch);
_tx_config = to_rmt_config_tx(cfg, esp_clk_apb_freq());
_tx_config.channel = ch;
_tx_config.gpio_num = tx_pin();
auto err = rmt_config(&_tx_config);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to configurate %d:%s", err, esp_err_to_name(err));
return false;
}
err = rmt_driver_install(_tx_config.channel, 0, 0);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to install %d:%s", err, esp_err_to_name(err));
return false;
}
}
// RMT RX
if (cfg.mode == gpio::Mode::RmtRX || cfg.mode == gpio::Mode::RmtRXTX) {
// TODO
}
return true;
}
m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len, const uint32_t waitMs) override
{
if (_adapter_cfg.mode == Mode::RmtTX || _adapter_cfg.mode == Mode::RmtRXTX) {
auto err = rmt_write_items(_tx_config.channel, (gpio::m5_rmt_item_t*)data, len, true);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to write %d:%s", err, esp_err_to_name(err));
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
if (err == ESP_OK && waitMs) {
err = rmt_wait_tx_done(_tx_config.channel, pdMS_TO_TICKS(50));
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to wait %d:%s", err, esp_err_to_name(err));
}
}
return err == ESP_OK ? m5::hal::error::error_t::OK : m5::hal::error::error_t::UNKNOWN_ERROR;
}
M5_LIB_LOGE("Failed invalid config");
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
void copy_from(GPIOImplV1* ptr)
{
_rx_pin = ptr->_rx_pin;
_tx_pin = ptr->_tx_pin;
_adapter_cfg = ptr->_adapter_cfg;
_rx_config = ptr->_rx_config;
_tx_config = ptr->_tx_config;
}
protected:
rmt_config_t _rx_config{}, _tx_config{};
};
AdapterGPIO::AdapterGPIO(const int8_t rx_pin, const int8_t tx_pin) : AdapterGPIOBase(new GPIOImplV1(rx_pin, tx_pin))
{
}
//
} // namespace unit
} // namespace m5
#endif
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2) && defined(RMT_CHANNEL_0)
#error "RMT v1 is mixed in with RMT v2 even though RMT v2 is used"
#endif

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio_v1.hpp
@brief Adapters to treat M5HAL and GPIO in the same way using RNT v1
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_GPIO_V1_HPP
#define M5_UNIT_COMPONENT_ADAPTER_GPIO_V1_HPP
#include "identify_functions.hpp"
#include "types.hpp"
#include "adapter_gpio.hpp"
#if !defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include <driver/rmt.h>
namespace m5 {
namespace unit {
/*!
@class m5::unit::AdapterGPIO
@brief GPIO access adapter
*/
class AdapterGPIO : public AdapterGPIOBase {
public:
AdapterGPIO(const int8_t rx_pin, const int8_t tx_pin);
};
} // namespace unit
} // namespace m5
#endif
#endif

View file

@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio_v2.cpp
@brief Adapters to treat M5HAL and GPIO in the same way using RMT v2
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#include "adapter_gpio_v2.hpp"
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include <esp_private/esp_clk.h>
using namespace m5::unit::gpio;
namespace {
rmt_encoder_handle_t copy_encoder{};
rmt_tx_channel_config_t to_rmt_tx_config(const adapter_config_t& cfg, const uint32_t apb_freq_hz)
{
rmt_tx_channel_config_t out{};
out.clk_src = RMT_CLK_SRC_DEFAULT;
out.mem_block_symbols = cfg.tx.mem_blocks * 16;
out.resolution_hz = calculate_rmt_resolution_hz(apb_freq_hz, cfg.tx.tick_ns);
out.trans_queue_depth = 4;
out.flags.with_dma = cfg.tx.with_dma;
return out;
}
#if 0
rmt_rx_channel_config_t to_rmt_rx_config(const adapter_config_t& cfg, const uint32_t apb_freq_hz)
{
rmt_rx_channel_config_t out{};
return out;
}
#endif
} // namespace
namespace m5 {
namespace unit {
//
class GPIOImplV2 : public AdapterGPIO::GPIOImpl {
public:
GPIOImplV2(const int8_t rx_pin, const int8_t tx_pin) : AdapterGPIOBase::GPIOImpl(rx_pin, tx_pin)
{
}
bool begin(const gpio::adapter_config_t& cfg)
{
// RMT TX
if (!_tx_handle && (cfg.mode == gpio::Mode::RmtTX || cfg.mode == gpio::Mode::RmtRXTX)) {
_tx_config = to_rmt_tx_config(cfg, esp_clk_apb_freq());
_tx_config.gpio_num = tx_pin();
auto err = rmt_new_tx_channel(&_tx_config, &_tx_handle);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to rmt_new_tx_channel pin:%d", tx_pin());
return false;
}
err = rmt_enable(_tx_handle);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to rmt_enable");
return false;
}
}
// RMT RX
if (!_rx_handle && (cfg.mode == gpio::Mode::RmtRX || cfg.mode == gpio::Mode::RmtRXTX)) {
// TODO
}
if (!copy_encoder) {
rmt_copy_encoder_config_t enc_cfg{};
auto err = rmt_new_copy_encoder(&enc_cfg, &copy_encoder);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to rmt_new_copy_encoder");
return false;
}
}
return true;
}
m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len, const uint32_t waitMs) override
{
rmt_transmit_config_t tx_config = {};
auto err = rmt_transmit(_tx_handle, copy_encoder, (gpio::m5_rmt_item_t*)data, len * sizeof(gpio::m5_rmt_item_t),
&tx_config);
if (err == ESP_OK && waitMs) {
err = rmt_tx_wait_all_done(_tx_handle, waitMs);
if (err != ESP_OK) {
M5_LIB_LOGE("Failed to wait %d:%s", err, esp_err_to_name(err));
}
}
return err == ESP_OK ? m5::hal::error::error_t::OK : m5::hal::error::error_t::UNKNOWN_ERROR;
}
protected:
rmt_channel_handle_t _rx_handle{}, _tx_handle{};
rmt_rx_channel_config_t _rx_config{};
rmt_tx_channel_config_t _tx_config{};
};
AdapterGPIO::AdapterGPIO(const int8_t rx_pin, const int8_t tx_pin) : AdapterGPIOBase(new GPIOImplV2(rx_pin, tx_pin))
{
}
} // namespace unit
} // namespace m5
#endif
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2) && defined(RMT_CHANNEL_0)
#error "RMT v1 is mixed in with RMT v2 even though RMT v2 is used"
#endif

View file

@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_gpio_v2.hpp
@brief Adapters to treat M5HAL and GPIO in the same way using RMT v2
@note Currently handles GPIO directly, but will handle via M5HAL in the future
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_GPIO_V2_HPP
#define M5_UNIT_COMPONENT_ADAPTER_GPIO_V2_HPP
#include "identify_functions.hpp"
#include "types.hpp"
#include "adapter_gpio.hpp"
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include <driver/rmt_tx.h>
#include <driver/rmt_rx.h>
#include <driver/rmt_encoder.h>
#include <driver/gpio.h>
namespace m5 {
namespace unit {
/*!
@class m5::unit::AdapterGPIO
@brief GPIO access adapter
*/
class AdapterGPIO : public AdapterGPIOBase {
public:
AdapterGPIO(const int8_t rx_pin, const int8_t tx_pin);
};
} // namespace unit
} // namespace m5
#endif
#endif

View file

@ -0,0 +1,396 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_i2c.cpp
@brief Adapter for I2C to treat M5HAL and TwoWire in the same way
@note Currently handles TwoWire directly, but will handle via M5HAL in the future
*/
#include "adapter.hpp"
#include <cassert>
#include <Wire.h>
#include <M5HAL.hpp>
#include <M5Utility.hpp>
#include <soc/gpio_struct.h>
#include <soc/gpio_sig_map.h>
#if defined(ARDUINO)
namespace {
int16_t search_pin_number(const int peripheral_sig)
{
int16_t no{-1};
#if defined(CONFIG_IDF_TARGET_ESP32C6)
// C6
no = (peripheral_sig >= 0 && peripheral_sig < m5::stl::size(GPIO.func_in_sel_cfg))
? GPIO.func_in_sel_cfg[peripheral_sig].in_sel
: -1;
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
// P4
#pragma message "ESP32P4 was not support"
#else
// Others
no = (peripheral_sig >= 0 && peripheral_sig < m5::stl::size(GPIO.func_in_sel_cfg))
? GPIO.func_in_sel_cfg[peripheral_sig].func_sel
: -1;
#endif
return (no < GPIO_NUM_MAX) ? no : -1;
}
int8_t idx_table[][2] = {
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
{I2CEXT0_SDA_IN_IDX, I2CEXT0_SCL_IN_IDX}, // Wire
#if !defined(CONFIG_IDF_TARGET_ESP32C6)
{I2CEXT1_SDA_IN_IDX, I2CEXT1_SCL_IN_IDX}, // Wire1
#else
{I2CEXT0_SDA_IN_IDX, I2CEXT0_SCL_IN_IDX}, // Same as Wire
#endif
#else
// ESP32-P4 Not support yet
{-1, -1},
{-1, -1},
#endif
};
} // namespace
namespace m5 {
namespace unit {
// Impl for TwoWire
AdapterI2C::WireImpl::WireImpl(TwoWire& wire, const uint8_t addr, const uint32_t clock)
: AdapterI2C::I2CImpl(addr, clock), _wire(&wire)
{
uint32_t w = (&wire != &Wire);
_sda = search_pin_number(idx_table[w][0]);
_scl = search_pin_number(idx_table[w][1]);
M5_LIB_LOGI("I2C SDA:%d, SCL:%d", _sda, _scl);
}
bool AdapterI2C::WireImpl::begin()
{
return _wire->begin();
}
bool AdapterI2C::WireImpl::end()
{
#if defined(WIRE_HAS_END)
return _wire->end();
#else
return false;
#endif
}
m5::hal::error::error_t AdapterI2C::WireImpl::readWithTransaction(uint8_t* data, const size_t len)
{
assert(_addr);
if (data && _wire->requestFrom(_addr, len)) {
auto count = std::min(len, (size_t)_wire->available());
for (size_t i = 0; i < count; ++i) {
data[i] = (uint8_t)_wire->read();
}
return (count == len) ? m5::hal::error::error_t::OK : m5::hal::error::error_t::I2C_BUS_ERROR;
}
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
m5::hal::error::error_t AdapterI2C::WireImpl::writeWithTransaction(const uint8_t* data, const size_t len,
const uint32_t stop)
{
return write_with_transaction(_addr, data, len, stop);
}
m5::hal::error::error_t AdapterI2C::WireImpl::writeWithTransaction(const uint8_t reg, const uint8_t* data,
const size_t len, const uint32_t stop)
{
assert(_addr);
_wire->setClock(_clock);
_wire->beginTransmission(_addr);
_wire->write(reg);
if (data && len) {
_wire->write(data, len);
}
auto ret = _wire->endTransmission(stop);
if (ret) {
M5_LIB_LOGE("%d endTransmission stop:%d", ret, stop);
}
return (ret == 0) ? m5::hal::error::error_t::OK : m5::hal::error::error_t::I2C_BUS_ERROR;
}
m5::hal::error::error_t AdapterI2C::WireImpl::writeWithTransaction(const uint16_t reg, const uint8_t* data,
const size_t len, const uint32_t stop)
{
assert(_addr);
_wire->setClock(_clock);
m5::types::big_uint16_t r(reg);
_wire->beginTransmission(_addr);
_wire->write(r.data(), r.size());
if (data && len) {
_wire->write(data, len);
}
auto ret = _wire->endTransmission(stop);
if (ret) {
M5_LIB_LOGE("%d endTransmission stop:%d", ret, stop);
}
return (ret == 0) ? m5::hal::error::error_t::OK : m5::hal::error::error_t::I2C_BUS_ERROR;
}
AdapterI2C::I2CImpl* AdapterI2C::WireImpl::duplicate(const uint8_t addr)
{
return new WireImpl(*_wire, addr, _clock);
}
m5::hal::error::error_t AdapterI2C::WireImpl::generalCall(const uint8_t* data, const size_t len)
{
return write_with_transaction(0x00, data, len, true);
}
m5::hal::error::error_t AdapterI2C::WireImpl::wakeup()
{
return write_with_transaction(_addr, nullptr, 0, true);
}
m5::hal::error::error_t AdapterI2C::WireImpl::write_with_transaction(const uint8_t addr, const uint8_t* data,
const size_t len, const uint32_t stop)
{
_wire->setClock(_clock);
_wire->beginTransmission(addr);
if (data) {
_wire->write(data, len);
}
auto ret = _wire->endTransmission(stop);
if (ret) {
M5_LIB_LOGE("%d endTransmission stop:%d", ret, stop);
}
return (ret == 0) ? m5::hal::error::error_t::OK : m5::hal::error::error_t::I2C_BUS_ERROR;
}
#endif
// Impl for M5HAL
AdapterI2C::BusImpl::BusImpl(m5::hal::bus::Bus* bus, const uint8_t addr, const uint32_t clock)
: AdapterI2C::I2CImpl(addr, clock), _bus(bus)
{
_access_cfg.i2c_addr = addr;
_access_cfg.freq = clock;
}
AdapterI2C::I2CImpl* AdapterI2C::BusImpl::duplicate(const uint8_t addr)
{
return new BusImpl(_bus, addr, _clock);
}
m5::hal::error::error_t AdapterI2C::BusImpl::readWithTransaction(uint8_t* data, const size_t len)
{
if (_bus && data) {
auto acc = _bus->beginAccess(_access_cfg);
if (acc) {
auto trans = acc.value();
auto result = trans->startRead().and_then([&trans, &data, &len]() {
return trans->read(data, len).and_then([&trans](size_t&&) { return trans->stop(); });
});
// Clean-up must be called
auto eresult = this->_bus->endAccess(std::move(trans));
return result.error_or(eresult);
}
return acc.error();
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
m5::hal::error::error_t AdapterI2C::BusImpl::writeWithTransaction(const uint8_t* data, const size_t len,
const uint32_t stop)
{
if (_bus) {
auto acc = _bus->beginAccess(_access_cfg);
if (acc) {
auto trans = acc.value();
auto result = trans->startWrite().and_then([&trans, &data, &len, &stop]() {
return trans->write(data, len).and_then([&trans, &stop](size_t&&) {
return stop ? trans->stop() : m5::stl::expected<void, m5::hal::error::error_t>();
});
});
// Clean-up must be called
auto eresult = this->_bus->endAccess(std::move(trans));
return result.error_or(eresult);
}
return acc.error();
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
m5::hal::error::error_t AdapterI2C::BusImpl::writeWithTransaction(const uint8_t reg, const uint8_t* data,
const size_t len, const uint32_t stop)
{
assert(_addr);
if (_bus) {
auto acc = _bus->beginAccess(_access_cfg);
if (acc) {
auto trans = acc.value();
auto result = trans->startWrite().and_then([&trans, &reg, &data, &len, &stop]() {
return trans->write(&reg, 1).and_then([&trans, &data, &len, &stop](size_t&&) {
return ((data && len) ? trans->write(data, len)
: m5::stl::expected<size_t, m5::hal::error::error_t>((size_t)0UL))
.and_then([&trans, &stop](size_t&&) {
return stop ? trans->stop() : m5::stl::expected<void, m5::hal::error::error_t>();
});
});
});
// Clean-up must be called
auto eresult = this->_bus->endAccess(std::move(trans));
return result.error_or(eresult);
}
return acc.error();
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
m5::hal::error::error_t AdapterI2C::BusImpl::writeWithTransaction(const uint16_t reg, const uint8_t* data,
const size_t len, const uint32_t stop)
{
assert(_addr);
if (_bus) {
auto acc = _bus->beginAccess(_access_cfg);
if (acc) {
m5::types::big_uint16_t r(reg);
auto trans = acc.value();
auto result = trans->startWrite().and_then([&trans, &r, &data, &len, &stop]() {
return trans->write(r.data(), r.size()).and_then([&trans, &data, &len, &stop](size_t&&) {
return ((data && len) ? trans->write(data, len)
: m5::stl::expected<size_t, m5::hal::error::error_t>((size_t)0UL))
.and_then([&trans, &stop](size_t&&) {
return stop ? trans->stop() : m5::stl::expected<void, m5::hal::error::error_t>();
});
});
});
// Clean-up must be called
auto eresult = this->_bus->endAccess(std::move(trans));
return result.error_or(eresult);
}
return acc.error();
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
m5::hal::error::error_t AdapterI2C::BusImpl::generalCall(const uint8_t* data, const size_t len)
{
m5::hal::bus::I2CMasterAccessConfig gcfg = _access_cfg;
gcfg.i2c_addr = 0x00;
return write_with_transaction(gcfg, data, len, true);
}
m5::hal::error::error_t AdapterI2C::BusImpl::wakeup()
{
return write_with_transaction(_access_cfg, nullptr, 0, true);
}
m5::hal::error::error_t AdapterI2C::BusImpl::write_with_transaction(const m5::hal::bus::I2CMasterAccessConfig& cfg,
const uint8_t* data, const size_t len,
const uint32_t stop)
{
if (_bus) {
auto acc = _bus->beginAccess(cfg);
if (acc) {
auto trans = acc.value();
auto result =
trans->startWrite()
.and_then([&trans, &data, &len]() {
return ((data && len) ? trans->write(data, len)
: m5::stl::expected<size_t, m5::hal::error::error_t>((size_t)0UL));
})
.and_then([&trans, &stop](size_t&&) {
return stop ? trans->stop() : m5::stl::expected<void, m5::hal::error::error_t>();
});
// Clean-up must be called
auto eresult = this->_bus->endAccess(std::move(trans));
return result.error_or(eresult);
}
return acc.error();
}
return m5::hal::error::error_t::INVALID_ARGUMENT;
}
// Adapter
#if defined(ARDUINO)
AdapterI2C::AdapterI2C(TwoWire& wire, const uint8_t addr, const uint32_t clock)
: Adapter(Adapter::Type::I2C, new AdapterI2C::WireImpl(wire, addr, clock))
{
assert(_impl);
}
#else
#pragma message "Not support TwoWire"
AdapterI2C::AdapterI2C(TwoWire& wire, const uint8_t addr, const uint32_t clock) : Adapter()
{
(void)wire;
(void)addr;
(void)clock;
assert(_impl);
M5_LIB_LOGE("Not support TwoWire");
}
#endif
AdapterI2C::AdapterI2C(m5::hal::bus::Bus* bus, const uint8_t addr, const uint32_t clock)
: Adapter(Adapter::Type::I2C, new AdapterI2C::BusImpl(bus, addr, clock))
{
assert(_impl);
}
Adapter* AdapterI2C::duplicate(const uint8_t addr)
{
auto ptr = new AdapterI2C();
if (ptr) {
ptr->_impl.reset(impl()->duplicate(addr));
if (ptr->_impl) {
return ptr;
}
delete ptr;
}
M5_LIB_LOGE("Failed to duplicate");
return nullptr;
}
bool AdapterI2C::pushPin()
{
#if defined(ARDUINO)
if (_backupSCL.getPin() < 0 && _backupSDA.getPin() < 0) {
_backupSCL.setPin(scl());
_backupSCL.backup();
_backupSDA.setPin(sda());
_backupSDA.backup();
M5_LIB_LOGD(">>Push SCL:%u SDA:%u", _backupSCL.getPin(), _backupSDA.getPin());
return true;
}
return false;
#else
return false;
#endif
}
bool AdapterI2C::popPin()
{
#if defined(ARDUINO)
if (_backupSCL.getPin() >= 0 && _backupSDA.getPin() >= 0) {
M5_LIB_LOGD("<<Pop SCL:%u SDA:%u", _backupSCL.getPin(), _backupSDA.getPin());
_backupSCL.restore();
_backupSDA.restore();
_backupSCL.setPin(-1);
_backupSDA.setPin(-1);
return true;
}
return false;
#else
return false;
#endif
}
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,247 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file adapter_i2c.hpp
@brief Adapter for I2C to treat M5HAL and TwoWire in the same way
*/
#ifndef M5_UNIT_COMPONENT_ADAPTER_I2C_HPP
#define M5_UNIT_COMPONENT_ADAPTER_I2C_HPP
#include "adapter_base.hpp"
#include "pin.hpp"
class TwoWire;
namespace m5 {
namespace unit {
/*!
@class m5::unit::AdapterI2C
@brief I2C access adapter
*/
class AdapterI2C : public Adapter {
public:
class I2CImpl : public Adapter::Impl {
public:
I2CImpl() = default;
I2CImpl(const uint8_t addr, const uint32_t clock) : Adapter::Impl(), _addr(addr), _clock(clock)
{
}
virtual ~I2CImpl() = default;
inline uint8_t address() const
{
return _addr;
}
inline void setAddress(const uint8_t addr)
{
_addr = addr;
}
inline uint32_t clock() const
{
return _clock;
}
inline virtual void setClock(const uint32_t clock)
{
_clock = clock;
}
//
virtual int16_t scl() const
{
return -1;
}
virtual int16_t sda() const
{
return -1;
}
//
virtual bool begin()
{
return false;
}
virtual bool end()
{
return false;
}
virtual m5::hal::error::error_t wakeup()
{
return m5::hal::error::error_t::UNKNOWN_ERROR;
}
//
virtual I2CImpl* duplicate(const uint8_t addr)
{
return new I2CImpl(addr, _clock);
}
virtual TwoWire* getWire()
{
return nullptr;
}
virtual m5::hal::bus::Bus* getBus()
{
return nullptr;
}
protected:
uint8_t _addr{};
uint32_t _clock{100 * 1000U};
};
//
#if defined(ARDUINO)
class WireImpl : public I2CImpl {
public:
WireImpl(TwoWire& wire, const uint8_t addr, const uint32_t clock);
inline virtual TwoWire* getWire() override
{
return _wire;
}
inline virtual int16_t scl() const override
{
return _scl;
}
inline virtual int16_t sda() const override
{
return _sda;
}
virtual bool begin() override;
virtual bool end() override;
virtual m5::hal::error::error_t readWithTransaction(uint8_t* data, const size_t len) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t reg, const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint16_t reg, const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual I2CImpl* duplicate(const uint8_t addr) override;
virtual m5::hal::error::error_t generalCall(const uint8_t* data, const size_t len) override;
virtual m5::hal::error::error_t wakeup() override;
protected:
m5::hal::error::error_t write_with_transaction(const uint8_t addr, const uint8_t* data, const size_t len,
const uint32_t stop);
private:
TwoWire* _wire{};
int16_t _sda{}, _scl{};
};
#endif
class BusImpl : public I2CImpl {
public:
BusImpl(m5::hal::bus::Bus* bus, const uint8_t addr, const uint32_t clock);
inline virtual m5::hal::bus::Bus* getBus() override
{
return _bus;
}
inline virtual void setClock(const uint32_t clock) override
{
I2CImpl::setClock(clock);
_access_cfg.freq = clock;
}
virtual I2CImpl* duplicate(const uint8_t addr) override;
virtual m5::hal::error::error_t readWithTransaction(uint8_t* data, const size_t len) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint8_t reg, const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual m5::hal::error::error_t writeWithTransaction(const uint16_t reg, const uint8_t* data, const size_t len,
const uint32_t stop) override;
virtual m5::hal::error::error_t generalCall(const uint8_t* data, const size_t len) override;
virtual m5::hal::error::error_t wakeup() override;
protected:
m5::hal::error::error_t write_with_transaction(const m5::hal::bus::I2CMasterAccessConfig& cfg,
const uint8_t* data, const size_t len, const uint32_t stop);
private:
m5::hal::bus::Bus* _bus{};
m5::hal::bus::I2CMasterAccessConfig _access_cfg{};
};
#if defined(ARDUINO)
AdapterI2C(TwoWire& wire, uint8_t addr, const uint32_t clock);
#endif
AdapterI2C(m5::hal::bus::Bus* bus, const uint8_t addr, const uint32_t clock);
AdapterI2C(m5::hal::bus::Bus& bus, const uint8_t addr, const uint32_t clock) : AdapterI2C(&bus, addr, clock)
{
}
inline I2CImpl* impl()
{
return static_cast<I2CImpl*>(_impl.get());
}
inline const I2CImpl* impl() const
{
return static_cast<I2CImpl*>(_impl.get());
}
inline uint8_t address() const
{
return impl()->address();
}
inline void setAddress(const uint8_t addr)
{
impl()->setAddress(addr);
}
inline uint32_t clock() const
{
return impl()->clock();
}
inline void setClock(const uint32_t clock)
{
impl()->setClock(clock);
}
inline int16_t scl() const
{
return impl()->scl();
}
inline int16_t sda() const
{
return impl()->sda();
}
virtual Adapter* duplicate(const uint8_t addr) override;
/// @warning Functionality required for a specific unit
/// @warning Will be improved when integrated with M5HAL
/// @name Temporary API
///@{
inline bool begin()
{
return impl()->begin();
}
inline bool end()
{
return impl()->end();
}
bool pushPin();
bool popPin();
///@}
protected:
AdapterI2C() : Adapter(Adapter::Type::I2C, new I2CImpl())
{
}
protected:
gpio::pin_backup_t _backupSCL{-1}, _backupSDA{-1};
};
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file identify_functions.hpp
@brief Identification of functions to be used
*/
#ifndef M5_UNIT_UNIFIED_IDENTIFY_FUNCTIONS_HPP
#define M5_UNIT_UNIFIED_IDENTIFY_FUNCTIONS_HPP
// Detect ESP-IDF version
#if __has_include(<esp_idf_version.h>)
#include <esp_idf_version.h>
#else // esp_idf_version.h has been introduced in Arduino 1.0.5 (ESP-IDF3.3)
#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(3, 2, 0)
#endif
// RMT
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define M5_UNIT_UNIFIED_USING_RMT_V2
#define M5_UNIT_UNIFIED_USING_ADC_ONESHOT
#endif
#endif

View file

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file pin.hpp
@brief PIN settings save/restore
@todo Will be transferred to M5HAL in the future
*/
#include "pin.hpp"
#include <M5Utility.hpp>
#include <cstddef>
#include <driver/i2c.h>
#include <soc/gpio_struct.h>
#include <soc/gpio_periph.h>
namespace m5 {
namespace unit {
namespace gpio {
pin_backup_t::pin_backup_t(int pin_num) : _pin_num{static_cast<gpio_num_t>(pin_num)}
{
if (pin_num >= 0) {
backup();
}
}
void pin_backup_t::backup(void)
{
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
auto pin_num = (size_t)_pin_num;
if (pin_num < GPIO_NUM_MAX) {
_io_mux_gpio_reg = *reinterpret_cast<uint32_t*>(GPIO_PIN_MUX_REG[pin_num]);
_gpio_pin_reg = *reinterpret_cast<uint32_t*>(GPIO_PIN0_REG + (pin_num * 4));
_gpio_func_out_reg = *reinterpret_cast<uint32_t*>(GPIO_FUNC0_OUT_SEL_CFG_REG + (pin_num * 4));
#if defined(GPIO_ENABLE1_REG)
_gpio_enable = (bool)((*reinterpret_cast<uint32_t*>(((pin_num & 32) ? GPIO_ENABLE1_REG : GPIO_ENABLE_REG)) &
(1U << (pin_num & 31))) != 0);
#else
_gpio_enable = (bool)((*reinterpret_cast<uint32_t*>(GPIO_ENABLE_REG) & (1U << (pin_num & 31)) != 0);
#endif
_in_func_num = -1;
size_t func_num = ((_gpio_func_out_reg >> GPIO_FUNC0_OUT_SEL_S) & GPIO_FUNC0_OUT_SEL_V);
if (func_num < sizeof(GPIO.func_in_sel_cfg) / sizeof(GPIO.func_in_sel_cfg[0])) {
_gpio_func_in_reg = *reinterpret_cast<uint32_t*>(GPIO_FUNC0_IN_SEL_CFG_REG + (func_num * 4));
if (func_num == ((_gpio_func_in_reg >> GPIO_FUNC0_IN_SEL_S) & GPIO_FUNC0_IN_SEL_V)) {
_in_func_num = func_num;
M5_LIB_LOGV("backup pin:%d : func_num:%d", pin_num, _in_func_num);
}
}
}
#else
#pragma message "ESP32P4 was not support"
#endif
}
void pin_backup_t::restore(void)
{
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
auto pin_num = (size_t)_pin_num;
if (pin_num < GPIO_NUM_MAX) {
if ((uint16_t)_in_func_num < 256) {
GPIO.func_in_sel_cfg[_in_func_num].val = _gpio_func_in_reg;
M5_LIB_LOGV("pin:%d in_func_num:%d", (int)pin_num, (int)_in_func_num);
}
M5_LIB_LOGV("restore pin:%d ", pin_num);
M5_LIB_LOGV("restore IO_MUX_GPIO0_REG :%08x -> %08x ",
*reinterpret_cast<uint32_t*>(GPIO_PIN_MUX_REG[pin_num]), _io_mux_gpio_reg);
M5_LIB_LOGV("restore GPIO_PIN0_REG :%08x -> %08x ",
*reinterpret_cast<uint32_t*>(GPIO_PIN0_REG + (pin_num * 4)), _gpio_pin_reg);
M5_LIB_LOGV("restore GPIO_FUNC0_OUT_SEL_CFG_REG:%08x -> %08x ",
*reinterpret_cast<uint32_t*>(GPIO_FUNC0_OUT_SEL_CFG_REG + (pin_num * 4)), _gpio_func_out_reg);
*reinterpret_cast<uint32_t*>(GPIO_PIN_MUX_REG[_pin_num]) = _io_mux_gpio_reg;
*reinterpret_cast<uint32_t*>(GPIO_PIN0_REG + (pin_num * 4)) = _gpio_pin_reg;
*reinterpret_cast<uint32_t*>(GPIO_FUNC0_OUT_SEL_CFG_REG + (pin_num * 4)) = _gpio_func_out_reg;
#if defined(GPIO_ENABLE1_REG)
auto gpio_enable_reg = reinterpret_cast<uint32_t*>(((pin_num & 32) ? GPIO_ENABLE1_REG : GPIO_ENABLE_REG));
#else
auto gpio_enable_reg = reinterpret_cast<uint32_t*>(GPIO_ENABLE_REG);
#endif
uint32_t pin_mask = 1 << (pin_num & 31);
uint32_t val = *gpio_enable_reg;
M5_LIB_LOGV("restore GPIO_ENABLE_REG:%08x", (int)*gpio_enable_reg);
if (_gpio_enable) {
val |= pin_mask;
} else {
val &= ~pin_mask;
}
*gpio_enable_reg = val;
}
#else
#pragma message "ESP32P4 was not support"
#endif
}
} // namespace gpio
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file pin.hpp
@brief PIN settings save/restore
@todo Will be transferred to M5HAL in the future
*/
#ifndef M5_UNIT_COMPONENT_PIN_HPP
#define M5_UNIT_COMPONENT_PIN_HPP
#include <cstdint>
namespace m5 {
namespace unit {
namespace gpio {
// From M5GFX
class pin_backup_t {
public:
explicit pin_backup_t(int pin_num = 1);
inline void setPin(int pin_num)
{
_pin_num = pin_num;
}
inline int8_t getPin(void) const
{
return _pin_num;
}
void backup(void);
void restore(void);
private:
uint32_t _io_mux_gpio_reg{};
uint32_t _gpio_pin_reg{};
uint32_t _gpio_func_out_reg{};
uint32_t _gpio_func_in_reg{};
int16_t _in_func_num{-1};
int8_t _pin_num{-1}; // GPIO_NUM_NC
bool _gpio_enable{};
};
} // namespace gpio
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file types.hpp
@brief Type and enumerator definitions
*/
#ifndef M5_UNIT_COMPONENT_TYPES_HPP
#define M5_UNIT_COMPONENT_TYPES_HPP
#include <cstdint>
#include <type_traits>
#include "identify_functions.hpp"
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
#include <driver/rmt_types.h>
#else
#include <soc/rmt_struct.h>
#endif
#include <driver/gpio.h>
namespace m5 {
namespace unit {
/*!
@namespace types
@brief Type and enumerator definitions
*/
namespace types {
/*!
@enum category_t
@brief Unit category (used for static class determination)
*/
enum class category_t {
None,
UnitLED, //!< Derived from UnitLED
};
using uid_t = uint32_t; //!< @brief Component unique identifier
using attr_t = uint32_t; //!< @brief Component attribute bits
using elapsed_time_t = unsigned long; //!< @brief Elapsed time unit (ms)
namespace attribute {
///@name Attribute
///@{
constexpr attr_t AccessI2C = 0x00000001; //!< I2C Accessible Unit
constexpr attr_t AccessGPIO = 0x00000002; //!< GPIO Accessible Unit
///@}
} // namespace attribute
} // namespace types
namespace gpio {
/*!
@enum Mode
@brief Pin mode
*/
enum class Mode : uint8_t {
Input,
Output,
Pullup,
InputPullup,
Pulldown,
InputPulldown,
OpenDrain,
OutputOpenDrain,
Analog,
// RMT access
RmtRX = 0x80,
RmtTX,
RmtRXTX,
};
/*!
@struct m5::unit::gpio::adapter_config_t
@brief Common pinMode, RMT v1 and v2 settings
*/
struct adapter_config_t {
struct config_t {
gpio_num_t gpio_num{};
uint32_t tick_ns{};
uint8_t mem_blocks{};
bool idle_output{};
bool idle_level_high{};
bool with_dma{};
bool loop_enabled{};
};
Mode mode{}; // Mode
config_t rx{}; // For RMT
config_t tx{}; // For RMT
};
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
using m5_rmt_item_t = rmt_symbol_word_t;
#else
using m5_rmt_item_t = rmt_item32_t;
#endif
} // namespace gpio
} // namespace unit
} // namespace m5
#endif