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: add support for serial passthrough to serial gimbals #5438

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 145 additions & 100 deletions radio/src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "hal/fatfs_diskio.h"
#include "hal/storage.h"
#include "hal/usb_driver.h"
#include "hal/serial_driver.h"

#include "tasks.h"
#include "tasks/mixer_task.h"
Expand Down Expand Up @@ -1045,150 +1046,194 @@ int cliSet(const char **argv)
}

#if defined(ENABLE_SERIAL_PASSTHROUGH)
#if defined(HARDWARE_INTERNAL_MODULE)
static etx_module_state_t *spInternalModuleState = nullptr;

static void spInternalModuleTx(uint8_t* buf, uint32_t len)
{
auto drv = modulePortGetSerialDrv(spInternalModuleState->tx);
auto ctx = modulePortGetCtx(spInternalModuleState->tx);
static const etx_serial_driver_t* _sp_drv = nullptr;
static void* _sp_ctx = nullptr;

static void _sp_Tx(uint8_t* buf, uint32_t len)
{
while (len > 0) {
drv->sendByte(ctx, *(buf++));
_sp_drv->sendByte(_sp_ctx, *(buf++));
len--;
}
}

static const etx_serial_init spIntmoduleSerialInitParams = {
.baudrate = 0,
.encoding = ETX_Encoding_8N1,
.direction = ETX_Dir_TX_RX,
.polarity = ETX_Pol_Normal,
};
#if defined(HARDWARE_INTERNAL_MODULE) || defined(HARDWARE_EXTERNAL_MODULE)
static etx_module_state_t *spModuleState = nullptr;

static void spModuleInit(int port_n, int baudrate)
{
etx_serial_init params = {
.baudrate = (uint32_t)baudrate,
.encoding = ETX_Encoding_8N1,
.direction = ETX_Dir_TX_RX,
.polarity = ETX_Pol_Normal,
};

spModuleState =
modulePortInitSerial(port_n, ETX_MOD_PORT_UART, &params, false);
_sp_drv = modulePortGetSerialDrv(spModuleState->rx);
_sp_ctx = modulePortGetCtx(spModuleState->rx);
}

static void spModuleDeInit(int port_n)
{
// and stop module
modulePortDeInit(spModuleState);
spModuleState = nullptr;

// power off the module and wait for a bit
modulePortSetPower(port_n, false);
delay_ms(200);
}

#endif // HARDWARE_INTERNAL_MODULE

#if defined(FLYSKY_GIMBAL)
#include "flysky_gimbal_driver.h"

static void spGimbalInit(int port_n, int baudrate)
{
etx_serial_init params = {
.baudrate = (uint32_t)baudrate,
.encoding = ETX_Encoding_8N1,
.direction = ETX_Dir_TX_RX,
.polarity = ETX_Pol_Normal,
};

(void)port_n;
flysky_gimbal_deinit();

auto port = flysky_gimbal_get_port();
_sp_drv = port->uart;
_sp_ctx = _sp_drv->init(port->hw_def, &params);
}

static void spGimbalDeInit(int port_n)
{
(void)port_n;
_sp_drv->deinit(_sp_ctx);
flysky_gimbal_init();
}
#endif

// TODO: use proper method instead
extern bool cdcConnected;
extern uint32_t usbSerialBaudRate(void*);

int cliSerialPassthrough(const char **argv)
{
const char* port_type = argv[1];
const char* port_num = argv[2];

// 3rd argument (baudrate is optional)
if (!port_type || !port_num) {
if (!port_type) {
cliSerialPrint("%s: missing argument", argv[0]);
return -1;
}

int baudrate = 0;
void (*initCB)(int, int) = nullptr;
void (*deinitCB)(int) = nullptr;

int port_n = 0;
int err = toInt(argv, 2, &port_n);
if (err == -1) return err;
if (err == 0) {
cliSerialPrint("%s: missing port #", argv[0]);
if (!strcmp("rfmod", port_type)) {
// parse port number
if (toInt(argv, 2, &port_n) <= 0) {
cliSerialPrint("%s: invalid or missing port number", argv[0]);
return -1;
}
// 3rd argument (baudrate is optional)
if (toInt(argv, 3, &baudrate) <= 0) {
baudrate = 0;
}

switch(port_n) {
#if defined(HARDWARE_INTERNAL_MODULE)
case INTERNAL_MODULE:
initCB = spModuleInit;
deinitCB = spModuleDeInit;
break;
#endif
// TODO:
// - external module (S.PORT?)
default:
cliSerialPrint("%s: invalid port # '%i'", port_n);
return -1;
}
} else if (!strcmp("gimbals", port_type)) {
#if defined(FLYSKY_GIMBAL)
// 2nd argument (baudrate is optional)
if (toInt(argv, 2, &baudrate) <= 0) {
baudrate = 0;
}

initCB = spGimbalInit;
deinitCB = spGimbalDeInit;
#else
cliSerialPrint("%s: serial gimbals not supported");
return -1;
#endif
} else {
cliSerialPrint("%s: invalid port type '%s'", port_type);
return -1;
}

int baudrate = 0;
err = toInt(argv, 3, &baudrate);
if (err <= 0) {
// stop pulses
watchdogSuspend(200/*2s*/);
pulsesStop();

// suspend RTOS scheduler
vTaskSuspendAll();

if (baudrate <= 0) {
// use current baudrate
baudrate = cliGetBaudRate();
if (!baudrate) {
// default to 115200
baudrate = 115200;
cliSerialPrint("%s: baudrate is 0, default to 115200", argv[0]);
}
}

if (!strcmp("rfmod", port_type)) {
if (initCB) initCB(port_n, baudrate);

if (port_n >= NUM_MODULES
// only internal module supported for now
&& port_n != INTERNAL_MODULE) {
cliSerialPrint("%s: invalid port # '%s'", port_num);
return -1;
}
if (_sp_drv != nullptr) {
auto backupCB = cliReceiveCallBack;
cliReceiveCallBack = _sp_Tx;

// stop pulses
watchdogSuspend(200/*2s*/);
pulsesStop();
// loop until cable disconnected
while (usbPlugged()) {

// suspend RTOS scheduler
vTaskSuspendAll();

#if defined(HARDWARE_INTERNAL_MODULE)
if (port_n == INTERNAL_MODULE) {

// setup serial com

// TODO: '8n1' param
etx_serial_init params(spIntmoduleSerialInitParams);
params.baudrate = baudrate;

spInternalModuleState = modulePortInitSerial(port_n, ETX_MOD_PORT_UART,
&params, false);

auto drv = modulePortGetSerialDrv(spInternalModuleState->rx);
auto ctx = modulePortGetCtx(spInternalModuleState->rx);

// backup and swap CLI input
auto backupCB = cliReceiveCallBack;
cliReceiveCallBack = spInternalModuleTx;

// loop until cable disconnected
while (usbPlugged()) {

uint32_t cli_br = cliGetBaudRate();
if (cli_br && (cli_br != (uint32_t)baudrate)) {
baudrate = cli_br;
drv->setBaudrate(ctx, baudrate);
}

uint8_t data;
if (drv->getByte(ctx, &data) > 0) {

uint8_t timeout = 10; // 10 ms
while(!usbSerialFreeSpace() && (timeout > 0)) {
delay_ms(1);
timeout--;
}
uint32_t cli_br = cliGetBaudRate();
if (cli_br && (cli_br != (uint32_t)baudrate)) {
baudrate = cli_br;
_sp_drv->setBaudrate(_sp_ctx, baudrate);
}

cliSerialPutc(data);
uint8_t data;
if (_sp_drv->getByte(_sp_ctx, &data) > 0) {
uint8_t timeout = 10; // 10 ms
while(!usbSerialFreeSpace() && (timeout > 0)) {
delay_ms(1);
timeout--;
}

// keep us up & running
WDG_RESET();
cliSerialPutc(data);
}

// restore callsbacks
cliReceiveCallBack = backupCB;

// and stop module
modulePortDeInit(spInternalModuleState);
spInternalModuleState = nullptr;

// power off the module and wait for a bit
modulePortSetPower(port_n, false);
delay_ms(200);
// keep us up & running
WDG_RESET();
}
#endif

// TODO:
// - external module (S.PORT?)
// restore callsbacks
cliReceiveCallBack = backupCB;
if (deinitCB != nullptr) deinitCB(port_n);
}

watchdogSuspend(200/*2s*/);
pulsesStart();
watchdogSuspend(200/*2s*/);
pulsesStart();

// suspend RTOS scheduler
xTaskResumeAll();

// suspend RTOS scheduler
xTaskResumeAll();

} else {
cliSerialPrint("%s: invalid port type '%s'", port_type);
return -1;
}

return 0;
}
#endif
Expand Down Expand Up @@ -1626,7 +1671,7 @@ const CliCommand cliCommands[] = {
{ "reboot", cliReboot, "[wdt]" },
{ "set", cliSet, "<what> <value>" },
#if defined(ENABLE_SERIAL_PASSTHROUGH)
{ "serialpassthrough", cliSerialPassthrough, "<port type> <port number>"},
{ "serialpassthrough", cliSerialPassthrough, "<port type> [<port number>] [<baudrate>]"},
#endif
#if defined(DEBUG)
{ "print", cliDisplay, "<address> [<size>] | <what>" },
Expand Down
16 changes: 14 additions & 2 deletions radio/src/targets/common/arm/stm32/flysky_gimbal_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

static const stm32_usart_t fsUSART = {
.USARTx = FLYSKY_HALL_SERIAL_USART,
.txGPIO = GPIO_UNDEF,
.txGPIO = FLYSKY_HALL_SERIAL_TX_GPIO,
.rxGPIO = FLYSKY_HALL_SERIAL_RX_GPIO,
.IRQn = FLYSKY_HALL_SERIAL_USART_IRQn,
.IRQ_Prio = 6,
Expand All @@ -49,6 +49,13 @@ static const stm32_usart_t fsUSART = {

DEFINE_STM32_SERIAL_PORT(FSGimbal, fsUSART, HALLSTICK_BUFF_SIZE, 0);

static const etx_serial_port_t _fs_gimbal_serial_port = {
.name = "gimbals",
.uart = &STM32SerialDriver,
.hw_def = REF_STM32_SERIAL_PORT(FSGimbal),
.set_pwr = nullptr,
};

static STRUCT_HALL HallProtocol = { 0 };

static void* _fs_usart_ctx = nullptr;
Expand Down Expand Up @@ -150,7 +157,7 @@ static void flysky_gimbal_loop(void*)
}
}

static void flysky_gimbal_deinit()
void flysky_gimbal_deinit()
{
STM32SerialDriver.deinit(_fs_usart_ctx);
}
Expand Down Expand Up @@ -181,3 +188,8 @@ bool flysky_gimbal_init()
flysky_gimbal_deinit();
return false;
}

const etx_serial_port_t* flysky_gimbal_get_port()
{
return &_fs_gimbal_serial_port;
}
6 changes: 6 additions & 0 deletions radio/src/targets/common/arm/stm32/flysky_gimbal_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#pragma once

#include "hal/serial_port.h"

#define HALLSTICK_BUFF_SIZE ( 512 )
#define FLYSKY_HALL_BAUDRATE ( 921600 )
Expand Down Expand Up @@ -124,3 +128,5 @@ extern unsigned short hall_adc_values[FLYSKY_HALL_CHANNEL_COUNT];
// returns true if the gimbals were detected properly
bool flysky_gimbal_init();

void flysky_gimbal_deinit();
const etx_serial_port_t* flysky_gimbal_get_port();
2 changes: 1 addition & 1 deletion radio/src/targets/common/arm/stm32/stm32_gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define GPIO_UNDEF (0xffffffff)

// #define GPIO_PIN(x, y) ((GPIOA_BASE + (x << 10)) | y)
#define GPIO_PIN(x, y) ((intptr_t)x | y)
#define GPIO_PIN(x, y) ((uintptr_t)x | y)

// Generate GPIO mode bitfields
//
Expand Down