From ff5e963499c4a6f33675985fd9e2bde4ccfe0d37 Mon Sep 17 00:00:00 2001 From: lbuque <1102390310@qq.com> Date: Thu, 6 Nov 2025 16:17:27 +0800 Subject: [PATCH] Add SoftI2C_Class. UnitC6L requires internal communication using I2C and PI4IOE5V6408. To reserve the Hardware I2C for Grove use, SoftI2C_Class is used for In_I2C. Signed-off-by: lbuque <1102390310@qq.com> --- src/M5Unified.cpp | 38 ++- src/utility/I2C_Class.hpp | 32 +- src/utility/IOExpander_Base.hpp | 5 +- src/utility/PI4IOE5V6408_Class.hpp | 3 + src/utility/SoftI2C_Class.cpp | 453 +++++++++++++++++++++++++++++ src/utility/SoftI2C_Class.hpp | 156 ++++++++++ 6 files changed, 667 insertions(+), 20 deletions(-) create mode 100644 src/utility/SoftI2C_Class.cpp create mode 100644 src/utility/SoftI2C_Class.hpp diff --git a/src/M5Unified.cpp b/src/M5Unified.cpp index 8ed781b..1aae42b 100644 --- a/src/M5Unified.cpp +++ b/src/M5Unified.cpp @@ -94,7 +94,7 @@ static constexpr const uint8_t _pin_table_i2c_ex_in[][5] = { #elif defined (CONFIG_IDF_TARGET_ESP32C3) { board_t::board_unknown , 255 ,255 , GPIO_NUM_0 ,GPIO_NUM_1 }, #elif defined (CONFIG_IDF_TARGET_ESP32C6) -{ board_t::board_M5UnitC6L ,GPIO_NUM_8 ,GPIO_NUM_10 , 255 ,255 }, +{ board_t::board_M5UnitC6L , 255 , 255 , 255 ,255 }, { board_t::board_ArduinoNessoN1,GPIO_NUM_8 ,GPIO_NUM_10 , GPIO_NUM_8 ,GPIO_NUM_10 }, { board_t::board_unknown , 255 ,255 , GPIO_NUM_1 ,GPIO_NUM_2 }, // NanoC6 #elif defined (CONFIG_IDF_TARGET_ESP32P4) @@ -123,7 +123,7 @@ static constexpr const uint8_t _pin_table_port_bc[][5] = { { board_t::board_M5PowerHub , 255 , 255 , GPIO_NUM_1 ,GPIO_NUM_2 }, #elif defined (CONFIG_IDF_TARGET_ESP32C3) #elif defined (CONFIG_IDF_TARGET_ESP32C6) -{ board_t::board_M5UnitC6L ,GPIO_NUM_4 ,GPIO_NUM_5 , GPIO_NUM_4 ,GPIO_NUM_5 }, +{ board_t::board_M5UnitC6L ,GPIO_NUM_4 ,GPIO_NUM_5 , 255 ,255 }, { board_t::board_ArduinoNessoN1,GPIO_NUM_4 ,GPIO_NUM_5 , GPIO_NUM_4 ,GPIO_NUM_5 }, #elif defined (CONFIG_IDF_TARGET_ESP32P4) { board_t::board_M5Tab5 , GPIO_NUM_17,GPIO_NUM_52, GPIO_NUM_7 ,GPIO_NUM_6 }, // Tab5 @@ -1383,9 +1383,32 @@ static constexpr const uint8_t _pin_table_mbus[][31] = { #elif defined (CONFIG_IDF_TARGET_ESP32C6) case board_t::board_M5UnitC6L: { - auto ioexp = new PI4IOE5V6408_Class(0x43); + In_SoftI2C.begin(I2C_NUM_0, 10, 8); + auto ioexp = new PI4IOE5V6408_Class(0x43, 400000, &In_SoftI2C); ioexp->begin(); _io_expander[0].reset(ioexp); + // user button(P0) input pullup + _io_expander[0]->setDirection(0, false); + _io_expander[0]->setPullMode(0, true); + _io_expander[0]->setHighImpedance(0, false); + // sx1262 reset(P7) + _io_expander[0]->setDirection(7, true); + _io_expander[0]->setPullMode(7, false); + _io_expander[0]->setHighImpedance(7, false); + _io_expander[0]->digitalWrite(7, false); + delay(10); + _io_expander[0]->digitalWrite(7, true); + delay(10); + // LAN EN + _io_expander[0]->setDirection(5, true); + _io_expander[0]->setPullMode(5, false); + _io_expander[0]->setHighImpedance(5, false); + _io_expander[0]->digitalWrite(5, true); + // SW EN + _io_expander[0]->setDirection(6, true); + _io_expander[0]->setPullMode(6, false); + _io_expander[0]->setHighImpedance(6, false); + _io_expander[0]->digitalWrite(6, true); } break; case board_t::board_ArduinoNessoN1: @@ -2325,6 +2348,15 @@ static constexpr const uint8_t _pin_table_mbus[][31] = { | (((raw_gpio32_39 >> (GPIO_NUM_39 & 31)) & 1)<<1); // gpio39 B break; + case board_t::board_M5UnitC6L: + { + use_rawstate_bits = 0b00001; + auto exp = static_cast(_io_expander[0].get()); + uint8_t value = exp->readRegister8(0x0F); + btn_rawstate_bits = (!(value & 0b00001) ? 0b00001 : 0); // BtnA + break; + } + default: break; } diff --git a/src/utility/I2C_Class.hpp b/src/utility/I2C_Class.hpp index da1302a..2f3e381 100644 --- a/src/utility/I2C_Class.hpp +++ b/src/utility/I2C_Class.hpp @@ -23,56 +23,56 @@ namespace m5 /// @param port_num I2C number. (I2C_NUM_0 or I2C_NUM_1). /// @param pin_sda SDA pin number. /// @param pin_scl SCL pin number. - void setPort(i2c_port_t port_num, int pin_sda, int pin_scl); + virtual void setPort(i2c_port_t port_num, int pin_sda, int pin_scl); /// setup and begin I2C peripheral. (No communication is performed.) /// @param port_num I2C number. (I2C_NUM_0 or I2C_NUM_1). /// @param pin_sda SDA pin number. /// @param pin_scl SCL pin number. /// @return success(true) or failed(false). - bool begin(i2c_port_t port_num, int pin_sda, int pin_scl); + virtual bool begin(i2c_port_t port_num, int pin_sda, int pin_scl); /// begin I2C peripheral. (No communication is performed.) /// @return success(true) or failed(false). - bool begin(void); + virtual bool begin(void); /// release I2C peripheral. /// @return success(true) or failed(false). - bool release(void) const; + virtual bool release(void) const; /// Sends the I2C start condition and the address of the slave. /// @param address slave addr. /// @param read bit of read flag. true=read / false=write. /// @return success(true) or failed(false). - bool start(std::uint8_t address, bool read, std::uint32_t freq) const; + virtual bool start(std::uint8_t address, bool read, std::uint32_t freq) const; /// Sends the I2C repeated start condition and the address of the slave. /// @param address slave addr. /// @param read bit of read flag. true=read / false=write. /// @return success(true) or failed(false). - bool restart(std::uint8_t address, bool read, std::uint32_t freq) const; + virtual bool restart(std::uint8_t address, bool read, std::uint32_t freq) const; /// Sends the I2C stop condition. /// If an ACK error occurs, return false. /// @return success(true) or failed(false). - bool stop(void) const; + virtual bool stop(void) const; /// Send 1 byte of data. /// @param data write data. /// @return success(true) or failed(false). - bool write(std::uint8_t data) const; + virtual bool write(std::uint8_t data) const; /// Send multiple bytes of data. /// @param[in] data write data array. /// @param length data array length. /// @return success(true) or failed(false). - bool write(const std::uint8_t* data, std::size_t length) const; + virtual bool write(const std::uint8_t* data, std::size_t length) const; /// Receive multiple bytes of data. /// @param[out] result read data array. /// @param length data array length. /// @return success(true) or failed(false). - bool read(std::uint8_t* result, std::size_t length, bool last_nack = false) const; + virtual bool read(std::uint8_t* result, std::size_t length, bool last_nack = false) const; //---------- @@ -82,7 +82,7 @@ namespace m5 /// @param[in] data write data array. /// @param length data array length. /// @return success(true) or failed(false). - bool writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* data, std::size_t length, std::uint32_t freq) const; + virtual bool writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* data, std::size_t length, std::uint32_t freq) const; /// Read multiple bytes value from the register. Performs a series of communications from START to STOP. /// @param address slave addr. @@ -90,34 +90,34 @@ namespace m5 /// @param[out] result read data array. /// @param length data array length. /// @return success(true) or failed(false). - bool readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* result, std::size_t length, std::uint32_t freq) const; + virtual bool readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* result, std::size_t length, std::uint32_t freq) const; /// Write a 1-byte value to the register. Performs a series of communications from START to STOP. /// @param address slave addr. /// @param reg register number. /// @param data write data. /// @return success(true) or failed(false). - bool writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; + virtual bool writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; /// Read a 1-byte value from the register. Performs a series of communications from START to STOP. /// @param address slave addr. /// @param reg register number. /// @return read value. - std::uint8_t readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const; + virtual std::uint8_t readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const; /// Write a 1-byte value to the register by bit add operation. Performs a series of communications from START to STOP. /// @param address slave addr. /// @param reg register number. /// @param data add bit data. /// @return success(true) or failed(false). - bool bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; + virtual bool bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; /// Write a 1-byte value to the register by bit erase operation. Performs a series of communications from START to STOP. /// @param address slave addr. /// @param reg register number. /// @param data erase bit data. /// @return success(true) or failed(false). - bool bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; + virtual bool bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const; /// execute I2C scan. (for 7bit address) /// @param[out] result data array needs 120 Bytes. diff --git a/src/utility/IOExpander_Base.hpp b/src/utility/IOExpander_Base.hpp index 93bade7..1ac75a4 100644 --- a/src/utility/IOExpander_Base.hpp +++ b/src/utility/IOExpander_Base.hpp @@ -3,10 +3,13 @@ #ifndef __M5_IOEXPANDER_BASE_H__ #define __M5_IOEXPANDER_BASE_H__ - #include #include "I2C_Class.hpp" +#if CONFIG_IDF_TARGET_ESP32C6 +#include "SoftI2C_Class.hpp" +#endif + namespace m5 { class IOExpander_Base : public I2C_Device diff --git a/src/utility/PI4IOE5V6408_Class.hpp b/src/utility/PI4IOE5V6408_Class.hpp index 97e1a95..265318c 100644 --- a/src/utility/PI4IOE5V6408_Class.hpp +++ b/src/utility/PI4IOE5V6408_Class.hpp @@ -15,6 +15,9 @@ #include "IOExpander_Base.hpp" #include "I2C_Class.hpp" +#if CONFIG_IDF_TARGET_ESP32C6 +#include "SoftI2C_Class.hpp" +#endif namespace m5 { diff --git a/src/utility/SoftI2C_Class.cpp b/src/utility/SoftI2C_Class.cpp new file mode 100644 index 0000000..c12f06e --- /dev/null +++ b/src/utility/SoftI2C_Class.cpp @@ -0,0 +1,453 @@ +// Copyright (c) M5Stack. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "SoftI2C_Class.hpp" +#include +#include "driver/gpio.h" +#include "esp_rom_sys.h" + + +namespace m5 +{ + SoftI2C_Class In_SoftI2C; + + SoftI2C_Class::SoftI2C_Class() + { + _pin_sda = -1; + _pin_scl = -1; + _freq = 100000; // default 100kHz + _delay_us = 5; + } + + SoftI2C_Class::~SoftI2C_Class() + { + release(); + } + + void SoftI2C_Class::setPins(int pin_sda, int pin_scl) + { + _pin_sda = pin_sda; + _pin_scl = pin_scl; + } + + bool SoftI2C_Class::begin(i2c_port_t port_num,int pin_sda, int pin_scl) + { + setPins(pin_sda, pin_scl); + return begin(); + } + + bool SoftI2C_Class::begin(void) + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + gpio_config_t scl_conf = {}; + scl_conf.pin_bit_mask = (1ULL << _pin_scl); + scl_conf.mode = GPIO_MODE_OUTPUT_OD; + scl_conf.pull_up_en = GPIO_PULLUP_ENABLE; + scl_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + scl_conf.intr_type = GPIO_INTR_DISABLE; + esp_err_t ret = gpio_config(&scl_conf); + if (ret != ESP_OK) { + return false; + } + + gpio_config_t sda_conf = {}; + sda_conf.pin_bit_mask = (1ULL << _pin_sda); + sda_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; + sda_conf.pull_up_en = GPIO_PULLUP_ENABLE; + sda_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + sda_conf.intr_type = GPIO_INTR_DISABLE; + ret = gpio_config(&sda_conf); + if (ret != ESP_OK) { + return false; + } + + _delay_us = (uint32_t)((1e6f / _freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + + return true; + } + + bool SoftI2C_Class::release(void) const + { + if (_pin_sda >= 0) { + gpio_set_direction((gpio_num_t)_pin_sda, GPIO_MODE_INPUT); + } + if (_pin_scl >= 0) { + gpio_set_direction((gpio_num_t)_pin_scl, GPIO_MODE_INPUT); + } + return true; + } + + void SoftI2C_Class::_delay() const + { + esp_rom_delay_us(_delay_us); + } + + void SoftI2C_Class::_scl_high() const + { + gpio_set_level((gpio_num_t)_pin_scl, 1); + } + + void SoftI2C_Class::_scl_low() const + { + gpio_set_level((gpio_num_t)_pin_scl, 0); + } + + void SoftI2C_Class::_sda_high() const + { + gpio_set_level((gpio_num_t)_pin_sda, 1); + } + + void SoftI2C_Class::_sda_low() const + { + gpio_set_level((gpio_num_t)_pin_sda, 0); + } + + bool SoftI2C_Class::_sda_read() const + { + return gpio_get_level((gpio_num_t)_pin_sda) == 1; + } + + bool SoftI2C_Class::_i2c_start() const + { + _sda_high(); + _scl_high(); + _delay(); + + _sda_low(); + _delay(); + + _scl_low(); + _delay(); + + return true; + } + + bool SoftI2C_Class::_i2c_stop() const + { + _sda_low(); + _delay(); + + _scl_high(); + _delay(); + + _sda_high(); + _delay(); + + return true; + } + + bool SoftI2C_Class::_i2c_write_byte(uint8_t data) const + { + for (int i = 7; i >= 0; i--) { + if (data & (1 << i)) { + _sda_high(); + } else { + _sda_low(); + } + _delay(); + + _scl_high(); + _delay(); + + _scl_low(); + _delay(); + } + + _sda_high(); + _delay(); + + _scl_high(); + _delay(); + + bool ack = !_sda_read(); + + _scl_low(); + _delay(); + + return ack; + } + + uint8_t SoftI2C_Class::_i2c_read_byte(bool ack) const + { + uint8_t data = 0; + + _sda_high(); + + for (int i = 7; i >= 0; i--) { + _scl_high(); + _delay(); + + if (_sda_read()) { + data |= (1 << i); + } + + _scl_low(); + _delay(); + } + + if (ack) { + _sda_low(); + } else { + _sda_high(); + } + _delay(); + + _scl_high(); + _delay(); + _scl_low(); + _delay(); + + return data; + } + + bool SoftI2C_Class::start(std::uint8_t address, bool read, std::uint32_t freq) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (freq > 0) { + _delay_us = (uint32_t)((1e6f / freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + } + + if (!_i2c_start()) { + return false; + } + + uint8_t address_byte = (address << 1) | (read ? 1 : 0); + if (!_i2c_write_byte(address_byte)) { + _i2c_stop(); + return false; + } + + return true; + } + + bool SoftI2C_Class::restart(std::uint8_t address, bool read, std::uint32_t freq) const + { + return start(address, read, freq); + } + + bool SoftI2C_Class::stop(void) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + return _i2c_stop(); + } + + bool SoftI2C_Class::write(std::uint8_t data) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + return _i2c_write_byte(data); + } + + bool SoftI2C_Class::write(const std::uint8_t* data, std::size_t length) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (!data || length == 0) { + return true; + } + + for (std::size_t i = 0; i < length; i++) { + if (!_i2c_write_byte(data[i])) { + return false; + } + } + + return true; + } + + bool SoftI2C_Class::read(std::uint8_t* result, std::size_t length, bool last_nack) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (!result || length == 0) { + return true; + } + + for (std::size_t i = 0; i < length; i++) { + bool send_ack = (i != length - 1) || !last_nack; + result[i] = _i2c_read_byte(send_ack); + } + + return true; + } + + bool SoftI2C_Class::writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* data, std::size_t length, std::uint32_t freq) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (freq > 0) { + _delay_us = (uint32_t)((1e6f / freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + } + + if (!_i2c_start()) { + return false; + } + + uint8_t address_byte = (address << 1) | 0; + if (!_i2c_write_byte(address_byte)) { + _i2c_stop(); + return false; + } + + if (!_i2c_write_byte(reg)) { + _i2c_stop(); + return false; + } + + for (std::size_t i = 0; i < length; i++) { + if (!_i2c_write_byte(data[i])) { + _i2c_stop(); + return false; + } + } + + // 发送 STOP 信号 + _i2c_stop(); + return true; + } + + bool SoftI2C_Class::readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* result, std::size_t length, std::uint32_t freq) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (freq > 0) { + _delay_us = (uint32_t)((1e6f / freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + } + + if (!_i2c_start()) { + return false; + } + + uint8_t address_byte = (address << 1) | 0; + if (!_i2c_write_byte(address_byte)) { + _i2c_stop(); + return false; + } + + if (!_i2c_write_byte(reg)) { + _i2c_stop(); + return false; + } + + if (!_i2c_start()) { + return false; + } + + address_byte = (address << 1) | 1; + if (!_i2c_write_byte(address_byte)) { + _i2c_stop(); + return false; + } + + for (std::size_t i = 0; i < length; i++) { + bool send_ack = (i != length - 1); + result[i] = _i2c_read_byte(send_ack); + } + + _i2c_stop(); + return true; + } + + bool SoftI2C_Class::writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const + { + return writeRegister(address, reg, &data, 1, freq); + } + + std::uint8_t SoftI2C_Class::readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const + { + std::uint8_t result = 0; + if (!readRegister(address, reg, &result, 1, freq)) { + return 0; + } + return result; + } + + bool SoftI2C_Class::bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const + { + std::uint8_t current = readRegister8(address, reg, freq); + if (current == 0 && !readRegister(address, reg, ¤t, 1, freq)) { + return false; + } + return writeRegister8(address, reg, current | data, freq); + } + + bool SoftI2C_Class::bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const + { + std::uint8_t current = readRegister8(address, reg, freq); + if (current == 0 && !readRegister(address, reg, ¤t, 1, freq)) { + return false; + } + return writeRegister8(address, reg, current & ~data, freq); + } + + void SoftI2C_Class::scanID(bool* result, std::uint32_t freq) const + { + if (!result) { + return; + } + + std::memset(result, false, 120); + + if (_pin_sda < 0 || _pin_scl < 0) { + return; + } + + if (freq > 0) { + _delay_us = (uint32_t)((1e6f / freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + } + + for (int addr = 0x03; addr < 0x78; addr++) { + if (scanID(addr, freq)) { + result[addr] = true; + } + } + } + + bool SoftI2C_Class::scanID(uint8_t addr, std::uint32_t freq) const + { + if (_pin_sda < 0 || _pin_scl < 0) { + return false; + } + + if (freq > 0) { + _delay_us = (uint32_t)((1e6f / freq) / 2.0f + 0.5f); + if (_delay_us < 1) _delay_us = 1; + } + + if (!_i2c_start()) { + return false; + } + + uint8_t address_byte = (addr << 1) | 0; + bool ack = _i2c_write_byte(address_byte); + + _i2c_stop(); + + return ack; + } + +} // namespace m5 \ No newline at end of file diff --git a/src/utility/SoftI2C_Class.hpp b/src/utility/SoftI2C_Class.hpp new file mode 100644 index 0000000..8e04e43 --- /dev/null +++ b/src/utility/SoftI2C_Class.hpp @@ -0,0 +1,156 @@ +// Copyright (c) M5Stack. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __M5_SOFT_I2C_CLASS_H__ +#define __M5_SOFT_I2C_CLASS_H__ + +#include +#include +#include +#include "I2C_Class.hpp" + +namespace m5 +{ + class SoftI2C_Class : public I2C_Class + { + public: + /// Constructor + SoftI2C_Class(); + + /// Destructor + ~SoftI2C_Class(); + + /// setup I2C pin parameters. (No begin) + /// @param pin_sda SDA pin number. + /// @param pin_scl SCL pin number. + void setPins(int pin_sda, int pin_scl); + + /// setup and begin I2C peripheral. (No communication is performed.) + /// @param port_num Invalid parameters. In this SoftI2C implementation, this parameter is ignored. default I2C_NUM_0. + /// @param pin_sda SDA pin number. + /// @param pin_scl SCL pin number. + /// @return success(true) or failed(false). + bool begin(i2c_port_t port_num, int pin_sda, int pin_scl) override; + + /// begin I2C peripheral. (No communication is performed.) + /// @return success(true) or failed(false). + bool begin(void) override; + + /// release I2C peripheral. + /// @return success(true) or failed(false). + bool release(void) const override; + + /// Sends the I2C start condition and the address of the slave. + /// @param address slave addr. + /// @param read bit of read flag. true=read / false=write. + /// @return success(true) or failed(false). + bool start(std::uint8_t address, bool read, std::uint32_t freq) const override; + + /// Sends the I2C repeated start condition and the address of the slave. + /// @param address slave addr. + /// @param read bit of read flag. true=read / false=write. + /// @return success(true) or failed(false). + bool restart(std::uint8_t address, bool read, std::uint32_t freq) const override; + + /// Sends the I2C stop condition. + /// If an ACK error occurs, return false. + /// @return success(true) or failed(false). + bool stop(void) const override; + + /// Send 1 byte of data. + /// @param data write data. + /// @return success(true) or failed(false). + bool write(std::uint8_t data) const override; + + /// Send multiple bytes of data. + /// @param[in] data write data array. + /// @param length data array length. + /// @return success(true) or failed(false). + bool write(const std::uint8_t* data, std::size_t length) const override; + + /// Receive multiple bytes of data. + /// @param[out] result read data array. + /// @param length data array length. + /// @return success(true) or failed(false). + bool read(std::uint8_t* result, std::size_t length, bool last_nack = false) const override; + + //---------- + + /// Write multiple bytes value to the register. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @param[in] data write data array. + /// @param length data array length. + /// @return success(true) or failed(false). + bool writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* data, std::size_t length, std::uint32_t freq) const override; + + /// Read multiple bytes value from the register. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @param[out] result read data array. + /// @param length data array length. + /// @return success(true) or failed(false). + bool readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* result, std::size_t length, std::uint32_t freq) const override; + + /// Write a 1-byte value to the register. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @param data write data. + /// @return success(true) or failed(false). + bool writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const override; + + /// Read a 1-byte value from the register. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @return read value. + std::uint8_t readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const override; + + /// Write a 1-byte value to the register by bit add operation. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @param data add bit data. + /// @return success(true) or failed(false). + bool bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const override; + + /// Write a 1-byte value to the register by bit erase operation. Performs a series of communications from START to STOP. + /// @param address slave addr. + /// @param reg register number. + /// @param data erase bit data. + /// @return success(true) or failed(false). + bool bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const override; + + /// execute I2C scan. (for 7bit address) + /// @param[out] result data array needs 120 Bytes. + void scanID(bool* result, std::uint32_t freq = 100000) const; + + bool scanID(uint8_t addr, std::uint32_t freq = 100000) const; + + int8_t getSDA(void) const { return _pin_sda; } + int8_t getSCL(void) const { return _pin_scl; } + uint32_t getFrequency(void) const { return _freq; } + + bool isEnabled(void) const { return _pin_sda >= 0 && _pin_scl >= 0; } + + private: + int8_t _pin_sda = -1; + int8_t _pin_scl = -1; + uint32_t _freq = 100000; + mutable uint32_t _delay_us = 5; + + void _delay() const; + void _scl_high() const; + void _scl_low() const; + void _sda_high() const; + void _sda_low() const; + bool _sda_read() const; + bool _i2c_start() const; + bool _i2c_stop() const; + bool _i2c_write_byte(uint8_t data) const; + uint8_t _i2c_read_byte(bool ack) const; + }; + + /// for internal I2C device + extern SoftI2C_Class In_SoftI2C; +} + +#endif