diff --git a/make/source.mk b/make/source.mk
index b28ef2a65..4196f9f93 100644
--- a/make/source.mk
+++ b/make/source.mk
@@ -34,6 +34,7 @@ COMMON_SRC = \
drivers/exti.c \
drivers/io.c \
drivers/light_led.c \
+ drivers/pinio.c \
drivers/resource.c \
drivers/rcc.c \
drivers/serial.c \
@@ -67,6 +68,7 @@ COMMON_SRC = \
pg/bus_spi.c \
pg/dashboard.c \
pg/max7456.c \
+ pg/pinio.c \
pg/pg.c \
pg/rx_pwm.c \
pg/sdcard.c \
diff --git a/src/main/drivers/pinio.c b/src/main/drivers/pinio.c
new file mode 100644
index 000000000..c760ee50c
--- /dev/null
+++ b/src/main/drivers/pinio.c
@@ -0,0 +1,76 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#include
+
+#include
+
+#ifdef USE_PINIO
+
+#include "build/debug.h"
+
+#include "pg/pinio.h"
+
+#include "drivers/io.h"
+
+typedef struct pinioRuntime_s {
+ IO_t io;
+ bool inverted;
+ bool state;
+} pinioRuntime_t;
+
+static pinioRuntime_t pinioRuntime[PINIO_COUNT];
+
+void pinioInit(const pinioConfig_t *pinioConfig)
+{
+ for (int i = 0; i < PINIO_COUNT; i++) {
+ IO_t io = IOGetByTag(pinioConfig->ioTag[i]);
+
+ if (!io) {
+ continue;
+ }
+
+ IOInit(io, OWNER_PINIO, RESOURCE_INDEX(i));
+
+ switch (pinioConfig->config[i] & PINIO_CONFIG_MODE_MASK) {
+ case PINIO_CONFIG_MODE_OUT_PP:
+ IOConfigGPIO(io, IOCFG_OUT_PP);
+ break;
+ }
+
+ if (pinioConfig->config[i] & PINIO_CONFIG_OUT_INVERTED)
+ {
+ pinioRuntime[i].inverted = true;
+ IOHi(io);
+ } else {
+ pinioRuntime[i].inverted = false;
+ IOLo(io);
+ }
+ pinioRuntime[i].io = io;
+ pinioRuntime[i].state = false;
+ }
+}
+
+void pinioSet(int index, bool on)
+{
+ bool newState = on ^ pinioRuntime[index].inverted;
+ if (newState != pinioRuntime[index].state) {
+ IOWrite(pinioRuntime[index].io, newState);
+ pinioRuntime[index].state = newState;
+ }
+}
+#endif
diff --git a/src/main/drivers/pinio.h b/src/main/drivers/pinio.h
new file mode 100644
index 000000000..8ad9b5302
--- /dev/null
+++ b/src/main/drivers/pinio.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#pragma once
+
+#ifndef PINIO_COUNT
+#define PINIO_COUNT 4
+#endif
+
+#define PINIO_CONFIG_OUT_INVERTED 0x80
+#define PINIO_CONFIG_MODE_MASK 0x7F
+#define PINIO_CONFIG_MODE_OUT_PP 0x01
+
+struct pinioConfig_s;
+
+void pinioInit(const struct pinioConfig_s *pinioConfig);
+void pinioSet(int index, bool on);
diff --git a/src/main/drivers/resource.c b/src/main/drivers/resource.c
index cc11efad7..dd6ba5e27 100644
--- a/src/main/drivers/resource.c
+++ b/src/main/drivers/resource.c
@@ -69,5 +69,6 @@ const char * const ownerNames[OWNER_TOTAL_COUNT] = {
"CAMERA_CONTROL",
"TIMUP",
"RANGEFINDER",
- "RX_SPI"
+ "RX_SPI",
+ "PINIO",
};
diff --git a/src/main/drivers/resource.h b/src/main/drivers/resource.h
index 30daf039e..c5f522f54 100644
--- a/src/main/drivers/resource.h
+++ b/src/main/drivers/resource.h
@@ -70,6 +70,7 @@ typedef enum {
OWNER_TIMUP,
OWNER_RANGEFINDER,
OWNER_RX_SPI,
+ OWNER_PINIO,
OWNER_TOTAL_COUNT
} resourceOwner_e;
diff --git a/src/main/fc/fc_init.c b/src/main/fc/fc_init.c
index 92ecf2472..ce6e73eb3 100644
--- a/src/main/fc/fc_init.c
+++ b/src/main/fc/fc_init.c
@@ -87,6 +87,7 @@
#include "pg/bus_i2c.h"
#include "pg/bus_spi.h"
#include "pg/flash.h"
+#include "pg/pinio.h"
#include "pg/pg.h"
#include "pg/rx_pwm.h"
#include "pg/sdcard.h"
@@ -533,6 +534,10 @@ void init(void)
servosFilterInit();
#endif
+#ifdef USE_PINIO
+ pinioInit(pinioConfig());
+#endif
+
LED1_ON;
LED0_OFF;
LED2_OFF;
diff --git a/src/main/interface/cli.c b/src/main/interface/cli.c
index 4b55e6012..f1cf8da2a 100644
--- a/src/main/interface/cli.c
+++ b/src/main/interface/cli.c
@@ -118,6 +118,7 @@ extern uint8_t __config_end;
#include "pg/beeper_dev.h"
#include "pg/bus_i2c.h"
#include "pg/bus_spi.h"
+#include "pg/pinio.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "pg/rx_pwm.h"
@@ -3223,6 +3224,9 @@ const cliResourceValue_t resourceTable[] = {
{ OWNER_SDCARD_CS, PG_SDCARD_CONFIG, offsetof(sdcardConfig_t, chipSelectTag), 0 },
{ OWNER_SDCARD_DETECT, PG_SDCARD_CONFIG, offsetof(sdcardConfig_t, cardDetectTag), 0 },
#endif
+#ifdef USE_PINIO
+ { OWNER_PINIO, PG_PINIO_CONFIG, offsetof(pinioConfig_t, ioTag), PINIO_COUNT },
+#endif
};
static ioTag_t *getIoTag(const cliResourceValue_t value, uint8_t index)
diff --git a/src/main/interface/settings.c b/src/main/interface/settings.c
index 2eca07a65..b5c7a8b25 100644
--- a/src/main/interface/settings.c
+++ b/src/main/interface/settings.c
@@ -33,6 +33,7 @@
#include "drivers/bus_spi.h"
#include "drivers/camera_control.h"
#include "drivers/light_led.h"
+#include "drivers/pinio.h"
#include "drivers/vtx_common.h"
#include "fc/config.h"
@@ -68,6 +69,7 @@
#include "pg/max7456.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
+#include "pg/pinio.h"
#include "pg/rx_pwm.h"
#include "pg/sdcard.h"
#include "pg/vcd.h"
@@ -860,6 +862,11 @@ const clivalue_t valueTable[] = {
#ifdef USE_RANGEFINDER
{ "rangefinder_hardware", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_RANGEFINDER_HARDWARE }, PG_RANGEFINDER_CONFIG, offsetof(rangefinderConfig_t, rangefinder_hardware) },
#endif
+
+// PG_PINIO_CONFIG
+#ifdef USE_PINIO
+ { "pinio_config", VAR_UINT8 | MASTER_VALUE | MODE_ARRAY, .config.array.length = PINIO_COUNT, PG_PINIO_CONFIG, offsetof(pinioConfig_t, config) },
+#endif
};
const uint16_t valueTableEntryCount = ARRAYLEN(valueTable);
diff --git a/src/main/pg/pg_ids.h b/src/main/pg/pg_ids.h
index e63a93999..589ef1fda 100644
--- a/src/main/pg/pg_ids.h
+++ b/src/main/pg/pg_ids.h
@@ -119,7 +119,8 @@
#define PG_TIME_CONFIG 526
#define PG_RANGEFINDER_CONFIG 527 // iNav
#define PG_TRICOPTER_CONFIG 528
-#define PG_BETAFLIGHT_END 528
+#define PG_PINIO_CONFIG 529
+#define PG_BETAFLIGHT_END 529
// OSD configuration (subject to change)
diff --git a/src/main/pg/pinio.c b/src/main/pg/pinio.c
new file mode 100644
index 000000000..386257eaa
--- /dev/null
+++ b/src/main/pg/pinio.c
@@ -0,0 +1,55 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#include "platform.h"
+
+#ifdef USE_PINIO
+
+#include "pg/pg_ids.h"
+#include "pinio.h"
+#include "drivers/io.h"
+
+#ifndef PINIO1_PIN
+#define PINIO1_PIN NONE
+#endif
+#ifndef PINIO2_PIN
+#define PINIO2_PIN NONE
+#endif
+#ifndef PINIO3_PIN
+#define PINIO3_PIN NONE
+#endif
+#ifndef PINIO4_PIN
+#define PINIO4_PIN NONE
+#endif
+
+PG_REGISTER_WITH_RESET_TEMPLATE(pinioConfig_t, pinioConfig, PG_PINIO_CONFIG, 0);
+
+PG_RESET_TEMPLATE(pinioConfig_t, pinioConfig,
+ .ioTag = {
+ IO_TAG(PINIO1_PIN),
+ IO_TAG(PINIO2_PIN),
+ IO_TAG(PINIO3_PIN),
+ IO_TAG(PINIO4_PIN),
+ },
+ .config = {
+ PINIO_CONFIG_MODE_OUT_PP,
+ PINIO_CONFIG_MODE_OUT_PP,
+ PINIO_CONFIG_MODE_OUT_PP,
+ PINIO_CONFIG_MODE_OUT_PP
+ },
+);
+#endif
diff --git a/src/main/pg/pinio.h b/src/main/pg/pinio.h
new file mode 100644
index 000000000..1b3cc5200
--- /dev/null
+++ b/src/main/pg/pinio.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#pragma once
+
+#include "pg/pg.h"
+#include "drivers/io_types.h"
+#include "drivers/pinio.h"
+
+typedef struct pinioConfig_s {
+ ioTag_t ioTag[PINIO_COUNT];
+ uint8_t config[PINIO_COUNT];
+} pinioConfig_t;
+
+PG_DECLARE(pinioConfig_t, pinioConfig);
diff --git a/src/main/target/common_fc_pre.h b/src/main/target/common_fc_pre.h
index 1bb8502e5..5f39c104b 100644
--- a/src/main/target/common_fc_pre.h
+++ b/src/main/target/common_fc_pre.h
@@ -136,6 +136,7 @@
#define USE_MSP_OVER_TELEMETRY
#define USE_OSD
#define USE_OSD_OVER_MSP_DISPLAYPORT
+#define USE_PINIO
#define USE_RCDEVICE
#define USE_RTC_TIME
#define USE_RX_MSP