diff --git a/boards/auterion/fmu-v6x/default.px4board b/boards/auterion/fmu-v6x/default.px4board index dece24a26f..6b463cd527 100644 --- a/boards/auterion/fmu-v6x/default.px4board +++ b/boards/auterion/fmu-v6x/default.px4board @@ -18,7 +18,7 @@ CONFIG_DRIVERS_DIFFERENTIAL_PRESSURE_AUAV=y CONFIG_COMMON_DISTANCE_SENSOR=y CONFIG_DRIVERS_DSHOT=y CONFIG_DRIVERS_GNSS_SEPTENTRIO=y -CONFIG_DRIVERS_GPIO_MCP23009=y +CONFIG_DRIVERS_GPIO_MCP23017=y CONFIG_DRIVERS_GPS=y CONFIG_DRIVERS_IMU_BOSCH_BMI088=y CONFIG_DRIVERS_IMU_INVENSENSE_ICM20602=y @@ -100,3 +100,4 @@ CONFIG_SYSTEMCMDS_TUNE_CONTROL=y CONFIG_SYSTEMCMDS_UORB=y CONFIG_SYSTEMCMDS_VER=y CONFIG_SYSTEMCMDS_WORK_QUEUE=y +CONFIG_SYSTEMCMDS_GPIO=y diff --git a/boards/auterion/fmu-v6x/init/rc.board_defaults b/boards/auterion/fmu-v6x/init/rc.board_defaults index 1a6fc9673c..1cde986e44 100644 --- a/boards/auterion/fmu-v6x/init/rc.board_defaults +++ b/boards/auterion/fmu-v6x/init/rc.board_defaults @@ -32,3 +32,6 @@ if ver hwbasecmp 00a 008 then mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10 fi + +mcp23017 start -b 2 -X -D 0x0000 -O 0x0000 -P 0x0000 -U 10 -R 0x0000 # set all pins to OUTPUT +#mcp23017 start -b 2 -X -D 0xFFFF -O 0x0000 -P 0x0000 -U 10 -R 0x0101 # set all pins to INPUT and enable interrupts on pins 0 and 8 diff --git a/boards/auterion/fmu-v6x/src/CMakeLists.txt b/boards/auterion/fmu-v6x/src/CMakeLists.txt index d0f1b0b1a7..dac2e1b7aa 100644 --- a/boards/auterion/fmu-v6x/src/CMakeLists.txt +++ b/boards/auterion/fmu-v6x/src/CMakeLists.txt @@ -70,5 +70,6 @@ else() nuttx_drivers # sdio px4_layer platform_gpio_mcp23009 + platform_gpio_mcp23017 ) endif() diff --git a/boards/auterion/fmu-v6x/src/init.cpp b/boards/auterion/fmu-v6x/src/init.cpp index 7e35a407c4..6fd494560a 100644 --- a/boards/auterion/fmu-v6x/src/init.cpp +++ b/boards/auterion/fmu-v6x/src/init.cpp @@ -75,6 +75,7 @@ #include #include #include +#include /**************************************************************************** * Pre-Processor Definitions @@ -283,6 +284,7 @@ __EXPORT int board_app_initialize(uintptr_t arg) # endif /* CONFIG_MMCSD */ ret = mcp23009_register_gpios(3, 0x25); + ret |= mcp23017_register_gpios(2, 0x27, 0, 0x0000); if (ret != OK) { led_on(LED_RED); diff --git a/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt b/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt index 208cc5c828..eab0f62c8c 100644 --- a/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt +++ b/platforms/nuttx/src/px4/common/gpio/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(mcp23009) +add_subdirectory(mcp23017) diff --git a/platforms/nuttx/src/px4/common/gpio/mcp23017/CMakeLists.txt b/platforms/nuttx/src/px4/common/gpio/mcp23017/CMakeLists.txt new file mode 100644 index 0000000000..9921c88561 --- /dev/null +++ b/platforms/nuttx/src/px4/common/gpio/mcp23017/CMakeLists.txt @@ -0,0 +1,36 @@ +############################################################################ +# +# Copyright (c) 2020 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(platform_gpio_mcp23017 + mcp23017.cpp + ) +target_link_libraries(platform_gpio_mcp23017 PRIVATE drivers__device) # device::I2C diff --git a/platforms/nuttx/src/px4/common/gpio/mcp23017/mcp23017.cpp b/platforms/nuttx/src/px4/common/gpio/mcp23017/mcp23017.cpp new file mode 100644 index 0000000000..52d2f80587 --- /dev/null +++ b/platforms/nuttx/src/px4/common/gpio/mcp23017/mcp23017.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** + * + * 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 mcp23017_gpio_dev_s { + struct gpio_dev_s gpio; + uint16_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; + } + } + uint16_t input; +}; + +static uORB::Publication toGpioRequest{ORB_ID(gpio_request)}; +static ReadCallback fromGpioIn{ORB_ID(gpio_in)}; +static int mcp23017_read(struct gpio_dev_s *dev, bool *value) +{ + mcp23017_gpio_dev_s *gpio = (struct mcp23017_gpio_dev_s *)dev; + *value = fromGpioIn.input & gpio->mask; + return OK; +} + +static uORB::Publication toGpioOut{ORB_ID(gpio_out)}; +static int mcp23017_write(struct gpio_dev_s *dev, bool value) +{ + mcp23017_gpio_dev_s *gpio = (struct mcp23017_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 mcp23017_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype) +{ + mcp23017_gpio_dev_s *gpio = (struct mcp23017_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 mcp23017_gpio_ops { + mcp23017_read, + mcp23017_write, + nullptr, + nullptr, + mcp23017_setpintype, +}; + +static constexpr uint8_t NUM_GPIOS = 16; +static mcp23017_gpio_dev_s _gpio[NUM_GPIOS]; + +// ---------------------------------------------------------------------------- +int mcp23017_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor, uint16_t dir_mask) +{ + for(int i=0; i + +int mcp23017_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor = 0, uint16_t dir_mask = 0x0000); +int mcp23017_unregister_gpios(int first_minor = 0); diff --git a/src/drivers/drv_sensor.h b/src/drivers/drv_sensor.h index 82969aac2c..4840ff8d83 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..46afaae7c3 100644 --- a/src/drivers/gpio/Kconfig +++ b/src/drivers/gpio/Kconfig @@ -3,6 +3,7 @@ menu "GPIO" bool "Common GPIOs" default n select DRIVERS_GPIO_MCP23009 + select DRIVERS_GPIO_MCP23017 ---help--- Enable default set of GPIO drivers rsource "*/Kconfig" diff --git a/src/drivers/gpio/mcp23017/CMakeLists.txt b/src/drivers/gpio/mcp23017/CMakeLists.txt new file mode 100644 index 0000000000..67f2855d95 --- /dev/null +++ b/src/drivers/gpio/mcp23017/CMakeLists.txt @@ -0,0 +1,41 @@ +############################################################################ +# +# Copyright (c) 2020 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__mcp23017 + MAIN mcp23017 + COMPILE_FLAGS + SRCS + mcp23017_main.cpp + mcp23017.cpp + DEPENDS + ) diff --git a/src/drivers/gpio/mcp23017/Kconfig b/src/drivers/gpio/mcp23017/Kconfig new file mode 100644 index 0000000000..8e446cd5af --- /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 diff --git a/src/drivers/gpio/mcp23017/mcp23017.cpp b/src/drivers/gpio/mcp23017/mcp23017.cpp new file mode 100644 index 0000000000..a048a399e4 --- /dev/null +++ b/src/drivers/gpio/mcp23017/mcp23017.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** + * + * 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.h" + +int MCP23017::read_reg(Register address, uint8_t &data) +{ + int ret = transfer((uint8_t *)&address, 1, &data, 1); + return ret; +} + +int MCP23017::write_reg(Register address, uint8_t value) +{ + uint8_t data[2] = {(uint8_t)address, value}; + return transfer(data, 2, nullptr, 0); +} + +int MCP23017::init(uint16_t direction, uint16_t state, uint16_t pull_up, uint16_t int_en, uint16_t ref_vals, bool split_int) +{ + int ret = I2C::init(); + + if (ret != PX4_OK) { + PX4_ERR("I2C init failed"); + return ret; + } + + // buffer the new initial states + _iodirA = (uint8_t) (direction & 0x00FF); + _olatA = (uint8_t) (state & 0x00FF); + _gppuA = (uint8_t) (pull_up & 0x00FF); + + _iodirB = (uint8_t) (direction >> 8); + _olatB = (uint8_t) (state >> 8); + _gppuB = (uint8_t) (pull_up >> 8); + + // Write the initial state to the device + ret = write_reg(Register::OLATA, _olatA); + ret |= write_reg(Register::OLATB, _olatB); + + //Set pins as input/output + ret |= write_reg(Register::IODIRA, _iodirA); + ret |= write_reg(Register::IODIRB, _iodirB); + + //Set pins as pullup/pulldown + ret |= write_reg(Register::GPPUA, _gppuA); + ret |= write_reg(Register::GPPUB, _gppuB); + + //Enable interrupts + ret |= write_reg(Register::GPINTENA, (uint8_t)(int_en & 0x00FF)); + ret |= write_reg(Register::GPINTENB, (uint8_t)(int_en >> 8)); + + //Write reference values + ret |= write_reg(Register::DEFVALA, (uint8_t)(ref_vals & 0x00FF)); + ret |= write_reg(Register::DEFVALB, (uint8_t)(ref_vals >> 8)); + + //Set interrupt type + ret |= write_reg(Register::INTCONA, 0xFF); + ret |= write_reg(Register::INTCONB, 0xFF); + + if(!split_int){ + ret |= write_reg(Register::IOCONA, 0x40); + } + + if (ret != PX4_OK) { + PX4_ERR("Device init failed (%i)", ret); + return ret; + } + + return init_uorb(); +} + +int MCP23017::probe() +{ + // no whoami, try to read IOCONA + uint8_t data; + return read_reg(Register::IOCONA, data); +} + +int MCP23017::read(uint16_t *mask) +{ + uint8_t maskA; + uint8_t maskB; + + int ret = read_reg(Register::GPIOA, maskA); + ret |= read_reg(Register::GPIOB, maskB); + + *mask = ((uint16_t) maskA & 0x00FF) | ((uint16_t) maskB << 8); + + return ret; +} + +int MCP23017::write(uint16_t mask_set, uint16_t mask_clear) +{ + // no need to read, we can use the buffered register value + uint8_t mask_setA = (uint8_t) (mask_set & 0x00FF); + uint8_t mask_clearA = (uint8_t) (mask_clear & 0x00FF); + + uint8_t mask_setB = (uint8_t) (mask_set >> 8); + uint8_t mask_clearB = (uint8_t) (mask_clear >> 8); + + _olatA = (_olatA & ~mask_clearA) | mask_setA; + _olatB = (_olatB & ~mask_clearB) | mask_setB; + + int ret = write_reg(Register::OLATA, _olatA); + ret |= write_reg(Register::OLATB, _olatB); + + return ret; +} + +int MCP23017::configure(uint16_t mask, PinType type) +{ + uint8_t maskA = (uint8_t) (mask & 0x00FF); + uint8_t maskB = (uint8_t) (mask >> 8); + + // no need to read, we can use the buffered register values + switch (type) { + case PinType::Input: + _iodirA |= maskA; + _iodirB |= maskB; + _gppuA &= ~maskA; + _gppuB &= ~maskB; + break; + + case PinType::InputPullUp: + _iodirA |= maskA; + _iodirB |= maskB; + _gppuA |= maskA; + _gppuB |= maskB; + break; + + case PinType::Output: + _iodirA &= ~maskA; + _iodirB &= ~maskB; + break; + + default: + return -EINVAL; + } + + int ret = write_reg(Register::GPPUA, _gppuA); + ret |= write_reg(Register::GPPUB, _gppuB); + ret |= write_reg(Register::IODIRA, _iodirA); + ret |= write_reg(Register::IODIRB, _iodirB); + + if (ret != 0) { + PX4_ERR("Configuring MCP23017 failed"); + return ret; + } + + return ret; +} + + diff --git a/src/drivers/gpio/mcp23017/mcp23017.h b/src/drivers/gpio/mcp23017/mcp23017.h new file mode 100644 index 0000000000..86fadb2f8e --- /dev/null +++ b/src/drivers/gpio/mcp23017/mcp23017.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * + * Copyright (C) 2020 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 +#include +#include +#include +#include +#include +#include +#include + + +using namespace time_literals; + + +class MCP23017 : public device::I2C, public I2CSPIDriver +{ +public: + MCP23017(const I2CSPIDriverConfig &config); + ~MCP23017() override; + + int init_uorb(); + int init(uint16_t direction, uint16_t state, uint16_t pull_up, uint16_t int_en, uint16_t ref_vals, bool split_int); + static void print_usage(); + void RunImpl(); + int probe() override; + static I2CSPIDriverBase *instantiate(const I2CSPIDriverConfig &config, int runtime_instance); + +protected: + + void print_status() override; + void exit_and_cleanup() override; + +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 + }; + + 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)}; + + perf_counter_t _cycle_perf; + + uint8_t _olatA; + uint8_t _olatB; + uint8_t _iodirA; + uint8_t _iodirB; + uint8_t _gppuA; + uint8_t _gppuB; + + int read(uint16_t *mask); + int write(uint16_t mask_set, uint16_t mask_clear); + int configure(uint16_t mask, PinType type); + + int read_reg(Register address, uint8_t &data); + int write_reg(Register address, uint8_t data); +}; diff --git a/src/drivers/gpio/mcp23017/mcp23017_main.cpp b/src/drivers/gpio/mcp23017/mcp23017_main.cpp new file mode 100644 index 0000000000..f01f015215 --- /dev/null +++ b/src/drivers/gpio/mcp23017/mcp23017_main.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ + +/** + * + * Driver for the MCP23017 connected via I2C. + */ + +#include "mcp23017.h" +#include + +MCP23017::MCP23017(const I2CSPIDriverConfig &config) : + I2C(config), + I2CSPIDriver(config), + _cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample")) +{ + + +} + +MCP23017::~MCP23017() +{ + ScheduleClear(); + perf_free(_cycle_perf); +} + +int MCP23017::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 MCP23017::exit_and_cleanup() +{ + _gpio_config_sub.unregisterCallback(); + _gpio_request_sub.unregisterCallback(); + _gpio_out_sub.unregisterCallback(); +} + +void MCP23017::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); + } + + //get_interrupts(); + + { + gpio_in_s _gpio_in; + _gpio_in.timestamp = hrt_absolute_time(); + _gpio_in.device_id = get_device_id(); + uint16_t input; + read(&input); + _gpio_in.state = input; + _to_gpio_in.publish(_gpio_in); + } + + perf_end(_cycle_perf); +} + +void MCP23017::print_usage() +{ + PRINT_MODULE_USAGE_NAME("MCP23017", "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('R', 0, 0, 65535, "Interrupt pins enable", true); + //PRINT_MODULE_USAGE_PARAM_INT('A', 0, 0, 65535, "Reference values for interrupts", true); + PRINT_MODULE_USAGE_PARAM_INT('U', 0, 0, 1000, "Update Interval [ms]", true); + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); +} + +void MCP23017::print_status() +{ + I2CSPIDriverBase::print_status(); + perf_print_counter(_cycle_perf); +} + +struct init_config_t { + uint16_t interval; + uint16_t direction; + uint16_t state; + uint16_t pullup; + uint16_t int_en; + uint16_t ref_vals; + bool split_int; +}; + +I2CSPIDriverBase *MCP23017::instantiate(const I2CSPIDriverConfig &config, int runtime_instance) +{ + auto *init = (const init_config_t *)config.custom_data; + auto *instance = new MCP23017(config); + + if (!instance) { + PX4_ERR("alloc failed"); + return nullptr; + } + + if (OK != instance->init(init->direction, init->state, init->pullup, init->int_en, init->ref_vals, init->split_int)) { + delete instance; + return nullptr; + } + + if (init->interval) { + instance->ScheduleOnInterval(init->interval * 1000); + } + + return instance; +} + +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; + init_config_t config_data{}; + + config_data.split_int = false; + config_data.ref_vals = 0x0000; + + int ch; + + while ((ch = cli.getOpt(argc, argv, "D:O:P:U:R:")) != 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; + case 'R': + config_data.int_en = (int)strtol(cli.optArg(), nullptr, 0); + break; + /*case 'A': + config_data.ref_vals = (int)strtol(cli.optArg(), nullptr, 0); + 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_MCP23017); + + 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; +}