diff --git a/boards/px4/fmu-v5x/default.px4board b/boards/px4/fmu-v5x/default.px4board index 5c662fbca8..0b6e7f6d7a 100644 --- a/boards/px4/fmu-v5x/default.px4board +++ b/boards/px4/fmu-v5x/default.px4board @@ -17,6 +17,7 @@ CONFIG_DRIVERS_CAMERA_TRIGGER=y CONFIG_COMMON_DIFFERENTIAL_PRESSURE=y CONFIG_COMMON_DISTANCE_SENSOR=y CONFIG_DRIVERS_DSHOT=y +CONFIG_DRIVERS_GPIO_MCP23009=y CONFIG_DRIVERS_GPS=y CONFIG_DRIVERS_HEATER=y CONFIG_DRIVERS_IMU_ANALOG_DEVICES_ADIS16448=y diff --git a/boards/px4/fmu-v5x/init/rc.board_defaults b/boards/px4/fmu-v5x/init/rc.board_defaults index ba812e4b7b..50402d17b2 100644 --- a/boards/px4/fmu-v5x/init/rc.board_defaults +++ b/boards/px4/fmu-v5x/init/rc.board_defaults @@ -19,3 +19,14 @@ param set-default SENS_EN_INA226 1 param set-default SYS_USE_IO 1 safety_button start + +# GPIO Expander driver on external I2C3 +if ver hwtypecmp V5X009000 V5X009001 +then + # No USB + mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -U 10 +fi +if ver hwtypecmp V5X00a000 V5X00a001 V5X008000 V5X008001 +then + mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10 +fi diff --git a/boards/px4/fmu-v5x/src/board_config.h b/boards/px4/fmu-v5x/src/board_config.h index 281c769f26..67cac1acd3 100644 --- a/boards/px4/fmu-v5x/src/board_config.h +++ b/boards/px4/fmu-v5x/src/board_config.h @@ -250,8 +250,8 @@ #define GPIO_VDD_3V3_SD_CARD_EN /* PC13 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTC|GPIO_PIN13) /* MCP23009 GPIO expander */ -#define BOARD_GPIO_VDD_5V_COMP_VALID "/dev/gpin4" -#define BOARD_GPIO_VDD_5V_CAN1_GPS1_VALID "/dev/gpin5" +#define BOARD_GPIO_VDD_5V_COMP_VALID "/dev/gpio4" +#define BOARD_GPIO_VDD_5V_CAN1_GPS1_VALID "/dev/gpio5" /* Spare GPIO */ diff --git a/boards/px4/fmu-v5x/src/init.cpp b/boards/px4/fmu-v5x/src/init.cpp index a6606761db..5987f66ffa 100644 --- a/boards/px4/fmu-v5x/src/init.cpp +++ b/boards/px4/fmu-v5x/src/init.cpp @@ -264,8 +264,9 @@ __EXPORT int board_app_initialize(uintptr_t arg) led_on(LED_RED); } + int ret; #ifdef CONFIG_MMCSD - int ret = stm32_sdio_initialize(); + ret = stm32_sdio_initialize(); if (ret != OK) { led_on(LED_RED); @@ -274,30 +275,11 @@ __EXPORT int board_app_initialize(uintptr_t arg) #endif /* CONFIG_MMCSD */ - int hw_version = board_get_hw_version(); + ret = mcp23009_register_gpios(3, 0x25); - if (hw_version == 0x9 || hw_version == 0xa) { - static MCP23009 mcp23009{3, 0x25}; - - // No USB - if (hw_version == 0x9) { - // < P8 - ret = mcp23009.init(0xf0, 0xf0, 0x0f); - // >= P8 - //ret = mcp23009.init(0xf1, 0xf0, 0x0f); - } - - if (hw_version == 0xa) { - // < P6 - //ret = mcp23009.init(0xf0, 0xf0, 0x0f); - // >= P6 - ret = mcp23009.init(0xf1, 0xf0, 0x0f); - } - - if (ret != OK) { - led_on(LED_RED); - return ret; - } + if (ret != OK) { + led_on(LED_RED); + return ret; } return OK; diff --git a/msg/CMakeLists.txt b/msg/CMakeLists.txt index 611fea9e85..3405c37a32 100644 --- a/msg/CMakeLists.txt +++ b/msg/CMakeLists.txt @@ -90,6 +90,7 @@ set(msg_files FollowTargetStatus.msg GeneratorStatus.msg GeofenceResult.msg + GimbalControls.msg GimbalDeviceAttitudeStatus.msg GimbalDeviceInformation.msg GimbalDeviceSetAttitude.msg @@ -97,9 +98,12 @@ set(msg_files GimbalManagerSetAttitude.msg GimbalManagerSetManualControl.msg GimbalManagerStatus.msg + GpioConfig.msg + GpioIn.msg + GpioOut.msg + GpioRequest.msg GpsDump.msg GpsInjectData.msg - GimbalControls.msg Gripper.msg HealthReport.msg HeaterStatus.msg diff --git a/msg/GpioConfig.msg b/msg/GpioConfig.msg new file mode 100644 index 0000000000..0ff393ec87 --- /dev/null +++ b/msg/GpioConfig.msg @@ -0,0 +1,28 @@ +# GPIO configuration + +uint64 timestamp # time since system start (microseconds) +uint32 device_id # Device id + +uint32 mask # Pin mask +uint32 state # Initial pin output state + +# Configuration Mask +# Bit 0-3: Direction: 0=Input, 1=Output +# Bit 4-7: Input Config: 0=Floating, 1=PullUp, 2=PullDown +# Bit 8-12: Output Config: 0=PushPull, 1=OpenDrain +# Bit 13-31: Reserved +uint32 INPUT = 0 # 0x0000 +uint32 OUTPUT = 1 # 0x0001 +uint32 PULLUP = 16 # 0x0010 +uint32 PULLDOWN = 32 # 0x0020 +uint32 OPENDRAIN = 256 # 0x0100 + +uint32 INPUT_FLOATING = 0 # 0x0000 +uint32 INPUT_PULLUP = 16 # 0x0010 +uint32 INPUT_PULLDOWN = 32 # 0x0020 + +uint32 OUTPUT_PUSHPULL = 0 # 0x0000 +uint32 OUTPUT_OPENDRAIN = 256 # 0x0100 +uint32 OUTPUT_OPENDRAIN_PULLUP = 272 # 0x0110 + +uint32 config diff --git a/msg/GpioIn.msg b/msg/GpioIn.msg new file mode 100644 index 0000000000..0482a2188e --- /dev/null +++ b/msg/GpioIn.msg @@ -0,0 +1,6 @@ +# GPIO mask and state + +uint64 timestamp # time since system start (microseconds) +uint32 device_id # Device id + +uint32 state # pin state mask diff --git a/msg/GpioOut.msg b/msg/GpioOut.msg new file mode 100644 index 0000000000..3865bbf2e9 --- /dev/null +++ b/msg/GpioOut.msg @@ -0,0 +1,7 @@ +# GPIO mask and state + +uint64 timestamp # time since system start (microseconds) +uint32 device_id # Device id + +uint32 mask # pin mask +uint32 state # pin state mask diff --git a/msg/GpioRequest.msg b/msg/GpioRequest.msg new file mode 100644 index 0000000000..3328b00145 --- /dev/null +++ b/msg/GpioRequest.msg @@ -0,0 +1,4 @@ +# Request GPIO mask to be read + +uint64 timestamp # time since system start (microseconds) +uint32 device_id # Device id diff --git a/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp b/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp index ede9c2c172..63a60e5484 100644 --- a/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp +++ b/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009.cpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * 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 @@ -31,182 +31,142 @@ * ****************************************************************************/ +#include #include -#include -#include "mcp23009_registers.hpp" +#include +#include +#include +#include +#include +#include +#include -using namespace Microchip_MCP23009; - -const struct gpio_operations_s MCP23009::gpio_ops = { -go_read : MCP23009::go_read, -go_write : MCP23009::go_write, -go_attach : nullptr, -go_enable : nullptr, -go_setpintype : MCP23009::go_setpintype, +static uint32_t DEVID{0}; +struct mcp23009_gpio_dev_s { + struct gpio_dev_s gpio; + uint8_t mask; }; -MCP23009::MCP23009(int bus, int address, int first_minor, int bus_frequency) : - I2C(DRV_GPIO_DEVTYPE_MCP23009, "MCP23009", bus, address, bus_frequency), - _first_minor(first_minor) +/* Copy the read input data */ +class ReadCallback : public uORB::SubscriptionCallback { -} +public: + using SubscriptionCallback::SubscriptionCallback; -MCP23009::~MCP23009() -{ - /* set all as input & unregister */ - for (int i = 0; i < num_gpios; ++i) { - go_setpintype(i, GPIO_INPUT_PIN); - gpio_pin_unregister(&_gpio[i].gpio, _first_minor + i); - } -} + void call() override + { + px4::msg::GpioIn new_input; -int MCP23009::go_read(struct gpio_dev_s *dev, bool *value) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - return gpio->obj->go_read(gpio->id, value); -} + if (update(&new_input) && new_input.device_id == DEVID) { + input = new_input.state; + } -int MCP23009::go_write(struct gpio_dev_s *dev, bool value) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - return gpio->obj->go_write(gpio->id, value); -} - -int MCP23009::go_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype) -{ - mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev; - return gpio->obj->go_setpintype(gpio->id, pintype); -} - - -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 intital, uint8_t pull_up) -{ - /* do I2C init (and probe) first */ - int ret = I2C::init(); - - if (ret != PX4_OK) { - return ret; } - /* Use this state as the out puts */ + uint8_t input; +}; - ret = write_reg(Register::OLAT, intital); - ret |= write_reg(Register::IODIR, direction); - ret |= write_reg(Register::GPPU, pull_up); +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; +} - if (ret != PX4_OK) { - return ret; +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; } - /* register the pins */ - for (int i = 0; i < num_gpios; ++i) { - _gpio[i].gpio.gp_pintype = GPIO_INPUT_PIN; - _gpio[i].gpio.gp_ops = &gpio_ops; - _gpio[i].id = i; - _gpio[i].obj = this; - ret = gpio_pin_register(&_gpio[i].gpio, _first_minor + i); + return toGpioConfig.publish(msg) ? OK : -ETIMEDOUT; +} - if (ret != PX4_OK) { + + +// ---------------------------------------------------------------------------- +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; } } - return ret; + fromGpioIn.registerCallback(); + return OK; } -int MCP23009::probe() +int mcp23009_unregister_gpios(int first_minor) { - // no whoami, try to read IOCON - uint8_t data; - return read_reg(Register::IOCON, data); -} - -int MCP23009::go_read(int id, bool *value) -{ - - uint8_t data; - int ret = read_reg(Register::GPIO, data); - - if (ret != 0) { - return ret; - } - - *value = data & (1 << id); - return 0; -} - -int MCP23009::go_write(int id, bool value) -{ - uint8_t data; - int ret = read_reg(Register::GPIO, data); - - if (ret != 0) { - return ret; - } - - if (value) { - data |= (1 << id); - - } else { - data &= ~(1 << id); - } - - return write_reg(Register::GPIO, data); -} - -int MCP23009::go_setpintype(int id, enum gpio_pintype_e pintype) -{ - uint8_t direction; - int ret = read_reg(Register::IODIR, direction); - - if (ret != 0) { - return ret; - } - - uint8_t pullup; - ret = read_reg(Register::GPPU, pullup); - - if (ret != 0) { - return ret; - } - - switch (pintype) { - case GPIO_INPUT_PIN: - direction |= (1 << id); - pullup &= ~(1 << id); - break; - - case GPIO_INPUT_PIN_PULLUP: - direction |= (1 << id); - pullup |= (1 << id); - break; - - case GPIO_OUTPUT_PIN: - direction &= ~(1 << id); - break; - - default: - return -EINVAL; - } - - _gpio[id].gpio.gp_pintype = pintype; - - ret = write_reg(Register::GPPU, pullup); - - if (ret != 0) { - return ret; - } - - return write_reg(Register::IODIR, direction); + 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/gpio/mcp23009/mcp23009_registers.hpp b/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009_registers.hpp deleted file mode 100644 index 1e99a0ea03..0000000000 --- a/platforms/nuttx/src/px4/common/gpio/mcp23009/mcp23009_registers.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** - * - * 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 - -// TODO: move to a central header -static constexpr uint8_t Bit0 = (1 << 0); -static constexpr uint8_t Bit1 = (1 << 1); -static constexpr uint8_t Bit2 = (1 << 2); -static constexpr uint8_t Bit3 = (1 << 3); -static constexpr uint8_t Bit4 = (1 << 4); -static constexpr uint8_t Bit5 = (1 << 5); -static constexpr uint8_t Bit6 = (1 << 6); -static constexpr uint8_t Bit7 = (1 << 7); - -namespace Microchip_MCP23009 -{ - -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 - -}; - -} // namespace Microchip_MCP23009 diff --git a/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp b/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp index 3a12aaaca7..481f5b51d7 100644 --- a/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp +++ b/platforms/nuttx/src/px4/common/include/px4_platform/gpio/mcp23009.hpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * 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 @@ -33,49 +33,7 @@ #pragma once -#include +#include -#include - -using namespace time_literals; - -namespace Microchip_MCP23009 -{ -enum class Register : uint8_t; -} - -class MCP23009 : public device::I2C -{ -public: - MCP23009(int bus, int address, int first_minor = 0, int bus_frequency = 400000); - virtual ~MCP23009(); - - int init(uint8_t direction, uint8_t intital = 0, uint8_t pull_up = 0); - -protected: - int probe() override; - -private: - static constexpr int num_gpios = 8; - static const gpio_operations_s gpio_ops; - - struct mcp23009_gpio_dev_s { - struct gpio_dev_s gpio; - uint8_t id; - MCP23009 *obj; - }; - - static int go_read(struct gpio_dev_s *dev, bool *value); - static int go_write(struct gpio_dev_s *dev, bool value); - static int go_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype); - - int go_read(int id, bool *value); - int go_write(int id, bool value); - int go_setpintype(int id, enum gpio_pintype_e pintype); - - int read_reg(Microchip_MCP23009::Register address, uint8_t &data); - int write_reg(Microchip_MCP23009::Register address, uint8_t data); - - const int _first_minor; - mcp23009_gpio_dev_s _gpio[num_gpios] {}; -}; +int mcp23009_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor = 0); +int mcp23009_unregister_gpios(int first_minor = 0); diff --git a/src/drivers/gpio/Kconfig b/src/drivers/gpio/Kconfig new file mode 100644 index 0000000000..902613ebbf --- /dev/null +++ b/src/drivers/gpio/Kconfig @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000000..c7897fea8e --- /dev/null +++ b/src/drivers/gpio/mcp23009/CMakeLists.txt @@ -0,0 +1,41 @@ +############################################################################ +# +# 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__mcp23009 + MAIN mcp23009 + COMPILE_FLAGS + SRCS + mcp23009_main.cpp + mcp23009.cpp + DEPENDS + ) diff --git a/src/drivers/gpio/mcp23009/Kconfig b/src/drivers/gpio/mcp23009/Kconfig new file mode 100644 index 0000000000..73078bf8eb --- /dev/null +++ b/src/drivers/gpio/mcp23009/Kconfig @@ -0,0 +1,5 @@ +menuconfig DRIVERS_GPIO_MCP23009 + bool "mcp23009" + default n + ---help--- + Enable support for mcp23009 diff --git a/src/drivers/gpio/mcp23009/mcp23009.cpp b/src/drivers/gpio/mcp23009/mcp23009.cpp new file mode 100644 index 0000000000..8d8eb92885 --- /dev/null +++ b/src/drivers/gpio/mcp23009/mcp23009.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** + * + * 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.h b/src/drivers/gpio/mcp23009/mcp23009.h new file mode 100644 index 0000000000..fce68576c5 --- /dev/null +++ b/src/drivers/gpio/mcp23009/mcp23009.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * + * 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 MCP23009 : public device::I2C, public I2CSPIDriver +{ +public: + MCP23009(const I2CSPIDriverConfig &config); + ~MCP23009() override; + + void RunImpl(); + + static void print_usage(); + static I2CSPIDriverBase *instantiate(const I2CSPIDriverConfig &config, int runtime_instance); + +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; + +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)}; + + perf_counter_t _cycle_perf; + + // buffer often used (write-only!) registers here + uint8_t _olat; + uint8_t _iodir; + uint8_t _gppu; + + int read(uint8_t *mask); + int write(uint8_t mask_set, uint8_t mask_clear); + int configure(uint8_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/mcp23009/mcp23009_main.cpp b/src/drivers/gpio/mcp23009/mcp23009_main.cpp new file mode 100644 index 0000000000..f5f9a2cfc4 --- /dev/null +++ b/src/drivers/gpio/mcp23009/mcp23009_main.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** + * + * 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/systemcmds/gpio/gpio.cpp b/src/systemcmds/gpio/gpio.cpp index 343c1bbd18..20db2a96d0 100644 --- a/src/systemcmds/gpio/gpio.cpp +++ b/src/systemcmds/gpio/gpio.cpp @@ -314,8 +314,8 @@ $ gpio read H4 PULLUP Set the output value on Port E pin 7 to high $ gpio write E7 1 --force -Set the output value on device /dev/gpin1 to high -$ gpio write /dev/gpin1 1 +Set the output value on device /dev/gpio1 to high +$ gpio write /dev/gpio1 1 )DESCR_STR");