diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 2bb1fa937..d369086d0 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1121,7 +1121,7 @@ int cliSerialPassthrough(const char **argv) params.baudrate = baudrate; spInternalModuleState = modulePortInitSerial(port_n, ETX_MOD_PORT_UART, - ¶ms); + ¶ms, false); auto drv = modulePortGetSerialDrv(spInternalModuleState->rx); auto ctx = modulePortGetCtx(spInternalModuleState->rx); diff --git a/radio/src/hal/module_port.cpp b/radio/src/hal/module_port.cpp index c6df3ead5..e9789a150 100644 --- a/radio/src/hal/module_port.cpp +++ b/radio/src/hal/module_port.cpp @@ -95,8 +95,36 @@ static void _init_timer_driver(etx_module_driver_t* d, const etx_module_port_t* } } +static bool _match_port(const etx_module_port_t* p, uint8_t type, uint8_t port, + uint8_t polarity, bool softserial_fallback) +{ + if (p->type == type && p->port == port) { + // either polarity matches or can be set + if (polarity != ETX_Pol_Inverted || + p->set_inverted || + port == ETX_MOD_PORT_SOFT_INV || + port == ETX_MOD_PORT_SPORT_INV) { + return true; + } + } + + // soft-serial fallback (1. USART -> 2. SOFT-SERIAL) + if (softserial_fallback && polarity == ETX_Pol_Inverted) { + if (port == ETX_MOD_PORT_UART && p->port == ETX_MOD_PORT_SOFT_INV) { + return true; + } + + if (port == ETX_MOD_PORT_SPORT && p->port == ETX_MOD_PORT_SPORT_INV) { + return true; + } + } + + return false; +} + static const etx_module_port_t* _find_port(uint8_t module, uint8_t type, - uint8_t port, uint8_t polarity) + uint8_t port, uint8_t polarity, + bool softserial_fallback) { if (module >= MAX_MODULES || module >= _n_modules || !_modules[module]) return nullptr; @@ -107,18 +135,9 @@ static const etx_module_port_t* _find_port(uint8_t module, uint8_t type, while(n_ports > 0) { - if (p->type == type && p->port == port) { - - // skip the port if polarity does not match - // and cannot be set - if (polarity != ETX_Pol_Inverted || - p->set_inverted || - port == ETX_MOD_PORT_SOFT_INV || - port == ETX_MOD_PORT_SPORT_INV) { - - found_port = p; - break; - } + if (_match_port(p, type, port, polarity, softserial_fallback)) { + found_port = p; + break; } ++p; --n_ports; @@ -146,7 +165,7 @@ const etx_module_t* modulePortGetModuleDescription(uint8_t module) const etx_module_port_t* modulePortFind(uint8_t module, uint8_t type, uint8_t port, uint8_t polarity) { - return _find_port(module, type, port, polarity); + return _find_port(module, type, port, polarity, false); } void modulePortSetPower(uint8_t module, uint8_t enable) @@ -170,11 +189,12 @@ bool modulePortPowered(uint8_t module) } etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port, - const etx_serial_init* params) + const etx_serial_init* params, + bool softserial_fallback) { - // TODO: match capabilities (1. USART -> 2. SOFT-SERIAL) const etx_module_port_t* found_port = _find_port(module, ETX_MOD_TYPE_SERIAL, - port, params->polarity); + port, params->polarity, + softserial_fallback); if (!found_port) return nullptr; auto state = &(_module_states[module]); @@ -206,7 +226,7 @@ etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port, const etx_timer_config_t* cfg) { const etx_module_port_t* found_port = _find_port(module, ETX_MOD_TYPE_TIMER, - port, ETX_Pol_Normal); + port, ETX_Pol_Normal, false); if (!found_port) return nullptr; auto state = &(_module_states[module]); diff --git a/radio/src/hal/module_port.h b/radio/src/hal/module_port.h index 677852f70..e60a83ab5 100644 --- a/radio/src/hal/module_port.h +++ b/radio/src/hal/module_port.h @@ -126,7 +126,8 @@ bool modulePortPowered(uint8_t module); // Init module port with params (driver & context stored locally) etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port, - const etx_serial_init* params); + const etx_serial_init* params, + bool softserial_fallback); // Init module port with params (driver & context stored locally) etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port, diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index 6d5e76f86..f6cd833d4 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -343,7 +343,7 @@ const char *FrskyDeviceFirmwareUpdate::doFlashFirmware( // uint8_t serial_module = module != SPORT_MODULE ? module : EXTERNAL_MODULE; - mod_st = modulePortInitSerial(serial_module, port, &cfg); + mod_st = modulePortInitSerial(serial_module, port, &cfg, false); if (!mod_st) return "Communication port error"; // assume RX port is the same as TX diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index b3651a240..429140e4c 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -90,31 +90,25 @@ bool MultiFirmwareUpdateDriver::init() { if (type == MULTI_TYPE_MULTIMODULE && module == INTERNAL_MODULE) { // duplex internal USART - mod_st = modulePortInitSerial(INTERNAL_MODULE, ETX_MOD_PORT_UART, &serialInitParams); + mod_st = modulePortInitSerial(INTERNAL_MODULE, ETX_MOD_PORT_UART, &serialInitParams, false); } else if (type == MULTI_TYPE_MULTIMODULE && module == EXTERNAL_MODULE) { // TX on PPM (inverted softserial) etx_serial_init params(serialInitParams); params.direction = ETX_Dir_TX; params.polarity = ETX_Pol_Inverted; - mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_UART, ¶ms); - if (!mod_st) { - params.polarity = ETX_Pol_Inverted; - mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SOFT_INV, ¶ms); - if (!mod_st) return false; - } + mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_UART, ¶ms, true); + if (!mod_st) return false; + // RX on S.PORT (inverted softserial) params.direction = ETX_Dir_RX; params.polarity = ETX_Pol_Inverted; - if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, ¶ms)) { - params.polarity = ETX_Pol_Inverted; - if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT_INV, ¶ms)) { - modulePortDeInit(mod_st); - return false; - } + if (!modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, ¶ms, true)) { + modulePortDeInit(mod_st); + return false; } } else if (type == MULTI_TYPE_ELRS && module == EXTERNAL_MODULE) { // half-duplex on S.PORT USART - mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, &serialInitParams); + mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, &serialInitParams, false); } if (!mod_st) return false; diff --git a/radio/src/pulses/afhds2.cpp b/radio/src/pulses/afhds2.cpp index b84b89685..dd62f863a 100644 --- a/radio/src/pulses/afhds2.cpp +++ b/radio/src/pulses/afhds2.cpp @@ -44,7 +44,7 @@ static void* afhds2Init(uint8_t module) // serial port setup resetPulsesAFHDS2(); auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, - &afhds2SerialInitParams); + &afhds2SerialInitParams, false); // mixer setup mixerSchedulerSetPeriod(module, AFHDS2_PERIOD); diff --git a/radio/src/pulses/afhds3.cpp b/radio/src/pulses/afhds3.cpp index e1a52ecc0..19e962ac1 100644 --- a/radio/src/pulses/afhds3.cpp +++ b/radio/src/pulses/afhds3.cpp @@ -1110,13 +1110,13 @@ static void* initModule(uint8_t module) params.baudrate = AFHDS3_UART_BAUDRATE; params.polarity = module == INTERNAL_MODULE ? ETX_Pol_Normal : ETX_Pol_Inverted; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, false); #if defined(CONFIGURABLE_MODULE_PORT) if (!mod_st && module == EXTERNAL_MODULE) { // Try Connect using aux serial mod params.polarity = ETX_Pol_Normal; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, false); } #endif @@ -1125,7 +1125,7 @@ static void* initModule(uint8_t module) params.baudrate = AFHDS3_SOFTSERIAL_BAUDRATE; params.direction = ETX_Dir_TX; period = AFHDS3_SOFTSERIAL_COMMAND_TIMEOUT * 1000 /* us */; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms, false); // TODO: telemetry RX ??? } diff --git a/radio/src/pulses/crossfire.cpp b/radio/src/pulses/crossfire.cpp index 26ba69f2c..bbc2f2f6a 100644 --- a/radio/src/pulses/crossfire.cpp +++ b/radio/src/pulses/crossfire.cpp @@ -226,7 +226,7 @@ static void* crossfireInit(uint8_t module) if (module == INTERNAL_MODULE) { params.baudrate = INT_CROSSFIRE_BAUDRATE; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, false); } #endif @@ -234,7 +234,7 @@ static void* crossfireInit(uint8_t module) if (module == EXTERNAL_MODULE) { params.baudrate = EXT_CROSSFIRE_BAUDRATE; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms, false); } #endif diff --git a/radio/src/pulses/dsm2.cpp b/radio/src/pulses/dsm2.cpp index df66b756c..4bfc122b4 100644 --- a/radio/src/pulses/dsm2.cpp +++ b/radio/src/pulses/dsm2.cpp @@ -203,19 +203,12 @@ static etx_module_state_t* dsmInit(uint8_t module, uint32_t baudrate, etx_serial_init params(dsmUartParams); params.baudrate = baudrate; - auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); - if (!mod_st) { - // inverted soft-serial fallback - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms); - if (!mod_st) return nullptr; - } + auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, true); + if (!mod_st) return nullptr; if (telemetry) { params.direction = ETX_Dir_RX; - if (!modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms)) { - // inverted soft-serial fallback - modulePortInitSerial(module, ETX_MOD_PORT_SPORT_INV, ¶ms); - } + modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms, true); } mixerSchedulerSetPeriod(module, period); diff --git a/radio/src/pulses/ghost.cpp b/radio/src/pulses/ghost.cpp index 56efac5f2..2a5f44a51 100755 --- a/radio/src/pulses/ghost.cpp +++ b/radio/src/pulses/ghost.cpp @@ -163,7 +163,7 @@ static const etx_serial_init ghostSerialParams = { static void* ghostInit(uint8_t module) { - auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &ghostSerialParams); + auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &ghostSerialParams, false); if (!mod_st) return nullptr; mixerSchedulerSetPeriod(module, GHOST_PERIOD); diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 5eea499ee..94a6da0a5 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -210,7 +210,7 @@ static void* multiInit(uint8_t module) // serial port setup // TODO: error handling cfg.direction = ETX_Dir_TX_RX; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg, false); } #endif @@ -219,19 +219,14 @@ static void* multiInit(uint8_t module) // serial port setup cfg.direction = ETX_Dir_TX; cfg.polarity = ETX_Pol_Inverted; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg); - if (!mod_st) { - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &cfg); - } - - // no TX port: bail out + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &cfg, true); if (!mod_st) return nullptr; // Init S.PORT RX channel cfg.direction = ETX_Dir_RX; cfg.polarity = ETX_Pol_Normal; - modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &cfg); + modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &cfg, false); } #endif diff --git a/radio/src/pulses/ppm.cpp b/radio/src/pulses/ppm.cpp index cc8e9124f..2ebf0e58e 100644 --- a/radio/src/pulses/ppm.cpp +++ b/radio/src/pulses/ppm.cpp @@ -111,12 +111,7 @@ static etx_serial_init ppmMLinkSerialParams = { static bool ppmInitMLinkTelemetry(uint8_t module) { // Try S.PORT hardware USART (requires HW inverters) - if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &ppmMLinkSerialParams) != nullptr) { - return true; - } - - // fall-back to softserial - if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT_INV, &ppmMLinkSerialParams) != nullptr) { + if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &ppmMLinkSerialParams, true) != nullptr) { return true; } diff --git a/radio/src/pulses/pxx1.cpp b/radio/src/pulses/pxx1.cpp index 7ea34ac92..eba0084c0 100644 --- a/radio/src/pulses/pxx1.cpp +++ b/radio/src/pulses/pxx1.cpp @@ -285,13 +285,13 @@ static void* pxx1Init(uint8_t module) if (module == INTERNAL_MODULE) { txCfg.baudrate = _pxx1_internal_baudrate; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg, false); if (!mod_st) { // assume that radios that don't have an internal UART // will have a module that uses legacy PXX1 (PWM) txCfg.encoding = ETX_Encoding_PXX1_PWM; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg, false); } if (!mod_st) return nullptr; @@ -305,14 +305,14 @@ static void* pxx1Init(uint8_t module) case MODULE_TYPE_R9M_LITE_PXX1: { txCfg.baudrate = EXTMODULE_PXX1_SERIAL_BAUDRATE; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg, false); if (!mod_st) return nullptr; } break; case MODULE_TYPE_XJT_PXX1: case MODULE_TYPE_R9M_PXX1: { txCfg.encoding = ETX_Encoding_PXX1_PWM; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &txCfg, false); if (!mod_st) return nullptr; } break; @@ -328,7 +328,7 @@ static void* pxx1Init(uint8_t module) rxCfg.direction = ETX_Dir_TX_RX; // TODO: handle init errors properly - if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &rxCfg) != nullptr) { + if (modulePortInitSerial(module, ETX_MOD_PORT_SPORT, &rxCfg, false) != nullptr) { auto drv = modulePortGetSerialDrv(mod_st->rx); auto ctx = modulePortGetCtx(mod_st->rx); if (drv && ctx && drv->setIdleCb) { diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index 688d6c0dc..3d953e42f 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -640,7 +640,7 @@ static void* pxx2Init(uint8_t module) if (module == INTERNAL_MODULE) { params.baudrate = PXX2_HIGHSPEED_BAUDRATE; - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, false); if (!mod_st) return nullptr; resetAccessAuthenticationCount(); } @@ -665,7 +665,7 @@ static void* pxx2Init(uint8_t module) return nullptr; } - mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms); + mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, false); if (!mod_st) return nullptr; } diff --git a/radio/src/pulses/sbus.cpp b/radio/src/pulses/sbus.cpp index d83d72e68..a5fb56209 100644 --- a/radio/src/pulses/sbus.cpp +++ b/radio/src/pulses/sbus.cpp @@ -21,6 +21,7 @@ #include "sbus.h" #include "hal/module_port.h" +#include "hal/serial_driver.h" #include "mixer_scheduler.h" #include "opentx.h" @@ -96,7 +97,7 @@ const etx_serial_init sbusUartParams = { .baudrate = SBUS_BAUDRATE, .encoding = ETX_Encoding_8E2, .direction = ETX_Dir_TX, - .polarity = ETX_Pol_Normal, + .polarity = ETX_Pol_Inverted, }; static void* sbusInit(uint8_t module) @@ -106,13 +107,8 @@ static void* sbusInit(uint8_t module) if (module == INTERNAL_MODULE) return nullptr; #endif - // auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &sbusUartParams); - // TODO: check if inverter is there, or mandate it somehow... - // if (!mod_st) { - // -> SOFT_INV has 'setPolarity' - auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, &sbusUartParams); + auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &sbusUartParams, true); if (!mod_st) return nullptr; - // } mixerSchedulerSetPeriod(module, SBUS_PERIOD(module)); return (void*)mod_st; @@ -139,9 +135,12 @@ static void sbusSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_ auto drv = modulePortGetSerialDrv(mod_st->tx); auto drv_ctx = modulePortGetCtx(mod_st->tx); - // TODO: check if the port has an inverter and use it instead - if(drv->setPolarity) + auto port = mod_st->tx.port; + if (port && port->set_inverted) { + port->set_inverted(GET_SBUS_POLARITY(module)); + } else if (drv->setPolarity) { drv->setPolarity(drv_ctx, GET_SBUS_POLARITY(module)); + } drv->sendBuffer(drv_ctx, buffer, p_data - buffer); diff --git a/radio/src/targets/common/arm/stm32/trainer_driver.cpp b/radio/src/targets/common/arm/stm32/trainer_driver.cpp index b35ea7502..cc4e79148 100644 --- a/radio/src/targets/common/arm/stm32/trainer_driver.cpp +++ b/radio/src/targets/common/arm/stm32/trainer_driver.cpp @@ -364,7 +364,7 @@ void init_trainer_module_sbus() if (sbus_trainer_mod_st) return; sbus_trainer_mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_UART, - &sbusTrainerParams); + &sbusTrainerParams, false); } void stop_trainer_module_sbus() diff --git a/radio/src/tests/module_ports.cpp b/radio/src/tests/module_ports.cpp new file mode 100644 index 000000000..10bc9341d --- /dev/null +++ b/radio/src/tests/module_ports.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include "gtests.h" +#include "hal/module_port.h" + +TEST(ports, softserialFallback) +{ + modulePortInit(); + + const etx_serial_init serialCfg = { + .baudrate = 57600, + .encoding = ETX_Encoding_8N1, + .direction = ETX_Dir_RX, + .polarity = ETX_Pol_Inverted, + }; + + bool has_softserial = modulePortFind(EXTERNAL_MODULE, ETX_MOD_TYPE_SERIAL, + ETX_MOD_PORT_SPORT_INV, ETX_Pol_Inverted); + if (has_softserial) { + auto mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, + &serialCfg, false); + EXPECT_TRUE(mod_st == nullptr); + + mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, + &serialCfg, true); + EXPECT_TRUE(mod_st != nullptr); + if (!mod_st) return; + + modulePortDeInit(mod_st); + } +}