diff --git a/boards/accton-godwit/ga1/src/CMakeLists.txt b/boards/accton-godwit/ga1/src/CMakeLists.txt index 39ec808e1e..605cf16ac9 100644 --- a/boards/accton-godwit/ga1/src/CMakeLists.txt +++ b/boards/accton-godwit/ga1/src/CMakeLists.txt @@ -71,6 +71,5 @@ else() nuttx_arch # sdio nuttx_drivers # sdio px4_layer - platform_gpio_mcp23009 ) endif() diff --git a/boards/accton-godwit/ga1/src/init.cpp b/boards/accton-godwit/ga1/src/init.cpp index 6aef4114a8..ca48a74479 100644 --- a/boards/accton-godwit/ga1/src/init.cpp +++ b/boards/accton-godwit/ga1/src/init.cpp @@ -74,7 +74,6 @@ #include #include #include -#include /**************************************************************************** * Pre-Processor Definitions @@ -286,13 +285,6 @@ __EXPORT int board_app_initialize(uintptr_t arg) # endif /* CONFIG_MMCSD */ - ret = mcp23009_register_gpios(3, 0x25); - - if (ret != OK) { - led_on(LED_RED); - return ret; - } - #endif /* !defined(BOOTLOADER) */ return OK; diff --git a/boards/auterion/fmu-v6x/init/rc.board_defaults b/boards/auterion/fmu-v6x/init/rc.board_defaults index e3fe7f0dc2..f004d1dfd4 100644 --- a/boards/auterion/fmu-v6x/init/rc.board_defaults +++ b/boards/auterion/fmu-v6x/init/rc.board_defaults @@ -20,9 +20,9 @@ safety_button start if ver hwbasecmp 009 010 011 then # No USB - mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -U 10 + mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -M 0 -U 10 fi if ver hwbasecmp 00a 008 then - mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10 + mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -M 0 -U 10 fi diff --git a/boards/auterion/fmu-v6x/src/CMakeLists.txt b/boards/auterion/fmu-v6x/src/CMakeLists.txt index d0f1b0b1a7..4e3d9bf3eb 100644 --- a/boards/auterion/fmu-v6x/src/CMakeLists.txt +++ b/boards/auterion/fmu-v6x/src/CMakeLists.txt @@ -69,6 +69,6 @@ else() nuttx_arch # sdio nuttx_drivers # sdio px4_layer - platform_gpio_mcp23009 + platform_gpio_mcp ) endif() diff --git a/boards/auterion/fmu-v6x/src/init.cpp b/boards/auterion/fmu-v6x/src/init.cpp index 7e35a407c4..d11026f677 100644 --- a/boards/auterion/fmu-v6x/src/init.cpp +++ b/boards/auterion/fmu-v6x/src/init.cpp @@ -74,7 +74,6 @@ #include #include #include -#include /**************************************************************************** * Pre-Processor Definitions @@ -282,13 +281,6 @@ __EXPORT int board_app_initialize(uintptr_t arg) # endif /* CONFIG_MMCSD */ - ret = mcp23009_register_gpios(3, 0x25); - - if (ret != OK) { - led_on(LED_RED); - return ret; - } - #endif /* !defined(BOOTLOADER) */ return OK; diff --git a/boards/cuav/fmu-v6x/src/CMakeLists.txt b/boards/cuav/fmu-v6x/src/CMakeLists.txt index a604f95974..36c8aec516 100644 --- a/boards/cuav/fmu-v6x/src/CMakeLists.txt +++ b/boards/cuav/fmu-v6x/src/CMakeLists.txt @@ -71,6 +71,5 @@ else() nuttx_arch # sdio nuttx_drivers # sdio px4_layer - platform_gpio_mcp23009 ) endif() diff --git a/boards/cuav/fmu-v6x/src/init.cpp b/boards/cuav/fmu-v6x/src/init.cpp index 8808576638..5ca52e8076 100644 --- a/boards/cuav/fmu-v6x/src/init.cpp +++ b/boards/cuav/fmu-v6x/src/init.cpp @@ -74,7 +74,6 @@ #include #include #include -#include /**************************************************************************** * Pre-Processor Definitions @@ -286,13 +285,6 @@ __EXPORT int board_app_initialize(uintptr_t arg) # endif /* CONFIG_MMCSD */ - ret = mcp23009_register_gpios(3, 0x25); - - if (ret != OK) { - led_on(LED_RED); - return ret; - } - #endif /* !defined(BOOTLOADER) */ return OK; diff --git a/boards/px4/fmu-v5x/init/rc.board_defaults b/boards/px4/fmu-v5x/init/rc.board_defaults index 0092ef856d..1757657cad 100644 --- a/boards/px4/fmu-v5x/init/rc.board_defaults +++ b/boards/px4/fmu-v5x/init/rc.board_defaults @@ -29,9 +29,9 @@ safety_button start if ver hwbasecmp 009 then # No USB - mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -U 10 + mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -M 0 -U 10 fi if ver hwbasecmp 00a 008 then - mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10 + mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -M 0 -U 10 fi diff --git a/boards/px4/fmu-v5x/src/CMakeLists.txt b/boards/px4/fmu-v5x/src/CMakeLists.txt index 311b7c9be8..d64d950d80 100644 --- a/boards/px4/fmu-v5x/src/CMakeLists.txt +++ b/boards/px4/fmu-v5x/src/CMakeLists.txt @@ -43,8 +43,6 @@ add_library(drivers_board usb.c ) -add_dependencies(drivers_board platform_gpio_mcp23009) - target_link_libraries(drivers_board PRIVATE arch_io_pins @@ -54,5 +52,5 @@ target_link_libraries(drivers_board nuttx_arch # sdio nuttx_drivers # sdio px4_layer - platform_gpio_mcp23009 + platform_gpio_mcp ) diff --git a/boards/px4/fmu-v5x/src/init.cpp b/boards/px4/fmu-v5x/src/init.cpp index b743bf1310..1aacfd6ebd 100644 --- a/boards/px4/fmu-v5x/src/init.cpp +++ b/boards/px4/fmu-v5x/src/init.cpp @@ -74,7 +74,6 @@ #include #include #include -#include /**************************************************************************** * Pre-Processor Definitions @@ -280,12 +279,5 @@ __EXPORT int board_app_initialize(uintptr_t arg) #endif /* CONFIG_MMCSD */ - ret = mcp23009_register_gpios(3, 0x25); - - if (ret != OK) { - led_on(LED_RED); - return ret; - } - return OK; } diff --git a/boards/px4/fmu-v6xrt/nuttx-config/nsh/defconfig b/boards/px4/fmu-v6xrt/nuttx-config/nsh/defconfig index 855c435833..32c89ba577 100644 --- a/boards/px4/fmu-v6xrt/nuttx-config/nsh/defconfig +++ b/boards/px4/fmu-v6xrt/nuttx-config/nsh/defconfig @@ -54,6 +54,7 @@ CONFIG_DEBUG_MEMFAULT=y CONFIG_DEBUG_SYMBOLS=y CONFIG_DEBUG_TCBINFO=y CONFIG_DEV_FIFO_SIZE=0 +CONFIG_DEV_GPIO=y CONFIG_DEV_PIPE_SIZE=70 CONFIG_DEV_URANDOM=y CONFIG_ETH0_PHY_MULTI=y diff --git a/msg/GpioIn.msg b/msg/GpioIn.msg index 0482a2188e..2dc6e3cff2 100644 --- a/msg/GpioIn.msg +++ b/msg/GpioIn.msg @@ -1,4 +1,5 @@ # GPIO mask and state +uint8 MAX_INSTANCES = 8 uint64 timestamp # time since system start (microseconds) uint32 device_id # Device id diff --git a/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt b/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt index 208cc5c828..d3a45a4e62 100644 --- a/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt +++ b/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt @@ -31,5 +31,4 @@ # ############################################################################ - -add_subdirectory(mcp23009) +add_subdirectory(mcp) diff --git a/platforms/nuttx/src/px4/common/gpio/mcp23009/CMakeLists.txt b/platforms/nuttx/src/px4/common/gpio/mcp/CMakeLists.txt similarity index 92% rename from platforms/nuttx/src/px4/common/gpio/mcp23009/CMakeLists.txt rename to platforms/nuttx/src/px4/common/gpio/mcp/CMakeLists.txt index da6dac979f..b8ba517d98 100644 --- a/platforms/nuttx/src/px4/common/gpio/mcp23009/CMakeLists.txt +++ b/platforms/nuttx/src/px4/common/gpio/mcp/CMakeLists.txt @@ -30,7 +30,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ############################################################################ -px4_add_library(platform_gpio_mcp23009 - mcp23009.cpp +px4_add_library(platform_gpio_mcp + mcp.cpp ) -target_link_libraries(platform_gpio_mcp23009 PRIVATE drivers__device) # device::I2C +target_link_libraries(platform_gpio_mcp PRIVATE drivers__device) # device::I2C diff --git a/platforms/nuttx/src/px4/common/gpio/mcp/mcp.cpp b/platforms/nuttx/src/px4/common/gpio/mcp/mcp.cpp new file mode 100644 index 0000000000..2b69a7cbf3 --- /dev/null +++ b/platforms/nuttx/src/px4/common/gpio/mcp/mcp.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +static int mcp230XX_read(struct gpio_dev_s *dev, bool *value) +{ + mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev; + *value = gpio->callback_handler->input & gpio->mask; + return OK; +} + +static uORB::PublicationMulti toGpioOut{ORB_ID(gpio_out)}; +static int mcp230XX_write(struct gpio_dev_s *dev, bool value) +{ + mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev; + gpio_out_s msg{ + hrt_absolute_time(), + gpio->callback_handler->dev_id, + gpio->mask, // clear mask + value ? gpio->mask : 0u, // set mask + }; + return toGpioOut.publish(msg) ? OK : -ETIMEDOUT; +} + +static uORB::PublicationMulti toGpioConfig{ORB_ID(gpio_config)}; +static int mcp230XX_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype) +{ + mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev; + gpio_config_s msg{ + hrt_absolute_time(), + gpio->callback_handler->dev_id, + gpio->mask, + }; + + switch (pintype) { + case GPIO_INPUT_PIN: + msg.config = gpio_config_s::INPUT; + break; + + case GPIO_INPUT_PIN_PULLUP: + msg.config = gpio_config_s::INPUT_PULLUP; + break; + + case GPIO_OUTPUT_PIN: + msg.config = gpio_config_s::OUTPUT; + break; + + default: + return -ENOTSUP; + } + + return toGpioConfig.publish(msg) ? OK : -ETIMEDOUT; +} + +static const struct gpio_operations_s mcp_gpio_ops { + mcp230XX_read, + mcp230XX_write, + nullptr, + nullptr, + mcp230XX_setpintype, +}; + +int mcp230XX_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor, uint16_t dir_mask, int num_pins, uint8_t device_type, + mcp_gpio_dev_s *_gpio) +{ + bool all_registered = true; + + for (int i = 0; i < num_pins; i++) { + uint16_t mask = 1u << i; + + if (!_gpio[i].registered) { + if (dir_mask & mask) { + _gpio[i] = { {GPIO_INPUT_PIN, {}, &mcp_gpio_ops}, mask, false, nullptr}; + + } else { + _gpio[i] = { {GPIO_OUTPUT_PIN, {}, &mcp_gpio_ops}, mask, false, nullptr}; + } + + int ret = gpio_pin_register(&_gpio[i].gpio, first_minor + i); + + if (ret != OK) { + all_registered = false; + + } else { + _gpio[i].registered = true; + } + } + } + + const auto device_id = device::Device::DeviceId{device::Device::DeviceBusType_I2C, i2c_bus, i2c_addr, device_type}; + CallbackHandler *callback_handler = new CallbackHandler(ORB_ID(gpio_in)); + callback_handler->dev_id = device_id.devid; + bool callback_registered = callback_handler->registerCallback(); + + if (!all_registered || !callback_registered) { + for (int i = 0; i < num_pins; i++) { + if (_gpio[i].registered) { + gpio_pin_unregister(&_gpio[i].gpio, first_minor + i); + _gpio[i].registered = false; + } + } + + callback_handler->unregisterCallback(); + delete callback_handler; + return ERROR; + } + + for (int i = 0; i < num_pins; i++) { + _gpio[i].callback_handler = callback_handler; + } + + return OK; +} + +int mcp230XX_unregister_gpios(int first_minor, int num_pins, mcp_gpio_dev_s *_gpio) +{ + if (_gpio[0].callback_handler) { + CallbackHandler *callback_handler = _gpio[0].callback_handler; + callback_handler->unregisterCallback(); + delete callback_handler; + } + + for (int i = 0; i < num_pins; ++i) { + if (_gpio[i].registered) { + mcp230XX_setpintype(&_gpio[i].gpio, GPIO_INPUT_PIN); + gpio_pin_unregister(&_gpio[i].gpio, first_minor + i); + _gpio[i].registered = false; + _gpio[i].callback_handler = nullptr; + } + } + + return OK; +} diff --git a/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp b/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp deleted file mode 100644 index 63a60e5484..0000000000 --- a/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2023 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static uint32_t DEVID{0}; -struct mcp23009_gpio_dev_s { - struct gpio_dev_s gpio; - uint8_t mask; -}; - -/* Copy the read input data */ -class ReadCallback : public uORB::SubscriptionCallback -{ -public: - using SubscriptionCallback::SubscriptionCallback; - - void call() override - { - px4::msg::GpioIn new_input; - - if (update(&new_input) && new_input.device_id == DEVID) { - input = new_input.state; - } - - } - - uint8_t input; -}; - -static uORB::Publication toGpioRequest{ORB_ID(gpio_request)}; -static ReadCallback fromGpioIn{ORB_ID(gpio_in)}; -static int mcp23009_read(struct gpio_dev_s *dev, bool *value) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - *value = fromGpioIn.input & gpio->mask; - return OK; -} - -static uORB::Publication toGpioOut{ORB_ID(gpio_out)}; -static int mcp23009_write(struct gpio_dev_s *dev, bool value) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - gpio_out_s msg{ - hrt_absolute_time(), - DEVID, - gpio->mask, // clear mask - value ? gpio->mask : 0u, // set mask - }; - return toGpioOut.publish(msg) ? OK : -ETIMEDOUT; -} - -static uORB::Publication toGpioConfig{ORB_ID(gpio_config)}; -static int mcp23009_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - gpio_config_s msg{ - hrt_absolute_time(), - DEVID, - gpio->mask, - }; - - switch (pintype) { - case GPIO_INPUT_PIN: - msg.config = gpio_config_s::INPUT; - break; - - case GPIO_INPUT_PIN_PULLUP: - msg.config = gpio_config_s::INPUT_PULLUP; - break; - - case GPIO_OUTPUT_PIN: - msg.config = gpio_config_s::OUTPUT; - break; - - default: - return -ENOTSUP; - } - - return toGpioConfig.publish(msg) ? OK : -ETIMEDOUT; -} - - - -// ---------------------------------------------------------------------------- -static const struct gpio_operations_s mcp23009_gpio_ops { - mcp23009_read, - mcp23009_write, - nullptr, - nullptr, - mcp23009_setpintype, -}; - -static constexpr uint8_t NUM_GPIOS = 8; -static mcp23009_gpio_dev_s _gpio[NUM_GPIOS] { - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 0) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 1) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 2) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 3) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 4) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 5) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 6) }, - { {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 7) } -}; - -// ---------------------------------------------------------------------------- -int mcp23009_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor) -{ - const auto device_id = device::Device::DeviceId{ - device::Device::DeviceBusType_I2C, i2c_bus, i2c_addr, DRV_GPIO_DEVTYPE_MCP23009}; - DEVID = device_id.devid; - - for (int i = 0; i < NUM_GPIOS; ++i) { - int ret = gpio_pin_register(&_gpio[i].gpio, first_minor + i); - - if (ret != OK) { - return ret; - } - } - - fromGpioIn.registerCallback(); - return OK; -} - -int mcp23009_unregister_gpios(int first_minor) -{ - for (int i = 0; i < NUM_GPIOS; ++i) { - mcp23009_setpintype(&_gpio[i].gpio, GPIO_INPUT_PIN); - gpio_pin_unregister(&_gpio[i].gpio, first_minor + i); - } - - fromGpioIn.unregisterCallback(); - return OK; -} diff --git a/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp.hpp b/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp.hpp new file mode 100644 index 0000000000..e9dfcf14e3 --- /dev/null +++ b/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp.hpp @@ -0,0 +1,53 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#ifndef CONFIG_DEV_GPIO +# error "CONFIG_DEV_GPIO is required to use MCP GPIO expander, enable it in your NuttX config" +#endif + +struct mcp_gpio_dev_s { + struct gpio_dev_s gpio; + uint16_t mask; + bool registered = false; + CallbackHandler *callback_handler; +}; + +int mcp230XX_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor = 0, uint16_t dir_mask = 0x0000, int num_pins = 8, + uint8_t device_type = 0, mcp_gpio_dev_s *_gpio = nullptr); +int mcp230XX_unregister_gpios(int first_minor = 0, int num_pins = 0, mcp_gpio_dev_s *_gpio = nullptr); diff --git a/src/drivers/drv_sensor.h b/src/drivers/drv_sensor.h index 01d2c70f92..61dbb2f35c 100644 --- a/src/drivers/drv_sensor.h +++ b/src/drivers/drv_sensor.h @@ -201,6 +201,7 @@ #define DRV_DIST_DEVTYPE_GY_US42 0x9C #define DRV_BAT_DEVTYPE_BATMON_SMBUS 0x9d +#define DRV_GPIO_DEVTYPE_MCP23017 0x9E #define DRV_GPIO_DEVTYPE_MCP23009 0x9F #define DRV_GPS_DEVTYPE_ASHTECH 0xA0 diff --git a/src/drivers/gpio/Kconfig b/src/drivers/gpio/Kconfig index 902613ebbf..131cd4431b 100644 --- a/src/drivers/gpio/Kconfig +++ b/src/drivers/gpio/Kconfig @@ -1,9 +1,3 @@ menu "GPIO" - menuconfig COMMON_GPIO - bool "Common GPIOs" - default n - select DRIVERS_GPIO_MCP23009 - ---help--- - Enable default set of GPIO drivers rsource "*/Kconfig" endmenu diff --git a/src/drivers/gpio/mcp23009/CMakeLists.txt b/src/drivers/gpio/mcp23009/CMakeLists.txt index c7897fea8e..60ff7868cf 100644 --- a/src/drivers/gpio/mcp23009/CMakeLists.txt +++ b/src/drivers/gpio/mcp23009/CMakeLists.txt @@ -30,12 +30,13 @@ # POSSIBILITY OF SUCH DAMAGE. # ############################################################################ + px4_add_module( - MODULE drivers__mcp23009 + MODULE drivers__gpio__mcp23009 MAIN mcp23009 - COMPILE_FLAGS SRCS - mcp23009_main.cpp - mcp23009.cpp + MCP23009_main.cpp + MCP23009.cpp DEPENDS + mcp_common ) diff --git a/src/drivers/gpio/mcp23009/Kconfig b/src/drivers/gpio/mcp23009/Kconfig index 73078bf8eb..52d9b05775 100644 --- a/src/drivers/gpio/mcp23009/Kconfig +++ b/src/drivers/gpio/mcp23009/Kconfig @@ -1,5 +1,5 @@ menuconfig DRIVERS_GPIO_MCP23009 - bool "mcp23009" + bool "MCP23009" default n ---help--- - Enable support for mcp23009 + Enable support for MCP23009 8-bit GPIO expander diff --git a/src/drivers/gpio/mcp23009/MCP23009.cpp b/src/drivers/gpio/mcp23009/MCP23009.cpp new file mode 100644 index 0000000000..1f258efb41 --- /dev/null +++ b/src/drivers/gpio/mcp23009/MCP23009.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include "MCP23009.hpp" + +MCP23009::MCP23009(const I2CSPIDriverConfig &config) : + MCP230XX(config) +{ +} + +int MCP23009::get_olat(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::OLAT; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23009::get_gppu(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::GPPU; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23009::get_iodir(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::IODIR; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23009::get_gpio(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::GPIO; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23009::get_probe_reg(uint8_t *addr) +{ + *addr = (uint8_t) Register::IOCON; + return PX4_OK; +} diff --git a/src/drivers/gpio/mcp23009/MCP23009.hpp b/src/drivers/gpio/mcp23009/MCP23009.hpp new file mode 100644 index 0000000000..263c27182b --- /dev/null +++ b/src/drivers/gpio/mcp23009/MCP23009.hpp @@ -0,0 +1,64 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#pragma once +#include + +using namespace time_literals; + +class MCP23009 : public MCP230XX +{ +public: + MCP23009(const I2CSPIDriverConfig &config); + +private: + enum class + Register : uint8_t { + IODIR = 0x00, + IPOL = 0x01, + GPINTEN = 0x02, + DEFVAL = 0x03, + INTCON = 0x04, + IOCON = 0x05, + GPPU = 0x06, + INTF = 0x07, + INTCAP = 0x08, + GPIO = 0x09, + OLAT = 0x0a + }; + + int get_olat(int bank, uint8_t *addr) override; + int get_gppu(int bank, uint8_t *addr) override; + int get_iodir(int bank, uint8_t *addr) override; + int get_gpio(int bank, uint8_t *addr) override; + int get_probe_reg(uint8_t *addr) override; +}; diff --git a/src/drivers/gpio/mcp23009/MCP23009_main.cpp b/src/drivers/gpio/mcp23009/MCP23009_main.cpp new file mode 100644 index 0000000000..106a9be055 --- /dev/null +++ b/src/drivers/gpio/mcp23009/MCP23009_main.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include "MCP23009.hpp" + +constexpr MCP230XX_config_t def_mcp_config{ + .device_type = DRV_GPIO_DEVTYPE_MCP23009, + .i2c_addr = I2C_ADDRESS_MCP23009, + .num_pins = 8, + .num_banks = 1, +}; + +extern "C" int mcp23009_main(int argc, char *argv[]) +{ + using ThisDriver = MCP23009; + BusCLIArguments cli{true, false}; + cli.default_i2c_frequency = 400000; + cli.i2c_address = 0x25; + MCP230XX_config_t mcp_config = def_mcp_config; + + int ch; + + while ((ch = cli.getOpt(argc, argv, "D:O:P:U:R:M:")) != EOF) { + switch (ch) { + + case 'D': + mcp_config.direction = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'O': + mcp_config.state = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'P': + mcp_config.pullup = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'U': + mcp_config.interval = (uint16_t)atoi(cli.optArg()); + break; + + case 'M': + mcp_config.first_minor = (uint8_t)atoi(cli.optArg()); + break; + } + } + + const char *verb = cli.optArg(); + + if (!verb) { + MCP230XX::print_usage(); + return -1; + } + + cli.custom_data = &mcp_config; + + BusInstanceIterator iterator("MCP23009", cli, mcp_config.device_type); + + if (!strcmp(verb, "start")) { + return ThisDriver::module_start(cli, iterator); + } + + if (!strcmp(verb, "stop")) { + return ThisDriver::module_stop(iterator); + } + + if (!strcmp(verb, "status")) { + return ThisDriver::module_status(iterator); + } + + MCP230XX::print_usage(); + return -1; +} diff --git a/src/drivers/gpio/mcp23009/mcp23009.cpp b/src/drivers/gpio/mcp23009/mcp23009.cpp deleted file mode 100644 index 8d8eb92885..0000000000 --- a/src/drivers/gpio/mcp23009/mcp23009.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2023 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include "mcp23009.h" - -int MCP23009::read_reg(Register address, uint8_t &data) -{ - return transfer((uint8_t *)&address, 1, &data, 1); -} - -int MCP23009::write_reg(Register address, uint8_t value) -{ - uint8_t data[2] = {(uint8_t)address, value}; - return transfer(data, sizeof(data), nullptr, 0); -} - -int MCP23009::init(uint8_t direction, uint8_t state, uint8_t pull_up) -{ - // do I2C init (and probe) first - int ret = I2C::init(); - - if (ret != PX4_OK) { - PX4_ERR("I2C init failed"); - return ret; - } - - // buffer the new initial states - _iodir = direction; - _olat = state; - _gppu = pull_up; - - // Write the initial state to the device - ret = write_reg(Register::OLAT, _olat); - ret |= write_reg(Register::IODIR, _iodir); - ret |= write_reg(Register::GPPU, _gppu); - - if (ret != PX4_OK) { - PX4_ERR("Device init failed (%i)", ret); - return ret; - } - - return init_uorb(); -} - -int MCP23009::probe() -{ - // no whoami, try to read IOCON - uint8_t data; - return read_reg(Register::IOCON, data); -} - -int MCP23009::read(uint8_t *mask) -{ - return read_reg(Register::GPIO, *mask); -} - -int MCP23009::write(uint8_t mask_set, uint8_t mask_clear) -{ - // no need to read, we can use the buffered register value - _olat = (_olat & ~mask_clear) | mask_set; - return write_reg(Register::OLAT, _olat); -} - -int MCP23009::configure(uint8_t mask, PinType type) -{ - // no need to read, we can use the buffered register values - switch (type) { - case PinType::Input: - _iodir |= mask; - _gppu &= ~mask; - break; - - case PinType::InputPullUp: - _iodir |= mask; - _gppu |= mask; - break; - - case PinType::Output: - _iodir &= ~mask; - break; - - default: - return -EINVAL; - } - - int ret = write_reg(Register::GPPU, _gppu); - - if (ret != 0) { - return ret; - } - - return write_reg(Register::IODIR, _iodir); -} diff --git a/src/drivers/gpio/mcp23009/mcp23009_main.cpp b/src/drivers/gpio/mcp23009/mcp23009_main.cpp deleted file mode 100644 index f5f9a2cfc4..0000000000 --- a/src/drivers/gpio/mcp23009/mcp23009_main.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/**************************************************************************** - * - * Copyright (C) 2023 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/** - * Driver for the MCP23009 connected via I2C. - */ - -#include "mcp23009.h" -#include - -MCP23009::MCP23009(const I2CSPIDriverConfig &config) : - I2C(config), - I2CSPIDriver(config), - _cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample")) -{ -} - -MCP23009::~MCP23009() -{ - ScheduleClear(); - perf_free(_cycle_perf); -} - -int MCP23009::init_uorb() -{ - if (!_gpio_config_sub.registerCallback() || - !_gpio_request_sub.registerCallback() || - !_gpio_out_sub.registerCallback()) { - PX4_ERR("callback registration failed"); - return -1; - } - - return PX4_OK; -} - -void MCP23009::exit_and_cleanup() -{ - _gpio_config_sub.unregisterCallback(); - _gpio_request_sub.unregisterCallback(); - _gpio_out_sub.unregisterCallback(); -} - -void MCP23009::RunImpl() -{ - perf_begin(_cycle_perf); - - gpio_config_s config; - - if (_gpio_config_sub.update(&config) && config.device_id == get_device_id()) { - PinType type = PinType::Input; - - switch (config.config) { - case config.INPUT_PULLUP: type = PinType::InputPullUp; break; - - case config.OUTPUT: type = PinType::Output; break; - } - - write(config.state, config.mask); - configure(config.mask, type); - } - - gpio_out_s output; - - if (_gpio_out_sub.update(&output) && output.device_id == get_device_id()) { - write(output.state, output.mask); - } - - // read every time we run, either when requested or when scheduled on interval - { - gpio_in_s _gpio_in; - _gpio_in.timestamp = hrt_absolute_time(); - _gpio_in.device_id = get_device_id(); - uint8_t input; - read(&input); - _gpio_in.state = input; - _to_gpio_in.publish(_gpio_in); - } - - perf_end(_cycle_perf); -} - -void MCP23009::print_usage() -{ - PRINT_MODULE_USAGE_NAME("MCP23009", "driver"); - PRINT_MODULE_USAGE_COMMAND("start"); - PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false); - PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x25); - PRINT_MODULE_USAGE_PARAM_INT('D', 0, 0, 255, "Direction", true); - PRINT_MODULE_USAGE_PARAM_INT('O', 0, 0, 255, "Output", true); - PRINT_MODULE_USAGE_PARAM_INT('P', 0, 0, 255, "Pullups", true); - PRINT_MODULE_USAGE_PARAM_INT('U', 0, 0, 1000, "Update Interval [ms]", true); - PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); -} - -void MCP23009::print_status() -{ - I2CSPIDriverBase::print_status(); - perf_print_counter(_cycle_perf); -} - -struct init_config_t { - uint16_t interval; - uint8_t direction; - uint8_t state; - uint8_t pullup; -}; - -I2CSPIDriverBase *MCP23009::instantiate(const I2CSPIDriverConfig &config, int runtime_instance) -{ - auto *init = (const init_config_t *)config.custom_data; - auto *instance = new MCP23009(config); - - if (!instance) { - PX4_ERR("alloc failed"); - return nullptr; - } - - if (OK != instance->init(init->direction, init->state, init->pullup)) { - delete instance; - return nullptr; - } - - if (init->interval) { - instance->ScheduleOnInterval(init->interval * 1000); - } - - return instance; -} - -extern "C" int mcp23009_main(int argc, char *argv[]) -{ - using ThisDriver = MCP23009; - BusCLIArguments cli{true, false}; - cli.default_i2c_frequency = 400000; - cli.i2c_address = 0x25; - init_config_t config_data{}; - - int ch; - - while ((ch = cli.getOpt(argc, argv, "D:O:P:U:")) != EOF) { - switch (ch) { - case 'D': - config_data.direction = (int)strtol(cli.optArg(), nullptr, 0); - break; - - case 'O': - config_data.state = (int)strtol(cli.optArg(), nullptr, 0); - break; - - case 'P': - config_data.pullup = (int)strtol(cli.optArg(), nullptr, 0); - break; - - case 'U': - config_data.interval = atoi(cli.optArg()); - break; - } - } - - cli.custom_data = &config_data; - - const char *verb = cli.optArg(); - - if (!verb) { - ThisDriver::print_usage(); - return -1; - } - - BusInstanceIterator iterator(MODULE_NAME, cli, DRV_GPIO_DEVTYPE_MCP23009); - - if (!strcmp(verb, "start")) { - return ThisDriver::module_start(cli, iterator); - } - - if (!strcmp(verb, "stop")) { - return ThisDriver::module_stop(iterator); - } - - if (!strcmp(verb, "status")) { - return ThisDriver::module_status(iterator); - } - - ThisDriver::print_usage(); - return -1; -} diff --git a/src/drivers/gpio/mcp23017/CMakeLists.txt b/src/drivers/gpio/mcp23017/CMakeLists.txt new file mode 100644 index 0000000000..62107d59ad --- /dev/null +++ b/src/drivers/gpio/mcp23017/CMakeLists.txt @@ -0,0 +1,42 @@ +############################################################################ +# +# Copyright (c) 2023 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +px4_add_module( + MODULE drivers__gpio__mcp23017 + MAIN mcp23017 + SRCS + MCP23017_main.cpp + MCP23017.cpp + DEPENDS + mcp_common + ) diff --git a/src/drivers/gpio/mcp23017/Kconfig b/src/drivers/gpio/mcp23017/Kconfig new file mode 100644 index 0000000000..8d0e44b071 --- /dev/null +++ b/src/drivers/gpio/mcp23017/Kconfig @@ -0,0 +1,5 @@ +menuconfig DRIVERS_GPIO_MCP23017 + bool "MCP23017" + default n + ---help--- + Enable support for MCP23017 16-bit GPIO expander diff --git a/src/drivers/gpio/mcp23017/MCP23017.cpp b/src/drivers/gpio/mcp23017/MCP23017.cpp new file mode 100644 index 0000000000..98be95ed06 --- /dev/null +++ b/src/drivers/gpio/mcp23017/MCP23017.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include "MCP23017.hpp" + +MCP23017::MCP23017(const I2CSPIDriverConfig &config) : + MCP230XX(config) +{ +} + +int MCP23017::get_olat(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::OLATA; + return PX4_OK; + + case 1: + *addr = (uint8_t) Register::OLATB; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23017::get_gppu(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::GPPUA; + return PX4_OK; + + case 1: + *addr = (uint8_t) Register::GPPUB; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23017::get_iodir(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::IODIRA; + return PX4_OK; + + case 1: + *addr = (uint8_t) Register::IODIRB; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23017::get_gpio(int bank, uint8_t *addr) +{ + switch (bank) { + case 0: + *addr = (uint8_t) Register::GPIOA; + return PX4_OK; + + case 1: + *addr = (uint8_t) Register::GPIOB; + return PX4_OK; + + default: + return PX4_ERROR; + } +} + +int MCP23017::get_probe_reg(uint8_t *addr) +{ + *addr = (uint8_t) Register::IOCONA; + return PX4_OK; +} diff --git a/src/drivers/gpio/mcp23017/MCP23017.hpp b/src/drivers/gpio/mcp23017/MCP23017.hpp new file mode 100644 index 0000000000..afa1f52872 --- /dev/null +++ b/src/drivers/gpio/mcp23017/MCP23017.hpp @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#pragma once +#include + +using namespace time_literals; + +class MCP23017 : public MCP230XX +{ +public: + MCP23017(const I2CSPIDriverConfig &config); + +private: + enum class + Register : uint8_t { + IODIRA = 0x00, + IODIRB = 0x01, + IPOLA = 0x02, + IPOLB = 0x03, + GPINTENA = 0x04, + GPINTENB = 0x05, + DEFVALA = 0x06, + DEFVALB = 0x07, + INTCONA = 0x08, + INTCONB = 0x09, + IOCONA = 0x0a, + IOCONB = 0x0b, + GPPUA = 0x0c, + GPPUB = 0x0d, + INTFA = 0x0e, + INTFB = 0x0f, + INTCAPA = 0x10, + INTCAPB = 0x11, + GPIOA = 0x12, + GPIOB = 0x13, + OLATA = 0x14, + OLATB = 0x15 + }; + + int get_olat(int bank, uint8_t *addr) override; + int get_gppu(int bank, uint8_t *addr) override; + int get_iodir(int bank, uint8_t *addr) override; + int get_gpio(int bank, uint8_t *addr) override; + int get_probe_reg(uint8_t *addr) override; +}; diff --git a/src/drivers/gpio/mcp23017/MCP23017_main.cpp b/src/drivers/gpio/mcp23017/MCP23017_main.cpp new file mode 100644 index 0000000000..5905786d6c --- /dev/null +++ b/src/drivers/gpio/mcp23017/MCP23017_main.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include "MCP23017.hpp" + +constexpr MCP230XX_config_t def_mcp_config{ + .device_type = DRV_GPIO_DEVTYPE_MCP23017, + .i2c_addr = I2C_ADDRESS_MCP23017, + .num_pins = 16, + .num_banks = 2, +}; + +extern "C" int mcp23017_main(int argc, char *argv[]) +{ + using ThisDriver = MCP23017; + BusCLIArguments cli{true, false}; + cli.default_i2c_frequency = 400000; + cli.i2c_address = 0x27; + MCP230XX_config_t mcp_config = def_mcp_config; + + int ch; + + while ((ch = cli.getOpt(argc, argv, "D:O:P:U:R:M:")) != EOF) { + switch (ch) { + + case 'D': + mcp_config.direction = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'O': + mcp_config.state = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'P': + mcp_config.pullup = (int)strtol(cli.optArg(), nullptr, 0); + break; + + case 'U': + mcp_config.interval = (uint16_t)atoi(cli.optArg()); + break; + + case 'M': + mcp_config.first_minor = (uint8_t)atoi(cli.optArg()); + break; + } + } + + const char *verb = cli.optArg(); + + if (!verb) { + MCP230XX::print_usage(); + return -1; + } + + cli.custom_data = &mcp_config; + + BusInstanceIterator iterator("MCP23017", cli, mcp_config.device_type); + + if (!strcmp(verb, "start")) { + return ThisDriver::module_start(cli, iterator); + } + + if (!strcmp(verb, "stop")) { + return ThisDriver::module_stop(iterator); + } + + if (!strcmp(verb, "status")) { + return ThisDriver::module_status(iterator); + } + + MCP230XX::print_usage(); + return -1; +} diff --git a/src/lib/drivers/CMakeLists.txt b/src/lib/drivers/CMakeLists.txt index 00c45648d4..1511d19332 100644 --- a/src/lib/drivers/CMakeLists.txt +++ b/src/lib/drivers/CMakeLists.txt @@ -36,6 +36,7 @@ add_subdirectory(device) add_subdirectory(gyroscope) add_subdirectory(led) add_subdirectory(magnetometer) +add_subdirectory(mcp_common) add_subdirectory(rangefinder) add_subdirectory(smbus) add_subdirectory(smbus_sbs) diff --git a/src/lib/drivers/mcp_common/CMakeLists.txt b/src/lib/drivers/mcp_common/CMakeLists.txt new file mode 100644 index 0000000000..8efcb58921 --- /dev/null +++ b/src/lib/drivers/mcp_common/CMakeLists.txt @@ -0,0 +1,40 @@ +############################################################################ +# +# Copyright (c) 2023 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +px4_add_library(mcp_common + MCP.cpp + ${PX4_SOURCE_DIR}/src/drivers/gpio/mcp23009/MCP23009.cpp + ${PX4_SOURCE_DIR}/src/drivers/gpio/mcp23017/MCP23017.cpp +) + +target_link_libraries(mcp_common PUBLIC platform_gpio_mcp) diff --git a/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp b/src/lib/drivers/mcp_common/CallbackHandler.hpp similarity index 74% rename from platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp rename to src/lib/drivers/mcp_common/CallbackHandler.hpp index 481f5b51d7..f0aa770cec 100644 --- a/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp +++ b/src/lib/drivers/mcp_common/CallbackHandler.hpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2023 PX4 Development Team. All rights reserved. + * Copyright (C) 2025 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,10 +30,32 @@ * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ - #pragma once #include +#include +#include -int mcp23009_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor = 0); -int mcp23009_unregister_gpios(int first_minor = 0); +class CallbackHandler : public uORB::SubscriptionCallback +{ +public: + uint32_t dev_id; + uint16_t input{0}; + + CallbackHandler(orb_id_t id) : uORB::SubscriptionCallback(id) {} + virtual ~CallbackHandler() {} + + void call() override + { + px4::msg::GpioIn new_input; + + for (int i = 0; i < new_input.MAX_INSTANCES; i++) { + ChangeInstance(i); + + if (update(&new_input) && new_input.device_id == dev_id) { + input = new_input.state; + break; + } + } + } +}; diff --git a/src/lib/drivers/mcp_common/MCP.cpp b/src/lib/drivers/mcp_common/MCP.cpp new file mode 100644 index 0000000000..7f36ba11ec --- /dev/null +++ b/src/lib/drivers/mcp_common/MCP.cpp @@ -0,0 +1,457 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#include "MCP.hpp" +#include +#include + +MCP230XX::MCP230XX(const I2CSPIDriverConfig &config) : + I2C(config), + I2CSPIDriver(config), + _cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample")), + _comms_errors(perf_alloc(PC_COUNT, MODULE_NAME": comms errors")), + _register_check(perf_alloc(PC_COUNT, MODULE_NAME": register check")) +{ +} + +MCP230XX::~MCP230XX() +{ + ScheduleClear(); + perf_free(_cycle_perf); + perf_free(_comms_errors); + perf_free(_register_check); +} + +int MCP230XX::init_uorb() +{ + if (!_gpio_config_sub.registerCallback() || + !_gpio_out_sub.registerCallback()) { + PX4_ERR("callback registration failed"); + return PX4_ERROR; + } + + return PX4_OK; +} + +void MCP230XX::cleanup_uorb() +{ + _gpio_config_sub.unregisterCallback(); + _gpio_out_sub.unregisterCallback(); + mcp230XX_unregister_gpios(mcp_config.first_minor, mcp_config.num_pins, mcp_config._gpio_handle); +} + +int MCP230XX::read(uint16_t *mask) +{ + *mask = 0; + int ret = PX4_OK; + + for (int i = 0; i < mcp_config.num_banks; i++) { + uint8_t gpio_addr; + uint8_t data; + + ret |= get_gpio(i, &gpio_addr); + ret |= read_reg(gpio_addr, data); + + *mask |= (((uint16_t) data) << (8 * i)); + } + + return ret; +} + +int MCP230XX::write(const uint16_t mask_set, const uint16_t mask_clear) +{ + int ret = PX4_OK; + _olat = (_olat & ~mask_clear) | mask_set; + + for (int i = 0; i < mcp_config.num_banks; i++) { + uint8_t reg_addr; + uint8_t data; + uint8_t curr_olat = (uint8_t)((_olat >> (8 * i)) & 0x00FF); + + ret = get_olat(i, ®_addr); + + if (ret != PX4_OK) { + return ret; + } + + ret |= write_reg(reg_addr, curr_olat); + ret |= read_reg(reg_addr, data); + + if (ret != PX4_OK || data != curr_olat) { + return PX4_ERROR; + } + } + + return ret; +} + +int MCP230XX::read_reg(const uint8_t address, uint8_t &data) +{ + int ret = transfer(&address, 1, &data, 1); + + if (ret != PX4_OK) { + perf_count(_comms_errors); + } + + return ret; +} + +int MCP230XX::write_reg(const uint8_t address, const uint8_t value) +{ + uint8_t data[2] = {address, value}; + int ret = transfer(data, 2, nullptr, 0); + + if (ret != PX4_OK) { + perf_count(_comms_errors); + } + + return ret; +} + +int MCP230XX::configure(const uint16_t mask, PinType type) +{ + switch (type) { + case PinType::Input: + _iodir |= mask; + _gppu &= ~mask; + break; + + case PinType::InputPullUp: + _iodir |= mask; + _gppu |= mask; + break; + + case PinType::Output: + _iodir &= ~mask; + break; + + default: + return -EINVAL; + } + + int ret = PX4_OK; + + for (int i = 0; i < mcp_config.num_banks; i++) { + uint8_t curr_iodir = (uint8_t)((_iodir >> (8 * i)) & 0x00FF); + uint8_t curr_gppu = (uint8_t)((_gppu >> (8 * i)) & 0x00FF); + uint8_t iodir_addr; + uint8_t gppu_addr; + + ret = get_iodir(i, &iodir_addr); + ret |= get_gppu(i, &gppu_addr); + + if (ret != PX4_OK) { + return ret; + } + + ret = write_reg(iodir_addr, curr_iodir); + ret |= write_reg(gppu_addr, curr_gppu); + + if (ret != PX4_OK) { + PX4_ERR("MCP configure failed"); + return ret; + } + } + + return PX4_OK; +} + +int MCP230XX::init() +{ + int ret = I2C::init(); + + if (ret != PX4_OK) { + PX4_ERR("I2C init failed"); + return ret; + } + + ScheduleNow(); + return PX4_OK; +} + +int MCP230XX::init_communication() +{ + int ret = mcp230XX_register_gpios(mcp_config.i2c_bus, mcp_config.i2c_addr, mcp_config.first_minor, _iodir, mcp_config.num_pins, + mcp_config.device_type, mcp_config._gpio_handle); + ret |= init_uorb(); + return ret; +} + +int MCP230XX::set_up() +{ + int ret = PX4_OK; + uint8_t iodir_addr; + uint8_t olat_addr; + uint8_t gppu_addr; + + for (int i = 0; i < mcp_config.num_banks; i++) { + uint8_t curr_iodir = (uint8_t)((_iodir >> (8 * i)) & 0x00FF); + uint8_t curr_olat = (uint8_t)((_olat >> (8 * i)) & 0x00FF); + uint8_t curr_gppu = (uint8_t)((_gppu >> (8 * i)) & 0x00FF); + + ret |= get_olat(i, &olat_addr); + ret |= get_iodir(i, &iodir_addr); + ret |= get_gppu(i, &gppu_addr); + + if (ret != PX4_OK) { + return ret; + } + + ret |= write_reg(iodir_addr, curr_iodir); + ret |= write_reg(olat_addr, curr_olat); + ret |= write_reg(gppu_addr, curr_gppu); + + if (ret != PX4_OK) { + return ret; + } + } + + return ret; +} + +int MCP230XX::sanity_check() +{ + int ret = PX4_OK; + + for (int i = 0; i < mcp_config.num_banks; i++) { + uint8_t curr_iodir = (uint8_t)((_iodir >> (8 * i)) & 0x00FF); + uint8_t curr_olat = (uint8_t)((_olat >> (8 * i)) & 0x00FF); + uint8_t curr_gppu = (uint8_t)((_gppu >> (8 * i)) & 0x00FF); + + uint8_t iodir_addr; + uint8_t olat_addr; + uint8_t gppu_addr; + + uint8_t read_iodir; + uint8_t read_olat; + uint8_t read_gppu; + + ret |= get_olat(i, &olat_addr); + ret |= get_iodir(i, &iodir_addr); + ret |= get_gppu(i, &gppu_addr); + + ret |= read_reg(iodir_addr, read_iodir); + ret |= read_reg(olat_addr, read_olat); + ret |= read_reg(gppu_addr, read_gppu); + + if (read_iodir != curr_iodir || read_olat != curr_olat || read_gppu != curr_gppu) { + perf_count(_register_check); + return PX4_ERROR; + } + } + + return ret; +} + +int MCP230XX::probe() +{ + // no whoami, try to read IOCONA + uint8_t data; + uint8_t addr; + int ret = PX4_OK; + + for (int i = 0; i < 10; i++) { + ret = get_probe_reg(&addr); + + if (ret == PX4_OK) { + ret = read_reg(addr, data); + + if (ret == PX4_OK) { + return PX4_OK; + } + } + + px4_usleep(10'000); + } + + return PX4_ERROR; +} + +void MCP230XX::RunImpl() +{ + if (should_exit()) { + cleanup_uorb(); + exit_and_cleanup(); + return; + } + + int ret = PX4_OK; + + switch (_curr_state) { + case STATE::INIT_COMMUNICATION: + ret = init_communication(); + + if (ret == PX4_OK) { + _curr_state = STATE::CONFIGURE; + ScheduleNow(); + + } else { + ScheduleDelayed(mcp_config.interval * 1000); + } + + break; + + case STATE::CONFIGURE: + ret = set_up(); + + if (ret == PX4_OK) { + _curr_state = STATE::CHECK; + ScheduleNow(); + + } else { + ScheduleDelayed(mcp_config.interval * 1000); + } + + break; + + case STATE::CHECK: + ret = sanity_check(); + + if (ret == PX4_OK) { + _curr_state = STATE::RUNNING; + ScheduleOnInterval(mcp_config.interval * 1000); + + } else { + _curr_state = STATE::CONFIGURE; + ScheduleDelayed(mcp_config.interval * 1000); + } + + break; + + case STATE::RUNNING: + perf_begin(_cycle_perf); + gpio_config_s config; + + if (_gpio_config_sub.update(&config) && config.device_id == get_device_id()) { + PinType type = PinType::Input; + + switch (config.config) { + case config.INPUT_PULLUP: type = PinType::InputPullUp; break; + + case config.OUTPUT: type = PinType::Output; break; + } + + ret |= write(config.state, config.mask); + ret |= configure(config.mask, type); + } + + gpio_out_s output; + + if (_gpio_out_sub.update(&output) && output.device_id == get_device_id()) { + ret |= write(output.state, output.mask); + } + + { + gpio_in_s _gpio_in; + _gpio_in.timestamp = hrt_absolute_time(); + _gpio_in.device_id = get_device_id(); + uint16_t input = 0; + + if (read(&input) == PX4_OK) { + _gpio_in.state = input; + _to_gpio_in.publish(_gpio_in); + } + } + + _count++; + + if (_count >= _checking_freq || ret != PX4_OK) { + _curr_state = STATE::CHECK; + _count = 0; + ScheduleClear(); + ScheduleNow(); + } + + perf_end(_cycle_perf); + } +} + +void MCP230XX::print_status() +{ + I2CSPIDriverBase::print_status(); + perf_print_counter(_cycle_perf); + perf_print_counter(_comms_errors); + perf_print_counter(_register_check); +} + +void MCP230XX::print_usage() +{ + PRINT_MODULE_USAGE_NAME("mcp230xx", "driver"); + PRINT_MODULE_USAGE_COMMAND("start"); + PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false); + PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x27); + PRINT_MODULE_USAGE_PARAM_INT('D', 0, 0, 65535, "Direction (1=Input, 0=Output)", true); + PRINT_MODULE_USAGE_PARAM_INT('O', 0, 0, 65535, "Output", true); + PRINT_MODULE_USAGE_PARAM_INT('P', 0, 0, 65535, "Pullups", true); + PRINT_MODULE_USAGE_PARAM_INT('U', 0, 0, 1000, "Update Interval [ms]", true); + PRINT_MODULE_USAGE_PARAM_INT('M', 0, 0, 255, "First minor number", true); + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); +} + +I2CSPIDriverBase *MCP230XX::instantiate(const I2CSPIDriverConfig &config, int runtime_instance) +{ + MCP230XX *instance = nullptr; + + switch (config.devid_driver_index) { + case DRV_GPIO_DEVTYPE_MCP23009: + instance = new MCP23009(config); + break; + + case DRV_GPIO_DEVTYPE_MCP23017: + instance = new MCP23017(config); + break; + + default: + instance = nullptr; + break; + } + + if (!instance) { + PX4_ERR("alloc failed"); + return nullptr; + } + + MCP230XX_config_t tmp_config = *static_cast(config.custom_data); + instance->mcp_config = tmp_config; + instance->mcp_config.i2c_bus = config.bus; + + if (OK != instance->init()) { + delete instance; + return nullptr; + } + + instance->_iodir = instance->mcp_config.direction; + instance->_olat = instance->mcp_config.state; + instance->_gppu = instance->mcp_config.pullup; + + return instance; +} diff --git a/src/drivers/gpio/mcp23009/mcp23009.h b/src/lib/drivers/mcp_common/MCP.hpp similarity index 56% rename from src/drivers/gpio/mcp23009/mcp23009.h rename to src/lib/drivers/mcp_common/MCP.hpp index fce68576c5..b4c1a235dd 100644 --- a/src/drivers/gpio/mcp23009/mcp23009.h +++ b/src/lib/drivers/mcp_common/MCP.hpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * Copyright (C) 2025 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,6 @@ * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ - #pragma once #include @@ -39,73 +38,91 @@ #include #include #include -#include -#include -#include +#include #include #include +#include +#include -using namespace time_literals; +static constexpr uint8_t I2C_ADDRESS_MCP23009 = 0x25; +static constexpr uint8_t I2C_ADDRESS_MCP23017 = 0x27; -class MCP23009 : public device::I2C, public I2CSPIDriver +enum class +PinType : uint8_t { + Output, + Input, + InputPullUp, +}; + +struct MCP230XX_config_t { + uint16_t device_type; + uint8_t i2c_addr = 0; + uint8_t i2c_bus = 0; + uint8_t first_minor = 0; + uint8_t num_pins = 8; + int num_banks = 1; + uint16_t interval = 10; + mcp_gpio_dev_s _gpio_handle[16]; + CallbackHandler *_callback_handler = nullptr; + + uint16_t direction = 0xFFFF; + uint16_t state = 0x0000; + uint16_t pullup = 0x0000; +}; + +class MCP230XX : public device::I2C, public I2CSPIDriver { public: - MCP23009(const I2CSPIDriverConfig &config); - ~MCP23009() override; + MCP230XX(const I2CSPIDriverConfig &config); + virtual ~MCP230XX(); + static I2CSPIDriverBase *instantiate(const I2CSPIDriverConfig &config, int runtime_instance); + static void print_usage(); void RunImpl(); - - static void print_usage(); - static I2CSPIDriverBase *instantiate(const I2CSPIDriverConfig &config, int runtime_instance); + void print_status() override; + int probe(); + virtual int init(); protected: - int init_uorb(); - int init(uint8_t direction, uint8_t state, uint8_t pull_up); - - int probe() override; - void print_status() override; - void exit_and_cleanup() override; + MCP230XX_config_t mcp_config; private: - enum class - Register : uint8_t { - IODIR = 0x00, - IPOL = 0x01, - GPINTEN = 0x02, - DEFVAL = 0x03, - INTCON = 0x04, - IOCON = 0x05, - GPPU = 0x06, - INTF = 0x07, - INTCAP = 0x08, - GPIO = 0x09, - OLAT = 0x0a - }; - - enum class - PinType : uint8_t { - Output, - Input, - InputPullUp, - }; - uORB::SubscriptionCallbackWorkItem _gpio_out_sub{this, ORB_ID(gpio_out)}; - uORB::SubscriptionCallbackWorkItem _gpio_request_sub{this, ORB_ID(gpio_request)}; uORB::SubscriptionCallbackWorkItem _gpio_config_sub{this, ORB_ID(gpio_config)}; - - uORB::Publication _to_gpio_in{ORB_ID(gpio_in)}; + uORB::PublicationMulti _to_gpio_in{ORB_ID(gpio_in)}; perf_counter_t _cycle_perf; + perf_counter_t _comms_errors; + perf_counter_t _register_check; - // buffer often used (write-only!) registers here - uint8_t _olat; - uint8_t _iodir; - uint8_t _gppu; + virtual int configure(uint16_t mask, PinType type); + virtual int read(uint16_t *mask); + int read_reg(uint8_t address, uint8_t &data); + int write(uint16_t mask_set, uint16_t mask_clear); + int write_reg(uint8_t address, uint8_t value); + int set_up(); + int sanity_check(); + int init_communication(); + int init_uorb(); + void cleanup_uorb(); - int read(uint8_t *mask); - int write(uint8_t mask_set, uint8_t mask_clear); - int configure(uint8_t mask, PinType type); + virtual int get_olat(int bank, uint8_t *addr) = 0; + virtual int get_gppu(int bank, uint8_t *addr) = 0; + virtual int get_iodir(int bank, uint8_t *addr) = 0; + virtual int get_gpio(int bank, uint8_t *addr) = 0; + virtual int get_probe_reg(uint8_t *addr) = 0; - int read_reg(Register address, uint8_t &data); - int write_reg(Register address, uint8_t data); + uint16_t _olat; + uint16_t _iodir; + uint16_t _gppu; + + uint16_t _checking_freq = 10; + int _count = 0; + + enum class STATE : uint8_t { + INIT_COMMUNICATION, + CONFIGURE, + CHECK, + RUNNING + } _curr_state {STATE::INIT_COMMUNICATION}; };