first commit
This commit is contained in:
commit
5893b00dd2
1669 changed files with 1982740 additions and 0 deletions
395
libraries/M5UnitUnified/src/M5UnitComponent.cpp
Normal file
395
libraries/M5UnitUnified/src/M5UnitComponent.cpp
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5UnitComponent.cpp
|
||||
@brief Base class for Unit Component
|
||||
*/
|
||||
#include "M5UnitComponent.hpp"
|
||||
#include <M5Utility.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
using namespace m5::unit::types;
|
||||
|
||||
namespace m5 {
|
||||
namespace unit {
|
||||
|
||||
const char Component::name[] = "";
|
||||
const types::uid_t Component::uid{};
|
||||
const types::attr_t Component::attr{};
|
||||
|
||||
Component::Component(const uint8_t addr) : _adapter{new Adapter()}, _addr{addr}
|
||||
{
|
||||
}
|
||||
|
||||
size_t Component::childrenSize() const
|
||||
{
|
||||
size_t sz{};
|
||||
auto it = childBegin();
|
||||
while (it != childEnd()) {
|
||||
++sz;
|
||||
++it;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
bool Component::existsChild(const uint8_t ch) const
|
||||
{
|
||||
if (!_child) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(childBegin(), childEnd(), [&ch](const Component& c) { return ch == c.channel(); });
|
||||
}
|
||||
|
||||
bool Component::canAccessI2C() const
|
||||
{
|
||||
return attribute() & attribute::AccessI2C;
|
||||
}
|
||||
|
||||
bool Component::canAccessGPIO() const
|
||||
{
|
||||
return attribute() & attribute::AccessGPIO;
|
||||
}
|
||||
|
||||
bool Component::add(Component& c, const int16_t ch)
|
||||
{
|
||||
if (childrenSize() >= _component_cfg.max_children) {
|
||||
M5_LIB_LOGE("Can't connect any more");
|
||||
return false;
|
||||
}
|
||||
if (existsChild(ch)) {
|
||||
M5_LIB_LOGE("Already connected an other unit at channel:%u", ch);
|
||||
return false;
|
||||
}
|
||||
if (isRegistered()) {
|
||||
M5_LIB_LOGE(
|
||||
"As the parent unit is already registered with the UnitUnified, no additional children can be added");
|
||||
return false;
|
||||
}
|
||||
if (c.isRegistered()) {
|
||||
M5_LIB_LOGE("Children already registered with UnitUnified cannot be added");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!add_child(&c)) {
|
||||
return false;
|
||||
}
|
||||
c._channel = ch;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Component::add_child(Component* c)
|
||||
{
|
||||
if (!c || c->_parent || c->_prev || c->_next) {
|
||||
M5_LIB_LOGE("Invalid child [%s] %p / %p / %p", c ? c->deviceName() : "null", c ? c->_parent : nullptr,
|
||||
c ? c->_next : nullptr, c ? c->_prev : nullptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
// Add to tail
|
||||
if (!_child) {
|
||||
_child = c;
|
||||
} else {
|
||||
auto last = _child;
|
||||
while (last->_next) {
|
||||
last = last->_next;
|
||||
}
|
||||
last->_next = c;
|
||||
c->_prev = last;
|
||||
}
|
||||
|
||||
c->_parent = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
Component* Component::child(const uint8_t ch) const
|
||||
{
|
||||
auto it = childBegin();
|
||||
while (it != childEnd()) {
|
||||
if (it->channel() == ch) {
|
||||
return const_cast<Component*>(&*it);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Component::assign(m5::hal::bus::Bus* bus)
|
||||
{
|
||||
if (_addr) {
|
||||
_adapter = std::make_shared<AdapterI2C>(bus, _addr, _component_cfg.clock);
|
||||
}
|
||||
return static_cast<bool>(_adapter);
|
||||
}
|
||||
|
||||
bool Component::assign(TwoWire& wire)
|
||||
{
|
||||
if (canAccessI2C() && _addr) {
|
||||
_adapter = std::make_shared<AdapterI2C>(wire, _addr, _component_cfg.clock);
|
||||
}
|
||||
return static_cast<bool>(_adapter);
|
||||
}
|
||||
|
||||
bool Component::assign(const int8_t rx_pin, const int8_t tx_pin)
|
||||
{
|
||||
if (canAccessGPIO()) {
|
||||
_adapter = std::make_shared<AdapterGPIO>(rx_pin, tx_pin);
|
||||
}
|
||||
return static_cast<bool>(_adapter);
|
||||
}
|
||||
|
||||
bool Component::selectChannel(const uint8_t ch)
|
||||
{
|
||||
bool ret{true};
|
||||
if (hasParent()) {
|
||||
ret = _parent->selectChannel(channel());
|
||||
}
|
||||
return ret && (select_channel(ch) == m5::hal::error::error_t::OK);
|
||||
}
|
||||
|
||||
m5::hal::error::error_t Component::readWithTransaction(uint8_t* data, const size_t len)
|
||||
{
|
||||
selectChannel(channel());
|
||||
auto r = adapter()->readWithTransaction(data, len);
|
||||
return r;
|
||||
}
|
||||
|
||||
m5::hal::error::error_t Component::writeWithTransaction(const uint8_t* data, const size_t len, const uint32_t exparam)
|
||||
{
|
||||
selectChannel(channel());
|
||||
return adapter()->writeWithTransaction(data, len, exparam);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
m5::hal::error::error_t Component::writeWithTransaction(const Reg reg, const uint8_t* data, const size_t len,
|
||||
const bool stop)
|
||||
{
|
||||
selectChannel(channel());
|
||||
return adapter()->writeWithTransaction(reg, data, len, stop);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::readRegister(const Reg reg, uint8_t* rbuf, const size_t len, const uint32_t delayMillis,
|
||||
const bool stop)
|
||||
{
|
||||
if (!writeRegister(reg, nullptr, 0U, stop)) {
|
||||
M5_LIB_LOGE("Failed to write");
|
||||
return false;
|
||||
}
|
||||
|
||||
m5::utility::delay(delayMillis);
|
||||
return (readWithTransaction(rbuf, len) == m5::hal::error::error_t::OK);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::readRegister8(const Reg reg, uint8_t& result, const uint32_t delayMillis, const bool stop)
|
||||
{
|
||||
return readRegister(reg, &result, 1, delayMillis, stop);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::read_register16E(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop,
|
||||
const bool endian)
|
||||
{
|
||||
uint8_t tmp[2]{};
|
||||
auto ret = readRegister(reg, tmp, 2, delayMillis, stop);
|
||||
if (ret) {
|
||||
result = (tmp[!endian] << 8) | tmp[endian];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::read_register32E(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop,
|
||||
const bool endian)
|
||||
{
|
||||
uint8_t tmp[4]{};
|
||||
auto ret = readRegister(reg, tmp, 4, delayMillis, stop);
|
||||
if (ret) {
|
||||
result = (tmp[0 + 3 * endian] | (tmp[1 + endian] << 8) | (tmp[2 - endian] << 16)) | (tmp[3 - 3 * endian] << 24);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::writeRegister(const Reg reg, const uint8_t* buf, const size_t len, const bool stop)
|
||||
{
|
||||
return (sizeof(Reg) == 2
|
||||
? writeWithTransaction(reg, buf, len, stop)
|
||||
: writeWithTransaction((uint8_t)(reg & 0xFF), buf, len, stop)) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::writeRegister8(const Reg reg, const uint8_t value, const bool stop)
|
||||
{
|
||||
return writeRegister(reg, &value, 1, stop);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::write_register16E(const Reg reg, const uint16_t value, const bool stop, const bool endian)
|
||||
{
|
||||
uint8_t tmp[2]{};
|
||||
tmp[endian] = value & 0xFF;
|
||||
tmp[!endian] = (value >> 8) & 0xFF;
|
||||
return writeRegister(reg, tmp, 2, stop);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type>
|
||||
bool Component::write_register32E(const Reg reg, const uint32_t value, const bool stop, const bool endian)
|
||||
{
|
||||
uint8_t tmp[4]{};
|
||||
tmp[0 + endian * 3] = value & 0xFF;
|
||||
tmp[1 + endian] = (value >> 8) & 0xFF;
|
||||
tmp[2 - endian] = (value >> 16) & 0xFF;
|
||||
tmp[3 - endian * 3] = (value >> 24) & 0xFF;
|
||||
return writeRegister(reg, tmp, 4, stop);
|
||||
}
|
||||
|
||||
bool Component::generalCall(const uint8_t* data, const size_t len)
|
||||
{
|
||||
return adapter()->generalCall(data, len) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::pinModeRX(const gpio::Mode m)
|
||||
{
|
||||
return adapter()->pinModeRX(m) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::writeDigitalRX(const bool high)
|
||||
{
|
||||
return adapter()->writeDigitalRX(high) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::readDigitalRX(bool& high)
|
||||
{
|
||||
return adapter()->readDigitalRX(high) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::writeAnalogRX(const uint16_t v)
|
||||
{
|
||||
return adapter()->writeAnalogRX(v) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::readAnalogRX(uint16_t& v)
|
||||
{
|
||||
return adapter()->readAnalogRX(v) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us)
|
||||
{
|
||||
return adapter()->pulseInRX(duration, state, timeout_us) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::pinModeTX(const gpio::Mode m)
|
||||
{
|
||||
return adapter()->pinModeTX(m) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::writeDigitalTX(const bool high)
|
||||
{
|
||||
return adapter()->writeDigitalTX(high) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::readDigitalTX(bool& high)
|
||||
{
|
||||
return adapter()->readDigitalTX(high) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::writeAnalogTX(const uint16_t v)
|
||||
{
|
||||
return adapter()->writeAnalogTX(v) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::readAnalogTX(uint16_t& v)
|
||||
{
|
||||
return adapter()->readAnalogTX(v) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us)
|
||||
{
|
||||
return adapter()->pulseInTX(duration, state, timeout_us) == m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
bool Component::changeAddress(const uint8_t addr)
|
||||
{
|
||||
if (canAccessI2C() && m5::utility::isValidI2CAddress(addr)) {
|
||||
auto ad = asAdapter<AdapterI2C>(Adapter::Type::I2C);
|
||||
if (ad) {
|
||||
M5_LIB_LOGI("Change to address %x", addr);
|
||||
_addr = addr;
|
||||
ad->setAddress(addr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
M5_LIB_LOGE("Failed to change, %u, %x", canAccessI2C(), addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Component::debugInfo() const
|
||||
{
|
||||
std::string tmp{};
|
||||
switch (_adapter->type()) {
|
||||
case Adapter::Type::I2C:
|
||||
tmp = m5::utility::formatString("%p:%u ADDR:%02X", _adapter.get(), _adapter.use_count(),
|
||||
asAdapter<AdapterI2C>(Adapter::Type::I2C)->address());
|
||||
break;
|
||||
case Adapter::Type::GPIO:
|
||||
tmp = m5::utility::formatString("%p:%u RX:%d TX:%d", _adapter.get(), _adapter.use_count(),
|
||||
asAdapter<AdapterGPIO>(Adapter::Type::GPIO)->rx_pin(),
|
||||
asAdapter<AdapterGPIO>(Adapter::Type::GPIO)->tx_pin());
|
||||
break;
|
||||
default:
|
||||
tmp = m5::utility::formatString("%p:%u Type:%d", _adapter.get(), _adapter.use_count(), _adapter->type());
|
||||
break;
|
||||
}
|
||||
return m5::utility::formatString("[%s]:ID{0X%08x}:%s CH:%d parent:%u children:%zu/%u", deviceName(), identifier(),
|
||||
tmp.c_str(), channel(), hasParent(), childrenSize(), _component_cfg.max_children);
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
template bool Component::readRegister<uint8_t>(const uint8_t, uint8_t*, const size_t, const uint32_t, const bool);
|
||||
template bool Component::readRegister<uint16_t>(const uint16_t, uint8_t*, const size_t, const uint32_t, const bool);
|
||||
template bool Component::readRegister8<uint8_t>(const uint8_t, uint8_t&, const uint32_t, const bool);
|
||||
template bool Component::readRegister8<uint16_t>(const uint16_t, uint8_t&, const uint32_t, const bool);
|
||||
template bool Component::read_register16E<uint8_t>(const uint8_t, uint16_t&, const uint32_t, const bool, const bool);
|
||||
template bool Component::read_register16E<uint16_t>(const uint16_t, uint16_t&, const uint32_t, const bool, const bool);
|
||||
template bool Component::read_register32E<uint8_t>(const uint8_t, uint32_t&, const uint32_t, const bool, const bool);
|
||||
template bool Component::read_register32E<uint16_t>(const uint16_t, uint32_t&, const uint32_t, const bool, const bool);
|
||||
|
||||
template bool Component::writeRegister<uint8_t>(const uint8_t, const uint8_t*, const size_t, const bool);
|
||||
template bool Component::writeRegister<uint16_t>(const uint16_t, const uint8_t*, const size_t, const bool);
|
||||
template bool Component::writeRegister8<uint8_t>(const uint8_t, const uint8_t, const bool);
|
||||
template bool Component::writeRegister8<uint16_t>(const uint16_t, const uint8_t, const bool);
|
||||
template bool Component::write_register16E<uint8_t>(const uint8_t, const uint16_t, const bool, const bool);
|
||||
template bool Component::write_register16E<uint16_t>(const uint16_t, const uint16_t, const bool, const bool);
|
||||
template bool Component::write_register32E<uint8_t>(const uint8_t, const uint32_t, const bool, const bool);
|
||||
template bool Component::write_register32E<uint16_t>(const uint16_t, const uint32_t, const bool, const bool);
|
||||
|
||||
template m5::hal::error::error_t Component::writeWithTransaction<uint8_t>(const uint8_t reg, const uint8_t* data,
|
||||
const size_t len, const bool stop);
|
||||
template m5::hal::error::error_t Component::writeWithTransaction<uint16_t>(const uint16_t reg, const uint8_t* data,
|
||||
const size_t len, const bool stop);
|
||||
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
13
libraries/M5UnitUnified/src/M5UnitComponent.h
Normal file
13
libraries/M5UnitUnified/src/M5UnitComponent.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef M5_UNIT_COMPONENT_H
|
||||
#define M5_UNIT_COMPONENT_H
|
||||
#ifdef __cplusplus
|
||||
#include "M5UnitComponent.hpp"
|
||||
#else
|
||||
#error M5UnitComponent requires a C++ compiler, please change file extension to .cc or .cpp
|
||||
#endif
|
||||
#endif
|
||||
754
libraries/M5UnitUnified/src/M5UnitComponent.hpp
Normal file
754
libraries/M5UnitUnified/src/M5UnitComponent.hpp
Normal file
|
|
@ -0,0 +1,754 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5UnitComponent.hpp
|
||||
@brief Main header of M5UnitComponent
|
||||
*/
|
||||
#ifndef M5_UNIT_COMPONENT_HPP
|
||||
#define M5_UNIT_COMPONENT_HPP
|
||||
|
||||
#include "m5_unit_component/types.hpp"
|
||||
#include "m5_unit_component/adapter.hpp"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
class TwoWire;
|
||||
|
||||
namespace m5 {
|
||||
namespace unit {
|
||||
|
||||
class UnitUnified;
|
||||
class Adapter;
|
||||
|
||||
/*!
|
||||
@class m5::unit::Component
|
||||
@brief Base class of unit component
|
||||
*/
|
||||
class Component {
|
||||
public:
|
||||
/*!
|
||||
@struct component_config_t
|
||||
@brief Component basic settings for begin
|
||||
*/
|
||||
struct component_config_t {
|
||||
//! Clock for communication (default as 100000)
|
||||
uint32_t clock{100000};
|
||||
//! Maximum number of periodic measurement data to be stored
|
||||
uint32_t stored_size{1};
|
||||
//! Does the user call Unit's update? (default as false)
|
||||
bool self_update{false};
|
||||
//! Maximum number of units that can be connected (default as 0)
|
||||
uint8_t max_children{0};
|
||||
};
|
||||
|
||||
///@warning Define the same name and type in the derived class.
|
||||
///@name Fixed parameters for class
|
||||
///@{
|
||||
static const types::uid_t uid; //!< @brief Unique identifier
|
||||
static const types::attr_t attr; //!< @brief Attributes
|
||||
static const char name[]; //!< @brief Device name string
|
||||
///@}
|
||||
|
||||
///@warning COPY PROHIBITED
|
||||
///@name Constructor
|
||||
///@{
|
||||
explicit Component(const uint8_t addr = 0x00); // I2C address
|
||||
|
||||
Component(const Component&) = delete;
|
||||
|
||||
Component(Component&&) noexcept = default;
|
||||
///@}
|
||||
|
||||
///@warning COPY PROHIBITED
|
||||
///@name Assignment
|
||||
///@{
|
||||
Component& operator=(const Component&) = delete;
|
||||
|
||||
Component& operator=(Component&&) noexcept = default;
|
||||
///@}
|
||||
|
||||
virtual ~Component() = default;
|
||||
|
||||
///@name Component settings
|
||||
///@{
|
||||
/*! @brief Gets the common configurations in each unit */
|
||||
inline component_config_t component_config()
|
||||
{
|
||||
return _component_cfg;
|
||||
}
|
||||
//! @brief Set the common configurations in each unit
|
||||
inline void component_config(const component_config_t& cfg)
|
||||
{
|
||||
_component_cfg = cfg;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Functions that must be inherited
|
||||
///@{
|
||||
/*!
|
||||
@brief Begin unit
|
||||
@details Initiate functions based on component config and unit config
|
||||
*/
|
||||
virtual bool begin()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*!
|
||||
@brief Update unit
|
||||
@param force Forced communication for updates if true
|
||||
*/
|
||||
virtual void update(const bool force = false)
|
||||
{
|
||||
(void)force;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Properties
|
||||
///@{
|
||||
/*! @brief Gets the device name */
|
||||
inline const char* deviceName() const
|
||||
{
|
||||
return unit_device_name();
|
||||
}
|
||||
//! @brief Gets the identifier
|
||||
inline types::uid_t identifier() const
|
||||
{
|
||||
return unit_identifier();
|
||||
}
|
||||
//! @brief Gets the attributes
|
||||
inline types::attr_t attribute() const
|
||||
{
|
||||
return unit_attribute();
|
||||
}
|
||||
//! @brief Gets the category
|
||||
inline types::category_t category() const
|
||||
{
|
||||
return unit_category();
|
||||
}
|
||||
//! @brief Gets the registered order (== 0 means not yet)
|
||||
inline uint32_t order() const
|
||||
{
|
||||
return _order;
|
||||
}
|
||||
//! @brief Gets the channel if connected to another unit
|
||||
inline int16_t channel() const
|
||||
{
|
||||
return _channel;
|
||||
}
|
||||
//! @brief Is the unit registered with the manager?
|
||||
inline bool isRegistered() const
|
||||
{
|
||||
return _manager != nullptr;
|
||||
}
|
||||
//! @brief Address used to I2C access the device
|
||||
inline uint8_t address() const
|
||||
{
|
||||
return _addr;
|
||||
}
|
||||
/*!
|
||||
@brief Gets the access adapter
|
||||
@warning Ownership is retained by the unit and should not be released
|
||||
*/
|
||||
inline Adapter* adapter() const
|
||||
{
|
||||
return _adapter.get();
|
||||
}
|
||||
//! @brief Gets the access adapter
|
||||
template <class T>
|
||||
inline auto asAdapter(const Adapter::Type t) ->
|
||||
typename std::remove_cv<typename std::remove_pointer<T>::type>::type*
|
||||
{
|
||||
using U = typename std::remove_cv<typename std::remove_pointer<T>::type>::type;
|
||||
static_assert(std::is_base_of<Adapter, U>::value, "T must be derived from Adapter");
|
||||
return (_adapter->type() == t) ? static_cast<U*>(_adapter.get()) : nullptr;
|
||||
}
|
||||
template <class T>
|
||||
inline auto asAdapter(const Adapter::Type t) const -> const
|
||||
typename std::remove_cv<typename std::remove_pointer<T>::type>::type*
|
||||
{
|
||||
using U = typename std::remove_cv<typename std::remove_pointer<T>::type>::type;
|
||||
static_assert(std::is_base_of<Adapter, U>::value, "T must be derived from Adapter");
|
||||
return (_adapter->type() == t) ? static_cast<const U*>(_adapter.get()) : nullptr;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Attributes
|
||||
///@{
|
||||
bool canAccessI2C() const;
|
||||
bool canAccessGPIO() const;
|
||||
///@}
|
||||
|
||||
///@name Periodic measurement
|
||||
///@{
|
||||
/*! @brief In periodic measurement? */
|
||||
inline bool inPeriodic() const
|
||||
{
|
||||
return in_periodic();
|
||||
}
|
||||
//! @brief Periodic measurement data updated?
|
||||
inline bool updated() const
|
||||
{
|
||||
return _updated;
|
||||
}
|
||||
/*!
|
||||
@brief Time elapsed since start-up when the measurement data was updated in update()
|
||||
@return Updated time (Unit: ms)
|
||||
*/
|
||||
inline types::elapsed_time_t updatedMillis() const
|
||||
{
|
||||
return _latest;
|
||||
}
|
||||
/*!
|
||||
@brief Gets the periodic measurement interval
|
||||
@return interval time (Unit: ms)
|
||||
*/
|
||||
inline types::elapsed_time_t interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Bus assignment
|
||||
///@{
|
||||
/*! @brief Assgin m5::hal::bus */
|
||||
virtual bool assign(m5::hal::bus::Bus* bus);
|
||||
/*! @brief Assgin TwoWire */
|
||||
virtual bool assign(TwoWire& wire);
|
||||
/*! @brief Assgin GPIO */
|
||||
virtual bool assign(const int8_t rx_pin, const int8_t tx_pin);
|
||||
///@}
|
||||
|
||||
///@note For daisy-chaining units such as hubs
|
||||
///@name Parent-children relationship
|
||||
///@{
|
||||
/*! @brief Has parent unit? */
|
||||
inline bool hasParent() const
|
||||
{
|
||||
return _parent != nullptr;
|
||||
}
|
||||
//! @brief Are there any other devices connected to the same parent unit besides yourself?
|
||||
inline bool hasSiblings() const
|
||||
{
|
||||
return (_prev != nullptr) || (_next != nullptr);
|
||||
}
|
||||
//! @brief Are there other devices connected to me?
|
||||
inline bool hasChildren() const
|
||||
{
|
||||
return _child;
|
||||
}
|
||||
//! @brief Number of units connected to me
|
||||
size_t childrenSize() const;
|
||||
//! @brief Is there an other unit connected to the specified channel?
|
||||
bool existsChild(const uint8_t ch) const;
|
||||
//! @brief Gets the parent unit
|
||||
inline Component* parent()
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
#if 0
|
||||
//! @brief Gets the parent unit
|
||||
inline const Component* parent() const
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
#endif
|
||||
//! @brief Gets the device connected to the specified channel
|
||||
Component* child(const uint8_t chhanle) const;
|
||||
//! @brief Connect the unit to the specified channel
|
||||
bool add(Component& c, const int16_t channel);
|
||||
//! @brief Select valid channel if exists
|
||||
bool selectChannel(const uint8_t ch = 8);
|
||||
///@}
|
||||
|
||||
///@cond 0
|
||||
template <typename T>
|
||||
class iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
explicit iterator(Component* c = nullptr) : _ptr(c)
|
||||
{
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return *_ptr;
|
||||
}
|
||||
pointer operator->() const
|
||||
{
|
||||
return _ptr;
|
||||
}
|
||||
iterator& operator++()
|
||||
{
|
||||
_ptr = _ptr ? _ptr->_next : nullptr;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
friend bool operator==(const iterator& a, const iterator& b)
|
||||
{
|
||||
return a._ptr == b._ptr;
|
||||
}
|
||||
friend bool operator!=(const iterator& a, const iterator& b)
|
||||
{
|
||||
return a._ptr != b._ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Component* _ptr;
|
||||
};
|
||||
|
||||
using child_iterator = iterator<Component>;
|
||||
using const_child_iterator = iterator<const Component>;
|
||||
inline child_iterator childBegin() noexcept
|
||||
{
|
||||
return child_iterator(_child);
|
||||
}
|
||||
inline child_iterator childEnd() noexcept
|
||||
{
|
||||
return child_iterator();
|
||||
}
|
||||
inline const_child_iterator childBegin() const noexcept
|
||||
{
|
||||
return const_child_iterator(_child);
|
||||
}
|
||||
inline const_child_iterator childEnd() const noexcept
|
||||
{
|
||||
return const_child_iterator();
|
||||
}
|
||||
///@endcond
|
||||
|
||||
/*! @brief General call for I2C*/
|
||||
bool generalCall(const uint8_t* data, const size_t len);
|
||||
|
||||
//! @brief Output information for debug
|
||||
virtual std::string debugInfo() const;
|
||||
|
||||
// I2C R/W
|
||||
///@cond 0
|
||||
m5::hal::error::error_t readWithTransaction(uint8_t* data, const size_t len);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool readRegister(const Reg reg, uint8_t* rbuf, const size_t len, const uint32_t delayMillis,
|
||||
const bool stop = true);
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool readRegister8(const Reg reg, uint8_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool readRegister16BE(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop = true)
|
||||
{
|
||||
return read_register16E(reg, result, delayMillis, stop, true);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool readRegister16LE(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop = true)
|
||||
{
|
||||
return read_register16E(reg, result, delayMillis, stop, false);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool readRegister32BE(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop = true)
|
||||
{
|
||||
return read_register32E(reg, result, delayMillis, stop, true);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool readRegister32LE(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop = true)
|
||||
{
|
||||
return read_register32E(reg, result, delayMillis, stop, false);
|
||||
}
|
||||
|
||||
m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len, const uint32_t exparam = 1);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
m5::hal::error::error_t writeWithTransaction(const Reg reg, const uint8_t* data, const size_t len,
|
||||
const bool stop = true);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool writeRegister(const Reg reg, const uint8_t* buf = nullptr, const size_t len = 0U, const bool stop = true);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool writeRegister8(const Reg reg, const uint8_t value, const bool stop = true);
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool writeRegister16BE(const Reg reg, const uint16_t value, const bool stop = true)
|
||||
{
|
||||
return write_register16E(reg, value, stop, true);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool writeRegister16LE(const Reg reg, const uint16_t value, const bool stop = true)
|
||||
{
|
||||
return write_register16E(reg, value, stop, false);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool writeRegister32BE(const Reg reg, const uint32_t value, const bool stop = true)
|
||||
{
|
||||
return write_register32E(reg, value, stop, true);
|
||||
}
|
||||
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
inline bool writeRegister32LE(const Reg reg, const uint32_t value, const bool stop = true)
|
||||
{
|
||||
return write_register32E(reg, value, stop, false);
|
||||
}
|
||||
|
||||
// GPIO
|
||||
bool pinModeRX(const gpio::Mode m);
|
||||
bool writeDigitalRX(const bool high);
|
||||
bool readDigitalRX(bool& high);
|
||||
bool writeAnalogRX(const uint16_t v);
|
||||
bool readAnalogRX(uint16_t& v);
|
||||
bool pulseInRX(uint32_t& duration, const int state, const uint32_t timeout_us = 1000000);
|
||||
|
||||
bool pinModeTX(const gpio::Mode m);
|
||||
bool writeDigitalTX(const bool high);
|
||||
bool readDigitalTX(bool& high);
|
||||
bool writeAnalogTX(const uint16_t v);
|
||||
bool readAnalogTX(uint16_t& v);
|
||||
bool pulseInTX(uint32_t& duration, const int state, const uint32_t timeout_us = 1000000);
|
||||
///@endcond
|
||||
|
||||
#if defined(DOXYGEN_PROCESS)
|
||||
// There is a problem with the Doxygen output of templates containing std::enable_if,
|
||||
// so we need a section for Dxygen output
|
||||
///@name Read/Write
|
||||
///@{
|
||||
//! @brief Read any data with transaction
|
||||
m5::hal::error::error_t readWithTransaction(uint8_t* data, const size_t len);
|
||||
//! @brief Read any data with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister(const Reg reg, uint8_t* rbuf, const size_t len, const uint32_t delayMillis,
|
||||
const bool stop = true);
|
||||
//! @brief Read byte with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister8(const Reg reg, uint8_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
//! @brief Read word in big-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister16BE(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
//! @brief Read word in little-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister16LE(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
//! @brief Read dword in big-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister32BE(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
//! @brief Read dword in little-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool readRegister32LE(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop = true);
|
||||
|
||||
//! @brief Write any data with transaction
|
||||
m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len, const bool stop = true);
|
||||
//! @brief Write any data with transaction to register
|
||||
template <typename Reg>
|
||||
m5::hal::error::error_t writeWithTransaction(const Reg reg, const uint8_t* data, const size_t len,
|
||||
const bool stop = true);
|
||||
//! @brief Write any data with transaction to register
|
||||
template <typename Reg>
|
||||
bool writeRegister(const Reg reg, const uint8_t* buf = nullptr, const size_t len = 0U, const bool stop = true);
|
||||
//! @brief Write byte with transaction to register
|
||||
template <typename Reg>
|
||||
bool writeRegister8(const Reg reg, const uint8_t value, const bool stop = true);
|
||||
//! @brief Write word in big-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool writeRegister16BE(const Reg reg, const uint16_t value, const bool stop = true);
|
||||
//! @brief Write word in little-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool writeRegister16LE(const Reg reg, const uint16_t value, const bool stop = true);
|
||||
//! @brief Write dword in big-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool writeRegister32BE(const Reg reg, const uint32_t value, const bool stop = true);
|
||||
//! @brief Write dword in little-endian order with transaction from register
|
||||
template <typename Reg>
|
||||
bool writeRegister32LE(const Reg reg, const uint32_t value, const bool stop = true);
|
||||
///@}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Proper implementation in derived classes is required
|
||||
virtual const char* unit_device_name() const = 0;
|
||||
virtual types::uid_t unit_identifier() const = 0;
|
||||
virtual types::attr_t unit_attribute() const = 0;
|
||||
inline virtual types::category_t unit_category() const
|
||||
{
|
||||
return types::category_t::None;
|
||||
}
|
||||
inline virtual bool in_periodic() const
|
||||
{
|
||||
return _periodic;
|
||||
}
|
||||
|
||||
inline virtual std::shared_ptr<Adapter> ensure_adapter(const uint8_t /*ch*/)
|
||||
{
|
||||
return _adapter; // By default, offer my adapter for sharing
|
||||
}
|
||||
|
||||
// Select valid channel if exists(PaHub etc...)
|
||||
inline virtual m5::hal::error::error_t select_channel(const uint8_t)
|
||||
{
|
||||
return m5::hal::error::error_t::OK;
|
||||
}
|
||||
|
||||
inline size_t stored_size() const
|
||||
{
|
||||
return _component_cfg.stored_size;
|
||||
}
|
||||
|
||||
bool add_child(Component* c);
|
||||
|
||||
// I2C
|
||||
bool changeAddress(const uint8_t addr); // Functions for dynamically addressable devices
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool read_register16E(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop,
|
||||
const bool endian);
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool write_register16E(const Reg reg, const uint16_t value, const bool stop, const bool endifan);
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool read_register32E(const Reg reg, uint32_t& result, const uint32_t delayMillis, const bool stop,
|
||||
const bool endian);
|
||||
template <typename Reg,
|
||||
typename std::enable_if<std::is_integral<Reg>::value && std::is_unsigned<Reg>::value && sizeof(Reg) <= 2,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
bool write_register32E(const Reg reg, const uint32_t value, const bool stop, const bool endifan);
|
||||
|
||||
protected:
|
||||
// For periodic measurement
|
||||
types::elapsed_time_t _latest{}, _interval{};
|
||||
bool _periodic{}; // During periodic measurement?
|
||||
bool _updated{};
|
||||
|
||||
private:
|
||||
UnitUnified* _manager{};
|
||||
std::shared_ptr<m5::unit::Adapter> _adapter{};
|
||||
|
||||
uint32_t _order{};
|
||||
component_config_t _component_cfg{};
|
||||
int16_t _channel{-1}; // valid [0...]
|
||||
uint8_t _addr{};
|
||||
bool _begun{};
|
||||
|
||||
// for chain
|
||||
Component* _parent{};
|
||||
Component* _next{};
|
||||
Component* _prev{};
|
||||
Component* _child{};
|
||||
|
||||
friend class UnitUnified;
|
||||
};
|
||||
|
||||
/*!
|
||||
@class PeriodicMeasurementAdapter
|
||||
@brief Interface class for periodic measurement (CRTP)
|
||||
@details Common interface for accumulated periodic measurement data
|
||||
@details Provide a common interface for periodic measurements for each unit
|
||||
@tparam Derived Derived class
|
||||
@tparam MD Type of the measurement data group
|
||||
@warning MUST IMPLEMENT some functions (NOT VERTUAL)
|
||||
- MD Derived::oldest_periodic_data() const;
|
||||
- MD Derived::latestt_periodic_data() const;
|
||||
- bool Derived::start_periodic_measurement(any arguments);
|
||||
- bool Derived::stop_periodic_measurement():
|
||||
@warning MUST ADD std::unique_ptr<m5::container::CircularBuffer<MD>> _data{}
|
||||
in Derived class
|
||||
@warning This class is an interface class and should not have any data
|
||||
@note See also M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER
|
||||
*/
|
||||
template <class Derived, typename MD>
|
||||
class PeriodicMeasurementAdapter {
|
||||
public:
|
||||
///@name Periodic measurement
|
||||
///@{
|
||||
/*!
|
||||
@brief Start periodic measurement
|
||||
@tparam Args Optional arguments
|
||||
@return True if successful
|
||||
@note Call Derived::start_periodic_measurement
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline bool startPeriodicMeasurement(Args&&... args)
|
||||
{
|
||||
// Prepare for future common initiation preprocessing needs
|
||||
return static_cast<Derived*>(this)->start_periodic_measurement(std::forward<Args>(args)...);
|
||||
}
|
||||
/*!
|
||||
@brief Stop periodic measurement
|
||||
@tparam Args Optional arguments
|
||||
@return True if successful
|
||||
@note Call Derived::stop_periodic_measurement
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline bool stopPeriodicMeasurement(Args&&... args)
|
||||
{
|
||||
// Prepare for future common stopping preprocessing needs
|
||||
return static_cast<Derived*>(this)->stop_periodic_measurement(std::forward<Args>(args)...);
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Data
|
||||
///@{
|
||||
//! @brief Gets the number of stored data
|
||||
inline size_t available() const
|
||||
{
|
||||
return available_periodic_measurement_data();
|
||||
}
|
||||
//! @brief Is empty stored data?
|
||||
inline bool empty() const
|
||||
{
|
||||
return empty_periodic_measurement_data();
|
||||
}
|
||||
//! @brief Is stored data full?
|
||||
inline bool full() const
|
||||
{
|
||||
return full_periodic_measurement_data();
|
||||
}
|
||||
//! @brief Retrieve oldest stored data
|
||||
inline MD oldest() const
|
||||
{
|
||||
return static_cast<const Derived*>(this)->oldest_periodic_data();
|
||||
}
|
||||
//! @brief Retrieve latest stored data
|
||||
inline MD latest() const
|
||||
{
|
||||
return static_cast<const Derived*>(this)->latest_periodic_data();
|
||||
}
|
||||
//! @brief Discard the oldest data accumulated
|
||||
inline void discard()
|
||||
{
|
||||
discard_periodic_measurement_data();
|
||||
}
|
||||
//! @brief Discard all data
|
||||
inline void flush()
|
||||
{
|
||||
flush_periodic_measurement_data();
|
||||
}
|
||||
///@}
|
||||
|
||||
protected:
|
||||
///@note Must implement in derived class
|
||||
///@name Pure virtual functions
|
||||
///@{
|
||||
virtual size_t available_periodic_measurement_data() const = 0;
|
||||
virtual bool empty_periodic_measurement_data() const = 0;
|
||||
virtual bool full_periodic_measurement_data() const = 0;
|
||||
virtual void discard_periodic_measurement_data() = 0;
|
||||
virtual void flush_periodic_measurement_data() = 0;
|
||||
///@}
|
||||
};
|
||||
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
|
||||
// Helper for creating derived classes from Component
|
||||
///@cond
|
||||
#define M5_UNIT_COMPONENT_HPP_BUILDER(cls, reg) \
|
||||
public: \
|
||||
constexpr static uint8_t DEFAULT_ADDRESS{(reg)}; \
|
||||
static const types::uid_t uid; \
|
||||
static const types::attr_t attr; \
|
||||
static const char name[]; \
|
||||
\
|
||||
cls(const cls&) = delete; \
|
||||
\
|
||||
cls& operator=(const cls&) = delete; \
|
||||
\
|
||||
cls(cls&&) noexcept = default; \
|
||||
\
|
||||
cls& operator=(cls&&) noexcept = default; \
|
||||
\
|
||||
protected: \
|
||||
inline virtual const char* unit_device_name() const override \
|
||||
{ \
|
||||
return name; \
|
||||
} \
|
||||
inline virtual types::uid_t unit_identifier() const override \
|
||||
{ \
|
||||
return uid; \
|
||||
} \
|
||||
inline virtual types::attr_t unit_attribute() const override \
|
||||
{ \
|
||||
return attr; \
|
||||
}
|
||||
|
||||
// Helper for creating derived class from PeriodicMeasurementAdapter
|
||||
#define M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(cls, md) \
|
||||
protected: \
|
||||
friend class PeriodicMeasurementAdapter<cls, md>; \
|
||||
\
|
||||
inline md oldest_periodic_data() const \
|
||||
{ \
|
||||
return !_data->empty() ? _data->front().value() : md{}; \
|
||||
} \
|
||||
inline md latest_periodic_data() const \
|
||||
{ \
|
||||
return !_data->empty() ? _data->back().value() : md{}; \
|
||||
} \
|
||||
inline virtual size_t available_periodic_measurement_data() const override \
|
||||
{ \
|
||||
return _data->size(); \
|
||||
} \
|
||||
inline virtual bool empty_periodic_measurement_data() const override \
|
||||
{ \
|
||||
return _data->empty(); \
|
||||
} \
|
||||
inline virtual bool full_periodic_measurement_data() const override \
|
||||
{ \
|
||||
return _data->full(); \
|
||||
} \
|
||||
inline virtual void discard_periodic_measurement_data() override \
|
||||
{ \
|
||||
_data->pop_front(); \
|
||||
} \
|
||||
inline virtual void flush_periodic_measurement_data() override \
|
||||
{ \
|
||||
_data->clear(); \
|
||||
}
|
||||
|
||||
///@endcond
|
||||
#endif
|
||||
150
libraries/M5UnitUnified/src/M5UnitUnified.cpp
Normal file
150
libraries/M5UnitUnified/src/M5UnitUnified.cpp
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5UnitUnified.cpp
|
||||
@brief class UnitUnified
|
||||
*/
|
||||
#include "M5UnitUnified.hpp"
|
||||
#include <M5Utility.hpp>
|
||||
|
||||
namespace m5 {
|
||||
namespace unit {
|
||||
|
||||
uint32_t UnitUnified::_registerCount{0};
|
||||
|
||||
bool UnitUnified::add(Component& u, m5::hal::bus::Bus* bus)
|
||||
{
|
||||
if (u.isRegistered()) {
|
||||
M5_LIB_LOGW("Already added");
|
||||
return false;
|
||||
}
|
||||
if (!bus) {
|
||||
M5_LIB_LOGE("Bus null");
|
||||
return false;
|
||||
}
|
||||
|
||||
M5_LIB_LOGD("Add [%s]:0x%02x", u.deviceName(), u.address());
|
||||
|
||||
u._manager = this;
|
||||
if (u.assign(bus)) {
|
||||
u._order = ++_registerCount;
|
||||
_units.emplace_back(&u);
|
||||
return add_children(u);
|
||||
}
|
||||
M5_LIB_LOGE("Failed to assign %s:%u", u.deviceName(), u.canAccessI2C());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnitUnified::add(Component& u, TwoWire& wire)
|
||||
{
|
||||
if (u.isRegistered()) {
|
||||
M5_LIB_LOGW("Already added");
|
||||
return false;
|
||||
}
|
||||
|
||||
M5_LIB_LOGD("Add [%s] addr:%02x children:%zu", u.deviceName(), u.address(), u.childrenSize());
|
||||
|
||||
u._manager = this;
|
||||
if (u.assign(wire)) {
|
||||
u._order = ++_registerCount;
|
||||
_units.emplace_back(&u);
|
||||
return add_children(u);
|
||||
}
|
||||
M5_LIB_LOGE("Failed to assign %s:%u", u.deviceName(), u.canAccessI2C());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnitUnified::add(Component& u, const int8_t rx_pin, const int8_t tx_pin)
|
||||
{
|
||||
if (u.isRegistered()) {
|
||||
M5_LIB_LOGW("Already added");
|
||||
return false;
|
||||
}
|
||||
|
||||
M5_LIB_LOGD("Add [%s] rx:%d tx:%d %zu", u.deviceName(), rx_pin, tx_pin, u.childrenSize());
|
||||
|
||||
u._manager = this;
|
||||
if (u.assign(rx_pin, tx_pin)) {
|
||||
u._order = ++_registerCount;
|
||||
_units.emplace_back(&u);
|
||||
return add_children(u);
|
||||
}
|
||||
M5_LIB_LOGE("Failed to assign %s:%u", u.deviceName(), u.canAccessGPIO());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add children if exists
|
||||
bool UnitUnified::add_children(Component& u)
|
||||
{
|
||||
auto it = u.childBegin();
|
||||
while (it != u.childEnd()) {
|
||||
auto ch = it->channel();
|
||||
|
||||
M5_LIB_LOGV("%s child:%s channel:%u", u.deviceName(), it->deviceName(), ch);
|
||||
if (it->isRegistered()) {
|
||||
M5_LIB_LOGE("Already registered %s", it->deviceName());
|
||||
return false;
|
||||
}
|
||||
it->_manager = this;
|
||||
it->_adapter = u.ensure_adapter(ch);
|
||||
M5_LIB_LOGD(" Shared:%u %u", u._adapter.use_count(), it->_adapter.use_count());
|
||||
it->_order = ++_registerCount;
|
||||
_units.emplace_back(&*it);
|
||||
|
||||
if (!add_children(*it)) {
|
||||
return false;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnitUnified::begin()
|
||||
{
|
||||
return !std::any_of(_units.begin(), _units.end(), [](Component* c) {
|
||||
M5_LIB_LOGV("Try begin:%s", c->deviceName());
|
||||
bool ret = c->_begun = c->begin();
|
||||
if (!ret) {
|
||||
M5_LIB_LOGE("Failed to begin: %s", c->debugInfo().c_str());
|
||||
}
|
||||
return !ret;
|
||||
});
|
||||
}
|
||||
|
||||
void UnitUnified::update(const bool force)
|
||||
{
|
||||
// Order of registration
|
||||
for (auto&& u : _units) {
|
||||
if (!u->_component_cfg.self_update && u->_begun) {
|
||||
u->update(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string UnitUnified::debugInfo() const
|
||||
{
|
||||
std::string s = m5::utility::formatString("\nM5UnitUnified: %zu units\n", _units.size());
|
||||
for (auto&& u : _units) {
|
||||
if (!u->hasParent()) {
|
||||
s += make_unit_info(u, 0);
|
||||
}
|
||||
}
|
||||
return m5::utility::trim(s);
|
||||
}
|
||||
|
||||
std::string UnitUnified::make_unit_info(const Component* u, const uint8_t indent) const
|
||||
{
|
||||
std::string s = m5::utility::formatString("%*c%s\n", indent * 4, ' ', u->debugInfo().c_str());
|
||||
|
||||
if (u->hasChildren()) {
|
||||
s += make_unit_info(u->_child, indent + 1);
|
||||
}
|
||||
u = u->_next;
|
||||
return u ? s += make_unit_info(u, indent) : s;
|
||||
}
|
||||
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
17
libraries/M5UnitUnified/src/M5UnitUnified.h
Normal file
17
libraries/M5UnitUnified/src/M5UnitUnified.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5UnitUnified.h
|
||||
*/
|
||||
#ifndef M5_UNIT_UNIFIED_H
|
||||
#define M5_UNIT_UNIFIED_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "M5UnitUnified.hpp"
|
||||
#else
|
||||
#error M5UnitUnified requires a C++ compiler, please change file extension to .cc or .cpp
|
||||
#endif
|
||||
#endif
|
||||
114
libraries/M5UnitUnified/src/M5UnitUnified.hpp
Normal file
114
libraries/M5UnitUnified/src/M5UnitUnified.hpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5UnitUnified.hpp
|
||||
@brief Main header of M5UnitUnified
|
||||
|
||||
@mainpage M5UnitUnified
|
||||
M5UnitUnified is a library for unified handling of various M5 unit products.
|
||||
- Unified APIs
|
||||
- Unified Connections
|
||||
- Unified Licensing
|
||||
*/
|
||||
#ifndef M5_UNIT_UNIFIED_HPP
|
||||
#define M5_UNIT_UNIFIED_HPP
|
||||
|
||||
#include "M5UnitComponent.hpp"
|
||||
#include <M5HAL.hpp>
|
||||
#if defined(M5_UNIT_UNIFIED_USING_RMT_V2)
|
||||
#else
|
||||
#include <driver/rmt.h>
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class TwoWire;
|
||||
|
||||
/*!
|
||||
@namespace m5
|
||||
@brief Top level namespace of M5stack
|
||||
*/
|
||||
namespace m5 {
|
||||
/*!
|
||||
@namespace unit
|
||||
@brief Unit-related namespace
|
||||
*/
|
||||
namespace unit {
|
||||
class Component;
|
||||
|
||||
/*!
|
||||
@class m5::unit::UnitUnified
|
||||
@brief For managing and leading units
|
||||
*/
|
||||
class UnitUnified {
|
||||
public:
|
||||
using container_type = std::vector<Component*>;
|
||||
|
||||
///@warning COPY PROHIBITED
|
||||
///@name Constructor
|
||||
///@{
|
||||
UnitUnified() = default;
|
||||
UnitUnified(const UnitUnified&) = delete;
|
||||
UnitUnified(UnitUnified&&) noexcept = default;
|
||||
///@}
|
||||
|
||||
///@warning COPY PROHIBITED
|
||||
///@name Assignment
|
||||
///@{
|
||||
UnitUnified& operator=(const UnitUnified&) = delete;
|
||||
|
||||
UnitUnified& operator=(UnitUnified&&) noexcept = default;
|
||||
///@}
|
||||
|
||||
///@name Add unit
|
||||
///@{
|
||||
/*!
|
||||
@brief Adding unit to be managed (I2C)
|
||||
@param u Unit Component
|
||||
@param wire TwoWire to be used
|
||||
@return True if successful
|
||||
*/
|
||||
bool add(Component& u, TwoWire& wire);
|
||||
/*!
|
||||
@brief Adding unit to be managed (GPIO)
|
||||
@param u Unit Component
|
||||
@param rx_pin Pin number to be used for RX
|
||||
@param tx_pin Pin number to be used for TX
|
||||
@return True if successful
|
||||
*/
|
||||
bool add(Component& u, const int8_t rx_pin, const int8_t tx_pin);
|
||||
/*!
|
||||
@brief Adding unit to be managed (M5HAL)
|
||||
@param u Unit Component
|
||||
@param bus Bus to be used
|
||||
@return True if successful
|
||||
*/
|
||||
bool add(Component& u, m5::hal::bus::Bus* bus);
|
||||
///@}
|
||||
|
||||
//! @brief Begin of all units under management
|
||||
bool begin();
|
||||
//! @brief Update of all units under management
|
||||
void update(const bool force = false);
|
||||
|
||||
//! @brief Output information for debug
|
||||
std::string debugInfo() const;
|
||||
|
||||
protected:
|
||||
bool add_children(Component& u);
|
||||
std::string make_unit_info(const Component* u, const uint8_t indent = 0) const;
|
||||
|
||||
protected:
|
||||
container_type _units{};
|
||||
|
||||
private:
|
||||
static uint32_t _registerCount;
|
||||
};
|
||||
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
86
libraries/M5UnitUnified/src/googletest/test_helper.hpp
Normal file
86
libraries/M5UnitUnified/src/googletest/test_helper.hpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file test_helper.hpp
|
||||
@brief Helper for testing UnitComponent
|
||||
@note Depends on GoogleTest
|
||||
*/
|
||||
#ifndef M5_UNIT_COMPONENT_GOOGLETEST_HELPER_HPP
|
||||
#define M5_UNIT_COMPONENT_GOOGLETEST_HELPER_HPP
|
||||
|
||||
#include <M5Utility.hpp>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
namespace m5 {
|
||||
namespace unit {
|
||||
namespace googletest {
|
||||
|
||||
template <class U>
|
||||
uint32_t test_periodic_measurement(U* unit, const uint32_t times, const uint32_t tolerance,
|
||||
const uint32_t timeout_duration, void (*callback)(U*), const bool skip_after_test)
|
||||
{
|
||||
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
|
||||
|
||||
auto interval = unit->interval();
|
||||
decltype(interval) avg{}, avgCnt{};
|
||||
uint32_t cnt{times};
|
||||
auto prev = unit->updatedMillis();
|
||||
auto timeout_at = m5::utility::millis() + timeout_duration;
|
||||
while (cnt && m5::utility::millis() <= timeout_at) {
|
||||
unit->update();
|
||||
if (unit->updated()) {
|
||||
--cnt;
|
||||
auto um = unit->updatedMillis();
|
||||
if (prev) {
|
||||
auto duration = um - prev;
|
||||
++avgCnt;
|
||||
avg += duration;
|
||||
// M5_LOGI("dur:%ld", duration);
|
||||
// EXPECT_LE(duration, interval + 1);
|
||||
}
|
||||
prev = um;
|
||||
if (callback) {
|
||||
callback(unit);
|
||||
}
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
if (!skip_after_test) {
|
||||
EXPECT_EQ(cnt, 0U);
|
||||
EXPECT_EQ(avgCnt, times - 1);
|
||||
if (avgCnt) {
|
||||
avg /= avgCnt;
|
||||
EXPECT_LE(avg, decltype(interval)(interval + tolerance));
|
||||
}
|
||||
return avg;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
uint32_t test_periodic_measurement(U* unit, const uint32_t times = 8, const uint32_t tolerance = 1,
|
||||
void (*callback)(U*) = nullptr, const bool skip_after_test = false)
|
||||
{
|
||||
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
|
||||
auto timeout_duration = (unit->interval() * 2) * times;
|
||||
return test_periodic_measurement(unit, times, tolerance, timeout_duration, callback, skip_after_test);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
uint32_t test_periodic_measurement(U* unit, const uint32_t times = 8, void (*callback)(U*) = nullptr,
|
||||
const bool skip_after_test = false)
|
||||
{
|
||||
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
|
||||
auto timeout_duration = (unit->interval() * 2) * times;
|
||||
return test_periodic_measurement(unit, times, 1, timeout_duration, callback, skip_after_test);
|
||||
}
|
||||
|
||||
} // namespace googletest
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
#endif
|
||||
115
libraries/M5UnitUnified/src/googletest/test_template.hpp
Normal file
115
libraries/M5UnitUnified/src/googletest/test_template.hpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file test_template.hpp
|
||||
@brief Helper for testing UnitComponent
|
||||
@note Depends on GoogleTest
|
||||
*/
|
||||
#ifndef M5_UNIT_COMPONENT_GOOGLETEST_TEMPLATE_HPP
|
||||
#define M5_UNIT_COMPONENT_GOOGLETEST_TEMPLATE_HPP
|
||||
|
||||
#include "../M5UnitComponent.hpp"
|
||||
#include <type_traits>
|
||||
#include <Wire.h>
|
||||
#include <esp32-hal-i2c.h>
|
||||
|
||||
namespace m5 {
|
||||
namespace unit {
|
||||
/*!
|
||||
@namespace googletest
|
||||
@brief For GoogleTest
|
||||
*/
|
||||
namespace googletest {
|
||||
|
||||
/*!
|
||||
@class GlobalFixture
|
||||
@brief Overall test environment configuration
|
||||
@tparam FREQ TwoWire operating frequency
|
||||
@tparam WNUM TwoWire number to be used (0 as default)
|
||||
*/
|
||||
template <uint32_t FREQ, uint32_t WNUM = 0>
|
||||
class GlobalFixture : public ::testing::Environment {
|
||||
static_assert(WNUM < 2, "Wire number must be lesser than 2");
|
||||
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
|
||||
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
TwoWire* w[1] = {&Wire};
|
||||
#else
|
||||
TwoWire* w[2] = {&Wire, &Wire1};
|
||||
#endif
|
||||
if (WNUM < m5::stl::size(w) && i2cIsInit(WNUM)) {
|
||||
M5_LOGW("Already inititlized Wire %d. Terminate and restart FREQ %u", WNUM, FREQ);
|
||||
w[WNUM]->end();
|
||||
}
|
||||
w[WNUM]->begin(pin_num_sda, pin_num_scl, FREQ);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@class ComponentTestBase
|
||||
@brief UnitComponent Derived class for testing
|
||||
@tparam U m5::unit::Component-derived classes to be tested
|
||||
@tparam TP parameter type for testing. see also INSTANTIATE_TEST_SUITE_P
|
||||
*/
|
||||
template <typename U, typename TP>
|
||||
class ComponentTestBase : public ::testing::TestWithParam<TP> {
|
||||
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
|
||||
|
||||
protected:
|
||||
virtual void SetUp() override
|
||||
{
|
||||
unit.reset(get_instance());
|
||||
if (!unit) {
|
||||
FAIL() << "Failed to get_instance";
|
||||
GTEST_SKIP();
|
||||
return;
|
||||
}
|
||||
ustr = m5::utility::formatString("%s:%s", unit->deviceName(), is_using_hal() ? "HAL" : "Wire");
|
||||
if (!begin()) {
|
||||
FAIL() << "Failed to begin " << ustr;
|
||||
GTEST_SKIP();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool begin()
|
||||
{
|
||||
if (is_using_hal()) {
|
||||
// Using M5HAL
|
||||
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
|
||||
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
|
||||
m5::hal::bus::I2CBusConfig i2c_cfg;
|
||||
i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda);
|
||||
i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl);
|
||||
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
|
||||
return Units.add(*unit, i2c_bus ? i2c_bus.value() : nullptr) && Units.begin();
|
||||
}
|
||||
// Using TwoWire
|
||||
return Units.add(*unit, Wire) && Units.begin();
|
||||
}
|
||||
|
||||
//!@brief Function returning true if M5HAL is used (decision based on TP)
|
||||
virtual bool is_using_hal() const = 0;
|
||||
//! @brief return m5::unit::Component-derived class instance (decision based on TP)
|
||||
virtual U* get_instance() = 0;
|
||||
|
||||
std::string ustr{};
|
||||
std::unique_ptr<U> unit{};
|
||||
m5::unit::UnitUnified Units;
|
||||
};
|
||||
|
||||
} // namespace googletest
|
||||
} // namespace unit
|
||||
} // namespace m5
|
||||
#endif
|
||||
27
libraries/M5UnitUnified/src/m5_unit_component/adapter.cpp
Normal file
27
libraries/M5UnitUnified/src/m5_unit_component/adapter.cpp
Normal 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
|
||||
21
libraries/M5UnitUnified/src/m5_unit_component/adapter.hpp
Normal file
21
libraries/M5UnitUnified/src/m5_unit_component/adapter.hpp
Normal 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
|
||||
233
libraries/M5UnitUnified/src/m5_unit_component/adapter_base.hpp
Normal file
233
libraries/M5UnitUnified/src/m5_unit_component/adapter_base.hpp
Normal 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
|
||||
507
libraries/M5UnitUnified/src/m5_unit_component/adapter_gpio.cpp
Normal file
507
libraries/M5UnitUnified/src/m5_unit_component/adapter_gpio.cpp
Normal 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
|
||||
163
libraries/M5UnitUnified/src/m5_unit_component/adapter_gpio.hpp
Normal file
163
libraries/M5UnitUnified/src/m5_unit_component/adapter_gpio.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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, ©_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
|
||||
|
|
@ -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
|
||||
396
libraries/M5UnitUnified/src/m5_unit_component/adapter_i2c.cpp
Normal file
396
libraries/M5UnitUnified/src/m5_unit_component/adapter_i2c.cpp
Normal 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, ®, &data, &len, &stop]() {
|
||||
return trans->write(®, 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
|
||||
247
libraries/M5UnitUnified/src/m5_unit_component/adapter_i2c.hpp
Normal file
247
libraries/M5UnitUnified/src/m5_unit_component/adapter_i2c.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
106
libraries/M5UnitUnified/src/m5_unit_component/pin.cpp
Normal file
106
libraries/M5UnitUnified/src/m5_unit_component/pin.cpp
Normal 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
|
||||
47
libraries/M5UnitUnified/src/m5_unit_component/pin.hpp
Normal file
47
libraries/M5UnitUnified/src/m5_unit_component/pin.hpp
Normal 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
|
||||
104
libraries/M5UnitUnified/src/m5_unit_component/types.hpp
Normal file
104
libraries/M5UnitUnified/src/m5_unit_component/types.hpp
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue