From bd2353fea83ac30d3f2073eb4c5e69d73e559442 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Mon, 12 Feb 2024 12:32:26 -0600 Subject: [PATCH] Feature/base peripheral (#150) * feat(base_peripheral): added base peripheral class * doc: include base peripheral in doc * feat: update peripherals * Update peripheral classes to use new base_peripheral as the base class * doc: rebuild * fix: fix bldc_motor and bldc_haptics example for breaking change to mt6701 config (read -> read_register) * fix(bldc_driver): ensure configuration structs are zeroed out * example(tla2528): update * Updated tla2528 example to test all analog input channels * fix(max1704x): fix u8 read to make it u16 so it reads the right values * fix: typo * doc: rebuild * fix(gt911): fix reading / writing 16bit register addresses --- components/ads1x15/CMakeLists.txt | 2 +- .../ads1x15/example/main/ads1x15_example.cpp | 9 +- components/ads1x15/include/ads1x15.hpp | 83 +--- components/ads1x15/src/ads1x15.cpp | 14 +- components/ads7138/CMakeLists.txt | 2 +- components/ads7138/include/ads7138.hpp | 148 +++---- components/as5600/CMakeLists.txt | 2 +- .../as5600/example/main/as5600_example.cpp | 7 +- components/as5600/include/as5600.hpp | 56 +-- components/aw9523/CMakeLists.txt | 2 +- .../aw9523/example/main/aw9523_example.cpp | 5 +- components/aw9523/include/aw9523.hpp | 122 ++---- components/base_peripheral/CMakeLists.txt | 3 + .../include/base_peripheral.hpp | 382 ++++++++++++++++++ .../bldc_driver/include/bldc_driver.hpp | 2 + .../example/main/bldc_haptics_example.cpp | 5 +- .../example/main/bldc_motor_example.cpp | 5 +- components/bm8563/CMakeLists.txt | 2 +- .../bm8563/example/main/bm8563_example.cpp | 5 +- components/bm8563/include/bm8563.hpp | 60 +-- components/drv2605/CMakeLists.txt | 2 +- .../drv2605/example/main/drv2605_example.cpp | 10 +- components/drv2605/include/drv2605.hpp | 99 ++--- components/ft5x06/CMakeLists.txt | 2 +- .../ft5x06/example/main/ft5x06_example.cpp | 6 +- components/ft5x06/include/ft5x06.hpp | 87 +--- components/gt911/CMakeLists.txt | 2 +- .../gt911/example/main/gt911_example.cpp | 10 +- components/gt911/include/gt911.hpp | 80 ++-- components/max1704x/CMakeLists.txt | 2 +- components/max1704x/include/max1704x.hpp | 103 +---- components/mcp23x17/CMakeLists.txt | 2 +- .../example/main/mcp23x17_example.cpp | 5 +- components/mcp23x17/include/mcp23x17.hpp | 99 ++--- components/mt6701/CMakeLists.txt | 2 +- .../mt6701/example/main/mt6701_example.cpp | 16 +- components/mt6701/include/mt6701.hpp | 58 +-- components/ndef/include/ndef.hpp | 8 +- components/qwiicnes/CMakeLists.txt | 2 +- .../example/main/qwiicnes_example.cpp | 13 +- components/qwiicnes/include/qwiicnes.hpp | 71 ++-- components/st25dv/CMakeLists.txt | 2 +- .../st25dv/example/main/st25dv_example.cpp | 21 +- components/st25dv/example/sdkconfig.defaults | 2 +- components/st25dv/include/st25dv.hpp | 139 ++++--- components/t_keyboard/CMakeLists.txt | 2 +- components/t_keyboard/include/t_keyboard.hpp | 61 +-- components/tla2528/CMakeLists.txt | 2 +- .../tla2528/example/main/tla2528_example.cpp | 31 +- components/tla2528/include/tla2528.hpp | 162 +++----- components/tt21100/CMakeLists.txt | 2 +- components/tt21100/include/tt21100.hpp | 56 ++- doc/Doxyfile | 1 + doc/en/base_peripheral.rst | 20 + doc/en/index.rst | 1 + docs/_sources/index.rst.txt | 1 + docs/adc/adc_types.html | 3 +- docs/adc/ads1x15.html | 195 +++++++-- docs/adc/ads7138.html | 186 ++++++++- docs/adc/continuous_adc.html | 5 +- docs/adc/index.html | 3 +- docs/adc/oneshot_adc.html | 5 +- docs/adc/tla2528.html | 213 ++++++++-- docs/base_component.html | 11 +- docs/base_peripheral.html | 269 +++++++++++- docs/battery/bldc_driver.html | 5 +- docs/battery/bldc_motor.html | 14 +- docs/battery/index.html | 7 +- docs/battery/max1704x.html | 178 +++++++- docs/bldc/bldc_driver.html | 5 +- docs/bldc/bldc_motor.html | 14 +- docs/bldc/index.html | 3 +- docs/button.html | 5 +- docs/cli.html | 7 +- docs/color.html | 5 +- docs/controller.html | 5 +- docs/csv.html | 5 +- docs/display/display.html | 5 +- docs/display/display_drivers.html | 7 +- docs/display/index.html | 3 +- docs/encoder/abi_encoder.html | 5 +- docs/encoder/as5600.html | 208 ++++++++-- docs/encoder/encoder_types.html | 3 +- docs/encoder/index.html | 3 +- docs/encoder/mt6701.html | 213 ++++++++-- docs/event_manager.html | 5 +- docs/file_system.html | 5 +- docs/filters/biquad.html | 5 +- docs/filters/butterworth.html | 5 +- docs/filters/index.html | 3 +- docs/filters/lowpass.html | 5 +- docs/filters/sos.html | 5 +- docs/filters/transfer_function.html | 3 +- docs/ftp/ftp_server.html | 7 +- docs/ftp/index.html | 3 +- docs/genindex.html | 297 +++++++++++++- docs/haptics/bldc_haptics.html | 9 +- docs/haptics/drv2605.html | 195 +++++++-- docs/haptics/index.html | 3 +- docs/i2c.html | 5 +- docs/index.html | 7 +- docs/input/encoder_input.html | 5 +- docs/input/ft5x06.html | 189 +++++++-- docs/input/gt911.html | 180 ++++++++- docs/input/index.html | 3 +- docs/input/keypad_input.html | 5 +- docs/input/t_keyboard.html | 192 +++++++-- docs/input/touchpad_input.html | 5 +- docs/input/tt21100.html | 180 ++++++++- docs/io_expander/aw9523.html | 190 +++++++-- docs/io_expander/index.html | 3 +- docs/io_expander/mcp23x17.html | 191 +++++++-- docs/joystick.html | 5 +- docs/led.html | 5 +- docs/led_strip.html | 5 +- docs/logger.html | 5 +- docs/math/bezier.html | 5 +- docs/math/fast_math.html | 3 +- docs/math/gaussian.html | 5 +- docs/math/index.html | 3 +- docs/math/range_mapper.html | 5 +- docs/math/vector2d.html | 5 +- docs/monitor.html | 5 +- docs/network/index.html | 3 +- docs/network/socket.html | 5 +- docs/network/tcp_socket.html | 5 +- docs/network/udp_socket.html | 5 +- docs/nfc/index.html | 3 +- docs/nfc/ndef.html | 5 +- docs/nfc/st25dv.html | 200 +++++++-- docs/objects.inv | Bin 64792 -> 66081 bytes docs/pid.html | 5 +- docs/qwiicnes.html | 208 ++++++++-- docs/rmt.html | 7 +- docs/rtc/bm8563.html | 188 +++++++-- docs/rtc/index.html | 3 +- docs/rtsp.html | 19 +- docs/search.html | 1 + docs/searchindex.js | 2 +- docs/serialization.html | 5 +- docs/state_machine.html | 11 +- docs/tabulate.html | 5 +- docs/task.html | 5 +- docs/thermistor.html | 5 +- docs/timer.html | 5 +- docs/wifi/index.html | 3 +- docs/wifi/wifi_ap.html | 5 +- docs/wifi/wifi_sta.html | 5 +- 148 files changed, 4414 insertions(+), 1738 deletions(-) create mode 100644 components/base_peripheral/CMakeLists.txt create mode 100644 components/base_peripheral/include/base_peripheral.hpp create mode 100644 doc/en/base_peripheral.rst diff --git a/components/ads1x15/CMakeLists.txt b/components/ads1x15/CMakeLists.txt index eaba5c0af..3616d6e54 100644 --- a/components/ads1x15/CMakeLists.txt +++ b/components/ads1x15/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" - REQUIRES "logger" "pthread" + REQUIRES "base_peripheral" "pthread" ) diff --git a/components/ads1x15/example/main/ads1x15_example.cpp b/components/ads1x15/example/main/ads1x15_example.cpp index 255ad24ed..d49047f71 100644 --- a/components/ads1x15/example/main/ads1x15_example.cpp +++ b/components/ads1x15/example/main/ads1x15_example.cpp @@ -24,10 +24,11 @@ extern "C" void app_main(void) { // make the actual ads class espp::Ads1x15 ads(espp::Ads1x15::Ads1015Config{ .device_address = espp::Ads1x15::DEFAULT_ADDRESS, - .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - .read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)}); + .write = [&i2c](uint8_t addr, const uint8_t *data, + size_t len) { return i2c.write(addr, data, len); }, + .read = [&i2c](uint8_t addr, uint8_t *data, + size_t len) { return i2c.read(addr, data, len); }, + }); // make the task which will get the raw data from the I2C ADC auto ads_read_task_fn = [&ads](std::mutex &m, std::condition_variable &cv) { static auto start = std::chrono::high_resolution_clock::now(); diff --git a/components/ads1x15/include/ads1x15.hpp b/components/ads1x15/include/ads1x15.hpp index d2bd596fc..d855587fb 100644 --- a/components/ads1x15/include/ads1x15.hpp +++ b/components/ads1x15/include/ads1x15.hpp @@ -2,10 +2,9 @@ #include #include -#include #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -14,28 +13,10 @@ namespace espp { * \section ads1x15_ex1 ADS1X15 Example * \snippet ads1x15_example.cpp ads1x15 example */ -class Ads1x15 { +class Ads1x15 : public espp::BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0x48); ///< I2C address of the ADS1x15 chips. - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if the write was successful. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful. - */ - typedef std::function read_fn; - /** * @brief Gain values for the ADC conversion. */ @@ -80,8 +61,8 @@ class Ads1x15 { */ struct Ads1015Config { uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address of the device. - write_fn write; ///< Function to write to the ADC - read_fn read; ///< Function to read from the ADC + BasePeripheral::write_fn write; ///< Function to write to the ADC + BasePeripheral::read_fn read; ///< Function to read from the ADC Gain gain{Gain::TWOTHIRDS}; ///< Gain for the ADC Ads1015Rate sample_rate{Ads1015Rate::SPS1600}; ///< Sample rate for the ADC espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< Verbosity for the logger. @@ -92,8 +73,8 @@ class Ads1x15 { */ struct Ads1115Config { uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address of the device. - write_fn write; ///< Function to write to the ADC - read_fn read; ///< Function to read from the ADC + BasePeripheral::write_fn write; ///< Function to write to the ADC + BasePeripheral::read_fn read; ///< Function to read from the ADC Gain gain{Gain::TWOTHIRDS}; ///< Gain for the ADC Ads1115Rate sample_rate{Ads1115Rate::SPS128}; ///< Sample rate for the ADC espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< Verbosity for the logger. @@ -104,18 +85,24 @@ class Ads1x15 { * @param config Configuration structure. */ explicit Ads1x15(const Ads1015Config &config) - : gain_(config.gain), ads1015rate_(config.sample_rate), bit_shift_(4), - address_(config.device_address), write_(config.write), read_(config.read), - logger_({.tag = "Ads1015", .level = config.log_level}) {} + : BasePeripheral( + {.address = config.device_address, .write = config.write, .read = config.read}, + "Ads1015", config.log_level) + , gain_(config.gain) + , ads1015rate_(config.sample_rate) + , bit_shift_(4) {} /** * @brief Construct Ads1x15 specficially for ADS1115. * @param config Configuration structure. */ explicit Ads1x15(const Ads1115Config &config) - : gain_(config.gain), ads1115rate_(config.sample_rate), bit_shift_(0), - address_(config.device_address), write_(config.write), read_(config.read), - logger_({.tag = "Ads1115", .level = config.log_level}) {} + : BasePeripheral( + {.address = config.device_address, .write = config.write, .read = config.read}, + "Ads1115", config.log_level) + , gain_(config.gain) + , ads1115rate_(config.sample_rate) + , bit_shift_(0) {} /** * @brief Communicate with the ADC to sample the channel and return the @@ -165,35 +152,6 @@ class Ads1x15 { return raw * (fsRange / (32768 >> bit_shift_)); } - uint16_t read_two_(uint8_t reg_addr, std::error_code &ec) { - // lock the mutex so that we don't have multiple threads trying to read - // from the device at the same time - std::lock_guard lock(mutex_); - // write the reg addr we want to read from - bool success = write_(address_, ®_addr, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - // then read the two bytes - uint8_t data[2]; - success = read_(address_, data, 2); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return (data[0] << 8) | data[1]; - } - - void write_two_(uint8_t reg_addr, uint16_t value, std::error_code &ec) { - uint8_t total_len = 3; - uint8_t data[total_len] = {reg_addr, (uint8_t)(value >> 8), (uint8_t)(value & 0xFF)}; - bool success = write_(address_, data, total_len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - enum class Register : uint8_t { POINTER_CONVERT = 0x00, ///< Conversion POINTER_CONFIG = 0x01, ///< Configuration @@ -208,10 +166,5 @@ class Ads1x15 { uint16_t rate_; }; int bit_shift_; - uint8_t address_; - write_fn write_; - read_fn read_; - std::mutex mutex_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/ads1x15/src/ads1x15.cpp b/components/ads1x15/src/ads1x15.cpp index 08756b58d..8566e4e66 100644 --- a/components/ads1x15/src/ads1x15.cpp +++ b/components/ads1x15/src/ads1x15.cpp @@ -51,10 +51,6 @@ static constexpr uint16_t REG_CONFIG_CQUE_NONE = (0x0003); ///< Disable the comparator and put ALERT/RDY in high state (default) int16_t Ads1x15::sample_raw(int channel, std::error_code &ec) { - if (!write_ || !read_) { - logger_.error("Write / read functions not properly configured, cannot sample!"); - return 0; - } // Start with default values uint16_t config = REG_CONFIG_MODE_SINGLE; // This is equivalent to the below (since the rest are 0x0000): @@ -72,17 +68,17 @@ int16_t Ads1x15::sample_raw(int channel, std::error_code &ec) { config |= REG_CONFIG_OS_SINGLE; // configure to read from mux 0 logger_.debug("configuring conversion for channel {}", channel); - write_two_((uint8_t)Register::POINTER_CONFIG, config, ec); + write_u16_to_register((uint8_t)Register::POINTER_CONFIG, config, ec); if (ec) { logger_.error("error configuring conversion for channel {}", channel); return 0; } - write_two_((uint8_t)Register::POINTER_HITHRESH, 0x8000, ec); + write_u16_to_register((uint8_t)Register::POINTER_HITHRESH, 0x8000, ec); if (ec) { logger_.error("error configuring hi threshold for channel {}", channel); return 0; } - write_two_((uint8_t)Register::POINTER_LOWTHRESH, 0x0000, ec); + write_u16_to_register((uint8_t)Register::POINTER_LOWTHRESH, 0x0000, ec); if (ec) { logger_.error("error configuring low threshold for channel {}", channel); return 0; @@ -97,7 +93,7 @@ int16_t Ads1x15::sample_raw(int channel, std::error_code &ec) { return 0; } logger_.debug("reading conversion result for channel {}", channel); - uint16_t val = read_two_((uint8_t)Register::POINTER_CONVERT, ec) >> bit_shift_; + uint16_t val = read_u16_from_register((uint8_t)Register::POINTER_CONVERT, ec) >> bit_shift_; if (ec) { logger_.error("error reading conversion result for channel {}", channel); return 0; @@ -112,7 +108,7 @@ int16_t Ads1x15::sample_raw(int channel, std::error_code &ec) { } bool Ads1x15::conversion_complete(std::error_code &ec) { - auto val = read_two_((uint8_t)Register::POINTER_CONFIG, ec); + auto val = read_u16_from_register((uint8_t)Register::POINTER_CONFIG, ec); if (ec) { logger_.error("error reading config register"); return false; diff --git a/components/ads7138/CMakeLists.txt b/components/ads7138/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/ads7138/CMakeLists.txt +++ b/components/ads7138/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/ads7138/include/ads7138.hpp b/components/ads7138/include/ads7138.hpp index 100c7f92a..6667c2e24 100644 --- a/components/ads7138/include/ads7138.hpp +++ b/components/ads7138/include/ads7138.hpp @@ -8,7 +8,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -25,30 +25,12 @@ namespace espp { * @section ads7138_ex1 ADS7138 Example * @snippet ads7138_example.cpp ads7138 example */ -class Ads7138 { +class Ads7138 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0x10); ///< Default I2C address of the device (when both R1 and R2 are DNP) (see data sheet ///< Table 2, p. 16) - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if successful, false otherwise. - */ - typedef std::function read_fn; - /// @brief Possible oversampling ratios, see data sheet Table 15 (p. 34) enum class OversamplingRatio : uint8_t { NONE = 0, ///< No oversampling @@ -167,8 +149,8 @@ class Ads7138 { ///< mode. OversamplingRatio oversampling_ratio = OversamplingRatio::NONE; ///< Oversampling ratio to use. bool statistics_enabled = true; ///< Enable statistics collection (min, max, recent) - write_fn write; ///< Function to write to the ADC - read_fn read; ///< Function to read from the ADC + BasePeripheral::write_fn write; ///< Function to write to the ADC + BasePeripheral::read_fn read; ///< Function to read from the ADC bool auto_init = true; ///< Automatically initialize the ADC on construction. If false, ///< initialize() must be called before any other functions. espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< Verbosity for the logger. @@ -179,15 +161,19 @@ class Ads7138 { * @param config Configuration structure. */ explicit Ads7138(const Config &config) - : config_(config), mode_(config.mode), avdd_mv_(config.avdd_volts * 1000.0f) // Convert to mV - , - data_format_(config.oversampling_ratio == OversamplingRatio::NONE ? DataFormat::RAW - : DataFormat::AVERAGED), - statistics_enabled_(config.statistics_enabled), analog_inputs_(config.analog_inputs), - digital_inputs_(config.digital_inputs), digital_outputs_(config.digital_outputs), - oversampling_ratio_(config.oversampling_ratio), address_(config.device_address), - write_(config.write), read_(config.read), - logger_({.tag = "Ads7138", .level = config.log_level}) { + : BasePeripheral( + {.address = config.device_address, .write = config.write, .read = config.read}, + "Ads7138", config.log_level) + , config_(config) + , mode_(config.mode) + , avdd_mv_(config.avdd_volts * 1000.0f) // Convert to mV + , data_format_(config.oversampling_ratio == OversamplingRatio::NONE ? DataFormat::RAW + : DataFormat::AVERAGED) + , statistics_enabled_(config.statistics_enabled) + , analog_inputs_(config.analog_inputs) + , digital_inputs_(config.digital_inputs) + , digital_outputs_(config.digital_outputs) + , oversampling_ratio_(config.oversampling_ratio) { // initialize the ADC if (config.auto_init) { std::error_code ec; @@ -525,6 +511,8 @@ class Ads7138 { /// @param ec Error code to set if an error occurs. /// @note This will reset all registers to their default values (converting /// all channels to analog inputs and disabling all events). + /// @note If the write is successful, the function will wait for the reset + /// to complete before returning void reset(std::error_code &ec) { std::lock_guard lock(mutex_); // reset the device @@ -1052,7 +1040,7 @@ class Ads7138 { logger_.info("Reading recent values for all channels"); std::vector values(analog_inputs_.size()); uint8_t raw_values[16]; - read_many_(Register::RECENT_CH0_LSB, raw_values, 16, ec); + read_block_(Register::RECENT_CH0_LSB, raw_values, 16, ec); if (ec) return {}; int analog_index = 0; @@ -1077,7 +1065,7 @@ class Ads7138 { logger_.info("Reading recent mapped values for all channels"); std::unordered_map values; uint8_t raw_values[16]; - read_many_(Register::RECENT_CH0_LSB, raw_values, 16, ec); + read_block_(Register::RECENT_CH0_LSB, raw_values, 16, ec); if (ec) return {}; // only pull out the ones that were configured as analog inputs @@ -1121,7 +1109,7 @@ class Ads7138 { std::vector values(analog_inputs_.size()); uint8_t raw_values[16]; int analog_index = 0; - read_many_(Register::MAX_CH0_LSB, raw_values, 16, ec); + read_block_(Register::MAX_CH0_LSB, raw_values, 16, ec); if (ec) return {}; // only pull out the ones that were configured as analog inputs @@ -1164,7 +1152,7 @@ class Ads7138 { std::vector values(analog_inputs_.size()); uint8_t raw_values[16]; int analog_index = 0; - read_many_(Register::MIN_CH0_LSB, raw_values, 16, ec); + read_block_(Register::MIN_CH0_LSB, raw_values, 16, ec); if (ec) return {}; // only pull out the ones that were configured as analog inputs @@ -1405,8 +1393,9 @@ class Ads7138 { // low threshold register contains 8 msb of low threshold static_cast(low_threshold >> 4)}; // write the data to the registers - write_many_(static_cast(static_cast(Register::HYSTERESIS_CH0) + channel * 4), - data, sizeof(data), ec); + write_block_( + static_cast(static_cast(Register::HYSTERESIS_CH0) + channel * 4), data, + sizeof(data), ec); } bool is_digital_input(Channel channel) { @@ -1423,20 +1412,15 @@ class Ads7138 { return std::find(analog_inputs_.begin(), analog_inputs_.end(), channel) != analog_inputs_.end(); } + // NOTE: this chip has specific read and write operation commands that are + // used, so we don't use the subclass's read_* and write_* methods directly + uint8_t read_one_(Register reg, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); + uint8_t data = 0; uint8_t read_one_command[] = {OP_READ_ONE, (uint8_t)reg}; - bool success = write_(address_, read_one_command, sizeof(read_one_command)); - if (!success) { - logger_.error("Failed to write read one command"); - ec = std::make_error_code(std::errc::io_error); - return 0; - } - uint8_t data; - success = read_(address_, &data, 1); - if (!success) { - logger_.error("Failed to read one byte"); - ec = std::make_error_code(std::errc::io_error); + write_then_read(read_one_command, sizeof(read_one_command), &data, 1, ec); + if (ec) { return 0; } return data; @@ -1444,82 +1428,52 @@ class Ads7138 { uint16_t read_two_(Register reg, std::error_code &ec) { uint8_t data[2]; - read_many_(reg, data, 2, ec); - if (ec) + read_block_(reg, data, 2, ec); + if (ec) { return 0; + } // NOTE: registers are little endian (LSB first, then MSB) so if we want // to read the value of a 16 bit register we need to read the LSB first, // then the MSB and combine them into a 16 bit value return (data[1] << 8) | data[0]; } - void read_many_(Register reg, uint8_t *data, uint8_t len, std::error_code &ec) { - std::lock_guard lock(mutex_); + void read_block_(Register reg, uint8_t *data, uint8_t len, std::error_code &ec) { + std::lock_guard lock(base_mutex_); uint8_t read_block_command[] = {OP_READ_BLOCK, (uint8_t)reg}; - bool success = write_(address_, read_block_command, sizeof(read_block_command)); - if (!success) { - logger_.error("Failed to write read block command"); - ec = std::make_error_code(std::errc::io_error); - return; - } - success = read_(address_, data, len); - if (!success) { - logger_.error("Failed to read {} bytes", len); - ec = std::make_error_code(std::errc::io_error); - return; - } + write_then_read(read_block_command, sizeof(read_block_command), data, len, ec); } void set_bits_(Register reg, uint8_t bit, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_SET_BITS, (uint8_t)reg, bit}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - logger_.error("Failed to write set bits command"); - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void clear_bits_(Register reg, uint8_t bit, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_CLR_BITS, (uint8_t)reg, bit}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - logger_.error("Failed to write clear bits command"); - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void write_one_(Register reg, uint8_t value, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_WRITE_ONE, (uint8_t)reg, value}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - logger_.error("Failed to write write one command"); - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void write_two_(Register reg, uint16_t value, std::error_code &ec) { - write_many_(reg, (uint8_t *)&value, 2, ec); + write_block_(reg, (uint8_t *)&value, 2, ec); } - void write_many_(Register reg, const uint8_t *data, uint8_t len, std::error_code &ec) { - std::lock_guard lock(mutex_); + void write_block_(Register reg, const uint8_t *data, uint8_t len, std::error_code &ec) { + std::lock_guard lock(base_mutex_); uint8_t total_len = len + 2; uint8_t data_with_header[total_len]; data_with_header[0] = OP_WRITE_BLOCK; data_with_header[1] = (uint8_t)reg; memcpy(data_with_header + 2, data, len); - bool success = write_(address_, data_with_header, total_len); - if (!success) { - logger_.error("Failed to write write block command"); - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data_with_header, total_len, ec); } Config config_; @@ -1534,11 +1488,7 @@ class Ads7138 { std::vector digital_inputs_; std::vector digital_outputs_; OversamplingRatio oversampling_ratio_; - uint8_t address_; - write_fn write_; - read_fn read_; std::recursive_mutex mutex_; ///< mutex for thread safety - espp::Logger logger_; }; } // namespace espp diff --git a/components/as5600/CMakeLists.txt b/components/as5600/CMakeLists.txt index a244e0474..f7698dd56 100644 --- a/components/as5600/CMakeLists.txt +++ b/components/as5600/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" "task" + REQUIRES "base_peripheral" "task" ) diff --git a/components/as5600/example/main/as5600_example.cpp b/components/as5600/example/main/as5600_example.cpp index e4f34b4ea..0964695cc 100644 --- a/components/as5600/example/main/as5600_example.cpp +++ b/components/as5600/example/main/as5600_example.cpp @@ -32,10 +32,9 @@ extern "C" void app_main(void) { // now make the as5600 which decodes the data espp::As5600 as5600( - {.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + {.write_then_read = + std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), .velocity_filter = filter_fn, .update_period = std::chrono::duration(encoder_update_period), .log_level = espp::Logger::Verbosity::WARN}); diff --git a/components/as5600/include/as5600.hpp b/components/as5600/include/as5600.hpp index 7ad19480c..7548f86f3 100644 --- a/components/as5600/include/as5600.hpp +++ b/components/as5600/include/as5600.hpp @@ -4,7 +4,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" #include "task.hpp" namespace espp { @@ -28,30 +28,10 @@ namespace espp { * \section as5600_ex1 As5600 Example * \snippet as5600_example.cpp as5600 example */ -class As5600 { +class As5600 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0b0110110); ///< I2C address of the AS5600 - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if the write was successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param reg_addr Register address to read from. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful, false otherwise. - */ - typedef std::function - read_fn; - /** * @brief Filter the input raw velocity and return it. * @param raw Most recent raw velocity measured. @@ -76,9 +56,9 @@ class As5600 { * @brief Configuration information for the As5600. */ struct Config { - uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address for this device. - write_fn write; ///< Function to write to the device. - read_fn read; ///< Function to read from the device. + uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address for this device. + BasePeripheral::write_then_read_fn + write_then_read; ///< Function to write then read from the device. velocity_filter_fn velocity_filter{nullptr}; ///< Function to filter the veolcity. @note Will be ///< called once every update_period seconds. std::chrono::duration update_period{ @@ -94,9 +74,11 @@ class As5600 { * @brief Construct the As5600 and start the update task. */ explicit As5600(const Config &config) - : address_(config.device_address), write_(config.write), read_(config.read), - velocity_filter_(config.velocity_filter), update_period_(config.update_period), - logger_({.tag = "As5600", .level = config.log_level}) { + : BasePeripheral( + {.address = config.device_address, .write_then_read = config.write_then_read}, "As5600", + config.log_level) + , velocity_filter_(config.velocity_filter) + , update_period_(config.update_period) { logger_.info("Initializing. Fastest measurable velocity will be {:.3f} RPM", // half a rotation in one update period is the fastest we can // measure @@ -180,11 +162,11 @@ class As5600 { int read_count(std::error_code &ec) { logger_.info("read_count"); // read the angle count registers - uint8_t angle_h = read_one_((uint8_t)Registers::ANGLE_H, ec); + uint8_t angle_h = read_u8_from_register((uint8_t)Registers::ANGLE_H, ec); if (ec) { return 0; } - uint8_t angle_l = read_one_((uint8_t)Registers::ANGLE_L, ec) >> 2; + uint8_t angle_l = read_u8_from_register((uint8_t)Registers::ANGLE_L, ec) >> 2; if (ec) { return 0; } @@ -304,25 +286,11 @@ class As5600 { static constexpr int MAGNET_LOW = (1 << 4); ///< For use with the STATUS register static constexpr int MAGNET_DETECTED = (1 << 5); ///< For use with the STATUS register - uint8_t read_one_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data; - bool success = read_(address_, reg_addr, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - - uint8_t address_; - write_fn write_; - read_fn read_; velocity_filter_fn velocity_filter_{nullptr}; std::chrono::duration update_period_; std::atomic count_{0}; std::atomic accumulator_{0}; std::atomic velocity_rpm_{0}; std::unique_ptr task_; - Logger logger_; }; } // namespace espp diff --git a/components/aw9523/CMakeLists.txt b/components/aw9523/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/aw9523/CMakeLists.txt +++ b/components/aw9523/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/aw9523/example/main/aw9523_example.cpp b/components/aw9523/example/main/aw9523_example.cpp index 408dab408..2d7db00e8 100644 --- a/components/aw9523/example/main/aw9523_example.cpp +++ b/components/aw9523/example/main/aw9523_example.cpp @@ -36,8 +36,9 @@ extern "C" void app_main(void) { .port_1_direction_mask = 0b00000011, .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .write_then_read = + std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), .log_level = espp::Logger::Verbosity::WARN}); std::error_code ec; aw9523.initialize(ec); // Initialized separately from the constructor. diff --git a/components/aw9523/include/aw9523.hpp b/components/aw9523/include/aw9523.hpp index bae93505f..b573d4fed 100644 --- a/components/aw9523/include/aw9523.hpp +++ b/components/aw9523/include/aw9523.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -14,30 +14,10 @@ namespace espp { * \section aw9523_ex1 AW9523 Example * \snippet aw9523_example.cpp aw9523 example */ -class Aw9523 { +class Aw9523 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = 0x58; ///< Lower 2 bits are AD1, AD0 pins on the chip. - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param reg_addr Register address to read from. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if successful, false otherwise. - */ - typedef std::function - read_fn; - /** * The two GPIO ports the Aw9523 has. */ @@ -74,11 +54,12 @@ class Aw9523 { uint8_t port_1_direction_mask = 0x00; ///< Direction mask (1 = input) for port 1. uint8_t port_1_interrupt_mask = 0x00; ///< Interrupt mask (1 = disable interrupt) for port 1. OutputDriveModeP0 output_drive_mode_p0 = - OutputDriveModeP0::OPEN_DRAIN; ///< Output drive mode for Port 0 - MaxLedCurrent max_led_current = MaxLedCurrent::IMAX; ///< Max current allowed on each LED. - write_fn write; ///< Function to write to the device. - read_fn read; ///< Function to read from the device. - bool auto_init = true; ///< Automatically initialize the device. + OutputDriveModeP0::OPEN_DRAIN; ///< Output drive mode for Port 0 + MaxLedCurrent max_led_current = MaxLedCurrent::IMAX; ///< Max current allowed on each LED. + BasePeripheral::write_fn write; ///< Function to write to the device. + BasePeripheral::write_then_read_fn + write_then_read; ///< Function to write then read from the device. + bool auto_init = true; ///< Automatically initialize the device. Logger::Verbosity log_level{Logger::Verbosity::WARN}; ///< Log verbosity for the component. }; @@ -87,8 +68,11 @@ class Aw9523 { * @param config Config structure for configuring the AW9523 */ explicit Aw9523(const Config &config) - : config_(config), address_(config.device_address), write_(config.write), read_(config.read), - logger_({.tag = "Aw9523", .level = config.log_level}) { + : BasePeripheral({.address = config.device_address, + .write = config.write, + .write_then_read = config.write_then_read}, + "Aw9523", config.log_level) + , config_(config) { if (config.auto_init) { std::error_code ec; initialize(ec); @@ -112,7 +96,7 @@ class Aw9523 { */ uint8_t get_pins(Port port, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::INPORT0 : Registers::INPORT1; - return read_one_((uint8_t)addr, ec); + return read_u8_from_register((uint8_t)addr, ec); } /** @@ -121,10 +105,10 @@ class Aw9523 { * @return The pin values as a 16 bit mask (P0_0 lsb, P1_7 msb). */ uint16_t get_pins(std::error_code &ec) { - uint16_t p0 = read_one_((uint8_t)Registers::INPORT0, ec); + uint16_t p0 = read_u8_from_register((uint8_t)Registers::INPORT0, ec); if (ec) return 0; - uint16_t p1 = read_one_((uint8_t)Registers::INPORT1, ec); + uint16_t p1 = read_u8_from_register((uint8_t)Registers::INPORT1, ec); if (ec) return 0; return (p1 << 8) | p0; @@ -140,7 +124,7 @@ class Aw9523 { */ void output(Port port, uint8_t value, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::OUTPORT0 : Registers::OUTPORT1; - write_one_((uint8_t)addr, value, ec); + write_u8_to_register((uint8_t)addr, value, ec); } /** @@ -181,11 +165,11 @@ class Aw9523 { */ void clear_pins(Port port, uint8_t mask, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::OUTPORT0 : Registers::OUTPORT1; - auto data = read_one_((uint8_t)addr, ec); + auto data = read_u8_from_register((uint8_t)addr, ec); if (ec) return; data &= ~mask; - write_one_((uint8_t)addr, data, ec); + write_u8_to_register((uint8_t)addr, data, ec); } /** @@ -224,11 +208,11 @@ class Aw9523 { */ void set_pins(Port port, uint8_t mask, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::OUTPORT0 : Registers::OUTPORT1; - auto data = read_one_((uint8_t)addr, ec); + auto data = read_u8_from_register((uint8_t)addr, ec); if (ec) return; data |= mask; - write_one_((uint8_t)addr, data, ec); + write_u8_to_register((uint8_t)addr, data, ec); } /** @@ -266,7 +250,7 @@ class Aw9523 { */ uint8_t get_output(Port port, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::OUTPORT0 : Registers::OUTPORT1; - return read_one_((uint8_t)addr, ec); + return read_u8_from_register((uint8_t)addr, ec); } /** @@ -275,10 +259,10 @@ class Aw9523 { * @return The pin values as a 16 bit mask (P0_0 lsb, P1_7 msb). */ uint16_t get_output(std::error_code &ec) { - uint16_t p0 = read_one_((uint8_t)Registers::OUTPORT0, ec); + uint16_t p0 = read_u8_from_register((uint8_t)Registers::OUTPORT0, ec); if (ec) return 0; - uint16_t p1 = read_one_((uint8_t)Registers::OUTPORT1, ec); + uint16_t p1 = read_u8_from_register((uint8_t)Registers::OUTPORT1, ec); if (ec) return 0; return (p1 << 8) | p0; @@ -293,7 +277,7 @@ class Aw9523 { void set_interrupt(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting interrupt on change for Port {} pins {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::INTPORT0 : Registers::INTPORT1; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -306,7 +290,7 @@ class Aw9523 { logger_.debug("Setting interrupt on change p0:{}, p1:{}", p0, p1); auto addr = Registers::INTPORT0; const uint8_t data[] = {p0, p1}; - write_many_((uint8_t)addr, data, 2, ec); + write_many_to_register((uint8_t)addr, data, 2, ec); } /** @@ -318,7 +302,7 @@ class Aw9523 { void set_direction(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting direction for Port {} to {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::DIRPORT0 : Registers::DIRPORT1; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -331,7 +315,7 @@ class Aw9523 { logger_.debug("Setting direction p0:{}, p1:{}", p0, p1); auto addr = Registers::DIRPORT0; const uint8_t data[] = {p0, p1}; - write_many_((uint8_t)addr, data, 2, ec); + write_many_to_register((uint8_t)addr, data, 2, ec); } /** @@ -343,7 +327,7 @@ class Aw9523 { void configure_led(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Configuring LED function for Port {} to {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::LEDMODE0 : Registers::LEDMODE1; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -356,7 +340,7 @@ class Aw9523 { logger_.debug("Configuring LED function p0:{}, p1:{}", p0, p1); auto addr = Registers::LEDMODE0; const uint8_t data[] = {p0, p1}; - write_many_((uint8_t)addr, data, 2, ec); + write_many_to_register((uint8_t)addr, data, 2, ec); } /** @@ -371,7 +355,7 @@ class Aw9523 { logger_.debug("Configuring LED function p0:{}, p1:{}", p0, p1); auto addr = Registers::LEDMODE0; const uint8_t data[] = {p0, p1}; - write_many_((uint8_t)addr, data, 2, ec); + write_many_to_register((uint8_t)addr, data, 2, ec); } /** @@ -436,7 +420,7 @@ class Aw9523 { return; // bad mask, don't do anything! break; } - write_one_(addr, brightness, ec); + write_u8_to_register(addr, brightness, ec); } /** @@ -455,7 +439,7 @@ class Aw9523 { uint8_t data = 0; data |= ((uint8_t)output_drive_mode_p0) << (int)ControlBit::GPOMD; data |= ((uint8_t)max_led_current) << (int)ControlBit::ISEL; - write_one_((uint8_t)addr, data, ec); + write_u8_to_register((uint8_t)addr, data, ec); } protected: @@ -516,46 +500,6 @@ class Aw9523 { set_interrupt(Port::PORT1, config.port_1_interrupt_mask, ec); } - uint8_t read_one_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data; - bool success = read_(address_, reg_addr, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - - uint16_t read_two_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data[2] = {0}; - bool success = read_(address_, reg_addr, data, 2); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return (data[1] << 8) | data[0]; - } - - void write_one_(uint8_t reg_addr, uint8_t data, std::error_code &ec) { - write_many_(reg_addr, &data, 1, ec); - } - - void write_many_(uint8_t reg_addr, const uint8_t *write_data, size_t write_data_len, - std::error_code &ec) { - uint8_t total_len = 1 + write_data_len; - uint8_t data[total_len]; - data[0] = reg_addr; - memcpy(&data[1], write_data, write_data_len); - bool success = write_(address_, data, total_len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - Config config_; - uint8_t address_; - write_fn write_; - read_fn read_; - Logger logger_; }; } // namespace espp diff --git a/components/base_peripheral/CMakeLists.txt b/components/base_peripheral/CMakeLists.txt new file mode 100644 index 000000000..704e9cb79 --- /dev/null +++ b/components/base_peripheral/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register( + INCLUDE_DIRS "include" + REQUIRES base_component) diff --git a/components/base_peripheral/include/base_peripheral.hpp b/components/base_peripheral/include/base_peripheral.hpp new file mode 100644 index 000000000..242770cff --- /dev/null +++ b/components/base_peripheral/include/base_peripheral.hpp @@ -0,0 +1,382 @@ +#pragma once + +#include +#include +#include + +#include "base_component.hpp" + +namespace espp { +/// Base class for all peripherals +/// This class provides a common interface for all peripherals +/// +/// It provides a way to probe the peripheral, write data to the peripheral, +/// read data from the peripheral, and write then read data from the +/// peripheral. +/// +/// The peripheral is protected by a mutex to ensure that only one +/// operation can be performed at a time. +class BasePeripheral : public BaseComponent { +public: + /// Function to probe the peripheral + /// \param address The address to probe + /// \return True if the peripheral is found at the given address + typedef std::function probe_fn; + + /// Function to write data to the peripheral + /// \param address The address of the peripheral to write to + /// \param data The data to write + /// \param length The length of the data to write + /// \return True if the write was successful + typedef std::function write_fn; + + /// Function to read data from the peripheral + /// \param address The address of the peripheral to read from + /// \param data The buffer to read into + /// \param length The length of the buffer + /// \return True if the read was successful + typedef std::function read_fn; + + /// Function to read data at a specific address from the peripheral + /// \param address The address of the peripheral to read from + /// \param reg_addr The address of the register to read from + /// \param data The buffer to read into + /// \param length The length of the buffer + /// \return True if the read was successful + typedef std::function read_register_fn; + + /// Function to write then read data from the peripheral + /// \param address The address of the peripheral to write to + /// \param write_data The data to write + /// \param write_length The length of the data to write + /// \param read_data The buffer to read into + /// \param read_length The length of the buffer + /// \return True if the write then read was successful + typedef std::function + write_then_read_fn; + + /// Configuration for the peripheral + struct Config { + uint8_t address{0}; ///< The address of the peripheral + probe_fn probe{nullptr}; ///< Function to probe the peripheral + write_fn write{nullptr}; ///< Function to write data to the peripheral + read_fn read{nullptr}; ///< Function to read data from the peripheral + read_register_fn read_register{ + nullptr}; ///< Function to read data at a specific address from the peripheral + write_then_read_fn write_then_read{ + nullptr}; ///< Function to write then read data from the peripheral + }; + +protected: + /// Constructor + /// \param config The configuration for the peripheral + /// \param name The name of the peripheral + /// \param verbosity The verbosity level for the peripheral + BasePeripheral(const Config &config, std::string_view name, + espp::Logger::Verbosity verbosity = espp::Logger::Verbosity::WARN) + : BaseComponent(name, verbosity) + , base_config_(config) {} + + /// Get the configuration for the peripheral + /// \return The configuration for the peripheral + const Config &config() const { return base_config_; } + + /// Write data to the peripheral + /// \param data The data to write + /// \param length The length of the data to write + /// \param ec The error code to set if there is an error + void write_many(const uint8_t *data, size_t length, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + if (!base_config_.write(base_config_.address, data, length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Write a uint8_t to the peripheral + /// \param data The data to write + /// \param ec The error code to set if there is an error + void write_u8(uint8_t data, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + if (!base_config_.write(base_config_.address, &data, 1)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Write a uint16_t to the peripheral + /// \param data The data to write + /// \param ec The error code to set if there is an error + void write_u16(uint16_t data, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + uint8_t buffer[2]; + buffer[0] = (data >> 8) & 0xff; + buffer[1] = data & 0xff; + if (!base_config_.write(base_config_.address, buffer, 2)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Read data from the peripheral + /// \param data The buffer to read into + /// \param length The length of the buffer + /// \param ec The error code to set if there is an error + void read_many(uint8_t *data, size_t length, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.read) { + if (!base_config_.read(base_config_.address, data, length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Read a uint8_t from the peripheral + /// \param ec The error code to set if there is an error + /// \return The data read from the peripheral + uint8_t read_u8(std::error_code &ec) { + std::lock_guard lock(base_mutex_); + uint8_t data = 0; + if (base_config_.read) { + if (!base_config_.read(base_config_.address, &data, 1)) { + ec = std::make_error_code(std::errc::io_error); + return 0; + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + return data; + } + + /// Read a uint16_t from the peripheral + /// \param ec The error code to set if there is an error + /// \return The data read from the peripheral + uint16_t read_u16(std::error_code &ec) { + std::lock_guard lock(base_mutex_); + uint16_t data = 0; + if (base_config_.read) { + uint8_t buffer[2]; + if (!base_config_.read(base_config_.address, buffer, 2)) { + ec = std::make_error_code(std::errc::io_error); + return 0; + } else { + data = (buffer[0] << 8) | buffer[1]; + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + return data; + } + + /// Write then read data from the peripheral + /// \param write_data The data to write + /// \param write_length The length of the data to write + /// \param read_data The buffer to read into + /// \param read_length The length of the buffer + /// \param ec The error code to set if there is an error + void write_then_read(const uint8_t *write_data, size_t write_length, uint8_t *read_data, + size_t read_length, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write_then_read) { + if (!base_config_.write_then_read(base_config_.address, write_data, write_length, read_data, + read_length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else if (base_config_.write && base_config_.read) { + // write the data + if (!base_config_.write(base_config_.address, write_data, write_length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + // read the data + if (!base_config_.read(base_config_.address, read_data, read_length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Write a uint8_t to the peripheral + /// \param reg_addr The address of the register to write to + /// \param data The data to write + /// \param ec The error code to set if there is an error + void write_u8_to_register(uint8_t reg_addr, uint8_t data, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + uint8_t buffer[2] = {reg_addr, data}; + if (!base_config_.write(base_config_.address, buffer, 2)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Write a uint16_t to the peripheral + /// \param reg_addr The address of the register to write to + /// \param data The data to write + /// \param ec The error code to set if there is an error + void write_u16_to_register(uint8_t reg_addr, uint16_t data, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + uint8_t buffer[3] = {reg_addr, uint8_t((data >> 8) & 0xff), uint8_t(data & 0xff)}; + if (!base_config_.write(base_config_.address, buffer, 3)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Write many bytes to a register on the peripheral + /// \param reg_addr The address of the register to write to + /// \param data The data to write + /// \param length The length of the data to write + /// \param ec The error code to set if there is an error + void write_many_to_register(uint8_t reg_addr, const uint8_t *data, size_t length, + std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.write) { + uint8_t buffer[length + 1]; + buffer[0] = reg_addr; + std::copy(data, data + length, buffer + 1); + if (!base_config_.write(base_config_.address, buffer, length + 1)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + ec = std::make_error_code(std::errc::operation_not_supported); + } + } + + /// Read a uint8_t from a register on the peripheral + /// \param register_address The address of the register to read from + /// \param ec The error code to set if there is an error + /// \return The data read from the peripheral + uint8_t read_u8_from_register(uint8_t register_address, std::error_code &ec) { + uint8_t data = 0; + if (base_config_.read_register) { + if (!base_config_.read_register(base_config_.address, register_address, &data, 1)) { + ec = std::make_error_code(std::errc::io_error); + return 0; + } else { + ec.clear(); + } + } else { + write_then_read(®ister_address, 1, &data, 1, ec); + if (ec) { + return 0; + } + } + return data; + } + + /// Read a uint16_t from a register on the peripheral + /// \param register_address The address of the register to read from + /// \param ec The error code to set if there is an error + /// \return The data read from the peripheral + uint16_t read_u16_from_register(uint8_t register_address, std::error_code &ec) { + uint8_t data[2]; + if (base_config_.read_register) { + if (!base_config_.read_register(base_config_.address, register_address, data, 2)) { + ec = std::make_error_code(std::errc::io_error); + return 0; + } else { + ec.clear(); + } + } else { + write_then_read(®ister_address, 1, (uint8_t *)&data, 2, ec); + if (ec) { + return 0; + } + } + return (data[0] << 8) | data[1]; + } + + /// Read many bytes from a register on the peripheral + /// \param register_address The address of the register to read from + /// \param data The buffer to read into + /// \param length The length of the buffer + /// \param ec The error code to set if there is an error + void read_many_from_register(uint8_t register_address, uint8_t *data, size_t length, + std::error_code &ec) { + std::lock_guard lock(base_mutex_); + if (base_config_.read_register) { + if (!base_config_.read_register(base_config_.address, register_address, data, length)) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } else { + write_then_read(®ister_address, 1, data, length, ec); + } + } + + /// Set bits in a register on the peripheral + /// \param register_address The address of the register to modify + /// \param mask The mask to set + /// \param ec The error code to set if there is an error + /// \note This function reads the register, sets the bits, and then writes the register + /// back to the peripheral + void set_bits_in_register(uint8_t register_address, uint8_t mask, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + uint8_t data = read_u8_from_register(register_address, ec); + if (ec) { + return; + } + data |= mask; + write_u8_to_register(register_address, data, ec); + } + + /// Clear bits in a register on the peripheral + /// \param register_address The address of the register to modify + /// \param mask The mask to clear + /// \param ec The error code to set if there is an error + /// \note This function reads the register, clears the bits, and then writes the register + /// back to the peripheral + void clear_bits_in_register(uint8_t register_address, uint8_t mask, std::error_code &ec) { + std::lock_guard lock(base_mutex_); + uint8_t data = read_u8_from_register(register_address, ec); + if (ec) { + return; + } + data &= ~mask; + write_u8_to_register(register_address, data, ec); + } + + Config base_config_; ///< The configuration for the peripheral + std::recursive_mutex base_mutex_; ///< The mutex to protect access to the peripheral +}; +} // namespace espp diff --git a/components/bldc_driver/include/bldc_driver.hpp b/components/bldc_driver/include/bldc_driver.hpp index d5bb8f297..3de25b7c8 100644 --- a/components/bldc_driver/include/bldc_driver.hpp +++ b/components/bldc_driver/include/bldc_driver.hpp @@ -274,6 +274,7 @@ class BldcDriver : public BaseComponent { void configure_operators() { logger_.info("Create MCPWM operator"); mcpwm_operator_config_t operator_config; + memset(&operator_config, 0, sizeof(operator_config)); operator_config.group_id = 0; for (int i = 0; i < 3; i++) { ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &operators_[i])); @@ -322,6 +323,7 @@ class BldcDriver : public BaseComponent { void configure_comparators() { logger_.info("Create comparators"); mcpwm_comparator_config_t compare_config; + memset(&compare_config, 0, sizeof(compare_config)); compare_config.flags.update_cmp_on_tez = true; for (int i = 0; i < 3; i++) { ESP_ERROR_CHECK(mcpwm_new_comparator(operators_[i], &compare_config, &comparators_[i])); diff --git a/components/bldc_haptics/example/main/bldc_haptics_example.cpp b/components/bldc_haptics/example/main/bldc_haptics_example.cpp index 0d6e2071e..6d4a2522f 100644 --- a/components/bldc_haptics/example/main/bldc_haptics_example.cpp +++ b/components/bldc_haptics/example/main/bldc_haptics_example.cpp @@ -53,8 +53,9 @@ extern "C" void app_main(void) { std::shared_ptr mt6701 = std::make_shared(espp::Mt6701::Config{ .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .read_register = + std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .velocity_filter = filter_fn, .update_period = std::chrono::duration(core_update_period), .log_level = espp::Logger::Verbosity::WARN}); diff --git a/components/bldc_motor/example/main/bldc_motor_example.cpp b/components/bldc_motor/example/main/bldc_motor_example.cpp index c3a8ab2fa..43d9ca0d6 100644 --- a/components/bldc_motor/example/main/bldc_motor_example.cpp +++ b/components/bldc_motor/example/main/bldc_motor_example.cpp @@ -54,8 +54,9 @@ extern "C" void app_main(void) { std::shared_ptr mt6701 = std::make_shared(espp::Mt6701::Config{ .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .read_register = + std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .velocity_filter = filter_fn, .update_period = std::chrono::duration(core_update_period), .log_level = espp::Logger::Verbosity::WARN}); diff --git a/components/bm8563/CMakeLists.txt b/components/bm8563/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/bm8563/CMakeLists.txt +++ b/components/bm8563/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/bm8563/example/main/bm8563_example.cpp b/components/bm8563/example/main/bm8563_example.cpp index 32b89af54..e8b1428ad 100644 --- a/components/bm8563/example/main/bm8563_example.cpp +++ b/components/bm8563/example/main/bm8563_example.cpp @@ -25,8 +25,9 @@ extern "C" void app_main(void) { auto bm8563 = espp::Bm8563({ .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .write_then_read = + std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), }); std::error_code ec; diff --git a/components/bm8563/include/bm8563.hpp b/components/bm8563/include/bm8563.hpp index aab237ccf..e8b59ade3 100644 --- a/components/bm8563/include/bm8563.hpp +++ b/components/bm8563/include/bm8563.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /// @brief The BM8563 RTC driver. @@ -10,26 +10,11 @@ namespace espp { /// /// \section Example /// \snippet bm8563_example.cpp bm8563 example -class Bm8563 { +class Bm8563 : public BasePeripheral { public: /// @brief The default I2C address for the BM8563. static constexpr uint8_t DEFAULT_ADDRESS = (0x51); - /// @brief Function prototype for the I2C write function. - /// @param address The I2C address to write to. - /// @param data The data to write. - /// @param size The number of bytes to write. - /// @return True if the write was successful, false otherwise. - typedef std::function write_fn; - - /// @brief Function prototype for the I2C read function. - /// @param address The I2C address to read from. - /// @param register_address The register address to read from. - /// @param data The data to read into. - /// @param size The number of bytes to read. - /// @return True if the read was successful, false otherwise. - typedef std::function read_fn; - /// @brief The date structure. struct Date { uint16_t year; ///< The year. @@ -53,8 +38,8 @@ class Bm8563 { /// @brief The configuration structure. struct Config { - write_fn write; ///< The I2C write function. - read_fn read; ///< The I2C read function. + BasePeripheral::write_fn write; ///< The I2C write function. + BasePeripheral::write_then_read_fn write_then_read; ///< The I2C write then read function. espp::Logger::Verbosity log_level{ espp::Logger::Verbosity::WARN}; ///< Log verbosity for the input driver. }; @@ -62,8 +47,10 @@ class Bm8563 { /// @brief Constructor. /// @param config The configuration. explicit Bm8563(const Config &config) - : write_(config.write), read_(config.read), - logger_({.tag = "Bm8563", .level = config.log_level}) { + : BasePeripheral({.address = DEFAULT_ADDRESS, + .write = config.write, + .write_then_read = config.write_then_read}, + "Bm8563", config.log_level) { std::error_code ec; init(ec); if (ec) { @@ -108,7 +95,7 @@ class Bm8563 { Date get_date(std::error_code &ec) { logger_.info("getting date"); uint8_t data[4]; - read_register(Registers::DATE, data, 4, ec); + read_many_from_register((uint8_t)Registers::DATE, data, 4, ec); if (ec) { return {}; } @@ -128,7 +115,7 @@ class Bm8563 { const uint8_t data[] = {byte2bcd(d.day), byte2bcd(d.weekday), (uint8_t)(byte2bcd(d.month) | ((d.year < 2000) ? 0x80 : 0x00)), byte2bcd(d.year % 100)}; - write_register(Registers::DATE, data, 4, ec); + write_many_to_register((uint8_t)Registers::DATE, data, 4, ec); } /// @brief Get the time. @@ -136,7 +123,7 @@ class Bm8563 { Time get_time(std::error_code &ec) { logger_.info("getting time"); uint8_t data[3]; - read_register(Registers::TIME, data, 3, ec); + read_many_from_register((uint8_t)Registers::TIME, data, 3, ec); if (ec) { return {}; } @@ -152,14 +139,14 @@ class Bm8563 { void set_time(const Time &t, std::error_code &ec) { logger_.info("Setting time"); const uint8_t data[] = {byte2bcd(t.second), byte2bcd(t.minute), byte2bcd(t.hour)}; - write_register(Registers::TIME, data, 3, ec); + write_many_to_register((uint8_t)Registers::TIME, data, 3, ec); } protected: void init(std::error_code &ec) { logger_.info("initializing"); const uint8_t data[] = {0, 0}; - write_register(Registers::CONTROL_STATUS1, data, 2, ec); + write_many_to_register((uint8_t)Registers::CONTROL_STATUS1, data, 2, ec); } static constexpr int CENTURY_BIT = 0b10000000; @@ -184,27 +171,6 @@ class Bm8563 { TIMER_CONTROL = 0x0e, TIMER = 0x0f, }; - - void read_register(Registers reg, uint8_t *data, size_t size, std::error_code &ec) { - bool success = read_(DEFAULT_ADDRESS, (uint8_t)reg, data, size); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - - void write_register(Registers reg, const uint8_t *data, size_t size, std::error_code &ec) { - uint8_t buf[size + 1]; - buf[0] = (uint8_t)reg; - memcpy(buf + 1, data, size); - bool success = write_(DEFAULT_ADDRESS, buf, size + 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - - write_fn write_; - read_fn read_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/drv2605/CMakeLists.txt b/components/drv2605/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/drv2605/CMakeLists.txt +++ b/components/drv2605/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/drv2605/example/main/drv2605_example.cpp b/components/drv2605/example/main/drv2605_example.cpp index 6a7bd44e2..c171dab30 100644 --- a/components/drv2605/example/main/drv2605_example.cpp +++ b/components/drv2605/example/main/drv2605_example.cpp @@ -26,8 +26,9 @@ extern "C" void app_main(void) { .device_address = espp::Drv2605::DEFAULT_ADDRESS, .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .read_register = + std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .motor_type = espp::Drv2605::MotorType::LRA}); std::error_code ec; // we're using an ERM motor, so select an ERM library (1-5). @@ -44,6 +45,11 @@ extern "C" void app_main(void) { auto elapsed = std::chrono::duration(now - start).count(); static uint8_t waveform = 0; std::error_code ec; + drv2605.stop(ec); + if (ec) { + logger.error("stop failed: {}", ec.message()); + return false; + } drv2605.set_waveform(0, (espp::Drv2605::Waveform)waveform, ec); if (ec) { logger.error("set waveform failed: {}", ec.message()); diff --git a/components/drv2605/include/drv2605.hpp b/components/drv2605/include/drv2605.hpp index 192988f21..df6ee2b6d 100644 --- a/components/drv2605/include/drv2605.hpp +++ b/components/drv2605/include/drv2605.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -15,30 +15,10 @@ namespace espp { * \section drv2605_ex1 DRV2605 Example * \snippet drv2605_example.cpp drv2605 example */ -class Drv2605 { +class Drv2605 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0x5A); - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if the write was successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param reg_addr Register address to read from. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful, false otherwise. - */ - typedef std::function - read_fn; - /** * @brief The mode of the vibration. */ @@ -113,8 +93,10 @@ class Drv2605 { */ struct Config { uint8_t device_address = DEFAULT_ADDRESS; /**< I2C address of the device. */ - write_fn write; /**< Function for writing a byte to a register on the Drv2605. */ - read_fn read; /**< Function for reading a byte from a register on the Drv2605. */ + BasePeripheral::write_fn + write; /**< Function for writing a byte to a register on the Drv2605. */ + BasePeripheral::read_register_fn + read_register; /**< Function for reading a register from the Drv2605. */ MotorType motor_type{MotorType::ERM}; /**< MotorType that this driver is driving. */ bool auto_init{true}; /**< If true, the driver will initialize the DRV2605 on construction. */ espp::Logger::Verbosity log_level{ @@ -125,8 +107,11 @@ class Drv2605 { * @brief Construct and initialize the DRV2605. */ explicit Drv2605(const Config &config) - : motor_type_(config.motor_type), address_(config.device_address), write_(config.write), - read_(config.read), logger_({.tag = "Drv2605", .level = config.log_level}) { + : BasePeripheral({.address = config.device_address, + .write = config.write, + .read_register = config.read_register}, + "Drv2605", config.log_level) + , motor_type_(config.motor_type) { if (config.auto_init) { std::error_code ec; initialize(ec); @@ -148,7 +133,7 @@ class Drv2605 { */ void start(std::error_code &ec) { logger_.info("Starting"); - write_one_((uint8_t)Register::START, 1, ec); + write_u8_to_register((uint8_t)Register::START, 1, ec); } /** @@ -157,7 +142,7 @@ class Drv2605 { */ void stop(std::error_code &ec) { logger_.info("Stopping"); - write_one_((uint8_t)Register::START, 0, ec); + write_u8_to_register((uint8_t)Register::START, 0, ec); } /** @@ -167,7 +152,7 @@ class Drv2605 { */ void set_mode(Mode mode, std::error_code &ec) { logger_.info("Setting mode {}", (uint8_t)mode); - write_one_((uint8_t)Register::MODE, (uint8_t)mode, ec); + write_u8_to_register((uint8_t)Register::MODE, (uint8_t)mode, ec); } /** @@ -182,7 +167,7 @@ class Drv2605 { */ void set_waveform(uint8_t slot, Waveform w, std::error_code &ec) { logger_.info("Setting waveform {}", (uint8_t)w); - write_one_((uint8_t)Register::WAVESEQ1 + slot, (uint8_t)w, ec); + write_u8_to_register((uint8_t)Register::WAVESEQ1 + slot, (uint8_t)w, ec); } /** @@ -195,16 +180,16 @@ class Drv2605 { if (motor_type_ == MotorType::LRA && lib != Library::LRA) { logger_.warn("LRA motor selected, but library {} is not an LRA library", lib); } - write_one_((uint8_t)Register::LIBRARY, (uint8_t)lib, ec); + write_u8_to_register((uint8_t)Register::LIBRARY, (uint8_t)lib, ec); } protected: void init(std::error_code &ec) { logger_.info("Initializing motor"); - write_one_((uint8_t)Register::MODE, 0, ec); // out of standby + write_u8_to_register((uint8_t)Register::MODE, 0, ec); // out of standby if (ec) return; - write_one_((uint8_t)Register::RTPIN, 0, ec); // no real-time playback + write_u8_to_register((uint8_t)Register::RTPIN, 0, ec); // no real-time playback if (ec) return; set_waveform(0, Waveform::STRONG_CLICK, ec); // Strong Click @@ -213,19 +198,19 @@ class Drv2605 { set_waveform(1, Waveform::END, ec); // end sequence if (ec) return; - write_one_((uint8_t)Register::OVERDRIVE, 0, ec); // no overdrive + write_u8_to_register((uint8_t)Register::OVERDRIVE, 0, ec); // no overdrive if (ec) return; - write_one_((uint8_t)Register::SUSTAINPOS, 0, ec); + write_u8_to_register((uint8_t)Register::SUSTAINPOS, 0, ec); if (ec) return; - write_one_((uint8_t)Register::SUSTAINNEG, 0, ec); + write_u8_to_register((uint8_t)Register::SUSTAINNEG, 0, ec); if (ec) return; - write_one_((uint8_t)Register::BREAK, 0, ec); + write_u8_to_register((uint8_t)Register::BREAK, 0, ec); if (ec) return; - write_one_((uint8_t)Register::AUDIOMAX, 0x64, ec); + write_u8_to_register((uint8_t)Register::AUDIOMAX, 0x64, ec); if (ec) return; // set the motor type based on the config @@ -233,20 +218,20 @@ class Drv2605 { if (ec) return; // turn on ERM OPEN LOOP - auto current_control3 = read_one_((uint8_t)Register::CONTROL3, ec); + auto current_control3 = read_u8_from_register((uint8_t)Register::CONTROL3, ec); if (ec) return; - write_one_((uint8_t)Register::CONTROL3, current_control3 | 0x20, ec); + write_u8_to_register((uint8_t)Register::CONTROL3, current_control3 | 0x20, ec); } void set_motor_type(MotorType motor_type, std::error_code &ec) { logger_.info("Setting motor type {}", motor_type == MotorType::ERM ? "ERM" : "LRA"); motor_type_ = motor_type; - auto current_feedback = read_one_((uint8_t)Register::FEEDBACK, ec); + auto current_feedback = read_u8_from_register((uint8_t)Register::FEEDBACK, ec); if (ec) return; uint8_t motor_config = (motor_type_ == MotorType::ERM) ? 0x7F : 0x80; - write_one_((uint8_t)Register::FEEDBACK, current_feedback | motor_config, ec); + write_u8_to_register((uint8_t)Register::FEEDBACK, current_feedback | motor_config, ec); } enum class Register : uint8_t { @@ -285,37 +270,7 @@ class Drv2605 { LRARSON = 0x22, ///< LRA resonance-period }; - uint8_t read_one_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data; - bool success = read_(address_, reg_addr, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - - void write_one_(uint8_t reg_addr, uint8_t data, std::error_code &ec) { - write_many_(reg_addr, &data, 1, ec); - } - - void write_many_(uint8_t reg_addr, const uint8_t *write_data, size_t write_data_len, - std::error_code &ec) { - uint8_t total_len = 1 + write_data_len; - uint8_t data[total_len]; - data[0] = reg_addr; - memcpy(&data[1], write_data, write_data_len); - bool success = write_(address_, data, total_len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - MotorType motor_type_; - uint8_t address_; - write_fn write_; - read_fn read_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/ft5x06/CMakeLists.txt b/components/ft5x06/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/ft5x06/CMakeLists.txt +++ b/components/ft5x06/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/ft5x06/example/main/ft5x06_example.cpp b/components/ft5x06/example/main/ft5x06_example.cpp index b0a9b935e..b659802f4 100644 --- a/components/ft5x06/example/main/ft5x06_example.cpp +++ b/components/ft5x06/example/main/ft5x06_example.cpp @@ -24,9 +24,9 @@ extern "C" void app_main(void) { // now make the ft5x06 which decodes the data espp::Ft5x06 ft5x06({.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read_at_register = std::bind( - &espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .read_register = std::bind(&espp::I2c::read_at_register, &i2c, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4), .log_level = espp::Logger::Verbosity::WARN}); // and finally, make the task to periodically poll the ft5x06 and print // the state diff --git a/components/ft5x06/include/ft5x06.hpp b/components/ft5x06/include/ft5x06.hpp index 08c572f3e..a23965d4c 100644 --- a/components/ft5x06/include/ft5x06.hpp +++ b/components/ft5x06/include/ft5x06.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /// @brief The FT5x06 touch controller. @@ -10,26 +10,11 @@ namespace espp { /// /// \section Example /// \snippet ft5x06_example.cpp ft5x06 example -class Ft5x06 { +class Ft5x06 : public BasePeripheral { public: /// @brief The default I2C address for the FT5x06. static constexpr uint8_t DEFAULT_ADDRESS = (0x38); - /// @brief The function to write data to the I2C bus. - /// @param address The I2C address of the device. - /// @param data The data to write to the I2C bus. - /// @param data_len The length of the data to write to the I2C bus. - /// @return true if the function succeeds. - typedef std::function write_fn; - - /// @brief The function to read data starting at a register from the I2C bus. - /// @param address The I2C address of the device. - /// @param register The register to write to the I2C bus. - /// @param data The data to read from the I2C bus. - /// @param data_len The length of the data to read from the I2C bus. - /// @return true if the function succeeds. - typedef std::function read_at_register_fn; - /// @brief The gesture that was detected. enum class Gesture : uint8_t { NONE = 0x00, @@ -43,17 +28,19 @@ class Ft5x06 { /// @brief The configuration for the FT5x06. struct Config { - write_fn write; ///< The function to write data to the I2C bus. - read_at_register_fn - read_at_register; ///< The function to write then read data from the I2C bus. + BasePeripheral::write_fn write; ///< The function to write data to the I2C bus. + BasePeripheral::read_register_fn + read_register; ///< The function to write then read data from the I2C bus. espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< The log level. }; /// @brief Construct a new FT5x06. /// @param config The configuration for the FT5x06. explicit Ft5x06(const Config &config) - : write_(config.write), read_at_register_(config.read_at_register), - logger_({.tag = "Ft5x06", .level = config.log_level}) { + : BasePeripheral({.address = DEFAULT_ADDRESS, + .write = config.write, + .read_register = config.read_register}, + "Ft5x06", config.log_level) { std::error_code ec; init(ec); if (ec) { @@ -65,7 +52,7 @@ class Ft5x06 { /// @param ec The error code if the function fails. /// @return The number of touch points. uint8_t get_num_touch_points(std::error_code &ec) { - return read_register(Registers::TOUCH_POINTS, ec); + return read_u8_from_register((uint8_t)Registers::TOUCH_POINTS, ec); } /// @brief Get the touch point. @@ -81,7 +68,7 @@ class Ft5x06 { *num_touch_points = tp; if (*num_touch_points != 0) { uint8_t data[4]; - read(Registers::TOUCH1_XH, data, 4, ec); + read_many_from_register((uint8_t)Registers::TOUCH1_XH, data, 4, ec); if (ec) { return; } @@ -95,45 +82,45 @@ class Ft5x06 { /// @param ec The error code if the function fails. /// @return The gesture that was detected. Gesture read_gesture(std::error_code &ec) { - return (Gesture)read_register(Registers::GESTURE_ID, ec); + return (Gesture)read_u8_from_register((uint8_t)Registers::GESTURE_ID, ec); } protected: void init(std::error_code &ec) { // Valid touching detect threshold - write_register(Registers::ID_G_THGROUP, 70, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THGROUP, 70, ec); if (ec) return; // valid touching peak detect threshold - write_register(Registers::ID_G_THPEAK, 60, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THPEAK, 60, ec); if (ec) return; // Touch focus threshold - write_register(Registers::ID_G_THCAL, 16, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THCAL, 16, ec); if (ec) return; // threshold when there is surface water - write_register(Registers::ID_G_THWATER, 60, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THWATER, 60, ec); if (ec) return; // threshold of temperature compensation - write_register(Registers::ID_G_THTEMP, 10, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THTEMP, 10, ec); if (ec) return; // Touch difference threshold - write_register(Registers::ID_G_THDIFF, 20, ec); + write_u8_to_register((uint8_t)Registers::ID_G_THDIFF, 20, ec); if (ec) return; // Delay to enter 'Monitor' status (s) - write_register(Registers::ID_G_TIME_ENTER_MONITOR, 2, ec); + write_u8_to_register((uint8_t)Registers::ID_G_TIME_ENTER_MONITOR, 2, ec); if (ec) return; // Period of 'Active' status (ms) - write_register(Registers::ID_G_PERIODACTIVE, 12, ec); + write_u8_to_register((uint8_t)Registers::ID_G_PERIODACTIVE, 12, ec); if (ec) return; // Timer to enter 'idle' when in 'Monitor' (ms) - write_register(Registers::ID_G_PERIODMONITOR, 40, ec); + write_u8_to_register((uint8_t)Registers::ID_G_PERIODMONITOR, 40, ec); } enum class Registers : uint8_t { @@ -192,37 +179,5 @@ class Ft5x06 { ID_G_FT5201ID = 0xA8, ID_G_ERR = 0xA9, }; - - uint8_t read_register(Registers reg, std::error_code &ec) { - uint8_t data = 0; - bool success = read_at_register_(DEFAULT_ADDRESS, (uint8_t)reg, &data, 1); - if (!success) { - logger_.error("Failed to read register {}", (uint8_t)reg); - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - - void read(Registers reg, uint8_t *data, size_t data_len, std::error_code &ec) { - bool success = read_at_register_(DEFAULT_ADDRESS, (uint8_t)reg, data, data_len); - if (!success) { - logger_.error("Failed to read register {}", (uint8_t)reg); - ec = std::make_error_code(std::errc::io_error); - } - } - - void write_register(Registers reg, uint8_t data, std::error_code &ec) { - uint8_t buf[2] = {(uint8_t)reg, data}; - bool success = write_(DEFAULT_ADDRESS, buf, 2); - if (!success) { - logger_.error("Failed to write register {}", (uint8_t)reg); - ec = std::make_error_code(std::errc::io_error); - } - } - - write_fn write_; - read_at_register_fn read_at_register_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/gt911/CMakeLists.txt b/components/gt911/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/gt911/CMakeLists.txt +++ b/components/gt911/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/gt911/example/main/gt911_example.cpp b/components/gt911/example/main/gt911_example.cpp index 929dc1555..cae360784 100644 --- a/components/gt911/example/main/gt911_example.cpp +++ b/components/gt911/example/main/gt911_example.cpp @@ -19,12 +19,14 @@ extern "C" void app_main(void) { .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); + // now make the gt911 which decodes the data espp::Gt911 gt911({.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .log_level = espp::Logger::Verbosity::WARN}); + .read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .log_level = espp::Logger::Verbosity::WARN}); + // and finally, make the task to periodically poll the gt911 and print // the state auto task_fn = [>911](std::mutex &m, std::condition_variable &cv) { @@ -51,7 +53,7 @@ extern "C" void app_main(void) { // task is being stopped / destroyed { std::unique_lock lk(m); - cv.wait_for(lk, 500ms); + cv.wait_for(lk, 100ms); } return false; // don't stop the task }; diff --git a/components/gt911/include/gt911.hpp b/components/gt911/include/gt911.hpp index 12b37ec48..ee934cda0 100644 --- a/components/gt911/include/gt911.hpp +++ b/components/gt911/include/gt911.hpp @@ -1,37 +1,26 @@ #pragma once +#include #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /// @brief Driver for the GT911 touch controller /// /// \section Example /// \snippet gt911_example.cpp gt911 example -class Gt911 { +class Gt911 : public BasePeripheral { public: /// Default address for the GT911 chip static constexpr uint8_t DEFAULT_ADDRESS_1 = 0x5D; /// Alternate address for the GT911 chip static constexpr uint8_t DEFAULT_ADDRESS_2 = 0x14; - /// @brief Function for writing to the i2c device - /// @param address The address of the i2c device - /// @param data The data to write to the chip - /// @param len The length of the data to write - typedef std::function write_fn; - - /// @brief Function signature for reading from the i2c device - /// @param dev_addr The device address - /// @param data The data to read - /// @param data_len The length of the data to read - typedef std::function read_fn; - /// @brief Configuration for the GT911 driver struct Config { - write_fn write; ///< Function for writing to the GT911 chip - read_fn read; ///< Function for reading from the GT911 chip + BasePeripheral::write_fn write; ///< Function for writing to the GT911 chip + BasePeripheral::read_fn read; ///< Function for reading from the GT911 chip uint8_t address = DEFAULT_ADDRESS_1; ///< Which address to use for this chip? espp::Logger::Verbosity log_level{ espp::Logger::Verbosity::WARN}; ///< Log verbosity for the input driver. @@ -40,8 +29,8 @@ class Gt911 { /// @brief Constructor for the GT911 driver /// @param config The configuration for the driver explicit Gt911(const Config &config) - : write_(config.write), read_(config.read), address_(config.address), - logger_({.tag = "Gt911", .level = config.log_level}) {} + : BasePeripheral({.address = config.address, .write = config.write, .read = config.read}, + "Gt911", config.log_level) {} /// @brief Update the state of the GT911 driver /// @param ec Error code to set if an error occurs @@ -49,14 +38,14 @@ class Gt911 { bool update(std::error_code &ec) { static constexpr size_t DATA_LEN = CONTACT_SIZE * MAX_CONTACTS; static uint8_t data[DATA_LEN]; - read(Registers::POINT_INFO, data, 1, ec); + read_register(Registers::POINT_INFO, data, 1, ec); if (ec) { return false; } num_touch_points_ = data[0] & 0x0f; + logger_.debug("Got {} touch points", num_touch_points_); if (num_touch_points_ > 0) { - logger_.debug("Got {} touch points", num_touch_points_); - read(Registers::POINTS, data, CONTACT_SIZE * num_touch_points_, ec); + read_register(Registers::POINTS, data, CONTACT_SIZE * num_touch_points_, ec); if (ec) { return false; } @@ -66,7 +55,7 @@ class Gt911 { y_ = point->y; logger_.debug("Touch at ({}, {})", x_, y_); } - write(Registers::POINT_INFO, 0x00, ec); // sync signal + write_register(Registers::POINT_INFO, 0x00, ec); // sync signal if (ec) { return false; } @@ -238,47 +227,26 @@ class Gt911 { GTKeyConfig keys; } __attribute__((packed)); - void write(Registers reg, uint8_t val, std::error_code &ec) { write(reg, &val, 1, ec); } - - void write(Registers reg, const uint8_t *data, size_t len, std::error_code &ec) { - uint16_t reg_addr = (uint16_t)reg; - size_t d_len = 2 + len; - uint8_t d[d_len]; - d[0] = reg_addr >> 8; - d[1] = reg_addr & 0xFF; - if (len > 0 && data != nullptr) - memcpy(&d[2], data, len); - bool success = write_(address_, d, d_len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } + // we use write_then_read since we have to write the u16 register address and + // then read the data + void read_register(Registers reg, uint8_t *data, size_t len, std::error_code &ec) { + uint16_t reg16 = static_cast(reg); + uint8_t reg_buf[2] = {static_cast(reg16 >> 8), static_cast(reg16 & 0xff)}; + write_then_read(reg_buf, 2, data, len, ec); } - uint8_t read(Registers reg, std::error_code &ec) { - uint8_t val = 0; - read(reg, &val, 1, ec); - if (ec) { - return 0; - } - return val; - } - - void read(Registers reg, uint8_t *data, size_t len, std::error_code &ec) { - write(reg, nullptr, 0, ec); - if (ec) return; - bool success = read_(address_, data, len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } + void write_register(Registers reg, const uint8_t value, std::error_code &ec) { + uint16_t reg16 = static_cast(reg); + uint8_t write_buf[3]; + write_buf[0] = static_cast(reg16 >> 8); + write_buf[1] = static_cast(reg16 & 0xff); + write_buf[2] = value; + write_many(write_buf, 3, ec); } - write_fn write_; - read_fn read_; - uint8_t address_; std::atomic home_button_pressed_{false}; std::atomic num_touch_points_; std::atomic x_; std::atomic y_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/max1704x/CMakeLists.txt b/components/max1704x/CMakeLists.txt index 5ae0fe990..3d35d0937 100644 --- a/components/max1704x/CMakeLists.txt +++ b/components/max1704x/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" "math" + REQUIRES "base_peripheral" "math" ) diff --git a/components/max1704x/include/max1704x.hpp b/components/max1704x/include/max1704x.hpp index 3fc047d74..07f1036b0 100644 --- a/components/max1704x/include/max1704x.hpp +++ b/components/max1704x/include/max1704x.hpp @@ -3,7 +3,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -17,28 +17,10 @@ namespace espp { * @section max1704x_ex1 MAX1704X Example * @snippet max1704x_example.cpp max1704x example */ -class Max1704x { +class Max1704x : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = 0x36; ///< Default address of the MAX1704x. - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if successful, false otherwise. - */ - typedef std::function read_fn; - enum class AlertStatus { SOC_CHANGE = 0x20, ///< Alert for state of charge change SOC_LOW = 0x10, ///< Alert for state of charge low @@ -52,8 +34,8 @@ class Max1704x { */ struct Config { uint8_t device_address{DEFAULT_ADDRESS}; ///< Address of the MAX1704x. - write_fn write; //< Function to write bytes to the device. - read_fn read; //< Function to read bytes from the device. + BasePeripheral::write_fn write; //< Function to write bytes to the device. + BasePeripheral::read_fn read; //< Function to read bytes from the device. bool auto_init{true}; ///< Whether to automatically initialize the MAX1704x. Logger::Verbosity log_level{Logger::Verbosity::WARN}; ///< Log level for the MAX1704x. }; @@ -63,8 +45,9 @@ class Max1704x { * @param config Configuration for the MAX1704x. */ explicit Max1704x(const Config &config) - : address_(config.device_address), write_(config.write), read_(config.read), - logger_({.tag = "Max1704x", .level = config.log_level}) { + : BasePeripheral( + {.address = config.device_address, .write = config.write, .read = config.read}, + "Max1704x", config.log_level) { if (config.auto_init) { std::error_code ec; initalize(ec); @@ -78,7 +61,7 @@ class Max1704x { * @brief Initialize the MAX1704x. */ void initalize(std::error_code &ec) { - std::scoped_lock lock(mutex_); + std::lock_guard lock(base_mutex_); // Get the IC version version_ = get_version(ec); if (ec) { @@ -99,8 +82,8 @@ class Max1704x { * @return The IC version. */ uint16_t get_version(std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint16_t data = read_register(Register::VERSION, ec); + std::lock_guard lock(base_mutex_); + uint16_t data = read_u16_from_register((uint8_t)Register::VERSION, ec); if (ec) { return 0; } @@ -114,8 +97,8 @@ class Max1704x { * @return The chip ID. */ uint8_t get_chip_id(std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint16_t data = read_register(Register::CHIPID, ec); + std::lock_guard lock(base_mutex_); + uint16_t data = read_u16_from_register((uint8_t)Register::CHIPID, ec); if (ec) { return 0; } @@ -129,8 +112,8 @@ class Max1704x { * @return The battery voltage in V. */ float get_battery_voltage(std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint16_t data = read_register(Register::VCELL, ec); + std::lock_guard lock(base_mutex_); + uint16_t data = read_u16_from_register((uint8_t)Register::VCELL, ec); if (ec) { return 0; } @@ -145,8 +128,8 @@ class Max1704x { * @return The battery state of charge in %. */ float get_battery_percentage(std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint16_t data = read_register(Register::SOC, ec); + std::lock_guard lock(base_mutex_); + uint16_t data = read_u16_from_register((uint8_t)Register::SOC, ec); if (ec) { return 0; } @@ -163,8 +146,8 @@ class Max1704x { * @return The battery charge or discharge rate in %/hr. */ float get_battery_charge_rate(std::error_code &ec) { - std::scoped_lock lock(mutex_); - int16_t data = read_register(Register::CRATE, ec); + std::lock_guard lock(base_mutex_); + int16_t data = read_u16_from_register((uint8_t)Register::CRATE, ec); if (ec) { return 0; } @@ -179,8 +162,8 @@ class Max1704x { * @return The battery alert status as an AlertStatus. */ AlertStatus get_alert_status(std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint16_t data = read_register(Register::STATUS, ec); + std::lock_guard lock(base_mutex_); + uint16_t data = read_u16_from_register((uint8_t)Register::STATUS, ec); if (ec) { return AlertStatus::SOC_CHANGE; } @@ -195,13 +178,7 @@ class Max1704x { * @param ec Error code set if an error occurs. */ void clear_alert_status(uint8_t flags_to_clear, std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint8_t data = read_register(Register::STATUS, ec); - if (ec) { - return; - } - data &= ~flags_to_clear; - write_register(Register::STATUS, data, ec); + clear_bits_in_register((uint8_t)Register::STATUS, flags_to_clear, ec); } protected: @@ -235,45 +212,7 @@ class Max1704x { VOLTAGE_HIGH = 0x02, ///< Alert for voltage high }; - void write_register(const Register reg, const uint8_t data, std::error_code &ec) { - std::scoped_lock lock(mutex_); - uint8_t buf[2]; - buf[0] = (uint8_t)reg; - buf[1] = (uint8_t)data; - if (!write_(address_, buf, sizeof(buf))) { - logger_.error("Failed to write register 0x{:02X}", (uint8_t)reg); - ec = std::make_error_code(std::errc::io_error); - return; - } - ec.clear(); - } - - uint16_t read_register(const Register reg, std::error_code &ec) { - std::scoped_lock lock(mutex_); - // write the address - if (!write_(address_, (uint8_t *)®, 1)) { - ec = std::make_error_code(std::errc::io_error); - logger_.error("Failed to write register 0x{:02X}", (uint8_t)reg); - return 0; - } - // and read the data - uint8_t data[2]; - if (!read_(address_, data, 2)) { - ec = std::make_error_code(std::errc::io_error); - logger_.error("Failed to read register 0x{:02X}", (uint8_t)reg); - return 0; - } - ec.clear(); - return (data[0] << 8) | data[1]; - } - uint16_t version_{0}; ///< IC version uint8_t chip_id_{0}; ///< Chip ID - - uint8_t address_; - write_fn write_; - read_fn read_; - std::recursive_mutex mutex_; - Logger logger_; }; } // namespace espp diff --git a/components/mcp23x17/CMakeLists.txt b/components/mcp23x17/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/mcp23x17/CMakeLists.txt +++ b/components/mcp23x17/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/mcp23x17/example/main/mcp23x17_example.cpp b/components/mcp23x17/example/main/mcp23x17_example.cpp index 2001245fb..5afccbdd3 100644 --- a/components/mcp23x17/example/main/mcp23x17_example.cpp +++ b/components/mcp23x17/example/main/mcp23x17_example.cpp @@ -27,8 +27,9 @@ extern "C" void app_main(void) { .port_1_interrupt_mask = (1 << 7), // interrupt on B7 .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .read_register = + std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .log_level = espp::Logger::Verbosity::WARN}); // set pull up on the input pins std::error_code ec; diff --git a/components/mcp23x17/include/mcp23x17.hpp b/components/mcp23x17/include/mcp23x17.hpp index e09e3af7f..96754886e 100644 --- a/components/mcp23x17/include/mcp23x17.hpp +++ b/components/mcp23x17/include/mcp23x17.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -12,30 +12,11 @@ namespace espp { * \section mcp23x17_ex1 MCP23x17 Example * \snippet mcp23x17_example.cpp mcp23x17 example */ -class Mcp23x17 { +class Mcp23x17 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = 0b0100000; ///< Lower 3 bits are A2, A2, A0 pins on the chip. - /** - * @brief Function to write bytes to the device - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param reg_addr Register address to read from. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful, false otherwise. - */ - typedef std::function - read_fn; - /** * The two GPIO ports the MCP23x17 has. */ @@ -53,10 +34,11 @@ class Mcp23x17 { uint8_t port_0_interrupt_mask = 0x00; ///< Interrupt mask (1 = interrupt) for port 0 / A uint8_t port_1_direction_mask = 0x00; ///< Direction mask (1 = input) for port 1 / B uint8_t port_1_interrupt_mask = 0x00; ///< Interrupt mask (1 = interrupt) for port 1 / B - write_fn write; ///< Function to write to the device. - read_fn read; ///< Function to read from the device. - bool auto_init = true; ///< True if the device should be initialized on - ///< construction. + BasePeripheral::write_fn write; ///< Function to write to the device. + BasePeripheral::read_register_fn + read_register; ///< Function to read bytes at a register address from the device. + bool auto_init = true; ///< True if the device should be initialized on + ///< construction. Logger::Verbosity log_level{Logger::Verbosity::WARN}; ///< Log verbosity for the component. }; @@ -65,11 +47,14 @@ class Mcp23x17 { * @param config Config structure for configuring the MCP23X17 */ explicit Mcp23x17(const Config &config) - : address_(config.device_address), port_0_direction_mask_(config.port_0_direction_mask), - port_0_interrupt_mask_(config.port_0_interrupt_mask), - port_1_direction_mask_(config.port_1_direction_mask), - port_1_interrupt_mask_(config.port_1_interrupt_mask), write_(config.write), - read_(config.read), logger_({.tag = "Mcp23x17", .level = config.log_level}) { + : BasePeripheral({.address = config.device_address, + .write = config.write, + .read_register = config.read_register}, + "Mcp23x17", config.log_level) + , port_0_direction_mask_(config.port_0_direction_mask) + , port_0_interrupt_mask_(config.port_0_interrupt_mask) + , port_1_direction_mask_(config.port_1_direction_mask) + , port_1_interrupt_mask_(config.port_1_interrupt_mask) { if (config.auto_init) { std::error_code ec; init(ec); @@ -95,7 +80,7 @@ class Mcp23x17 { */ uint8_t get_pins(Port port, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::GPIOA : Registers::GPIOB; - auto val = read_one_((uint8_t)addr, ec); + auto val = read_u8_from_register((uint8_t)addr, ec); if (ec) { logger_.error("Failed to read pins: {}", ec.message()); return 0; @@ -109,10 +94,10 @@ class Mcp23x17 { * @return The pin values as a 16 bit mask (PA_0 lsb, PB_7 msb). */ uint16_t get_pins(std::error_code &ec) { - uint16_t p0 = read_one_((uint8_t)Registers::GPIOA, ec); + uint16_t p0 = read_u8_from_register((uint8_t)Registers::GPIOA, ec); if (ec) return 0; - uint16_t p1 = read_one_((uint8_t)Registers::GPIOB, ec); + uint16_t p1 = read_u8_from_register((uint8_t)Registers::GPIOB, ec); if (ec) return 0; return (p1 << 8) | p0; @@ -126,7 +111,7 @@ class Mcp23x17 { */ void set_pins(Port port, uint8_t output, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::GPIOA : Registers::GPIOB; - write_one_((uint8_t)addr, output, ec); + write_u8_to_register((uint8_t)addr, output, ec); } /** @@ -137,7 +122,7 @@ class Mcp23x17 { */ uint8_t get_interrupt_capture(Port port, std::error_code &ec) { auto addr = port == Port::PORT0 ? Registers::INTCAPA : Registers::INTCAPB; - auto val = read_one_((uint8_t)addr, ec); + auto val = read_u8_from_register((uint8_t)addr, ec); if (ec) { logger_.error("Failed to read interrupt capture: {}", ec.message()); return 0; @@ -154,7 +139,7 @@ class Mcp23x17 { void set_interrupt_on_change(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting interrupt on change for {} pins {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::GPINTENA : Registers::GPINTENB; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -169,7 +154,7 @@ class Mcp23x17 { val_mask); // set the pin to enable interrupt auto addr = port == Port::PORT0 ? Registers::GPINTENA : Registers::GPINTENB; - write_one_((uint8_t)addr, pin_mask, ec); + write_u8_to_register((uint8_t)addr, pin_mask, ec); if (ec) { logger_.error("Failed to enable interrupt: {}", ec.message()); return; @@ -177,7 +162,7 @@ class Mcp23x17 { // set the pin to interrupt on comparison to defval register addr = port == Port::PORT0 ? Registers::INTCONA : Registers::INTCONB; - write_one_((uint8_t)addr, pin_mask, ec); + write_u8_to_register((uint8_t)addr, pin_mask, ec); if (ec) { logger_.error("Failed to set pin interrupt on comparison: {}", ec.message()); return; @@ -185,7 +170,7 @@ class Mcp23x17 { // set the defval register to be the value to compare against addr = port == Port::PORT0 ? Registers::DEFVALA : Registers::DEFVALB; - write_one_((uint8_t)addr, val_mask, ec); + write_u8_to_register((uint8_t)addr, val_mask, ec); if (ec) { logger_.error("Failed to set defval: {}", ec.message()); return; @@ -201,7 +186,7 @@ class Mcp23x17 { void set_direction(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting direction for {} to {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::IODIRA : Registers::IODIRB; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -213,7 +198,7 @@ class Mcp23x17 { void set_input_polarity(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting input polarity for {} to {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::IOPOLA : Registers::IOPOLB; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -225,7 +210,7 @@ class Mcp23x17 { void set_pull_up(Port port, uint8_t mask, std::error_code &ec) { logger_.debug("Setting pull-up for {} to {}", (uint8_t)port, mask); auto addr = port == Port::PORT0 ? Registers::GPPUA : Registers::GPPUB; - write_one_((uint8_t)addr, mask, ec); + write_u8_to_register((uint8_t)addr, mask, ec); } /** @@ -237,7 +222,7 @@ class Mcp23x17 { void set_interrupt_mirror(bool mirror, std::error_code &ec) { logger_.debug("Setting interrupt mirror: {}", mirror); auto addr = (uint8_t)Registers::IOCON; - auto config = read_one_(addr, ec); + auto config = read_u8_from_register(addr, ec); if (ec) { logger_.error("Failed to read config: {}", ec.message()); return; @@ -250,7 +235,7 @@ class Mcp23x17 { } // now write it back logger_.debug("Writing new config: {}", config); - write_one_(addr, config, ec); + write_u8_to_register(addr, config, ec); } /** @@ -262,7 +247,7 @@ class Mcp23x17 { void set_interrupt_polarity(bool active_high, std::error_code &ec) { logger_.debug("Setting interrupt polarity: {}", active_high); auto addr = (uint8_t)Registers::IOCON; - auto config = read_one_(addr, ec); + auto config = read_u8_from_register(addr, ec); if (ec) { logger_.error("Failed to read config: {}", ec.message()); return; @@ -275,7 +260,7 @@ class Mcp23x17 { } // now write it back logger_.debug("Writing new config: {}", config); - write_one_(addr, config, ec); + write_u8_to_register(addr, config, ec); } protected: @@ -343,31 +328,9 @@ class Mcp23x17 { set_interrupt_on_change(Port::PORT1, port_1_interrupt_mask_, ec); } - void write_one_(uint8_t reg_addr, uint8_t write_data, std::error_code &ec) { - uint8_t data[2] = {reg_addr, write_data}; - bool success = write_(address_, data, 2); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } - } - - uint8_t read_one_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data; - bool success = read_(address_, reg_addr, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - - uint8_t address_; uint8_t port_0_direction_mask_; uint8_t port_0_interrupt_mask_; uint8_t port_1_direction_mask_; uint8_t port_1_interrupt_mask_; - write_fn write_; - read_fn read_; - Logger logger_; }; } // namespace espp diff --git a/components/mt6701/CMakeLists.txt b/components/mt6701/CMakeLists.txt index a244e0474..f7698dd56 100644 --- a/components/mt6701/CMakeLists.txt +++ b/components/mt6701/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" "task" + REQUIRES "base_peripheral" "task" ) diff --git a/components/mt6701/example/main/mt6701_example.cpp b/components/mt6701/example/main/mt6701_example.cpp index 47661ee02..062d4a142 100644 --- a/components/mt6701/example/main/mt6701_example.cpp +++ b/components/mt6701/example/main/mt6701_example.cpp @@ -27,14 +27,14 @@ extern "C" void app_main(void) { {.normalized_cutoff_frequency = 2.0f * filter_cutoff_hz * encoder_update_period}); auto filter_fn = [&filter](float raw) -> float { return filter.update(raw); }; // now make the mt6701 which decodes the data - espp::Mt6701 mt6701( - {.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - .velocity_filter = filter_fn, - .update_period = std::chrono::duration(encoder_update_period), - .log_level = espp::Logger::Verbosity::WARN}); + espp::Mt6701 mt6701({.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .read_register = std::bind(&espp::I2c::read_at_register, &i2c, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4), + .velocity_filter = filter_fn, + .update_period = std::chrono::duration(encoder_update_period), + .log_level = espp::Logger::Verbosity::WARN}); // and finally, make the task to periodically poll the mt6701 and print the // state. NOTE: the Mt6701 runs its own task to maintain state, so we're // just polling the current state. diff --git a/components/mt6701/include/mt6701.hpp b/components/mt6701/include/mt6701.hpp index 8762757de..2156305f2 100644 --- a/components/mt6701/include/mt6701.hpp +++ b/components/mt6701/include/mt6701.hpp @@ -4,7 +4,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" #include "task.hpp" namespace espp { @@ -26,30 +26,10 @@ namespace espp { * \section mt6701_ex1 Mt6701 Example * \snippet mt6701_example.cpp mt6701 example */ -class Mt6701 { +class Mt6701 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0b0000110); ///< I2C address of the MT6701 - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if the write was successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param reg_addr Register address to read from. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful, false otherwise. - */ - typedef std::function - read_fn; - /** * @brief Filter the input raw velocity and return it. * @param raw Most recent raw velocity measured. @@ -74,9 +54,10 @@ class Mt6701 { * @brief Configuration information for the Mt6701. */ struct Config { - uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address of the device. - write_fn write; ///< Function to write to the device. - read_fn read; ///< Function to read from the device. + uint8_t device_address = DEFAULT_ADDRESS; ///< I2C address of the device. + BasePeripheral::write_fn write; ///< Function to write to the device. + BasePeripheral::read_register_fn + read_register; ///< Function to read data from a register on the device. velocity_filter_fn velocity_filter{nullptr}; ///< Function to filter the veolcity. @note Will be ///< called once every update_period seconds. std::chrono::duration update_period{ @@ -92,9 +73,12 @@ class Mt6701 { * @brief Construct the Mt6701 and start the update task. */ explicit Mt6701(const Config &config) - : address_(config.device_address), write_(config.write), read_(config.read), - velocity_filter_(config.velocity_filter), update_period_(config.update_period), - logger_({.tag = "Mt6701", .level = config.log_level}) { + : BasePeripheral({.address = config.device_address, + .write = config.write, + .read_register = config.read_register}, + "Mt6701", config.log_level) + , velocity_filter_(config.velocity_filter) + , update_period_(config.update_period) { if (config.auto_init) { std::error_code ec; initialize(ec); @@ -184,11 +168,11 @@ class Mt6701 { int read_count(std::error_code &ec) { logger_.info("read_count"); // read the angle count registers - uint8_t angle_h = read_one_((uint8_t)Registers::ANGLE_H, ec); + uint8_t angle_h = read_u8_from_register((uint8_t)Registers::ANGLE_H, ec); if (ec) { return 0; } - uint8_t angle_l = read_one_((uint8_t)Registers::ANGLE_L, ec) >> 2; + uint8_t angle_l = read_u8_from_register((uint8_t)Registers::ANGLE_L, ec) >> 2; if (ec) { return 0; } @@ -265,16 +249,6 @@ class Mt6701 { task_->start(); } - uint8_t read_one_(uint8_t reg_addr, std::error_code &ec) { - uint8_t data; - bool success = read_(address_, reg_addr, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; - } - /** * @brief Register map for the MT6701. * @@ -304,15 +278,11 @@ class Mt6701 { A_STOP_LOW = 0x40, ///< A_STOP[7:0] }; - uint8_t address_; - write_fn write_; - read_fn read_; velocity_filter_fn velocity_filter_{nullptr}; std::chrono::duration update_period_; std::atomic count_{0}; std::atomic accumulator_{0}; std::atomic velocity_rpm_{0}; std::unique_ptr task_; - Logger logger_; }; } // namespace espp diff --git a/components/ndef/include/ndef.hpp b/components/ndef/include/ndef.hpp index 94488911b..a288c91b0 100644 --- a/components/ndef/include/ndef.hpp +++ b/components/ndef/include/ndef.hpp @@ -245,7 +245,9 @@ class Ndef { * @param payload The payload data for the packet */ explicit Ndef(TNF tnf, std::string_view type, std::string_view payload) - : tnf_(tnf), type_(type), payload_(payload) {} + : tnf_(tnf) + , type_(type) + , payload_(payload) {} /** * @brief Static function to make an NDEF record for transmitting english @@ -656,7 +658,7 @@ class Ndef { uint8_t ME : 1; uint8_t MB : 1; }; - uint8_t raw; + uint8_t raw = 0; }; }; @@ -760,7 +762,7 @@ class Ndef { } TNF tnf_; - Flags flags_{0}; + Flags flags_{}; int id_{-1}; std::string type_{""}; std::string payload_{""}; diff --git a/components/qwiicnes/CMakeLists.txt b/components/qwiicnes/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/qwiicnes/CMakeLists.txt +++ b/components/qwiicnes/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/qwiicnes/example/main/qwiicnes_example.cpp b/components/qwiicnes/example/main/qwiicnes_example.cpp index 2d6cfbd0b..77ac31ca1 100644 --- a/components/qwiicnes/example/main/qwiicnes_example.cpp +++ b/components/qwiicnes/example/main/qwiicnes_example.cpp @@ -20,12 +20,13 @@ extern "C" void app_main(void) { .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); // now make the qwiicnes which decodes the data - espp::QwiicNes qwiicnes({.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .write_read = std::bind(&espp::I2c::read_at_register, &i2c, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3, std::placeholders::_4), - .log_level = espp::Logger::Verbosity::WARN}); + espp::QwiicNes qwiicnes( + {.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3), + .read_register = + std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .log_level = espp::Logger::Verbosity::WARN}); // and finally, make the task to periodically poll the qwiicnes and print // the state auto task_fn = [&quit_test, &qwiicnes](std::mutex &m, std::condition_variable &cv) { diff --git a/components/qwiicnes/include/qwiicnes.hpp b/components/qwiicnes/include/qwiicnes.hpp index afef45267..d3712baeb 100644 --- a/components/qwiicnes/include/qwiicnes.hpp +++ b/components/qwiicnes/include/qwiicnes.hpp @@ -2,7 +2,7 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /// @brief A class to interface with the Qwiic NES controller. @@ -13,26 +13,11 @@ namespace espp { /// /// \section Example /// \snippet qwiicnes_example.cpp qwiicnes example -class QwiicNes { +class QwiicNes : public BasePeripheral { public: /// @brief The default I2C address of the device. static constexpr uint8_t DEFAULT_ADDRESS = (0x54); - /// @brief The function to write data to the I2C bus. - /// @param address The I2C address of the device. - /// @param data The data to write to the I2C bus. - /// @param data_len The length of the data to write to the I2C bus. - /// @return true if the function succeeds. - typedef std::function write_fn; - - /// @brief The function to write then read data from the I2C bus. - /// @param address The I2C address of the device. - /// @param register The register to write to the I2C bus. - /// @param data The data to read from the I2C bus. - /// @param data_len The length of the data to read from the I2C bus. - /// @return true if the function succeeds. - typedef std::function write_read_fn; - /// @brief The buttons on the NES controller. /// @deftails The values in this enum match the button's corresponding bit /// field in the byte returned by read_current_state() and @@ -53,14 +38,15 @@ class QwiicNes { uint8_t left : 1; uint8_t right : 1; }; - uint8_t raw; + uint8_t raw = 0; }; }; /// @brief The configuration for the QwiicNes class. struct Config { - write_fn write; ///< The function to write data to the I2C bus. - write_read_fn write_read; ///< The function to write then read data from the I2C bus. + BasePeripheral::write_fn write; ///< The function to write data to the I2C bus. + BasePeripheral::read_register_fn + read_register; ///< The function to write then read data from the I2C bus. espp::Logger::Verbosity log_level{ espp::Logger::Verbosity::WARN}; ///< The log level for the class. }; @@ -68,8 +54,10 @@ class QwiicNes { /// @brief Construct a new QwiicNes object. /// @param config The configuration for the QwiicNes class. explicit QwiicNes(const Config &config) - : write_(config.write), write_read_(config.write_read), - logger_({.tag = "QwiicNes", .level = config.log_level}) {} + : BasePeripheral({.address = DEFAULT_ADDRESS, + .write = config.write, + .read_register = config.read_register}, + "QwiicNes", config.log_level) {} /// @brief Return true if the given button is pressed. /// @param state The byte returned by read_current_state(). @@ -126,8 +114,8 @@ class QwiicNes { /// function. /// The current state represents the buttons which are currently /// pressed. - uint8_t read_current_state(std::error_code &ec) const { - return read_register(Registers::CURRENT_STATE, ec); + uint8_t read_current_state(std::error_code &ec) { + return read_u8_from_register((uint8_t)Registers::CURRENT_STATE, ec); } /// @brief Read the current I2C address of the device. @@ -140,7 +128,9 @@ class QwiicNes { /// significant bit is set to 0. /// For example, if the I2C address is 0x54, then the value returned /// by this function will be 0xA8. - uint8_t read_address(std::error_code &ec) const { return read_register(Registers::ADDRESS, ec); } + uint8_t read_address(std::error_code &ec) { + return read_u8_from_register((uint8_t)Registers::ADDRESS, ec); + } /// @brief Update the I2C address of the device. /// @param new_address The new I2C address of the device. @@ -151,14 +141,13 @@ class QwiicNes { /// The 7-bit I2C address is shifted left by 1 bit and the least /// significant bit is set to 0. void update_address(uint8_t new_address, std::error_code &ec) { - uint8_t data[2] = {(uint8_t)Registers::CHANGE_ADDRESS, new_address}; - bool success = write_(address_, data, 2); - if (!success) { - logger_.error("failed to update address"); - ec = std::make_error_code(std::errc::io_error); + write_u8_to_register((uint8_t)Registers::CHANGE_ADDRESS, new_address, ec); + if (ec) { + logger_.error("failed to update address: {}", ec.message()); return; } - address_ = new_address; + // update the address in the class + base_config_.address = new_address; } protected: @@ -177,26 +166,12 @@ class QwiicNes { /// The button accumulator represents all buttons which have been /// pressed since the last time the accumulator was read - which is /// the last time the update() function or this function was called. - uint8_t read_button_accumulator(std::error_code &ec) const { - return read_register(Registers::ACCUMULATOR, ec); - } - - uint8_t read_register(Registers reg, std::error_code &ec) const { - uint8_t data; - bool success = write_read_(address_, (uint8_t)reg, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return data; + uint8_t read_button_accumulator(std::error_code &ec) { + return read_u8_from_register((uint8_t)Registers::ACCUMULATOR, ec); } - write_fn write_; - write_read_fn write_read_; uint8_t accumulated_states_{0}; - ButtonState button_state_{0}; - uint8_t address_{DEFAULT_ADDRESS}; - espp::Logger logger_; + ButtonState button_state_{}; }; } // namespace espp diff --git a/components/st25dv/CMakeLists.txt b/components/st25dv/CMakeLists.txt index 04ffe6760..814d4f0e5 100644 --- a/components/st25dv/CMakeLists.txt +++ b/components/st25dv/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" "ndef" + REQUIRES "base_peripheral" "ndef" ) diff --git a/components/st25dv/example/main/st25dv_example.cpp b/components/st25dv/example/main/st25dv_example.cpp index 70609035b..8e9af1003 100644 --- a/components/st25dv/example/main/st25dv_example.cpp +++ b/components/st25dv/example/main/st25dv_example.cpp @@ -69,13 +69,13 @@ extern "C" void app_main(void) { .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); + // now make the st25dv which decodes the data - espp::St25dv st25dv( - {.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - .read = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - .log_level = espp::Logger::Verbosity::DEBUG}); + espp::St25dv st25dv({.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .log_level = espp::Logger::Verbosity::DEBUG}); std::array programmed_data; std::error_code ec; @@ -130,10 +130,17 @@ extern "C" void app_main(void) { // and finally, make the task to periodically poll the st25dv and print the // state. The task will trigger sample quit when the phone reads the tag. auto task_fn = [&quit_test, &st25dv](std::mutex &m, std::condition_variable &cv) { + { + std::unique_lock lock(m); + cv.wait_for(lock, 30ms); + } std::error_code ec; auto it_sts = st25dv.get_interrupt_status(ec); if (ec) { fmt::print("Failed to get interrupt status: {}\n", ec.message()); + // wait a bit before trying again + std::unique_lock lock(m); + cv.wait_for(lock, 300ms); return false; } static auto last_it_sts = it_sts; @@ -141,8 +148,6 @@ extern "C" void app_main(void) { fmt::print("[{:.3f}] IT STS: {:02x}\n", elapsed(), it_sts); } last_it_sts = it_sts; - std::unique_lock lock(m); - cv.wait_for(lock, 10ms); // we don't want to stop the task, so return false return false; }; diff --git a/components/st25dv/example/sdkconfig.defaults b/components/st25dv/example/sdkconfig.defaults index ef8d3a465..5ed4913e9 100644 --- a/components/st25dv/example/sdkconfig.defaults +++ b/components/st25dv/example/sdkconfig.defaults @@ -16,7 +16,7 @@ CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 # SPI Flash Config: -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # Partition Table CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/components/st25dv/include/st25dv.hpp b/components/st25dv/include/st25dv.hpp index 918bcadf2..acffe4782 100644 --- a/components/st25dv/include/st25dv.hpp +++ b/components/st25dv/include/st25dv.hpp @@ -6,9 +6,8 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" #include "ndef.hpp" -#include "task.hpp" namespace espp { /** @@ -24,7 +23,7 @@ namespace espp { * \section st25dv_ex1 St25dv Example * \snippet st25dv_example.cpp st25dv example */ -class St25dv { +class St25dv : public BasePeripheral { public: // NOTE: when the datasheet mentions E2 device select, they are talking // about Bit 4 of the address which selects between the data (user memory, @@ -54,43 +53,22 @@ class St25dv { static constexpr int RF_WRITE = 0b10000000; ///< Write in eeprom }; - /** - * @brief Function to write bytes to St25dv. - * @param addr I2C address to write to - * @param data Data to be written. - * @param length Number of bytes to write. - * @return True if the write was successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read a sequence of bytes from St25dv. - * @param addr I2C address to read from - * @param reg_addr Start register address to read from. - * @param data Pointer to memory which will be filled with data read - * from St25dv. - * @param length Number of bytes to read - * @return True if the read was successful, false otherwise. - */ - typedef std::function - read_fn; - /** * @brief Configuration information for the St25dv. */ struct Config { - write_fn write; ///< Function to write to the device. - read_fn read; ///< Function to read from the device. + BasePeripheral::write_fn write; ///< Function to write to the device. + BasePeripheral::read_fn read; ///< Function to read from the device. bool auto_init{true}; ///< Automatically initialize the device. Logger::Verbosity log_level{Logger::Verbosity::WARN}; /**< Log verbosity for the component. */ }; /** - * @brief Construct the St25dv and start the update task. + * @brief Construct the St25dv with the provided configuration. */ explicit St25dv(const Config &config) - : write_(config.write), read_(config.read), - logger_({.tag = "St25dv", .level = config.log_level}) { + : BasePeripheral({.address = DATA_ADDRESS, .write = config.write, .read = config.read}, + "St25dv", config.log_level) { if (config.auto_init) { std::error_code ec; initialize(ec); @@ -115,9 +93,11 @@ class St25dv { */ uint8_t get_interrupt_status(std::error_code &ec) { uint8_t it_sts = 0; - bool success = read_(DATA_ADDRESS, (uint16_t)Registers::IT_STS, &it_sts, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + // NOTE: we have to use the underlying peripheral's functions since we + // have to use different device addresses for the different types of + // registers + read_syst_register(Registers::IT_STS, &it_sts, 1, ec); + if (ec) { return 0; } return it_sts; @@ -227,9 +207,11 @@ class St25dv { data[0] = (uint8_t)(AREA_1_START_ADDR >> 8); data[1] = (uint8_t)(AREA_1_START_ADDR & 0xFF); memcpy(&data[2], payload.data(), payload_size); - bool success = write_(DATA_ADDRESS, data, sizeof(data)); + bool success = base_config_.write(DATA_ADDRESS, data, sizeof(data)); if (!success) { ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); } } @@ -254,10 +236,8 @@ class St25dv { * reading. */ void read(uint8_t *data, uint8_t length, uint16_t offset, std::error_code &ec) { - bool success = read_(DATA_ADDRESS, AREA_1_START_ADDR + offset, data, length); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } + uint16_t reg = AREA_1_START_ADDR + offset; + read_data_register((Registers)reg, data, length, ec); } /** @@ -278,9 +258,11 @@ class St25dv { // data MB_CTRL::EN, }; - bool success = write_(DATA_ADDRESS, data, sizeof(data)); + bool success = base_config_.write(DATA_ADDRESS, data, sizeof(data)); if (!success) { ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); } } @@ -300,9 +282,11 @@ class St25dv { // data 0, }; - bool success = write_(DATA_ADDRESS, data, sizeof(data)); + bool success = base_config_.write(DATA_ADDRESS, data, sizeof(data)); if (!success) { ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); } } @@ -315,9 +299,8 @@ class St25dv { */ uint8_t get_ftm_length(std::error_code &ec) { uint8_t len = 0; - bool success = read_(DATA_ADDRESS, (uint16_t)Registers::MB_LEN, &len, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_data_register(Registers::MB_LEN, &len, 1, ec); + if (ec) { return 0; } return len; @@ -378,7 +361,7 @@ class St25dv { all_data[0] = (uint8_t)(FTM_START_ADDR >> 8); all_data[1] = (uint8_t)(FTM_START_ADDR & 0xFF); memcpy(&all_data[2], data, length); - bool success = write_(DATA_ADDRESS, all_data, length + 2); + bool success = base_config_.write(DATA_ADDRESS, all_data, length + 2); if (!success) { ec = std::make_error_code(std::errc::io_error); } @@ -390,17 +373,14 @@ class St25dv { void read_ftm(uint8_t *data, uint8_t length, uint8_t offset, std::error_code &ec) { // read can start from any byte offset within the FTM mailbox. - bool success = read_(DATA_ADDRESS, FTM_START_ADDR + offset, data, length); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - } + uint16_t reg = FTM_START_ADDR + offset; + read_data_register((Registers)reg, data, length, ec); } uint64_t read_uuid(std::error_code &ec) { uint8_t uuid[8]; - bool success = read_(SYST_ADDRESS, (uint16_t)Registers::UID, uuid, sizeof(uuid)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_syst_register(Registers::UID, uuid, sizeof(uuid), ec); + if (ec) { return 0; } memcpy(&uuid_, uuid, sizeof(uuid)); @@ -410,9 +390,8 @@ class St25dv { uint8_t read_block_size_bytes(std::error_code &ec) { uint8_t block_size_bytes = 0; - bool success = read_(SYST_ADDRESS, (uint16_t)Registers::MEM_SIZE, &block_size_bytes, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_syst_register(Registers::MEM_SIZE, &block_size_bytes, 1, ec); + if (ec) { return 0; } logger_.debug("Block size (B): {}", block_size_bytes); @@ -420,22 +399,21 @@ class St25dv { } uint16_t read_memory_size_blocks(std::error_code &ec) { - uint16_t memory_size_blocks = 0; - bool success = - read_(SYST_ADDRESS, (uint16_t)Registers::BLK_SIZE, (uint8_t *)&memory_size_blocks, 2); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + uint8_t buffer[2]; + read_syst_register(Registers::BLK_SIZE, buffer, 2, ec); + if (ec) { return 0; } + uint16_t memory_size_blocks = 0; + memory_size_blocks = (buffer[0] << 8) | buffer[1]; logger_.debug("Memory size (blocks): {}", memory_size_blocks); return memory_size_blocks; } uint64_t read_password(std::error_code &ec) { uint8_t pswds[8]; - bool success = read_(SYST_ADDRESS, (uint16_t)Registers::I2C_PWD, pswds, sizeof(pswds)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_syst_register(Registers::I2C_PWD, pswds, sizeof(pswds), ec); + if (ec) { return 0; } memcpy(&password_, pswds, sizeof(pswds)); @@ -458,7 +436,7 @@ class St25dv { data[2 + i + 13] = data[2 + i + 4]; } logger_.debug("Presenting password: {}\n", data); - bool success = write_(SYST_ADDRESS, data, sizeof(data)); + bool success = base_config_.write(SYST_ADDRESS, data, sizeof(data)); if (!success) { ec = std::make_error_code(std::errc::io_error); } @@ -595,11 +573,42 @@ class St25dv { 0x2008; /**< Start address of the Fast Transfer Mode Mailbox. */ static constexpr int FTM_SIZE = 0xFF; /**< Number of bytes in the Fast Transfer Mode Mailbox. */ - write_fn write_; - read_fn read_; + void read_syst_register(Registers reg, uint8_t *data, uint8_t length, std::error_code &ec) { + uint8_t reg_addr[2]; + reg_addr[0] = (uint16_t)reg >> 8; + reg_addr[1] = (uint16_t)reg & 0xFF; + bool success = base_config_.write(SYST_ADDRESS, reg_addr, 2); + if (!success) { + ec = std::make_error_code(std::errc::io_error); + return; + } + success = base_config_.read(SYST_ADDRESS, data, length); + if (!success) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } + + void read_data_register(Registers reg, uint8_t *data, uint8_t length, std::error_code &ec) { + uint8_t reg_addr[2]; + reg_addr[0] = (uint16_t)reg >> 8; + reg_addr[1] = (uint16_t)reg & 0xFF; + bool success = base_config_.write(DATA_ADDRESS, reg_addr, 2); + if (!success) { + ec = std::make_error_code(std::errc::io_error); + return; + } + success = base_config_.read(DATA_ADDRESS, data, length); + if (!success) { + ec = std::make_error_code(std::errc::io_error); + } else { + ec.clear(); + } + } + uint32_t memory_size_bytes_; uint64_t uuid_; uint64_t password_; - Logger logger_; }; } // namespace espp diff --git a/components/t_keyboard/CMakeLists.txt b/components/t_keyboard/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/t_keyboard/CMakeLists.txt +++ b/components/t_keyboard/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/t_keyboard/include/t_keyboard.hpp b/components/t_keyboard/include/t_keyboard.hpp index 126154be0..1c3a5d829 100644 --- a/components/t_keyboard/include/t_keyboard.hpp +++ b/components/t_keyboard/include/t_keyboard.hpp @@ -4,7 +4,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" #include "task.hpp" namespace espp { @@ -16,27 +16,11 @@ namespace espp { /// /// \section Example /// \snippet t_keyboard_example.cpp tkeyboard example -class TKeyboard { +class TKeyboard : public BasePeripheral { public: /// The default address of the keyboard. static constexpr uint8_t DEFAULT_ADDRESS = 0x55; - /// \brief The function signature for the write function. - /// \details This function is used to write data to the keyboard. - /// \param address The address to write to. - /// \param data The data to write. - /// \param size The size of the data to write. - /// \return True if the write was successful, false otherwise. - typedef std::function write_fn; - - /// \brief The function signature for the read function. - /// \details This function is used to read data from the keyboard. - /// \param address The address to read from. - /// \param data The data to read into. - /// \param size The size of the data to read. - /// \return True if the read was successful, false otherwise. - typedef std::function read_fn; - /// \brief The function signature for the key callback function. /// \details This function is called when a key is pressed. /// \param key The key that was pressed. @@ -45,10 +29,10 @@ class TKeyboard { /// The configuration structure for the keyboard. struct Config { /// The write function to use. - write_fn write; + BasePeripheral::write_fn write; /// The read function to use. - read_fn read; + BasePeripheral::read_fn read; /// The key callback function to use. This function will be called when a /// key is pressed if it is not null and the keyboard task is running. @@ -70,9 +54,10 @@ class TKeyboard { /// \brief Constructor for the TKeyboard class. /// \param config The configuration to use. explicit TKeyboard(const Config &config) - : write_(config.write), read_(config.read), key_cb_(config.key_cb), address_(config.address), - polling_interval_(config.polling_interval), - logger_({.tag = "TKeyboard", .level = config.log_level}) { + : BasePeripheral({.address = config.address, .write = config.write, .read = config.read}, + "TKeyboard", config.log_level) + , key_cb_(config.key_cb) + , polling_interval_(config.polling_interval) { logger_.info("TKeyboard created"); task_ = std::make_shared(espp::Task::Config{ .name = "tkeyboard_task", @@ -105,7 +90,7 @@ class TKeyboard { ec = std::make_error_code(std::errc::operation_in_progress); return 0; } - return read_char(ec); + return read_u8(ec); } /// \brief Start the keyboard task. @@ -126,7 +111,7 @@ class TKeyboard { bool key_task(std::mutex &m, std::condition_variable &cv) { std::error_code ec; auto start_time = std::chrono::steady_clock::now(); - auto key = read_char(ec); + auto key = read_u8(ec); if (!ec) { pressed_key_ = key; if (key != 0 && key_cb_) { @@ -143,35 +128,9 @@ class TKeyboard { return false; } - uint8_t read_char(std::error_code &ec) { - uint8_t data = 0; - read(&data, 1, ec); - if (ec) { - logger_.error("Failed to read char: {}", ec.message()); - return 0; - } - return data; - } - - void write(uint8_t *data, size_t size, std::error_code &ec) { - if (!write_(address_, data, size)) { - ec = std::make_error_code(std::errc::io_error); - } - } - - void read(uint8_t *data, size_t size, std::error_code &ec) { - if (!read_(address_, data, size)) { - ec = std::make_error_code(std::errc::io_error); - } - } - - write_fn write_; - read_fn read_; key_cb_fn key_cb_; - uint8_t address_; std::atomic pressed_key_{0}; std::chrono::milliseconds polling_interval_; std::shared_ptr task_; - espp::Logger logger_; }; } // namespace espp diff --git a/components/tla2528/CMakeLists.txt b/components/tla2528/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/tla2528/CMakeLists.txt +++ b/components/tla2528/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/tla2528/example/main/tla2528_example.cpp b/components/tla2528/example/main/tla2528_example.cpp index dcb09a425..944767c9c 100644 --- a/components/tla2528/example/main/tla2528_example.cpp +++ b/components/tla2528/example/main/tla2528_example.cpp @@ -23,9 +23,10 @@ extern "C" void app_main(void) { .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); - static auto NTC_CHANNEL = espp::Tla2528::Channel::CH1; - static auto X_CHANNEL = espp::Tla2528::Channel::CH6; - static auto Y_CHANNEL = espp::Tla2528::Channel::CH7; + static std::vector channels = { + espp::Tla2528::Channel::CH0, espp::Tla2528::Channel::CH1, espp::Tla2528::Channel::CH2, + espp::Tla2528::Channel::CH3, espp::Tla2528::Channel::CH4, espp::Tla2528::Channel::CH5, + espp::Tla2528::Channel::CH6, espp::Tla2528::Channel::CH7}; // make the actual tla class espp::Tla2528 tla(espp::Tla2528::Config{ @@ -33,7 +34,7 @@ extern "C" void app_main(void) { // of 0x10 becomes 0x16 .device_address = espp::Tla2528::DEFAULT_ADDRESS | 0x06, .mode = espp::Tla2528::Mode::AUTO_SEQ, - .analog_inputs = {NTC_CHANNEL, X_CHANNEL, Y_CHANNEL}, + .analog_inputs = channels, .digital_inputs = {}, .digital_outputs = {}, // enable oversampling / averaging @@ -54,16 +55,16 @@ extern "C" void app_main(void) { // get the analog input data individually; NOTE: this only works if you have configured the // TLA2528 to use MANUAL mode - // auto ntc_mv = tla.get_mv(NTC_CHANNEL); - // auto x_mv = tla.get_mv(X_CHANNEL); - // auto y_mv = tla.get_mv(Y_CHANNEL); + // auto ch0_mv = tla.get_mv(channels[0]); + // auto ch1_mv = tla.get_mv(channels[1]); + // auto ch2_mv = tla.get_mv(channels[2]); // Could also read them all at once; NOTE: this only works if you have configured the // TLA2528 to use AUTO_SEQ mode (which is more efficient) // auto all_mv = tla.get_all_mv(); - // auto ntc_mv = all_mv[0]; - // auto x_mv = all_mv[1]; - // auto y_mv = all_mv[2]; + // auto ch0_mv = all_mv[0]; + // auto ch1_mv = all_mv[1]; + // auto ch2_mv = all_mv[2]; // Could also use the mapped version; NOTE: this only works if you have configured the // TLA2528 to use AUTO_SEQ mode (which is more efficient) @@ -73,13 +74,15 @@ extern "C" void app_main(void) { logger.error("error reading TLA2528: {}", ec.message()); return false; } - auto ntc_mv = all_mv_map[NTC_CHANNEL]; - auto x_mv = all_mv_map[X_CHANNEL]; - auto y_mv = all_mv_map[Y_CHANNEL]; // use fmt to print so it doesn't have the prefix and can be used more // easily as CSV (for plotting using uart_serial_plotter) - fmt::print("{:.3f}, {:.3f}, {:.3f}, {:.3f}\n", elapsed, ntc_mv, x_mv, y_mv); + fmt::print("{:.3f}", elapsed); + for (auto &[ch, mv] : all_mv_map) { + fmt::print(", {:.1f}", mv); + } + fmt::print("\n"); + // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { diff --git a/components/tla2528/include/tla2528.hpp b/components/tla2528/include/tla2528.hpp index c661803a0..0cc8700e6 100644 --- a/components/tla2528/include/tla2528.hpp +++ b/components/tla2528/include/tla2528.hpp @@ -8,7 +8,7 @@ #include #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /** @@ -25,30 +25,12 @@ namespace espp { * \section tla2528_ex1 TLA2528 Example * \snippet tla2528_example.cpp tla2528 example */ -class Tla2528 { +class Tla2528 : public BasePeripheral { public: static constexpr uint8_t DEFAULT_ADDRESS = (0x10); ///< Default I2C address of the device (when both R1 and R2 are DNP) (see data sheet ///< Table 2, p. 16) - /** - * @brief Function to write bytes to the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to write. - * @param data_len Number of data bytes to write. - * @return True if the write was successful, false otherwise. - */ - typedef std::function write_fn; - - /** - * @brief Function to read bytes from the device. - * @param dev_addr Address of the device to write to. - * @param data Pointer to array of bytes to read into. - * @param data_len Number of data bytes to read. - * @return True if the read was successful, false otherwise. - */ - typedef std::function read_fn; - /// @brief Possible oversampling ratios, see data sheet Table 15 (p. 34) enum class OversamplingRatio : uint8_t { NONE = 0, ///< No oversampling @@ -144,11 +126,11 @@ class Tla2528 { ///< the default value is false in open-drain ///< mode. OversamplingRatio oversampling_ratio = OversamplingRatio::NONE; ///< Oversampling ratio to use. - Append append = Append::NONE; ///< What data to append to samples when reading analog inputs. - write_fn write; ///< Function to write to the ADC - read_fn read; ///< Function to read from the ADC - bool auto_init = true; ///< Automatically initialize the ADC on construction. If false, - ///< initialize() must be called before any other functions. + Append append = Append::NONE; ///< What data to append to samples when reading analog inputs. + BasePeripheral::write_fn write; ///< Function to write to the ADC + BasePeripheral::read_fn read; ///< Function to read from the ADC + bool auto_init = true; ///< Automatically initialize the ADC on construction. If false, + ///< initialize() must be called before any other functions. espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< Verbosity for the logger. }; @@ -157,15 +139,19 @@ class Tla2528 { * @param config Configuration structure. */ explicit Tla2528(const Config &config) - : config_(config), mode_(config.mode), avdd_mv_(config.avdd_volts * 1000.0f) // Convert to mV - , - data_format_(config.oversampling_ratio == OversamplingRatio::NONE ? DataFormat::RAW - : DataFormat::AVERAGED), - append_(config.append), analog_inputs_(config.analog_inputs), - digital_inputs_(config.digital_inputs), digital_outputs_(config.digital_outputs), - oversampling_ratio_(config.oversampling_ratio), address_(config.device_address), - write_(config.write), read_(config.read), - logger_({.tag = "Tla2528", .level = config.log_level}) { + : BasePeripheral( + {.address = config.device_address, .write = config.write, .read = config.read}, + "Tla2528", config.log_level) + , config_(config) + , mode_(config.mode) + , avdd_mv_(config.avdd_volts * 1000.0f) + , data_format_(config.oversampling_ratio == OversamplingRatio::NONE ? DataFormat::RAW + : DataFormat::AVERAGED) + , append_(config.append) + , analog_inputs_(config.analog_inputs) + , digital_inputs_(config.digital_inputs) + , digital_outputs_(config.digital_outputs) + , oversampling_ratio_(config.oversampling_ratio) { num_bytes_per_sample_ = 2; if (data_format_ == DataFormat::AVERAGED && append_ == Append::CHANNEL_ID) { num_bytes_per_sample_ = 3; @@ -205,16 +191,15 @@ class Tla2528 { * configured as an analog input. */ float get_mv(Channel channel, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); // we need to trigger a conversion and then read the result trigger_conversion(channel, ec); if (ec) { return 0.0f; } uint8_t data[num_bytes_per_sample_]; - bool success = read_(address_, data, num_bytes_per_sample_); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_many(data, num_bytes_per_sample_, ec); + if (ec) { return 0.0f; } uint16_t raw = parse_frame(data); @@ -237,7 +222,7 @@ class Tla2528 { * ADC's buffer (blocking until conversion is complete). */ std::vector get_all_mv(std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); // TODO: handle the non-autonomous case auto raw_values = read_all(ec); if (ec) { @@ -263,7 +248,7 @@ class Tla2528 { * ADC's buffer (blocking until conversion is complete). */ std::unordered_map get_all_mv_map(std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); std::unordered_map values; // TODO: handle the non-autonomous case auto raw_values = read_all_map(ec); @@ -283,7 +268,7 @@ class Tla2528 { /// @param ec Error code to set if an error occurs. /// @note The channel must have been configured as a digital output. void set_digital_output_mode(Channel channel, OutputMode output_mode, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); if (!is_digital_output(channel)) { logger_.error("Channel {} is not configured as a digital output", channel); ec = std::make_error_code(std::errc::invalid_argument); @@ -306,7 +291,7 @@ class Tla2528 { * @note The channel must have been configured as a digital output. */ void set_digital_output_value(Channel channel, bool value, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); if (!is_digital_output(channel)) { logger_.error("Channel {} is not configured as a digital output", channel); ec = std::make_error_code(std::errc::invalid_argument); @@ -327,7 +312,7 @@ class Tla2528 { * @note The channel must have been configured as a digital input. */ bool get_digital_input_value(Channel channel, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); if (!is_digital_input(channel)) { logger_.error("Channel {} is not configured as a digital input", channel); ec = std::make_error_code(std::errc::invalid_argument); @@ -357,7 +342,7 @@ class Tla2528 { /// @note This will reset all registers to their default values (converting /// all channels to analog inputs and disabling all events). void reset(std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); // reset the device write_one_(Register::GENERAL_CFG, SW_RST, ec); if (ec) { @@ -685,9 +670,8 @@ class Tla2528 { if (ec) { return {}; } - bool success = read_(address_, raw_values, num_bytes); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + read_many(raw_values, num_bytes, ec); + if (ec) { return {}; } // stop the auto conversion sequence @@ -841,29 +825,15 @@ class Tla2528 { return std::find(analog_inputs_.begin(), analog_inputs_.end(), channel) != analog_inputs_.end(); } - uint8_t read_only_(std::error_code &ec) { - std::lock_guard lock(mutex_); - uint8_t val; - bool success = read_(address_, &val, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - return val; - } + // NOTE: this chip has specific read and write operation commands that are + // used, so we don't use the subclass's read_* and write_* methods directly uint8_t read_one_(Register reg, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); + uint8_t data = 0; uint8_t read_one_command[] = {OP_READ_ONE, (uint8_t)reg}; - bool success = write_(address_, read_one_command, sizeof(read_one_command)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return 0; - } - uint8_t data; - success = read_(address_, &data, 1); - if (!success) { - ec = std::make_error_code(std::errc::io_error); + write_then_read(read_one_command, sizeof(read_one_command), &data, 1, ec); + if (ec) { return 0; } return data; @@ -871,7 +841,7 @@ class Tla2528 { uint16_t read_two_(Register reg, std::error_code &ec) { uint8_t data[2]; - read_many_(reg, data, 2, ec); + read_block_(reg, data, 2, ec); if (ec) { return 0; } @@ -881,67 +851,42 @@ class Tla2528 { return (data[1] << 8) | data[0]; } - void read_many_(Register reg, uint8_t *data, uint8_t len, std::error_code &ec) { - std::lock_guard lock(mutex_); + void read_block_(Register reg, uint8_t *data, uint8_t len, std::error_code &ec) { + std::lock_guard lock(base_mutex_); uint8_t read_block_command[] = {OP_READ_BLOCK, (uint8_t)reg}; - bool success = write_(address_, read_block_command, sizeof(read_block_command)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } - success = read_(address_, data, len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } + write_then_read(read_block_command, sizeof(read_block_command), data, len, ec); } void set_bits_(Register reg, uint8_t bit, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_SET_BITS, (uint8_t)reg, bit}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void clear_bits_(Register reg, uint8_t bit, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_CLR_BITS, (uint8_t)reg, bit}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void write_one_(Register reg, uint8_t value, std::error_code &ec) { - std::lock_guard lock(mutex_); + std::lock_guard lock(base_mutex_); uint8_t data[] = {OP_WRITE_ONE, (uint8_t)reg, value}; - bool success = write_(address_, data, sizeof(data)); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data, sizeof(data), ec); } void write_two_(Register reg, uint16_t value, std::error_code &ec) { - write_many_(reg, (uint8_t *)&value, 2, ec); + write_block_(reg, (uint8_t *)&value, 2, ec); } - void write_many_(Register reg, const uint8_t *data, uint8_t len, std::error_code &ec) { - std::lock_guard lock(mutex_); + void write_block_(Register reg, const uint8_t *data, uint8_t len, std::error_code &ec) { + std::lock_guard lock(base_mutex_); uint8_t total_len = len + 2; uint8_t data_with_header[total_len]; data_with_header[0] = OP_WRITE_BLOCK; data_with_header[1] = (uint8_t)reg; memcpy(data_with_header + 2, data, len); - bool success = write_(address_, data_with_header, total_len); - if (!success) { - ec = std::make_error_code(std::errc::io_error); - return; - } + write_many(data_with_header, total_len, ec); } Config config_; @@ -955,11 +900,6 @@ class Tla2528 { std::vector digital_inputs_; std::vector digital_outputs_; OversamplingRatio oversampling_ratio_; - uint8_t address_; - write_fn write_; - read_fn read_; - std::recursive_mutex mutex_; ///< mutex for thread safety - espp::Logger logger_; }; } // namespace espp diff --git a/components/tt21100/CMakeLists.txt b/components/tt21100/CMakeLists.txt index 17999ceee..d43ade275 100644 --- a/components/tt21100/CMakeLists.txt +++ b/components/tt21100/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" - REQUIRES "logger" + REQUIRES "base_peripheral" ) diff --git a/components/tt21100/include/tt21100.hpp b/components/tt21100/include/tt21100.hpp index 143551546..1fe00e270 100644 --- a/components/tt21100/include/tt21100.hpp +++ b/components/tt21100/include/tt21100.hpp @@ -2,41 +2,30 @@ #include -#include "logger.hpp" +#include "base_peripheral.hpp" namespace espp { /// @brief Driver for the Tt21100 touch controller /// /// \section Example /// \snippet tt21100_example.cpp tt21100 example -class Tt21100 { +class Tt21100 : public BasePeripheral { public: /// @brief The default i2c address static constexpr uint8_t DEFAULT_ADDRESS = (0x24); - /// @brief Function for writing to the i2c device - /// @param address The address of the i2c device - /// @param data The data to write to the chip - /// @param len The length of the data to write - typedef std::function write_fn; - - /// @brief Function signature for reading from the i2c device - /// @param dev_addr The device address - /// @param data The data to read - /// @param data_len The length of the data to read - typedef std::function read_fn; - /// @brief Configuration for the Tt21100 driver struct Config { - write_fn write = nullptr; ///< Function for writing to the i2c device (unused) - read_fn read; ///< Function for reading from the i2c device + BasePeripheral::write_fn write = nullptr; ///< Function for writing to the i2c device (unused) + BasePeripheral::read_fn read; ///< Function for reading from the i2c device espp::Logger::Verbosity log_level{espp::Logger::Verbosity::WARN}; ///< Log level }; /// @brief Constructor /// @param config The configuration for the driver explicit Tt21100(const Config &config) - : read_(config.read), logger_({.tag = "Tt21100", .level = config.log_level}) { + : BasePeripheral({.address = DEFAULT_ADDRESS, .read = config.read}, "Tt21100", + config.log_level) { std::error_code ec; init(ec); if (ec) { @@ -51,25 +40,28 @@ class Tt21100 { static uint16_t data_len; static uint8_t data[256]; - bool success = read_(DEFAULT_ADDRESS, (uint8_t *)&data_len, sizeof(data_len)); - if (!success) { - logger_.error("Failed to read data length"); - ec = std::make_error_code(std::errc::io_error); + // NOTE: this chip is weird, and even though we're reading a u16, we can't + // use the read_u16 since that function assumes the data is in little + // endian format, but this chip sends the data in big endian format meaning + // the bytes are swapped + + read_many((uint8_t *)&data_len, 2, ec); + if (ec) { + logger_.error("Failed to read data length: {}", ec.message()); return false; } logger_.debug("Data length: {}", data_len); - if (data_len == 0xff) { + if (data_len >= 0xff) { logger_.error("Invalid data length"); ec = std::make_error_code(std::errc::io_error); return false; } - success = read_(DEFAULT_ADDRESS, data, data_len); - if (!success) { + read_many(data, data_len, ec); + if (ec) { logger_.error("Failed to read data"); - ec = std::make_error_code(std::errc::io_error); return false; } @@ -137,17 +129,21 @@ class Tt21100 { void init(std::error_code &ec) { logger_.debug("Initializing..."); uint16_t reg_val = 0; + static constexpr int max_tries = 10; + int num_tries = 0; do { using namespace std::chrono_literals; - bool success = read_(DEFAULT_ADDRESS, (uint8_t *)®_val, 2); - if (!success) { + read_many((uint8_t *)®_val, 2, ec); + if (ec) { logger_.error("Failed to read..."); - ec = std::make_error_code(std::errc::io_error); return; } logger_.debug("reg_val: {:#04x}", reg_val); std::this_thread::sleep_for(20ms); - } while (0x0002 != reg_val); + } while (0x0002 != reg_val && ++num_tries < max_tries); + if (num_tries >= max_tries) { + logger_.warn("Reached max tries trying to read..."); + } } enum class Registers : uint8_t { @@ -190,11 +186,9 @@ class Tt21100 { uint16_t btn_signal[4]; } __attribute__((packed)); - read_fn read_; std::atomic home_button_pressed_{false}; std::atomic num_touch_points_; std::atomic x_; std::atomic y_; - espp::Logger logger_; }; } // namespace espp diff --git a/doc/Doxyfile b/doc/Doxyfile index 32a7edceb..54a44abcf 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -85,6 +85,7 @@ INPUT += $(PROJECT_PATH)/components/ads7138/include/ads7138.hpp INPUT += $(PROJECT_PATH)/components/as5600/include/as5600.hpp INPUT += $(PROJECT_PATH)/components/aw9523/include/aw9523.hpp INPUT += $(PROJECT_PATH)/components/base_component/include/base_component.hpp +INPUT += $(PROJECT_PATH)/components/base_peripheral/include/base_peripheral.hpp INPUT += $(PROJECT_PATH)/components/bldc_driver/include/bldc_driver.hpp INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/bldc_haptics.hpp INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/detent_config.hpp diff --git a/doc/en/base_peripheral.rst b/doc/en/base_peripheral.rst new file mode 100644 index 000000000..cf48e7675 --- /dev/null +++ b/doc/en/base_peripheral.rst @@ -0,0 +1,20 @@ +Base Peripheral +*************** + +The `espp::BasePeripheral` class is a base class for all peripherals. It +provides a common interface for all peripherals and is used to access the +peripheral's registers. It is primarily designed to be used as a base class for +peripheral classes that communicate using I2C (address-based) or SPI (CS-based) +protocols. + +The base class provides an interface for specifying different communications +functions that the peripheral may use, as well as providing some base +implementations for common functionality such as reading / writing u8 and u16 +values from / to a register. + +.. ---------------------------- API Reference ---------------------------------- + +API Reference +------------- + +.. include-build-file:: inc/base_peripheral.inc diff --git a/doc/en/index.rst b/doc/en/index.rst index 85689db81..706501485 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -12,6 +12,7 @@ This is the documentation for esp-idf c++ components, ESPP (`espp
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -147,7 +148,7 @@
  • ADC APIs »
  • ADC Types
  • - Edit on GitHub + Edit on GitHub

  • diff --git a/docs/adc/ads1x15.html b/docs/adc/ads1x15.html index e1970fa6a..a1bbb80a8 100644 --- a/docs/adc/ads1x15.html +++ b/docs/adc/ads1x15.html @@ -92,6 +92,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -148,7 +149,7 @@
  • ADC APIs »
  • ADS1x15 I2C ADC
  • - Edit on GitHub + Edit on GitHub

  • @@ -165,14 +166,14 @@

    API Reference

    Header File

    Classes

    -class espp::Ads1x15
    +class espp::Ads1x15 : public espp::BasePeripheral

    Class for reading values from the ADS1x15 family of ADC chips.

    ADS1X15 Example

    @@ -185,10 +186,11 @@

    ADS1X15 Example // make the actual ads class espp::Ads1x15 ads(espp::Ads1x15::Ads1015Config{ .device_address = espp::Ads1x15::DEFAULT_ADDRESS, - .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - .read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)}); + .write = [&i2c](uint8_t addr, const uint8_t *data, + size_t len) { return i2c.write(addr, data, len); }, + .read = [&i2c](uint8_t addr, uint8_t *data, + size_t len) { return i2c.read(addr, data, len); }, + }); // make the task which will get the raw data from the I2C ADC auto ads_read_task_fn = [&ads](std::mutex &m, std::condition_variable &cv) { static auto start = std::chrono::high_resolution_clock::now(); @@ -374,42 +376,105 @@

    ADS1X15 Example +
    +typedef std::function<bool(uint8_t)> probe_fn
    +

    Function to probe the peripheral

    +
    +
    Param address
    +

    The address to probe

    +
    +
    Return
    +

    True if the peripheral is found at the given address

    +
    +
    +

    +
    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> write_fn
    -

    Function to write bytes to the device.

    +typedef std::function<bool(uint8_t, const uint8_t*, size_t)> write_fn
    +

    Function to write data to the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to write to

    Param data
    -

    Pointer to array of bytes to write.

    +

    The data to write

    -
    Param data_len
    -

    Number of data bytes to write.

    +
    Param length
    +

    The length of the data to write

    Return
    -

    True if the write was successful.

    +

    True if the write was successful

    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> read_fn
    -

    Function to read bytes from the device.

    +typedef std::function<bool(uint8_t, uint8_t*, size_t)> read_fn
    +

    Function to read data from the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to read from

    Param data
    -

    Pointer to array of bytes to read into.

    +

    The buffer to read into

    +
    +
    Param length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, uint8_t, uint8_t*, size_t)> read_register_fn
    +

    Function to read data at a specific address from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to read from

    +
    +
    Param reg_addr
    +

    The address of the register to read from

    +
    +
    Param data
    +

    The buffer to read into

    -
    Param data_len
    -

    Number of data bytes to read.

    +
    Param length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, const uint8_t*, size_t, uint8_t*, size_t)> write_then_read_fn
    +

    Function to write then read data from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to write to

    +
    +
    Param write_data
    +

    The data to write

    +
    +
    Param write_length
    +

    The length of the data to write

    +
    +
    Param read_data
    +

    The buffer to read into

    +
    +
    Param read_length
    +

    The length of the buffer

    Return
    -

    True if the read was successful.

    +

    True if the write then read was successful

    @@ -456,6 +521,82 @@

    ADS1X15 Example +
    +inline void set_log_tag(const std::string_view &tag)
    +

    Set the tag for the logger

    +
    +
    Parameters
    +

    tag – The tag to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_level(Logger::Verbosity level)
    +

    Set the log level for the logger

    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_verbosity(Logger::Verbosity level)
    +

    Set the log verbosity for the logger

    +

    See also

    +

    set_log_level

    +
    +
    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +

    Note

    +

    This is a convenience method that calls set_log_level

    +
    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
    +

    Set the rate limit for the logger

    +

    +
    +

    Note

    +

    Only calls to the logger that have _rate_limit suffix will be rate limited

    +
    +
    +
    Parameters
    +

    rate_limit – The rate limit to use for the logger

    +
    +
    +
    +

    Public Static Attributes

    @@ -480,13 +621,13 @@

    ADS1X15 Example
    -write_fn write
    +BasePeripheral::write_fn write

    Function to write to the ADC.

    -read_fn read
    +BasePeripheral::read_fn read

    Function to read from the ADC.

    @@ -525,13 +666,13 @@

    ADS1X15 Example
    -write_fn write
    +BasePeripheral::write_fn write

    Function to write to the ADC.

    -read_fn read
    +BasePeripheral::read_fn read

    Function to read from the ADC.

    diff --git a/docs/adc/ads7138.html b/docs/adc/ads7138.html index fee30e20e..ce8efefa7 100644 --- a/docs/adc/ads7138.html +++ b/docs/adc/ads7138.html @@ -92,6 +92,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -148,7 +149,7 @@
  • ADC APIs »
  • ADS7138 I2C ADC
  • - Edit on GitHub + Edit on GitHub

  • @@ -170,14 +171,14 @@

    API Reference

    Header File

    Classes

    -class espp::Ads7138
    +class espp::Ads7138 : public espp::BasePeripheral

    Class for reading values from the ADS7138 family of ADC chips.

    The ADS7138 is a 16-bit, 8-channel ADC with 8 digital I/O pins. It supports a variety of sampling modes, including autonomous sampling, manual sampling, and auto sequence sampling. It also supports oversampling ratios of 2, 4, 8, 16, 32, 64, and 128. It additionally allows the user to configure the analog or digital inputs to trigger an alert when the value goes above or below a threshold (enter or leave a region of voltage).

    See also

    @@ -641,42 +642,105 @@

    ADS7138 Example +
    +typedef std::function<bool(uint8_t)> probe_fn
    +

    Function to probe the peripheral

    +
    +
    Param address
    +

    The address to probe

    +
    +
    Return
    +

    True if the peripheral is found at the given address

    +
    +
    +

    +
    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> write_fn
    -

    Function to write bytes to the device.

    +typedef std::function<bool(uint8_t, const uint8_t*, size_t)> write_fn
    +

    Function to write data to the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to write to

    Param data
    -

    Pointer to array of bytes to write.

    +

    The data to write

    -
    Param data_len
    -

    Number of data bytes to write.

    +
    Param length
    +

    The length of the data to write

    Return
    -

    True if successful, false otherwise.

    +

    True if the write was successful

    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> read_fn
    -

    Function to read bytes from the device.

    +typedef std::function<bool(uint8_t, uint8_t*, size_t)> read_fn
    +

    Function to read data from the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to read from

    Param data
    -

    Pointer to array of bytes to read into.

    +

    The buffer to read into

    -
    Param data_len
    -

    Number of data bytes to read.

    +
    Param length
    +

    The length of the buffer

    Return
    -

    True if successful, false otherwise.

    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, uint8_t, uint8_t*, size_t)> read_register_fn
    +

    Function to read data at a specific address from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to read from

    +
    +
    Param reg_addr
    +

    The address of the register to read from

    +
    +
    Param data
    +

    The buffer to read into

    +
    +
    Param length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, const uint8_t*, size_t, uint8_t*, size_t)> write_then_read_fn
    +

    Function to write then read data from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to write to

    +
    +
    Param write_data
    +

    The data to write

    +
    +
    Param write_length
    +

    The length of the data to write

    +
    +
    Param read_data
    +

    The buffer to read into

    +
    +
    Param read_length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the write then read was successful

    @@ -1034,6 +1098,10 @@

    ADS7138 ExampleNote

    This will reset all registers to their default values (converting all channels to analog inputs and disabling all events).

    +
    +

    Note

    +

    If the write is successful, the function will wait for the reset to complete before returning

    +
    Parameters

    ec – Error code to set if an error occurs.

    @@ -1041,6 +1109,82 @@

    ADS7138 Example +
    +inline void set_log_tag(const std::string_view &tag)
    +

    Set the tag for the logger

    +
    +
    Parameters
    +

    tag – The tag to use for the logger

    +
    +
    +

    + +
    +
    +inline void set_log_level(Logger::Verbosity level)
    +

    Set the log level for the logger

    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_verbosity(Logger::Verbosity level)
    +

    Set the log verbosity for the logger

    +

    See also

    +

    set_log_level

    +
    +
    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +

    Note

    +

    This is a convenience method that calls set_log_level

    +
    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
    +

    Set the rate limit for the logger

    +

    +
    +

    Note

    +

    Only calls to the logger that have _rate_limit suffix will be rate limited

    +
    +
    +
    Parameters
    +

    rate_limit – The rate limit to use for the logger

    +
    +
    +
    +

    Public Static Attributes

    @@ -1119,13 +1263,13 @@

    ADS7138 Example
    -write_fn write
    +BasePeripheral::write_fn write

    Function to write to the ADC.

    -read_fn read
    +BasePeripheral::read_fn read

    Function to read from the ADC.

    diff --git a/docs/adc/continuous_adc.html b/docs/adc/continuous_adc.html index 30e5e1de5..db1994f30 100644 --- a/docs/adc/continuous_adc.html +++ b/docs/adc/continuous_adc.html @@ -92,6 +92,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -148,7 +149,7 @@
  • ADC APIs »
  • Continuous ADC
  • - Edit on GitHub + Edit on GitHub

  • @@ -170,7 +171,7 @@

    API Reference

    Header File

    diff --git a/docs/adc/index.html b/docs/adc/index.html index 625745817..9173a24ca 100644 --- a/docs/adc/index.html +++ b/docs/adc/index.html @@ -85,6 +85,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -140,7 +141,7 @@
  • »
  • ADC APIs
  • - Edit on GitHub + Edit on GitHub

  • diff --git a/docs/adc/oneshot_adc.html b/docs/adc/oneshot_adc.html index c5213c5ed..2e4740933 100644 --- a/docs/adc/oneshot_adc.html +++ b/docs/adc/oneshot_adc.html @@ -92,6 +92,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -148,7 +149,7 @@
  • ADC APIs »
  • Oneshot ADC
  • - Edit on GitHub + Edit on GitHub

  • @@ -169,7 +170,7 @@

    API Reference

    Header File

    diff --git a/docs/adc/tla2528.html b/docs/adc/tla2528.html index 92b911a5f..43982e2fa 100644 --- a/docs/adc/tla2528.html +++ b/docs/adc/tla2528.html @@ -92,6 +92,7 @@
  • Base Compoenent
  • +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -148,7 +149,7 @@
  • ADC APIs »
  • TLA2528 I2C ADC
  • - Edit on GitHub + Edit on GitHub

  • @@ -170,14 +171,14 @@

    API Reference

    Header File

    Classes

    -class espp::Tla2528
    +class espp::Tla2528 : public espp::BasePeripheral

    Class for reading values from the TLA2528 family of ADC chips.

    The TLA2528 is a 16-bit, 8-channel ADC with 8 digital I/O pins. It supports a variety of sampling modes, including manual sampling, and auto sequence sampling. It also supports oversampling ratios of 2, 4, 8, 16, 32, 64, and 128. It additionally allows the user to configure the analog or digital inputs to trigger an alert when the value goes above or below a threshold (enter or leave a region of voltage).

    See also

    @@ -193,9 +194,10 @@

    TLA2528 Example .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); - static auto NTC_CHANNEL = espp::Tla2528::Channel::CH1; - static auto X_CHANNEL = espp::Tla2528::Channel::CH6; - static auto Y_CHANNEL = espp::Tla2528::Channel::CH7; + static std::vector<espp::Tla2528::Channel> channels = { + espp::Tla2528::Channel::CH0, espp::Tla2528::Channel::CH1, espp::Tla2528::Channel::CH2, + espp::Tla2528::Channel::CH3, espp::Tla2528::Channel::CH4, espp::Tla2528::Channel::CH5, + espp::Tla2528::Channel::CH6, espp::Tla2528::Channel::CH7}; // make the actual tla class espp::Tla2528 tla(espp::Tla2528::Config{ @@ -203,7 +205,7 @@

    TLA2528 Example // of 0x10 becomes 0x16 .device_address = espp::Tla2528::DEFAULT_ADDRESS | 0x06, .mode = espp::Tla2528::Mode::AUTO_SEQ, - .analog_inputs = {NTC_CHANNEL, X_CHANNEL, Y_CHANNEL}, + .analog_inputs = channels, .digital_inputs = {}, .digital_outputs = {}, // enable oversampling / averaging @@ -224,16 +226,16 @@

    TLA2528 Example // get the analog input data individually; NOTE: this only works if you have configured the // TLA2528 to use MANUAL mode - // auto ntc_mv = tla.get_mv(NTC_CHANNEL); - // auto x_mv = tla.get_mv(X_CHANNEL); - // auto y_mv = tla.get_mv(Y_CHANNEL); + // auto ch0_mv = tla.get_mv(channels[0]); + // auto ch1_mv = tla.get_mv(channels[1]); + // auto ch2_mv = tla.get_mv(channels[2]); // Could also read them all at once; NOTE: this only works if you have configured the // TLA2528 to use AUTO_SEQ mode (which is more efficient) // auto all_mv = tla.get_all_mv(); - // auto ntc_mv = all_mv[0]; - // auto x_mv = all_mv[1]; - // auto y_mv = all_mv[2]; + // auto ch0_mv = all_mv[0]; + // auto ch1_mv = all_mv[1]; + // auto ch2_mv = all_mv[2]; // Could also use the mapped version; NOTE: this only works if you have configured the // TLA2528 to use AUTO_SEQ mode (which is more efficient) @@ -243,13 +245,15 @@

    TLA2528 Example logger.error("error reading TLA2528: {}", ec.message()); return false; } - auto ntc_mv = all_mv_map[NTC_CHANNEL]; - auto x_mv = all_mv_map[X_CHANNEL]; - auto y_mv = all_mv_map[Y_CHANNEL]; // use fmt to print so it doesn't have the prefix and can be used more // easily as CSV (for plotting using uart_serial_plotter) - fmt::print("{:.3f}, {:.3f}, {:.3f}, {:.3f}\n", elapsed, ntc_mv, x_mv, y_mv); + fmt::print("{:.3f}", elapsed); + for (auto &[ch, mv] : all_mv_map) { + fmt::print(", {:.1f}", mv); + } + fmt::print("\n"); + // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { @@ -503,42 +507,105 @@

    TLA2528 Example +
    +typedef std::function<bool(uint8_t)> probe_fn
    +

    Function to probe the peripheral

    +
    +
    Param address
    +

    The address to probe

    +
    +
    Return
    +

    True if the peripheral is found at the given address

    +
    +
    +

    +
    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> write_fn
    -

    Function to write bytes to the device.

    +typedef std::function<bool(uint8_t, const uint8_t*, size_t)> write_fn
    +

    Function to write data to the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to write to

    Param data
    -

    Pointer to array of bytes to write.

    +

    The data to write

    -
    Param data_len
    -

    Number of data bytes to write.

    +
    Param length
    +

    The length of the data to write

    Return
    -

    True if the write was successful, false otherwise.

    +

    True if the write was successful

    -typedef std::function<bool(uint8_t dev_addr, uint8_t *data, size_t data_len)> read_fn
    -

    Function to read bytes from the device.

    +typedef std::function<bool(uint8_t, uint8_t*, size_t)> read_fn
    +

    Function to read data from the peripheral

    -
    Param dev_addr
    -

    Address of the device to write to.

    +
    Param address
    +

    The address of the peripheral to read from

    Param data
    -

    Pointer to array of bytes to read into.

    +

    The buffer to read into

    -
    Param data_len
    -

    Number of data bytes to read.

    +
    Param length
    +

    The length of the buffer

    Return
    -

    True if the read was successful, false otherwise.

    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, uint8_t, uint8_t*, size_t)> read_register_fn
    +

    Function to read data at a specific address from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to read from

    +
    +
    Param reg_addr
    +

    The address of the register to read from

    +
    +
    Param data
    +

    The buffer to read into

    +
    +
    Param length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the read was successful

    +
    +
    +
    + +
    +
    +typedef std::function<bool(uint8_t, const uint8_t*, size_t, uint8_t*, size_t)> write_then_read_fn
    +

    Function to write then read data from the peripheral

    +
    +
    Param address
    +

    The address of the peripheral to write to

    +
    +
    Param write_data
    +

    The data to write

    +
    +
    Param write_length
    +

    The length of the data to write

    +
    +
    Param read_data
    +

    The buffer to read into

    +
    +
    Param read_length
    +

    The length of the buffer

    +
    +
    Return
    +

    True if the write then read was successful

    @@ -749,6 +816,82 @@

    TLA2528 Example +
    +inline void set_log_tag(const std::string_view &tag)
    +

    Set the tag for the logger

    +
    +
    Parameters
    +

    tag – The tag to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_level(Logger::Verbosity level)
    +

    Set the log level for the logger

    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_verbosity(Logger::Verbosity level)
    +

    Set the log verbosity for the logger

    +

    See also

    +

    set_log_level

    +
    +
    +

    See also

    +

    Logger::Verbosity

    +
    +
    +

    See also

    +

    Logger::set_verbosity

    +
    +

    +
    +

    Note

    +

    This is a convenience method that calls set_log_level

    +
    +
    +
    Parameters
    +

    level – The verbosity level to use for the logger

    +
    +
    +
    + +
    +
    +inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
    +

    Set the rate limit for the logger

    +

    +
    +

    Note

    +

    Only calls to the logger that have _rate_limit suffix will be rate limited

    +
    +
    +
    Parameters
    +

    rate_limit – The rate limit to use for the logger

    +
    +
    +
    +

    Public Static Attributes

    @@ -827,13 +970,13 @@

    TLA2528 Example
    -write_fn write
    +BasePeripheral::write_fn write

    Function to write to the ADC.

    -read_fn read
    +BasePeripheral::read_fn read

    Function to read from the ADC.

    diff --git a/docs/base_component.html b/docs/base_component.html index 6f535f87a..26c6f92c8 100644 --- a/docs/base_component.html +++ b/docs/base_component.html @@ -38,7 +38,7 @@ - + @@ -84,6 +84,7 @@ +
  • Base Peripheral
  • Battery APIs
  • BLDC APIs
  • Button APIs
  • @@ -139,7 +140,7 @@
  • »
  • Base Compoenent
  • - Edit on GitHub + Edit on GitHub

  • @@ -159,7 +160,7 @@

    API Reference

    Header File

    @@ -168,7 +169,7 @@

    Classes class espp::BaseComponent

    Base class for all components Provides a logger and some basic logging configuration

    -

    Subclassed by espp::AbiEncoder< T >, espp::BldcDriver, espp::BldcHaptics< M >, espp::BldcMotor< D, S, CS >, espp::Button, espp::ContinuousAdc, espp::Controller, espp::Display, espp::EncoderInput, espp::EventManager, espp::FileSystem, espp::FtpClientSession, espp::FtpServer, espp::I2c, espp::Joystick, espp::KeypadInput, espp::Led, espp::LedStrip, espp::OneshotAdc, espp::Pid, espp::Rmt, espp::RtspClient, espp::RtspServer, espp::RtspSession, espp::Socket, espp::Task, espp::TaskMonitor, espp::Thermistor, espp::Timer, espp::TouchpadInput, espp::WifiAp, espp::WifiSta

    +

    Subclassed by espp::AbiEncoder< T >, espp::BasePeripheral, espp::BldcDriver, espp::BldcHaptics< M >, espp::BldcMotor< D, S, CS >, espp::Button, espp::ContinuousAdc, espp::Controller, espp::Display, espp::EncoderInput, espp::EventManager, espp::FileSystem, espp::FtpClientSession, espp::FtpServer, espp::I2c, espp::Joystick, espp::KeypadInput, espp::Led, espp::LedStrip, espp::OneshotAdc, espp::Pid, espp::Rmt, espp::RtspClient, espp::RtspServer, espp::RtspSession, espp::Socket, espp::Task, espp::TaskMonitor, espp::Thermistor, espp::Timer, espp::TouchpadInput, espp::WifiAp, espp::WifiSta

    Public Functions

    @@ -259,7 +260,7 @@

    Classes - +


    diff --git a/docs/base_peripheral.html b/docs/base_peripheral.html index ec3804c69..ec021756c 100644 --- a/docs/base_peripheral.html +++ b/docs/base_peripheral.html @@ -37,7 +37,9 @@ - + + + @@ -72,9 +74,17 @@

    -