From b7270917c87c163f435c871fabbb80d1dcfb129d Mon Sep 17 00:00:00 2001 From: azolyoung Date: Sat, 8 Jun 2019 22:58:41 +0800 Subject: [PATCH 1/2] add left side choosable for passthrough --- docs/Serial.md | 19 ++++- src/main/cli/cli.c | 199 ++++++++++++++++++++++++++++++--------------- 2 files changed, 149 insertions(+), 69 deletions(-) diff --git a/docs/Serial.md b/docs/Serial.md index 990b23690..ba0fe8c6d 100644 --- a/docs/Serial.md +++ b/docs/Serial.md @@ -40,7 +40,7 @@ Configure serial ports first, then enable/disable features that use the ports. If the configuration is invalid the serial port configuration will reset to its defaults and features may be disabled. * There must always be a port available to use for MSP/CLI. -* There is a maximum of 2 MSP ports. +* There is a maximum of 3 MSP ports. * To use a port for a function, the function's corresponding feature must be also be enabled. e.g. after configuring a port for GPS enable the GPS feature. * If SoftSerial is used, then all SoftSerial ports must use the same baudrate. @@ -89,9 +89,11 @@ Cleanflight can enter a special passthrough mode whereby it passes serial data t To initiate passthrough mode, use the CLI command `serialpassthrough` This command takes four arguments. - serialpassthrough [baud] [mode] [DTR PINIO] + serialpassthrough [port1 baud] [port1 mode] [port1 DTR PINIO] [port2 id] [port2 baud] [port2 mode] -ID is the internal identifier of the serial port from Cleanflight source code (see serialPortIdentifier_e in the source). For instance UART1-UART4 are 0-3 and SoftSerial1/SoftSerial2 are 30/31 respectively. Baud is the desired baud rate, and mode is a combination of the keywords rx and tx (rxtx is full duplex). The baud and mode parameters can be used to override the configured values for the specified port. `DTR PINIO` identifies the PINIO resource which is optionally connected to a DTR line of the attached device. +`PortX ID` is the internal identifier of the serial port from Cleanflight source code (see serialPortIdentifier_e in the source). For instance UART1-UART4 are 0-3 and SoftSerial1/SoftSerial2 are 30/31 respectively. PortX Baud is the desired baud rate, and portX mode is a combination of the keywords rx and tx (rxtx is full duplex). The baud and mode parameters can be used to override the configured values for the specified port. `port1 DTR PINIO` identifies the PINIO resource which is optionally connected to a DTR line of the attached device. + +If port2 config(the last three arguments) is not specified, the passthrough will run between port1 and VCP. The last three arguments are used for `Passthrough between UARTs`, see that section to get detail. For example. If you have your MWOSD connected to UART 2, you could enable communicaton to this device using the following command. This command does not specify the baud rate or mode, using the one configured for the port (see above). @@ -103,7 +105,7 @@ _To use a tool such as the MWOSD GUI, it is necessary to disconnect or exit Clea **To exit serial passthrough mode, power cycle your flight control board.** -In order to reflash an Arduino based device such as a MWOSD via `serialpassthrough` if is necessary to connect the DTR line in addition to the RX and TX serial lines. The DTR is used as a reset line to invoke the bootloader. The DTR line may be connected to any GPIO pin on the flight control board. This pin must then be associated with a PINIO resource, the instance of which is then passed to the serialpassthrough command. The DTR line associated with any given UART may be set using the CLI command `resource` specifying it as a PINIO resource. +In order to reflash an Arduino based device such as a MWOSD via `serialpassthrough` if is necessary to connect the DTR line in addition to the RX and TX serial lines. The DTR is used as a reset line to invoke the bootloader. The DTR line may be connected to any GPIO pin on the flight control board. This pin must then be associated with a PINIO resource, the instance of which is then passed to the serialpassthrough command. If you don't need it, you can ignore it or set it to `none`. The DTR line associated with any given UART may be set using the CLI command `resource` specifying it as a PINIO resource. For example, the following configuration for an OpenPilot Revolution shows the UART6 serial port to be configured with TX on pin C06, RX on pin C07 and a DTR connection using PINIO on pin C08. @@ -144,6 +146,15 @@ Note that if DTR is left configured on a port being used with a standard build o 1. Assign the DTR pin using the resource command above prior to reflashing MWOSD, and then dissasociate DTR from the pin. 2. Rebuild MWOSD with MAX_SOFTRESET defined. The MWOSD will then be reset correctly every time the flight controller is reset. +### Passthrough between UARTs +in BetaFlight 4.1 or later, you can make a serial passthrough between UARTs. +the last three arguments of `serialpassthrough` are used to the passthrough between UARTs: `[port2 id]` `[port2 baud]` `[port2 mode]`, if you don't need passthrough between UARTs, just ignore them, and use `serialpassthrough` according to above description. +if you want passthrough between UARTs, `[port2 id]` is a required argument, the value range is same with `port1 ID` argument, it is the internal identifier of the serial port. `[port2 baud]`and`[port2 mode]` is optional argument, the default of them are `57600` and `MODE_RXTX`. +For example. If you using a filght controller built-in BLE chip, and the BLE chip was inner connected to a UART, you can use the following command to let the UART to talk with other UART: +``` +serialpassthrough 0 115200 rxtx none 4 19200 +``` +the command will run a serial passthrough between UART1 and UART5, UART1 baud is 115200, mode is MODE_RXTX, DTR is none, UART5 baud is 19200, mode is not specifie, it will take default value MODE_RXTX. diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 9cd5aa85f..78fe22220 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -1235,6 +1235,20 @@ static void cbCtrlLine(void *context, uint16_t ctrl) } } +static int cliParseSerialMode(const char *tok) +{ + int mode = 0; + + if (strcasestr(tok, "rx")) { + mode |= MODE_RX; + } + if (strcasestr(tok, "tx")) { + mode |= MODE_TX; + } + + return mode; +} + static void cliSerialPassthrough(char *cmdline) { if (isEmpty(cmdline)) { @@ -1242,12 +1256,15 @@ static void cliSerialPassthrough(char *cmdline) return; } - int id = -1; - uint32_t baud = 0; + int port1Id = -1; + uint32_t port1Baud = 0; + unsigned port1Mode = 0; + int port2Id = -1; + uint32_t port2Baud = 0; + int port2Mode = 0; bool enableBaudCb = false; - int pinioDtr = 0; - bool resetOnDtr = false; - unsigned mode = 0; + int port1PinioDtr = 0; + bool port1ResetOnDtr = false; char *saveptr; char* tok = strtok_r(cmdline, " ", &saveptr); int index = 0; @@ -1255,114 +1272,166 @@ static void cliSerialPassthrough(char *cmdline) while (tok != NULL) { switch (index) { case 0: - id = atoi(tok); + port1Id = atoi(tok); break; case 1: - baud = atoi(tok); + port1Baud = atoi(tok); break; case 2: - if (strcasestr(tok, "rx")) { - mode |= MODE_RX; - } - if (strcasestr(tok, "tx")) { - mode |= MODE_TX; - } + port1Mode = cliParseSerialMode(tok); break; case 3: if (strncasecmp(tok, "reset", strlen(tok)) == 0) { - resetOnDtr = true; + port1ResetOnDtr = true; #ifdef USE_PINIO + } else if (strncasecmp(tok, "none", strlen(tok)) == 0) { + port1PinioDtr = 0; } else { - pinioDtr = atoi(tok); + port1PinioDtr = atoi(tok); #endif /* USE_PINIO */ } - + break; + case 4: + port2Id = atoi(tok); + break; + case 5: + port2Baud = atoi(tok); + break; + case 6: + port2Mode = cliParseSerialMode(tok); break; } index++; tok = strtok_r(NULL, " ", &saveptr); } - if (baud == 0) { + if (port1Baud == 0 && port2Id == -1) { enableBaudCb = true; } - cliPrintf("Port %d ", id); - serialPort_t *passThroughPort; - serialPortUsage_t *passThroughPortUsage = findSerialPortUsageByIdentifier(id); - if (!passThroughPortUsage || passThroughPortUsage->serialPort == NULL) { - if (enableBaudCb) { - // Set default baud - baud = 57600; - } + if (port1PinioDtr < 0 || port1PinioDtr > PINIO_COUNT) { + cliPrintLinef("Invalid PinIO number %d", port1PinioDtr); + return ; + } - if (!mode) { - mode = MODE_RXTX; - } + // Port checks + if (findSerialPortIndexByIdentifier(port1Id) == -1) { + cliPrintLinef("Invalid port1 %d", port1Id); + } else { + cliPrintLinef("Port1: %d ", port1Id); + } - passThroughPort = openSerialPort(id, FUNCTION_NONE, NULL, NULL, - baud, mode, - SERIAL_NOT_INVERTED); - if (!passThroughPort) { - cliPrintLine("could not be opened."); - return; - } - - if (enableBaudCb) { - cliPrintf("opened, default baud = %d.\r\n", baud); + if (port2Id != -1) { + if (findSerialPortIndexByIdentifier(port2Id) == -1) { + cliPrintLinef("Invalid port2 %d", port2Id); + } if (port2Id == port1Id) { + cliPrintLinef("Port1 and port2 are same"); + return ; } else { - cliPrintf("opened, baud = %d.\r\n", baud); + cliPrintLinef("Port2: %d", port2Id); } } else { - passThroughPort = passThroughPortUsage->serialPort; - // If the user supplied a mode, override the port's mode, otherwise - // leave the mode unchanged. serialPassthrough() handles one-way ports. - // Set the baud rate if specified - if (baud) { - cliPrintf("already open, setting baud = %d.\n\r", baud); - serialSetBaudRate(passThroughPort, baud); + cliPrintLine("Port2: VCP port"); + } + + serialPort_t *port1 = NULL; + serialPort_t *port2 = cliPort; + serialPort_t **passthroughPorts[2] = { &port1, &port2 }; + int portIds[2] = { port1Id, 0 }; + uint32_t bauds[2] = { port1Baud, 0 }; + unsigned modes[2] = { port1Mode, 0 }; + + if (port2Id != -1) { + *(passthroughPorts[1]) = NULL; + portIds[1] = port2Id; + bauds[1] = port2Baud; + modes[1] = port2Mode; + } + + for (int i = 0; i < 2; i++) { + serialPort_t **port = passthroughPorts[i]; + if (*port != NULL) { + continue; + } + + int portIndex = i + 1; + serialPortUsage_t *portUsage = findSerialPortUsageByIdentifier(portIds[i]); + if (!portUsage || portUsage->serialPort == NULL) { + bool isUseDefaultBaud = false; + if (bauds[i] == 0) { + // Set default baud + bauds[i] = 57600; + isUseDefaultBaud = true; + } + + if (!modes[i]) { + modes[i] = MODE_RXTX; + } + + *port = openSerialPort(portIds[i], FUNCTION_NONE, NULL, NULL, + bauds[i], modes[i], + SERIAL_NOT_INVERTED); + if (!*port) { + cliPrintLinef("Port%d could not be opened.", portIndex); + return; + } + + if (isUseDefaultBaud) { + cliPrintf("Port%d opened, default baud = %d.\r\n", portIndex, bauds[i]); + } else { + cliPrintf("Port%d opened, baud = %d.\r\n", portIndex, bauds[i]); + } } else { - cliPrintf("already open, baud = %d.\n\r", passThroughPort->baudRate); - } + *port = portUsage->serialPort; + // If the user supplied a mode, override the port's mode, otherwise + // leave the mode unchanged. serialPassthrough() handles one-way ports. + // Set the baud rate if specified + if (bauds[i]) { + cliPrintf("Port%d is already open, setting baud = %d.\n\r", portIndex, bauds[i]); + serialSetBaudRate(*port, bauds[i]); + } else { + cliPrintf("Port%d is already open, baud = %d.\n\r", portIndex, (*port)->baudRate); + } - if (mode && passThroughPort->mode != mode) { - cliPrintf("Mode changed from %d to %d.\r\n", - passThroughPort->mode, mode); - serialSetMode(passThroughPort, mode); - } + if (modes[i] && (*port)->mode != modes[i]) { + cliPrintf("Port%d mode changed from %d to %d.\r\n", + portIndex, (*port)->mode, modes[i]); + serialSetMode(*port, modes[i]); + } - // If this port has a rx callback associated we need to remove it now. - // Otherwise no data will be pushed in the serial port buffer! - if (passThroughPort->rxCallback) { - passThroughPort->rxCallback = 0; + // If this port has a rx callback associated we need to remove it now. + // Otherwise no data will be pushed in the serial port buffer! + if ((*port)->rxCallback) { + (*port)->rxCallback = NULL; + } } } // If no baud rate is specified allow to be set via USB if (enableBaudCb) { - cliPrintLine("Baud rate change over USB enabled."); + cliPrintLine("Port1 baud rate change over USB enabled."); // Register the right side baud rate setting routine with the left side which allows setting of the UART // baud rate over USB without setting it using the serialpassthrough command - serialSetBaudRateCb(cliPort, serialSetBaudRate, passThroughPort); + serialSetBaudRateCb(*(passthroughPorts[0]), serialSetBaudRate, *(passthroughPorts[1])); } char *resetMessage = ""; - if (resetOnDtr) { + if (port1ResetOnDtr) { resetMessage = "or drop DTR "; } cliPrintLinef("Forwarding, power cycle %sto exit.", resetMessage); - if (resetOnDtr + if ((port2Id == -1) && (port1ResetOnDtr #ifdef USE_PINIO - || pinioDtr + || port1PinioDtr #endif /* USE_PINIO */ - ) { + )) { // Register control line state callback - serialSetCtrlLineStateCb(cliPort, cbCtrlLine, (void *)(intptr_t)(pinioDtr)); + serialSetCtrlLineStateCb(*(passthroughPorts[0]), cbCtrlLine, (void *)(intptr_t)(port1PinioDtr)); } - serialPassthrough(cliPort, passThroughPort, NULL, NULL); + serialPassthrough(*(passthroughPorts[0]), *(passthroughPorts[1]), NULL, NULL); } #endif From 568dc62087c2e108b8ce6f0179c95faa677e6a7f Mon Sep 17 00:00:00 2001 From: azolyoung Date: Thu, 13 Jun 2019 12:24:42 +0800 Subject: [PATCH 2/2] use array to store the port1&port2 config --- src/main/cli/cli.c | 115 +++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 78fe22220..a1d234803 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -168,6 +168,13 @@ extern uint8_t __config_end; #include "cli.h" +typedef struct serialPassthroughPort_e { + int id; + uint32_t baud; + unsigned mode; + serialPort_t *port; +} serialPassthroughPort_t; + static serialPort_t *cliPort; #ifdef STM32F1 @@ -1256,12 +1263,7 @@ static void cliSerialPassthrough(char *cmdline) return; } - int port1Id = -1; - uint32_t port1Baud = 0; - unsigned port1Mode = 0; - int port2Id = -1; - uint32_t port2Baud = 0; - int port2Mode = 0; + serialPassthroughPort_t ports[2] = { {SERIAL_PORT_NONE, 0, 0, NULL}, { SERIAL_PORT_USB_VCP, 0, 0, cliPort} }; bool enableBaudCb = false; int port1PinioDtr = 0; bool port1ResetOnDtr = false; @@ -1272,13 +1274,13 @@ static void cliSerialPassthrough(char *cmdline) while (tok != NULL) { switch (index) { case 0: - port1Id = atoi(tok); + ports[0].id = atoi(tok); break; case 1: - port1Baud = atoi(tok); + ports[0].baud = atoi(tok); break; case 2: - port1Mode = cliParseSerialMode(tok); + ports[0].mode = cliParseSerialMode(tok); break; case 3: if (strncasecmp(tok, "reset", strlen(tok)) == 0) { @@ -1288,88 +1290,69 @@ static void cliSerialPassthrough(char *cmdline) port1PinioDtr = 0; } else { port1PinioDtr = atoi(tok); + if (port1PinioDtr < 0 || port1PinioDtr > PINIO_COUNT) { + cliPrintLinef("Invalid PinIO number %d", port1PinioDtr); + return ; + } #endif /* USE_PINIO */ } break; case 4: - port2Id = atoi(tok); + ports[1].id = atoi(tok); + ports[1].port = NULL; break; case 5: - port2Baud = atoi(tok); + ports[1].baud = atoi(tok); break; case 6: - port2Mode = cliParseSerialMode(tok); + ports[1].mode = cliParseSerialMode(tok); break; } index++; tok = strtok_r(NULL, " ", &saveptr); } - if (port1Baud == 0 && port2Id == -1) { - enableBaudCb = true; - } - - if (port1PinioDtr < 0 || port1PinioDtr > PINIO_COUNT) { - cliPrintLinef("Invalid PinIO number %d", port1PinioDtr); + // Port checks + if (ports[0].id == ports[1].id) { + cliPrintLinef("Port1 and port2 are same"); return ; } - // Port checks - if (findSerialPortIndexByIdentifier(port1Id) == -1) { - cliPrintLinef("Invalid port1 %d", port1Id); - } else { - cliPrintLinef("Port1: %d ", port1Id); - } - - if (port2Id != -1) { - if (findSerialPortIndexByIdentifier(port2Id) == -1) { - cliPrintLinef("Invalid port2 %d", port2Id); - } if (port2Id == port1Id) { - cliPrintLinef("Port1 and port2 are same"); + for (int i = 0; i < 2; i++) { + if (findSerialPortIndexByIdentifier(ports[i].id) == -1) { + cliPrintLinef("Invalid port%d %d", i + 1, ports[i].id); return ; } else { - cliPrintLinef("Port2: %d", port2Id); + cliPrintLinef("Port%d: %d ", i + 1, ports[i].id); } - } else { - cliPrintLine("Port2: VCP port"); } - serialPort_t *port1 = NULL; - serialPort_t *port2 = cliPort; - serialPort_t **passthroughPorts[2] = { &port1, &port2 }; - int portIds[2] = { port1Id, 0 }; - uint32_t bauds[2] = { port1Baud, 0 }; - unsigned modes[2] = { port1Mode, 0 }; - - if (port2Id != -1) { - *(passthroughPorts[1]) = NULL; - portIds[1] = port2Id; - bauds[1] = port2Baud; - modes[1] = port2Mode; + if (ports[0].baud == 0 && ports[1].id == SERIAL_PORT_USB_VCP) { + enableBaudCb = true; } for (int i = 0; i < 2; i++) { - serialPort_t **port = passthroughPorts[i]; + serialPort_t **port = &(ports[i].port); if (*port != NULL) { continue; } int portIndex = i + 1; - serialPortUsage_t *portUsage = findSerialPortUsageByIdentifier(portIds[i]); + serialPortUsage_t *portUsage = findSerialPortUsageByIdentifier(ports[i].id); if (!portUsage || portUsage->serialPort == NULL) { bool isUseDefaultBaud = false; - if (bauds[i] == 0) { + if (ports[i].baud == 0) { // Set default baud - bauds[i] = 57600; + ports[i].baud = 57600; isUseDefaultBaud = true; } - if (!modes[i]) { - modes[i] = MODE_RXTX; + if (!ports[i].mode) { + ports[i].mode = MODE_RXTX; } - *port = openSerialPort(portIds[i], FUNCTION_NONE, NULL, NULL, - bauds[i], modes[i], + *port = openSerialPort(ports[i].id, FUNCTION_NONE, NULL, NULL, + ports[i].baud, ports[i].mode, SERIAL_NOT_INVERTED); if (!*port) { cliPrintLinef("Port%d could not be opened.", portIndex); @@ -1377,26 +1360,26 @@ static void cliSerialPassthrough(char *cmdline) } if (isUseDefaultBaud) { - cliPrintf("Port%d opened, default baud = %d.\r\n", portIndex, bauds[i]); + cliPrintf("Port%d opened, default baud = %d.\r\n", portIndex, ports[i].baud); } else { - cliPrintf("Port%d opened, baud = %d.\r\n", portIndex, bauds[i]); + cliPrintf("Port%d opened, baud = %d.\r\n", portIndex, ports[i].baud); } } else { *port = portUsage->serialPort; // If the user supplied a mode, override the port's mode, otherwise // leave the mode unchanged. serialPassthrough() handles one-way ports. // Set the baud rate if specified - if (bauds[i]) { - cliPrintf("Port%d is already open, setting baud = %d.\n\r", portIndex, bauds[i]); - serialSetBaudRate(*port, bauds[i]); + if (ports[i].baud) { + cliPrintf("Port%d is already open, setting baud = %d.\n\r", portIndex, ports[i].baud); + serialSetBaudRate(*port, ports[i].baud); } else { cliPrintf("Port%d is already open, baud = %d.\n\r", portIndex, (*port)->baudRate); } - if (modes[i] && (*port)->mode != modes[i]) { + if (ports[i].mode && (*port)->mode != ports[i].mode) { cliPrintf("Port%d mode changed from %d to %d.\r\n", - portIndex, (*port)->mode, modes[i]); - serialSetMode(*port, modes[i]); + portIndex, (*port)->mode, ports[i].mode); + serialSetMode(*port, ports[i].mode); } // If this port has a rx callback associated we need to remove it now. @@ -1412,26 +1395,26 @@ static void cliSerialPassthrough(char *cmdline) cliPrintLine("Port1 baud rate change over USB enabled."); // Register the right side baud rate setting routine with the left side which allows setting of the UART // baud rate over USB without setting it using the serialpassthrough command - serialSetBaudRateCb(*(passthroughPorts[0]), serialSetBaudRate, *(passthroughPorts[1])); + serialSetBaudRateCb(ports[0].port, serialSetBaudRate, ports[1].port); } char *resetMessage = ""; - if (port1ResetOnDtr) { + if (port1ResetOnDtr && ports[1].id == SERIAL_PORT_USB_VCP) { resetMessage = "or drop DTR "; } cliPrintLinef("Forwarding, power cycle %sto exit.", resetMessage); - if ((port2Id == -1) && (port1ResetOnDtr + if ((ports[1].id == SERIAL_PORT_USB_VCP) && (port1ResetOnDtr #ifdef USE_PINIO || port1PinioDtr #endif /* USE_PINIO */ )) { // Register control line state callback - serialSetCtrlLineStateCb(*(passthroughPorts[0]), cbCtrlLine, (void *)(intptr_t)(port1PinioDtr)); + serialSetCtrlLineStateCb(ports[0].port, cbCtrlLine, (void *)(intptr_t)(port1PinioDtr)); } - serialPassthrough(*(passthroughPorts[0]), *(passthroughPorts[1]), NULL, NULL); + serialPassthrough(ports[0].port, ports[1].port, NULL, NULL); } #endif