diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index befe290db..ddcb6714e 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -23,6 +23,7 @@ #include "mixer_scheduler.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" +#include "hal/module_port.h" #include "switches.h" #if defined(USBJ_EX) @@ -1972,10 +1973,11 @@ void menuModelSetup(event_t event) #endif if (isModuleR9MNonAccess(moduleIdx)) { lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY); - if (isSportLineUsedByInternalModule()) - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL); - else + if (modulePortIsPortUsedByModule(moduleIdx, ETX_MOD_PORT_SPORT)) { lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON); + } else { + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL); + } } else if (isModuleSBUS(moduleIdx)) { lcdDrawTextAlignedLeft(y, STR_WARN_BATTVOLTAGE); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index eceda5c15..d06d5621f 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -22,6 +22,7 @@ #include "hal/adc_driver.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" +#include "hal/module_port.h" #include "opentx.h" #include "mixer_scheduler.h" @@ -1739,11 +1740,11 @@ void menuModelSetup(event_t event) #endif if (isModuleR9MNonAccess(moduleIdx)) { lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY); - if (isSportLineUsedByInternalModule()) { - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL); + if (modulePortIsPortUsedByModule(moduleIdx, ETX_MOD_PORT_SPORT)) { + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON); } else { - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL); } } else if (isModuleSBUS(moduleIdx)) { diff --git a/radio/src/hal/module_port.cpp b/radio/src/hal/module_port.cpp index e9789a150..c2f24aed9 100644 --- a/radio/src/hal/module_port.cpp +++ b/radio/src/hal/module_port.cpp @@ -57,11 +57,14 @@ static void modulePortClear(etx_module_state_t* st) memset(st, 0, sizeof(etx_module_state_t)); } -static void _init_serial_driver(etx_module_driver_t* d, const etx_module_port_t* port, +static bool _init_serial_driver(etx_module_driver_t* d, const etx_module_port_t* port, const etx_serial_init* params) { auto drv = port->drv.serial; - d->ctx = drv->init(port->hw_def, params); + auto ctx = drv->init(port->hw_def, params); + if (!ctx) return false; + + d->ctx = ctx; d->port = port; // S.PORT specific HW settings @@ -80,19 +83,26 @@ static void _init_serial_driver(etx_module_driver_t* d, const etx_module_port_t* if (port->set_inverted) { port->set_inverted(params->polarity == ETX_Pol_Inverted); } + + return true; } -static void _init_timer_driver(etx_module_driver_t* d, const etx_module_port_t* port, +static bool _init_timer_driver(etx_module_driver_t* d, const etx_module_port_t* port, const etx_timer_config_t* cfg) { auto drv = port->drv.timer; - d->ctx = drv->init(port->hw_def, cfg); + auto ctx = drv->init(port->hw_def, cfg); + if (!ctx) return false; + + d->ctx = ctx; d->port = port; // setup polarity if (port->set_inverted) { port->set_inverted(false); } + + return true; } static bool _match_port(const etx_module_port_t* p, uint8_t type, uint8_t port, @@ -202,10 +212,11 @@ etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port, const uint8_t duplex = ETX_Dir_TX_RX; uint8_t dir = params->direction & duplex; + bool init_ok = false; if (dir == duplex) { // init RX first, in case TX was already done previously - _init_serial_driver(&state->rx, found_port, params); + init_ok = _init_serial_driver(&state->rx, found_port, params); // do not overwrite TX state if it has already been set: // -> support using S.PORT in bidir mode @@ -214,12 +225,12 @@ etx_module_state_t* modulePortInitSerial(uint8_t module, uint8_t port, state->tx.ctx = state->rx.ctx; } } else if (dir == ETX_Dir_TX) { - _init_serial_driver(&state->tx, found_port, params); + init_ok = _init_serial_driver(&state->tx, found_port, params); } else if (dir == ETX_Dir_RX) { - _init_serial_driver(&state->rx, found_port, params); + init_ok = _init_serial_driver(&state->rx, found_port, params); } - return state; + return init_ok ? state : nullptr; } etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port, @@ -230,9 +241,9 @@ etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port, if (!found_port) return nullptr; auto state = &(_module_states[module]); - _init_timer_driver(&state->tx, found_port, cfg); + bool init_ok = _init_timer_driver(&state->tx, found_port, cfg); - return state; + return init_ok ? state : nullptr; } static void _deinit_driver(etx_module_driver_t* d) @@ -262,6 +273,14 @@ void modulePortDeInit(etx_module_state_t* st) modulePortClear(st); } +void modulePortDeInitRxPort(etx_module_state_t* st) +{ + if (st->rx.port) { + _deinit_driver(&st->rx); + memset(&st->rx, 0, sizeof(etx_module_driver_t)); + } +} + etx_module_state_t* modulePortGetState(uint8_t module) { if (module >= MAX_MODULES) return nullptr; @@ -272,3 +291,31 @@ uint8_t modulePortGetModule(etx_module_state_t* st) { return st - _module_states; } + +bool modulePortIsPortUsedByModule(uint8_t module, uint8_t port) +{ + auto mod_st = modulePortGetState(module); + if (!mod_st) return false; + + auto tx_port = mod_st->tx.port; + auto rx_port = mod_st->rx.port; + + return (tx_port && tx_port->port == port) || + (rx_port && rx_port->port == port); +} + +bool modulePortIsPortUsed(uint8_t port) +{ + return modulePortGetModuleForPort(port) >= 0; +} + +int8_t modulePortGetModuleForPort(uint8_t port) +{ + for (uint8_t module = 0; module < MAX_MODULES; module++) { + if (modulePortIsPortUsedByModule(module, port)) { + return module; + } + } + + return -1; +} diff --git a/radio/src/hal/module_port.h b/radio/src/hal/module_port.h index e60a83ab5..1a227dfb5 100644 --- a/radio/src/hal/module_port.h +++ b/radio/src/hal/module_port.h @@ -136,6 +136,9 @@ etx_module_state_t* modulePortInitTimer(uint8_t module, uint8_t port, // De-init port and clear data void modulePortDeInit(etx_module_state_t* st); +// De-init RX part +void modulePortDeInitRxPort(etx_module_state_t* st); + // Once initialized, retrieve module serial driver and context etx_module_state_t* modulePortGetState(uint8_t module); @@ -159,3 +162,7 @@ inline void* modulePortGetCtx(const etx_module_driver_t& d) { return d.ctx; } +bool modulePortIsPortUsedByModule(uint8_t module, uint8_t port); +bool modulePortIsPortUsed(uint8_t port); + +int8_t modulePortGetModuleForPort(uint8_t port); diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 6fb1aa766..1c8bf5a9b 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -28,6 +28,7 @@ #include "storage/storage.h" #include "globals.h" #include "MultiProtoDefs.h" +#include "hal/module_port.h" #if defined(MULTIMODULE) #include "telemetry/multi.h" @@ -728,25 +729,13 @@ inline bool isBindCh9To16Allowed(uint8_t moduleIndex) } } -#if defined(PCBTARANIS) || defined(PCBHORUS) -inline bool isSportLineUsedByInternalModule() -{ - return g_model.moduleData[INTERNAL_MODULE].type == MODULE_TYPE_XJT_PXX1; -} -#else -inline bool isSportLineUsedByInternalModule() -{ - return false; -} -#endif - inline bool isTelemAllowedOnBind(uint8_t moduleIndex) { #if defined(HARDWARE_INTERNAL_MODULE) if (moduleIndex == INTERNAL_MODULE) return true; - if (isSportLineUsedByInternalModule()) + if (!modulePortIsPortUsedByModule(moduleIndex, ETX_MOD_PORT_SPORT)) return false; #endif diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index e1c55a679..c6bdb5a09 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -139,8 +139,8 @@ static void setupPulsesMulti(uint8_t*& p_buf, uint8_t module) } // Invert telemetry if needed - if (invert[module] & 0x80 && - !g_model.moduleData[module].multi.disableTelemetry) { + uint8_t disableTelemetry = modulePortIsPortUsedByModule(module, ETX_MOD_PORT_SPORT) ? 0 : 1; + if (invert[module] & 0x80 && !disableTelemetry) { if (getMultiModuleStatus(module).isValid()) { invert[module] &= 0x08; // Telemetry received, stop searching } else if (counter[module] % 100 == 0) { @@ -170,7 +170,7 @@ static void setupPulsesMulti(uint8_t*& p_buf, uint8_t module) | (g_model.header.modelId[module] & 0x30) | (invert[module] & 0x08) //| 0x04 // Future use - | (g_model.moduleData[module].multi.disableTelemetry << 1) + | (disableTelemetry << 1) | g_model.moduleData[module].multi.disableMapping)); } diff --git a/radio/src/pulses/multi.h b/radio/src/pulses/multi.h index 9bed85e9c..eb1f8d9ec 100644 --- a/radio/src/pulses/multi.h +++ b/radio/src/pulses/multi.h @@ -21,23 +21,6 @@ #pragma once -#include "pulses_common.h" -#include "hal/serial_driver.h" #include "hal/module_driver.h" -class UartMultiPulses: public DataBuffer -{ - public: - void initFrame() - { - initBuffer(); - } - - void sendByte(uint8_t b) - { - if (getSize() < 64) - *ptr++ = b; - } -}; - extern const etx_proto_driver_t MultiDriver; diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index d73685a30..f4f6db58e 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -111,6 +111,19 @@ void restartModule(uint8_t module) mixerTaskStart(); } +void pulsesRestartModuleUnsafe(uint8_t module) +{ + if (module >= MAX_MODULES) + return; + + auto mod_drv = pulsesGetModuleDriver(module); + if (!mod_drv->drv) return; + + auto drv = mod_drv->drv; + drv->deinit(mod_drv->ctx); + mod_drv->ctx = drv->init(module); +} + #if !defined(SIMU) #include #include diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index a54043e9f..4ba572d75 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -175,6 +175,12 @@ void pulsesSetModuleDeInitCb(module_deinit_cb_t cb); void restartModule(uint8_t module); bool restartModuleAsync(uint8_t module, uint8_t cnt_delay); +// Re-Init module +// +// Note: this can only be used from within +// module init. +void pulsesRestartModuleUnsafe(uint8_t module); + void pulsesModuleSettingsUpdate(uint8_t module); void setupPulsesPPMTrainer(); diff --git a/radio/src/pulses/pxx.cpp b/radio/src/pulses/pxx.cpp new file mode 100644 index 000000000..6d6c77d71 --- /dev/null +++ b/radio/src/pulses/pxx.cpp @@ -0,0 +1,46 @@ +/* + * 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 "pxx.h" +#include "hal/module_port.h" + +bool pxxClearSPort() +{ + auto sport_module = modulePortGetModuleForPort(ETX_MOD_PORT_SPORT); + if (sport_module >= 0) { + // verify S.PORT is not used for TX (otherwise fail early) + auto mod_st = modulePortGetState(sport_module); + if (mod_st && mod_st->tx.port && + mod_st->tx.port->port == ETX_MOD_PORT_SPORT) { + return false; + } + } else { + // S.PORT soft-serial cannot be used for sending + sport_module = modulePortGetModuleForPort(ETX_MOD_PORT_SPORT_INV); + } + + if (sport_module >= 0) { + auto mod_st = modulePortGetState(sport_module); + if (mod_st) modulePortDeInitRxPort(mod_st); + } + + return true; +} diff --git a/radio/src/pulses/pxx.h b/radio/src/pulses/pxx.h index 3e6aca2a0..41b8e42f0 100644 --- a/radio/src/pulses/pxx.h +++ b/radio/src/pulses/pxx.h @@ -45,3 +45,7 @@ #define EXTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/ #define EXTMODULE_PXX1_SERIAL_BAUDRATE 420000 + +// Releases S.PORT usage from +// the module driver using it. +bool pxxClearSPort(); diff --git a/radio/src/pulses/pxx1.cpp b/radio/src/pulses/pxx1.cpp index eba0084c0..d002fb16f 100644 --- a/radio/src/pulses/pxx1.cpp +++ b/radio/src/pulses/pxx1.cpp @@ -19,6 +19,7 @@ * GNU General Public License for more details. */ +#include "dataconstants.h" #include "hal/module_port.h" #include "heartbeat_driver.h" #include "mixer_scheduler.h" @@ -69,14 +70,17 @@ void Pxx1Pulses::addExtraFlags(uint8_t module) extraFlags |= (g_model.moduleData[module].pxx.receiverTelemetryOff << 1); extraFlags |= (g_model.moduleData[module].pxx.receiverHigherChannels << 2); if (isModuleR9MNonAccess(module)) { - extraFlags |= (min(g_model.moduleData[module].pxx.power, isModuleR9M_FCC_VARIANT(module) ? (uint8_t)R9M_FCC_POWER_MAX : (uint8_t)R9M_LBT_POWER_MAX) << 3); - if (isModuleR9M_EUPLUS(module)) - extraFlags |= (1 << 6); + extraFlags |= (min(g_model.moduleData[module].pxx.power, + isModuleR9M_FCC_VARIANT(module) + ? (uint8_t)R9M_FCC_POWER_MAX + : (uint8_t)R9M_LBT_POWER_MAX) + << 3); + if (isModuleR9M_EUPLUS(module)) extraFlags |= (1 << 6); } #if defined(HARDWARE_EXTERNAL_MODULE) && defined(HARDWARE_INTERNAL_MODULE) - // Disable S.PORT if internal module is active - if (module == EXTERNAL_MODULE && isSportLineUsedByInternalModule()) { + // Disable S.PORT if port is not used (might be used by the internal module) + if (module == EXTERNAL_MODULE && !modulePortIsPortUsedByModule(module, ETX_MOD_PORT_SPORT)) { extraFlags |= (1 << 5); } #endif @@ -284,6 +288,8 @@ static void* pxx1Init(uint8_t module) if (module == INTERNAL_MODULE) { + if (!pxxClearSPort()) return nullptr; + txCfg.baudrate = _pxx1_internal_baudrate; mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, &txCfg, false); @@ -361,7 +367,12 @@ static void* pxx1Init(uint8_t module) static void pxx1DeInit(void* ctx) { auto mod_st = (etx_module_state_t*)ctx; + auto module = modulePortGetModule(mod_st); modulePortDeInit(mod_st); + + if (module == INTERNAL_MODULE) { + pulsesRestartModuleUnsafe(EXTERNAL_MODULE); + } } static void pxx1SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels) diff --git a/radio/src/serial.cpp b/radio/src/serial.cpp index cc756fbf8..e11e164f1 100644 --- a/radio/src/serial.cpp +++ b/radio/src/serial.cpp @@ -294,7 +294,7 @@ static void serialSetupPort(int mode, etx_serial_init& params) case UART_MODE_TELEMETRY_MIRROR: // TODO: query telemetry baudrate / add setting for module #if defined(CROSSFIRE) - if (modelTelemetryProtocol() == PROTOCOL_TELEMETRY_CROSSFIRE) { + if (isModuleCrossfire(EXTERNAL_MODULE) || isModuleCrossfire(INTERNAL_MODULE)) { params.baudrate = CROSSFIRE_TELEM_MIRROR_BAUDRATE; break; } @@ -303,7 +303,8 @@ static void serialSetupPort(int mode, etx_serial_init& params) break; case UART_MODE_TELEMETRY: - if (modelTelemetryProtocol() == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { + if (isModulePPM(EXTERNAL_MODULE) && + g_model.telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { params.baudrate = FRSKY_D_BAUDRATE; params.direction = ETX_Dir_RX; } diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 449bbcd27..3daf7cd69 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -212,6 +212,7 @@ if(PXX1) add_definitions(-DPXX -DPXX1) set(PULSES_SRC ${PULSES_SRC} + pxx.cpp pxx1.cpp ) endif() diff --git a/radio/src/targets/common/arm/stm32/stm32_serial_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_serial_driver.cpp index 693fcae4c..79830760d 100644 --- a/radio/src/targets/common/arm/stm32/stm32_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_serial_driver.cpp @@ -226,7 +226,6 @@ static void _on_rx_fifo(uint8_t data) static void* stm32_serial_init(void* hw_def, const etx_serial_init* params) { - // TODO: check if port is already in use auto sp = (const stm32_serial_port*)hw_def; if (!sp) return nullptr; @@ -234,8 +233,8 @@ static void* stm32_serial_init(void* hw_def, const etx_serial_init* params) auto st = stm32_serial_find_state(usart); if (!st || st->sp) return nullptr; - stm32_usart_init(usart, params); - st->sp = sp; // TODO: only iff init() is sucessful + if (!stm32_usart_init(usart, params)) return nullptr; + st->sp = sp; if (params->direction & ETX_Dir_TX) { // prepare for send_byte() diff --git a/radio/src/targets/common/arm/stm32/stm32_softserial_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_softserial_driver.cpp index 5a58fdc90..9104f1dee 100644 --- a/radio/src/targets/common/arm/stm32/stm32_softserial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_softserial_driver.cpp @@ -21,6 +21,7 @@ #include "stm32_softserial_driver.h" #include "stm32_exti_driver.h" +#include "stm32_timer.h" #include @@ -67,9 +68,13 @@ static inline void _rx_fifo_clear() rxRidx = 0; } -static void _softserial_init_rx(const stm32_softserial_rx_port* port, +static bool _softserial_init_rx(const stm32_softserial_rx_port* port, const etx_serial_init* params) { + // Test if pin is in reset state + uint32_t mode = LL_GPIO_GetPinMode(port->GPIOx, port->GPIO_Pin); + if (mode != LL_GPIO_MODE_INPUT) return false; + rxBitCount = 0; rxBuffer = port->buffer.buffer; rxBufLen = port->buffer.length; @@ -80,11 +85,11 @@ static void _softserial_init_rx(const stm32_softserial_rx_port* port, LL_TIM_StructInit(&timInit); uint32_t freq = params->baudrate * 16; - if (!freq) return; + if (!freq) return false; timInit.Prescaler = __LL_TIM_CALC_PSC(port->TIM_Freq, freq); - // TODO: enable_tim_clock(port->TIMx); + stm32_timer_enable_clock(port->TIMx); LL_TIM_Init(port->TIMx, &timInit); LL_TIM_ClearFlag_UPDATE(port->TIMx); @@ -119,6 +124,8 @@ static void _softserial_init_rx(const stm32_softserial_rx_port* port, // Configure EXTI for raising edge (start bit; assuming inverted serial) _softserialPort = port; stm32_exti_enable(port->EXTI_Line, LL_EXTI_TRIGGER_RISING, _softserial_exti); + + return true; } static void _softserial_deinit_gpio(const stm32_softserial_rx_port* port) @@ -142,7 +149,7 @@ static void _softserial_deinit_rx(const stm32_softserial_rx_port* port) static void* stm32_softserial_rx_init(void* hw_def, const etx_serial_init* params) { auto port = (const stm32_softserial_rx_port*)hw_def; - _softserial_init_rx(port, params); + if (!_softserial_init_rx(port, params)) return nullptr; return hw_def; } diff --git a/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp index 71f513004..d5f6423d1 100644 --- a/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp @@ -221,8 +221,17 @@ void stm32_usart_deinit_rx_dma(const stm32_usart_t* usart) // - asymmetric bitrates for half-duplex (GHOST) // - ??? ability to switch RX DMA OFF ??? (-> X12S) // -void stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params) +bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params) { + // Test if the GPIO pins are in reset state + uint32_t pins = usart->GPIO_Pin; + while(pins != 0) { + uint32_t pin = 1 << POSITION_VAL(pins); + uint32_t mode = LL_GPIO_GetPinMode(usart->GPIOx, pin); + if (mode != LL_GPIO_MODE_INPUT) return false; + pins ^= pin; + } + enable_usart_clock(usart->USARTx); LL_USART_DeInit(usart->USARTx); @@ -258,8 +267,8 @@ void stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params) break; default: - // TODO: return some error - return; + stm32_usart_deinit(usart); + return false; } usartInit.HardwareFlowControl = LL_USART_HWCONTROL_NONE; @@ -302,6 +311,8 @@ void stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params) LL_USART_IsEnabledIT_RXNE(usart->USARTx)) { _enable_usart_irq(usart); } + + return true; } void stm32_usart_deinit(const stm32_usart_t* usart) diff --git a/radio/src/targets/common/arm/stm32/stm32_usart_driver.h b/radio/src/targets/common/arm/stm32/stm32_usart_driver.h index 0a3ed976b..bc9e53627 100644 --- a/radio/src/targets/common/arm/stm32/stm32_usart_driver.h +++ b/radio/src/targets/common/arm/stm32/stm32_usart_driver.h @@ -54,7 +54,7 @@ struct stm32_usart_t { uint8_t txDMA_IRQ_Prio; }; -void stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params); +bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params); void stm32_usart_init_rx_dma(const stm32_usart_t* usart, const void* buffer, uint32_t length); void stm32_usart_enable_tx_irq(const stm32_usart_t* usart); void stm32_usart_set_idle_irq(const stm32_usart_t* usart, uint32_t enabled); diff --git a/radio/src/targets/simu/module_drivers.cpp b/radio/src/targets/simu/module_drivers.cpp index eec84a656..85ede59ec 100644 --- a/radio/src/targets/simu/module_drivers.cpp +++ b/radio/src/targets/simu/module_drivers.cpp @@ -23,6 +23,7 @@ #include "hal/serial_driver.h" #include "hal/module_port.h" #include "dataconstants.h" +#include "debug.h" void intmoduleStop() {} void intmoduleFifoError() {} @@ -60,8 +61,32 @@ void stop_trainer_module_sbus() {} void init_intmodule_heartbeat() {} void stop_intmodule_heartbeat() {} -static void* init(void*, const etx_serial_init*) { return (void*)1; } -static void deinit(void*) {} +static bool _sport_used = false; + +static void* init(void* ctx, const etx_serial_init*) +{ + if (ctx == nullptr) { + return (void*)1; + } + + if (ctx == (void*)&_sport_used) { + if (_sport_used) { + return nullptr; + } + + _sport_used = true; + return &_sport_used; + } + + return nullptr; +} + +static void deinit(void* ctx) +{ + if (ctx != (void*)&_sport_used) return; + _sport_used = false; +} + static void sendByte(void*, uint8_t) {} static void sendBuffer(void*, const uint8_t*, uint32_t) {} static void waitForTxCompleted(void*) {} @@ -127,7 +152,7 @@ const etx_module_port_t _internal_ports[] = { .type = ETX_MOD_TYPE_SERIAL, .dir_flags = ETX_MOD_DIR_TX | ETX_MOD_DIR_RX, .drv = { .serial = &_fakeSerialDriver }, - .hw_def = nullptr, + .hw_def = &_sport_used, }, #endif }; @@ -170,7 +195,7 @@ const etx_module_port_t _external_ports[] = { .drv = { .timer = &_fakeTimerDriver }, .hw_def = nullptr, }, - // TX inverted DMA pulse train on S.PORT + // TX inverted DMA pulse train on PPM { .port = ETX_MOD_PORT_SOFT_INV, .type = ETX_MOD_TYPE_SERIAL, @@ -184,7 +209,7 @@ const etx_module_port_t _external_ports[] = { .type = ETX_MOD_TYPE_SERIAL, .dir_flags = ETX_MOD_DIR_TX | ETX_MOD_DIR_RX, .drv = { .serial = &_fakeSerialDriver }, - .hw_def = nullptr, + .hw_def = &_sport_used, }, #if defined(TELEMETRY_TIMER) // RX soft-serial sampled bit-by-bit via timer IRQ on S.PORT diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 0ec330215..1c6b951f4 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -19,6 +19,7 @@ * GNU General Public License for more details. */ #include "opentx.h" +#include "hal/module_port.h" #include "telemetry.h" #include "io/multi_protolist.h" @@ -83,6 +84,11 @@ MultiModuleStatus &getMultiModuleStatus(uint8_t module) return multiModuleStatus[module]; } +static uint8_t _getMultiStatusModuleIdx(const MultiModuleStatus* p) +{ + return p - multiModuleStatus; +} + uint8_t getMultiBindStatus(uint8_t module) { return multiBindStatus[module]; @@ -116,6 +122,11 @@ MultiModuleStatus& getMultiModuleStatus(uint8_t) return multiModuleStatus; } +static uint8_t _getMultiStatusModuleIdx(const MultiModuleStatus*) +{ + return EXTERNAL_MODULE; +} + uint8_t getMultiBindStatus(uint8_t) { return multiBindStatus; @@ -497,14 +508,11 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) void MultiModuleStatus::getStatusString(char * statusText) const { if (!isValid()) { -#if defined(PCBFRSKY) -#if !defined(INTERNAL_MODULE_MULTI) - if (isSportLineUsedByInternalModule()) + if (!modulePortIsPortUsedByModule(getModuleIndex(), ETX_MOD_PORT_SPORT)) { strcpy(statusText, STR_DISABLE_INTERNAL); - else -#endif -#endif - strcpy(statusText, STR_MODULE_NO_TELEMETRY); + } else { + strcpy(statusText, STR_MODULE_NO_TELEMETRY); + } return; } if (!protocolValid()) { @@ -556,6 +564,10 @@ void MultiModuleStatus::getStatusString(char * statusText) const } } +uint8_t MultiModuleStatus::getModuleIndex() const { + return _getMultiStatusModuleIdx(this); +} + static void processMultiTelemetryByte(const uint8_t data, uint8_t module) { uint8_t * rxBuffer = getTelemetryRxBuffer(module); diff --git a/radio/src/telemetry/multi.h b/radio/src/telemetry/multi.h index f5ec3cc8c..6bc111b25 100644 --- a/radio/src/telemetry/multi.h +++ b/radio/src/telemetry/multi.h @@ -120,6 +120,8 @@ struct MultiModuleStatus { void getStatusString(char * statusText) const; + uint8_t getModuleIndex() const; + inline bool isValid() const { return (bool)(get_tmr10ms() - lastUpdate < 200); } inline bool isBufferFull() const { return (bool) (flags & 0x80); } inline bool supportsDisableMapping() const { return (bool) (flags & 0x40); } diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index a1737d2a5..192162c14 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -68,8 +68,6 @@ uint8_t telemetryState = TELEMETRY_INIT; TelemetryData telemetryData; -uint8_t telemetryProtocol = 255; - #if defined(INTERNAL_MODULE_SERIAL_TELEMETRY) static uint8_t intTelemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; static uint8_t intTelemetryRxBufferCount; @@ -418,60 +416,9 @@ void telemetryReset() } telemetryStreaming = 0; // reset counter only if valid telemetry packets are being detected - telemetryState = TELEMETRY_INIT; } -// we don't reset the telemetry here as we would also reset the consumption after model load -void telemetryInit(uint8_t protocol) -{ - telemetryProtocol = protocol; - -// if (protocol == PROTOCOL_TELEMETRY_FRSKY_D) { -// telemetryPortInit(FRSKY_D_BAUDRATE, TELEMETRY_SERIAL_DEFAULT); -// } -// #if defined(MULTIMODULE) -// else if (protocol == PROTOCOL_TELEMETRY_MULTIMODULE) { -// // The DIY Multi module always speaks 100000 baud regardless of the -// // telemetry protocol in use -// telemetryPortInit(MULTIMODULE_BAUDRATE, TELEMETRY_SERIAL_8E2); -// #if defined(LUA) -// outputTelemetryBuffer.reset(); -// #endif -// telemetryPortSetDirectionInput(); -// } else if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM) { -// // Spektrum's own small race RX (SPM4648) uses 125000 8N1, use the same -// // since there is no real standard -// telemetryPortInit(125000, TELEMETRY_SERIAL_DEFAULT); -// } -// #endif - -// #if defined(GHOST) -// else if (protocol == PROTOCOL_TELEMETRY_GHOST) { -// telemetryPortInit(GHOST_BAUDRATE, TELEMETRY_SERIAL_DEFAULT); -// #if defined(LUA) -// outputTelemetryBuffer.reset(); -// #endif -// telemetryPortSetDirectionOutput(); -// } -// #endif - -// #if defined(AUX_SERIAL) -// else if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { -// telemetryPortInit(0, TELEMETRY_SERIAL_DEFAULT); -// } -// #endif -// else if (protocol == PROTOCOL_TELEMETRY_DSMP) { -// // soft serial -// telemetryPortInvertedInit(115200); -// } else { -// telemetryPortInit(FRSKY_SPORT_BAUDRATE, TELEMETRY_SERIAL_WITHOUT_DMA); -// #if defined(LUA) -// outputTelemetryBuffer.reset(); -// #endif -// } -} - #if defined(LOG_TELEMETRY) && !defined(SIMU) extern FIL g_telemetryFile; void logTelemetryWriteStart() diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 6fc6d01df..123cb4081 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -133,60 +133,6 @@ typedef struct { rxStatStruct *getRxStatLabels(); -// TODO: this should handle only the external S.PORT line -// - and go away in the end: one proto per module, not global! -// -inline uint8_t modelTelemetryProtocol() -{ -#if defined(HARDWARE_EXTERNAL_MODULE) - bool sportUsed = isSportLineUsedByInternalModule(); - -#if defined(CROSSFIRE) - if (isModuleCrossfire(EXTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_CROSSFIRE; - } -#endif - -#if defined(GHOST) - if (isModuleGhost(EXTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_GHOST; - } -#endif - - // TODO: PPM driver should support setting up a telemetry parser callback - if (!sportUsed && isModulePPM(EXTERNAL_MODULE)) { - return g_model.telemetryProtocol; - } - -#if defined(MULTIMODULE) - if (!sportUsed && isModuleMultimodule(EXTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_MULTIMODULE; - } -#endif - -#if defined(AFHDS3) - if (isModuleAFHDS3(EXTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_AFHDS3; - } -#endif - - if (isModuleDSMP(EXTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_DSMP; - } - -#endif // HARDWARE_EXTERNAL_MODULE - - // TODO: Check if that is really necessary... -#if defined(AFHDS2) && defined(HARDWARE_INTERNAL_MODULE) - if (isModuleAFHDS2A(INTERNAL_MODULE)) { - return PROTOCOL_TELEMETRY_FLYSKY_NV14; - } -#endif - - // default choice - return PROTOCOL_TELEMETRY_FRSKY_SPORT; -} - #include "telemetry_sensors.h" #if defined(LOG_TELEMETRY) && !defined(SIMU) diff --git a/radio/src/tests/module_ports.cpp b/radio/src/tests/module_ports.cpp index 10bc9341d..9302d5961 100644 --- a/radio/src/tests/module_ports.cpp +++ b/radio/src/tests/module_ports.cpp @@ -20,7 +20,11 @@ */ #include "gtests.h" +#include "hal/module_driver.h" #include "hal/module_port.h" +#include "pulses/modules_constants.h" +#include "pulses/pulses.h" +#include "translations.h" TEST(ports, softserialFallback) { @@ -48,3 +52,188 @@ TEST(ports, softserialFallback) modulePortDeInit(mod_st); } } + +#if defined(HARDWARE_EXTERNAL_MODULE) +TEST(ports, isPortUsed) +{ + modulePortInit(); + + const etx_serial_init serialCfg = { + .baudrate = 57600, + .encoding = ETX_Encoding_8N1, + .direction = ETX_Dir_TX_RX, + .polarity = ETX_Pol_Normal, + }; + + auto mod_st = modulePortInitSerial(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT, + &serialCfg, false); + EXPECT_TRUE(mod_st != nullptr); + EXPECT_TRUE(mod_st && mod_st->rx.port != nullptr); + + auto module = modulePortGetModuleForPort(ETX_MOD_PORT_SPORT); + EXPECT_EQ(EXTERNAL_MODULE, module); + + if (mod_st) modulePortDeInit(mod_st); + EXPECT_FALSE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); +} +#endif + +#if defined(INTERNAL_MODULE_PXX1) && defined(HARDWARE_EXTERNAL_MODULE) +#include "pulses/pxx1.h" + +static void _setModuleDrv(uint8_t module, const etx_proto_driver_t* drv, void* ctx) +{ + auto mod_drv = pulsesGetModuleDriver(module); + mod_drv->drv = drv; + mod_drv->ctx = ctx; +} + +static void _deinitModuleDrv(uint8_t module) +{ + auto mod_drv = pulsesGetModuleDriver(module); + auto drv = mod_drv->drv; + auto ctx = mod_drv->ctx; + + drv->deinit(ctx); + memset(mod_drv, 0, sizeof(module_pulse_driver)); +} + +static void _sendPulses(uint8_t module, uint8_t* buffer) +{ + auto mod_drv = pulsesGetModuleDriver(module); + auto drv = mod_drv->drv; + auto ctx = mod_drv->ctx; + + int16_t* channels = &channelOutputs[0]; + drv->sendPulses(ctx, buffer, channels, 16); +} + +TEST(ports, deactivateRX_pxx1) +{ + modulePortInit(); + g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_R9M_PXX1; + + void* ext_ctx = Pxx1Driver.init(EXTERNAL_MODULE); + EXPECT_TRUE(ext_ctx != nullptr); + EXPECT_TRUE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); + + if (!ext_ctx) return; + _setModuleDrv(EXTERNAL_MODULE, &Pxx1Driver, ext_ctx); + + void* int_ctx = Pxx1Driver.init(INTERNAL_MODULE); + EXPECT_TRUE(int_ctx != nullptr); + EXPECT_EQ(INTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + if (!int_ctx) return; + _setModuleDrv(INTERNAL_MODULE, &Pxx1Driver, int_ctx); + + _deinitModuleDrv(INTERNAL_MODULE); + EXPECT_EQ(EXTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + _deinitModuleDrv(EXTERNAL_MODULE); + EXPECT_FALSE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); +} + +#include "pulses/multi.h" +#include "telemetry/multi.h" + +TEST(ports, deactivateRX_multi) +{ + modulePortInit(); + g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; + + void* ext_ctx = MultiDriver.init(EXTERNAL_MODULE); + EXPECT_TRUE(ext_ctx != nullptr); + EXPECT_TRUE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); + + if (!ext_ctx) return; + _setModuleDrv(EXTERNAL_MODULE, &MultiDriver, ext_ctx); + + uint8_t buffer[64]; + _sendPulses(EXTERNAL_MODULE, buffer); + EXPECT_FALSE(buffer[0x1A] & 2); + + auto& mpm_status = getMultiModuleStatus(EXTERNAL_MODULE); + mpm_status.invalidate(); + mpm_status.getStatusString((char*)buffer); + EXPECT_STREQ(STR_MODULE_NO_TELEMETRY, (char*)buffer); + + void* int_ctx = Pxx1Driver.init(INTERNAL_MODULE); + EXPECT_TRUE(int_ctx != nullptr); + EXPECT_EQ(INTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + if (!int_ctx) return; + _setModuleDrv(INTERNAL_MODULE, &Pxx1Driver, int_ctx); + + _sendPulses(EXTERNAL_MODULE, buffer); + EXPECT_TRUE(buffer[0x1A] & 2); + + mpm_status.getStatusString((char*)buffer); + EXPECT_STREQ(STR_DISABLE_INTERNAL, (char*)buffer); + + _deinitModuleDrv(INTERNAL_MODULE); + EXPECT_EQ(EXTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + _sendPulses(EXTERNAL_MODULE, buffer); + EXPECT_FALSE(buffer[0x1A] & 2); + + _deinitModuleDrv(EXTERNAL_MODULE); + EXPECT_FALSE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); +} + +TEST(ports, boot_pxx1_multi) +{ + modulePortInit(); + g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; + + // Init PXX1 internal module first + void* int_ctx = Pxx1Driver.init(INTERNAL_MODULE); + EXPECT_TRUE(int_ctx != nullptr); + + if (!int_ctx) return; + _setModuleDrv(INTERNAL_MODULE, &Pxx1Driver, int_ctx); + + EXPECT_TRUE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); + EXPECT_EQ(INTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + // Init MPM external module second + void* ext_ctx = MultiDriver.init(EXTERNAL_MODULE); + EXPECT_TRUE(ext_ctx != nullptr); + + if (!ext_ctx) return; + _setModuleDrv(EXTERNAL_MODULE, &MultiDriver, ext_ctx); + + // Verify internal module still owns S.PORT + EXPECT_EQ(INTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + EXPECT_FALSE(modulePortIsPortUsedByModule(EXTERNAL_MODULE, ETX_MOD_PORT_SPORT)); + + // Verify MPM sends "disable telemetry" bit + uint8_t buffer[64]; + _sendPulses(EXTERNAL_MODULE, buffer); + EXPECT_TRUE(buffer[0x1A] & 2); + + // Verify MPM status + auto& mpm_status = getMultiModuleStatus(EXTERNAL_MODULE); + mpm_status.invalidate(); + mpm_status.getStatusString((char*)buffer); + EXPECT_STREQ(STR_DISABLE_INTERNAL, (char*)buffer); + + // Disable internal module + _deinitModuleDrv(INTERNAL_MODULE); + + // Verify external module now owns S.PORT + EXPECT_EQ(EXTERNAL_MODULE, modulePortGetModuleForPort(ETX_MOD_PORT_SPORT)); + + // Verify MPM does not send "disable telemetry" bit + _sendPulses(EXTERNAL_MODULE, buffer); + EXPECT_FALSE(buffer[0x1A] & 2); + + // Verify MPM status + mpm_status.getStatusString((char*)buffer); + EXPECT_STREQ(STR_MODULE_NO_TELEMETRY, (char*)buffer); + + // disable MPM + _deinitModuleDrv(EXTERNAL_MODULE); + EXPECT_FALSE(modulePortIsPortUsed(ETX_MOD_PORT_SPORT)); +} +#endif