Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(radio): discover SBUS trainer availability #4266

Merged
merged 11 commits into from
Jan 5, 2024
3 changes: 2 additions & 1 deletion radio/src/bluetooth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "opentx.h"
#include "io/frsky_firmware_update.h"
#include "bluetooth_driver.h"
#include "trainer.h"

#if defined(LIBOPENUI)
#include "libopenui.h"
Expand Down Expand Up @@ -145,7 +146,7 @@ void Bluetooth::processTrainerFrame(const uint8_t * buffer)
trainerInput[channel+1] = ((buffer[i+1] & 0x0f) << 4) + ((buffer[i+2] & 0xf0) >> 4) + ((buffer[i+2] & 0x0f) << 8) - 1500;
}

trainerInputValidityTimer = TRAINER_IN_VALID_TIMEOUT;
trainerResetTimer();
}

void Bluetooth::appendTrainerByte(uint8_t data)
Expand Down
1 change: 1 addition & 0 deletions radio/src/boards/generic_stm32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(BOARD_LIB_SRC
${CMAKE_CURRENT_BINARY_DIR}/hal_adc_inputs.inc

boards/generic_stm32/module_ports.cpp
boards/generic_stm32/trainer_ports.cpp
boards/generic_stm32/aux_ports.cpp
boards/generic_stm32/sport_update.cpp
boards/generic_stm32/intmodule_heartbeat.cpp
Expand Down
35 changes: 35 additions & 0 deletions radio/src/boards/generic_stm32/intmodule_heartbeat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
#include "heartbeat_driver.h"
#include "mixer_scheduler.h"
#include "dataconstants.h"

#include "pulses/pxx.h"
#include "trainer.h"

#include "opentx.h"
#include "pulses/pulses.h"

#if defined(PXX1)
#include "pulses/pxx1.h"
Expand All @@ -40,9 +45,21 @@
#define isPxx2Driver(drv) (false)
#endif

static bool _trainer_mode_uses_ext_module(uint8_t mode)
{
return mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE ||
mode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE;
}

void _intmodule_heartbeat_init(uint8_t module, const etx_proto_driver_t* drv)
{
if (module == INTERNAL_MODULE && (isPxx1Driver(drv) || isPxx2Driver(drv))) {

// heartbeat cannot be used if the trainer is using the module bay
if (_trainer_mode_uses_ext_module(currentTrainerMode)) {
return;
}

init_intmodule_heartbeat();
if (isPxx1Driver(drv)) {
// XJT / iXJT
Expand All @@ -61,3 +78,21 @@ void _intmodule_heartbeat_deinit(uint8_t module, const etx_proto_driver_t* drv)
stop_intmodule_heartbeat();
}
}

void _intmodule_heartbeat_trainer_hook(uint8_t old_mode, uint8_t new_mode)
{
bool restart_int_module = false;

if (_trainer_mode_uses_ext_module(new_mode)) {
restart_int_module = true;
} else if (_trainer_mode_uses_ext_module(old_mode)) {
restart_int_module = true;
}

if (restart_int_module) {
auto pdrv = pulsesGetModuleDriver(INTERNAL_MODULE);
if (pdrv && (isPxx2Driver(pdrv->drv) || isPxx1Driver(pdrv->drv))) {
restartModule(INTERNAL_MODULE);
}
}
}
2 changes: 1 addition & 1 deletion radio/src/boards/generic_stm32/intmodule_heartbeat.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@

void _intmodule_heartbeat_init(uint8_t module, const etx_proto_driver_t* drv);
void _intmodule_heartbeat_deinit(uint8_t module, const etx_proto_driver_t* drv);

void _intmodule_heartbeat_trainer_hook(uint8_t old_mode, uint8_t new_mode);
66 changes: 65 additions & 1 deletion radio/src/boards/generic_stm32/module_ports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "stm32_serial_driver.h"
#include "stm32_softserial_driver.h"
#include "stm32_dma.h"
#include "trainer_driver.h"

#include "module_ports.h"
#include "board.h"
Expand Down Expand Up @@ -177,6 +178,24 @@ static const stm32_usart_t extmoduleUSART = {

DEFINE_STM32_SERIAL_PORT(ExternalModule, extmoduleUSART, INTMODULE_FIFO_SIZE, 0);

#elif defined(TRAINER_MODULE_SBUS_USART)

static const stm32_usart_t sbus_trainer_USART = {
.USARTx = TRAINER_MODULE_SBUS_USART,
.GPIOx = TRAINER_MODULE_SBUS_GPIO,
.GPIO_Pin = TRAINER_MODULE_SBUS_GPIO_PIN,
.IRQn = (IRQn_Type)-1,
.IRQ_Prio = 0,
.txDMA = nullptr,
.txDMA_Stream = 0,
.txDMA_Channel = 0,
.rxDMA = TRAINER_MODULE_SBUS_DMA,
.rxDMA_Stream = TRAINER_MODULE_SBUS_DMA_STREAM_LL,
.rxDMA_Channel = TRAINER_MODULE_SBUS_DMA_CHANNEL,
};

DEFINE_STM32_SERIAL_PORT(SbusTrainer, sbus_trainer_USART, 32, 0);

#endif

static stm32_pulse_dma_tc_cb_t _ext_timer_DMA_TC_Callback;
Expand Down Expand Up @@ -223,7 +242,30 @@ extern "C" void EXTMODULE_TIMER_IRQHandler()
}

DEFINE_STM32_SOFTSERIAL_PORT(ExternalModule, extmoduleTimer);
#endif

#if defined(TRAINER_MODULE_CPPM_TIMER)

static_assert(__IS_TRAINER_TIMER_IN_CHANNEL_SUPPORTED(TRAINER_MODULE_CPPM_TIMER_Channel),
"Unsupported trainer timer input channel");

static const stm32_pulse_timer_t trainerModuleTimer = {
.GPIOx = TRAINER_MODULE_CPPM_GPIO,
.GPIO_Pin = TRAINER_MODULE_CPPM_GPIO_PIN,
.GPIO_Alternate = TRAINER_MODULE_CPPM_GPIO_AF,
.TIMx = TRAINER_MODULE_CPPM_TIMER,
.TIM_Freq = TRAINER_MODULE_CPPM_FREQ,
.TIM_Channel = TRAINER_MODULE_CPPM_TIMER_Channel,
.TIM_IRQn = TRAINER_MODULE_CPPM_TIMER_IRQn,
.DMAx = nullptr,
.DMA_Stream = 0,
.DMA_Channel = 0,
.DMA_IRQn = (IRQn_Type)0,
.DMA_TC_CallbackPtr = nullptr,
};

#endif // TRAINER_MODULE_CPPM_TIMER

#endif // HARDWARE_EXTERNAL_MODULE

#define TELEMETRY_USART_IRQ_PRIORITY 0
#define TELEMETRY_DMA_IRQ_PRIORITY 0
Expand Down Expand Up @@ -456,6 +498,28 @@ static const etx_module_port_t _external_ports[] = {
.set_inverted = nullptr,
#endif
},
#if !defined(EXTMODULE_USART) && defined(TRAINER_MODULE_SBUS_USART)
// RX on HEARTBEAT
{
.port = ETX_MOD_PORT_UART,
.type = ETX_MOD_TYPE_SERIAL,
.dir_flags = ETX_MOD_DIR_RX,
.drv = { .serial = &STM32SerialDriver },
.hw_def = REF_STM32_SERIAL_PORT(SbusTrainer),
.set_inverted = nullptr,
},
#endif
#if defined(TRAINER_MODULE_CPPM_TIMER)
// Timer input on HEARTBEAT
{
.port = ETX_MOD_PORT_TIMER,
.type = ETX_MOD_TYPE_TIMER,
.dir_flags = ETX_MOD_DIR_RX,
.drv = { .timer = nullptr },
.hw_def = (void*)&trainerModuleTimer,
.set_inverted = nullptr,
},
#endif
// TX/RX half-duplex on S.PORT
{
.port = ETX_MOD_PORT_SPORT,
Expand Down
147 changes: 147 additions & 0 deletions radio/src/boards/generic_stm32/trainer_ports.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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 "stm32_pulse_driver.h"
#include "stm32_gpio_driver.h"
#include "trainer_driver.h"

#include "hal/module_port.h"
#include "dataconstants.h"

#include "hal.h"

void board_trainer_init()
{
#if defined(TRAINER_DETECT_GPIO_PIN)
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);

pinInit.Pin = TRAINER_DETECT_GPIO_PIN;
pinInit.Mode = LL_GPIO_MODE_INPUT;
pinInit.Pull = LL_GPIO_PULL_UP;
stm32_gpio_enable_clock(TRAINER_DETECT_GPIO);
LL_GPIO_Init(TRAINER_DETECT_GPIO, &pinInit);
#endif

trainer_init();
}

#if defined(TRAINER_GPIO)

bool trainer_dsc_available() { return true; }

static_assert(__IS_TRAINER_TIMER_OUT_CHANNEL_SUPPORTED(TRAINER_OUT_TIMER_Channel),
"Unsupported trainer timer output channel");

static_assert(__IS_TRAINER_TIMER_IN_CHANNEL_SUPPORTED(TRAINER_IN_TIMER_Channel),
"Unsupported trainer timer input channel");

static const stm32_pulse_timer_t trainerOutputTimer = {
.GPIOx = TRAINER_GPIO,
.GPIO_Pin = TRAINER_OUT_GPIO_PIN,
.GPIO_Alternate = TRAINER_GPIO_AF,
.TIMx = TRAINER_TIMER,
.TIM_Freq = TRAINER_TIMER_FREQ,
.TIM_Channel = TRAINER_OUT_TIMER_Channel,
.TIM_IRQn = TRAINER_TIMER_IRQn,
.DMAx = nullptr,
.DMA_Stream = 0,
.DMA_Channel = 0,
.DMA_IRQn = (IRQn_Type)0,
.DMA_TC_CallbackPtr = nullptr,
};

void trainer_init_dsc_out()
{
trainer_init_output(&trainerOutputTimer);
}

static const stm32_pulse_timer_t trainerInputTimer = {
.GPIOx = TRAINER_GPIO,
.GPIO_Pin = TRAINER_IN_GPIO_PIN,
.GPIO_Alternate = TRAINER_GPIO_AF,
.TIMx = TRAINER_TIMER,
.TIM_Freq = TRAINER_TIMER_FREQ,
.TIM_Channel = TRAINER_IN_TIMER_Channel,
.TIM_IRQn = TRAINER_TIMER_IRQn,
.DMAx = nullptr,
.DMA_Stream = 0,
.DMA_Channel = 0,
.DMA_IRQn = (IRQn_Type)0,
.DMA_TC_CallbackPtr = nullptr,
};

void trainer_init_dsc_in()
{
trainer_init_capture(&trainerInputTimer);
}

void trainer_stop_dsc() { trainer_stop(); }

#else
bool trainer_dsc_available() { return false; }
void trainer_init_dsc_out() {}
void trainer_init_dsc_in() {}
void trainer_stop_dsc() {}
#endif

bool is_trainer_dsc_connected()
{
#if defined(TRAINER_DETECT_GPIO_PIN)
bool set = LL_GPIO_IsInputPinSet(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN);
#if defined(TRAINER_DETECT_INVERTED)
return !set;
#else
return set;
#endif
#else // TRAINER_DETECT_GPIO_PIN
return true;
#endif
}

void trainer_init_module_cppm()
{
auto port = modulePortFind(EXTERNAL_MODULE, ETX_MOD_TYPE_TIMER,
ETX_MOD_PORT_TIMER, ETX_Pol_Normal,
ETX_MOD_DIR_RX);
if (!port) return;

auto tim = (const stm32_pulse_timer_t*)port->hw_def;
if (!tim) return;

modulePortSetPower(EXTERNAL_MODULE,true);
trainer_init_capture(tim);
}

void trainer_stop_module_cppm()
{
trainer_stop();
modulePortSetPower(EXTERNAL_MODULE,false);
}

#if defined(TRAINER_TIMER_IRQHandler)
extern "C" void TRAINER_TIMER_IRQHandler() { trainer_timer_isr(); }
#endif

#if defined(TRAINER_MODULE_CPPM_TIMER_IRQHandler) && \
TRAINER_TIMER_IRQHandler != TRAINER_MODULE_CPPM_TIMER_IRQHandler
extern "C" void TRAINER_MODULE_CPPM_TIMER_IRQHandler() { trainer_timer_isr(); }
#endif
2 changes: 1 addition & 1 deletion radio/src/gui/212x64/view_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ void displayTopBar()
x -= 12;
}
}
else if (is_trainer_connected()) {
else if (isTrainerConnected()) {
LCD_NOTIF_ICON(x, ICON_TRAINER);
x -= 12;
}
Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/colorlcd/view_statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ void DebugViewPage::build(FormWindow* window)
#endif

#if defined(INTERNAL_GPS)
if (hasSerialMode(UART_MODE_GPS) != -1) {
if (serialGetModePort(UART_MODE_GPS) >= 0) {
line = form->newLine(&grid);
line->padAll(2);

Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/colorlcd/widgets/radio_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ class InternalGPSWidget: public TopBarWidget

void refresh(BitmapBuffer * dc) override
{
if (hasSerialMode(UART_MODE_GPS) != -1) {
if (serialGetModePort(UART_MODE_GPS) >= 0) {
if (gpsData.fix) {
char s[10];
sprintf(s, "%d", gpsData.numSat);
Expand Down
Loading