1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-23 16:25:26 +03:00

serial: add writeBuf() and implement for USB.

This lets USB send up to 32 bytes in a frame instead of 32 one byte
frames.  Add a fallback for drivers that don't implement writeBuf().

serial: allow buffering to speed up USB virtual COM ports.

Add begin write and end write hints.  If implemented by the serial
driver, then the driver can buffer up data sent via serialWrite() and
flush it when serialEndWrite() is called.

Implemented at the buffer level as it requires the least change to how
serial_msp and serial_cli are architected.

Also tidy up the visibility in the VCP driver.

Prevent serial tx buffer overflow.

Note: this is likely not the best solution, we can investigate further
in due course.

drivers: add a buffering writer.

This wraps around the serial API and buffers a configurable number of
characters before flushing.

cli: add buffering.

This greatly speeds up the CLI when running over USB VCP under a
virtual machine.

msp: add buffering around the writes.

This bulks up the writes and lets the USB VCP driver send one 20 byte
frame instead of 20 one byte frames.  This speeds up the blackbox
download and makes VCP much more reliable when running under a virtual
machine.

Fix for: serial buffer broke BLHeli 1wire pass through
This commit is contained in:
Michael Hope 2015-07-21 21:23:59 +02:00 committed by Konstantin Sharlaimov (DigitalEntity)
parent f063c46e9c
commit a7e2e2c7b2
11 changed files with 337 additions and 98 deletions

View file

@ -238,6 +238,7 @@ COMMON_SRC = build_config.c \
drivers/system.c \
drivers/gps_i2cnav.c \
drivers/gyro_sync.c \
drivers/buf_writer.c \
io/beeper.c \
io/rc_controls.c \
io/rc_curves.c \

View file

@ -0,0 +1,47 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight 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.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "buf_writer.h"
bufWriter_t *bufWriterInit(uint8_t *b, int total_size, bufWrite_t writer, void *arg)
{
bufWriter_t *buf = (bufWriter_t *)b;
buf->writer = writer;
buf->arg = arg;
buf->at = 0;
buf->capacity = total_size - sizeof(*buf);
return buf;
}
void bufWriterAppend(bufWriter_t *b, uint8_t ch)
{
b->data[b->at++] = ch;
if (b->at >= b->capacity) {
bufWriterFlush(b);
}
}
void bufWriterFlush(bufWriter_t *b)
{
if (b->at != 0) {
b->writer(b->arg, b->data, b->at);
b->at = 0;
}
}

View file

@ -0,0 +1,38 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight 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.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Called to flush the buffer.
typedef void (*bufWrite_t)(void *arg, void *data, int count);
typedef struct bufWriter_s {
bufWrite_t writer;
void *arg;
uint8_t capacity;
uint8_t at;
uint8_t data[];
} bufWriter_t;
// Initialise a block of memory as a buffered writer.
//
// b should be sizeof(bufWriter_t) + the number of bytes to buffer.
// total_size should be the total size of b.
//
bufWriter_t *bufWriterInit(uint8_t *b, int total_size, bufWrite_t writer, void *p);
void bufWriterAppend(bufWriter_t *b, uint8_t ch);
void bufWriterFlush(bufWriter_t *b);

View file

@ -40,6 +40,22 @@ void serialWrite(serialPort_t *instance, uint8_t ch)
instance->vTable->serialWrite(instance, ch);
}
void serialWriteBuf(serialPort_t *instance, uint8_t *data, int count)
{
if (instance->vTable->writeBuf) {
instance->vTable->writeBuf(instance, data, count);
} else {
for (uint8_t *p = data; count > 0; count--, p++) {
while (!serialTxBytesFree(instance)) {
};
serialWrite(instance, *p);
}
}
}
uint8_t serialRxBytesWaiting(serialPort_t *instance)
{
return instance->vTable->serialTotalRxWaiting(instance);
@ -70,3 +86,19 @@ void serialSetMode(serialPort_t *instance, portMode_t mode)
instance->vTable->setMode(instance, mode);
}
void serialWriteBufShim(void *instance, uint8_t *data, int count)
{
serialWriteBuf((serialPort_t *)instance, data, count);
}
void serialBeginWrite(serialPort_t *instance)
{
if (instance->vTable->beginWrite)
instance->vTable->beginWrite(instance);
}
void serialEndWrite(serialPort_t *instance)
{
if (instance->vTable->endWrite)
instance->vTable->endWrite(instance);
}

View file

@ -73,14 +73,25 @@ struct serialPortVTable {
bool (*isSerialTransmitBufferEmpty)(serialPort_t *instance);
void (*setMode)(serialPort_t *instance, portMode_t mode);
void (*writeBuf)(serialPort_t *instance, void *data, int count);
// Optional functions used to buffer large writes.
void (*beginWrite)(serialPort_t *instance);
void (*endWrite)(serialPort_t *instance);
};
void serialWrite(serialPort_t *instance, uint8_t ch);
uint8_t serialRxBytesWaiting(serialPort_t *instance);
uint8_t serialTxBytesFree(serialPort_t *instance);
void serialWriteBuf(serialPort_t *instance, uint8_t *data, int count);
uint8_t serialRead(serialPort_t *instance);
void serialSetBaudRate(serialPort_t *instance, uint32_t baudRate);
void serialSetMode(serialPort_t *instance, portMode_t mode);
bool isSerialTransmitBufferEmpty(serialPort_t *instance);
void serialPrint(serialPort_t *instance, const char *str);
uint32_t serialGetBaudRate(serialPort_t *instance);
// A shim that adapts the bufWriter API to the serialWriteBuf() API.
void serialWriteBufShim(void *instance, uint8_t *data, int count);
void serialBeginWrite(serialPort_t *instance);
void serialEndWrite(serialPort_t *instance);

View file

@ -479,6 +479,7 @@ const struct serialPortVTable softSerialVTable[] = {
softSerialSetBaudRate,
isSoftSerialTransmitBufferEmpty,
softSerialSetMode,
.writeBuf = NULL,
}
};

View file

@ -322,5 +322,8 @@ const struct serialPortVTable uartVTable[] = {
uartSetBaudRate,
isUartTransmitBufferEmpty,
uartSetMode,
.writeBuf = NULL,
.beginWrite = NULL,
.endWrite = NULL,
}
};

View file

@ -23,6 +23,7 @@
#include "platform.h"
#include "build_config.h"
#include "common/utils.h"
#include "usb_core.h"
#include "usb_init.h"
@ -82,21 +83,63 @@ uint8_t usbVcpRead(serialPort_t *instance)
return buf[0];
}
void usbVcpWrite(serialPort_t *instance, uint8_t c)
static void usbVcpWriteBuf(serialPort_t *instance, void *data, int count)
{
UNUSED(instance);
uint32_t txed;
uint32_t start = millis();
if (!(usbIsConnected() && usbIsConfigured())) {
return;
}
do {
txed = CDC_Send_DATA((uint8_t*)&c, 1);
} while (txed < 1 && (millis() - start < USB_TIMEOUT));
uint32_t start = millis();
for (uint8_t *p = data; count > 0; ) {
uint32_t txed = CDC_Send_DATA(p, count);
count -= txed;
p += txed;
if (millis() - start > USB_TIMEOUT) {
break;
}
}
}
static bool usbVcpFlush(vcpPort_t *port)
{
uint8_t count = port->txAt;
port->txAt = 0;
if (count == 0) {
return true;
}
if (!usbIsConnected() || !usbIsConfigured()) {
return false;
}
uint32_t txed;
uint32_t start = millis();
do {
txed = CDC_Send_DATA(port->txBuf, count);
} while (txed != count && (millis() - start < USB_TIMEOUT));
return txed == count;
}
static void usbVcpWrite(serialPort_t *instance, uint8_t c)
{
vcpPort_t *port = container_of(instance, vcpPort_t, port);
port->txBuf[port->txAt++] = c;
if (!port->buffering || port->txAt >= ARRAYLEN(port->txBuf)) {
usbVcpFlush(port);
}
}
static void usbVcpBeginWrite(serialPort_t *instance)
{
vcpPort_t *port = container_of(instance, vcpPort_t, port);
port->buffering = true;
}
uint8_t usbTxBytesFree() {
@ -104,7 +147,27 @@ uint8_t usbTxBytesFree() {
return 255;
}
const struct serialPortVTable usbVTable[] = { { usbVcpWrite, usbVcpAvailable, usbTxBytesFree, usbVcpRead, usbVcpSetBaudRate, isUsbVcpTransmitBufferEmpty, usbVcpSetMode } };
static void usbVcpEndWrite(serialPort_t *instance)
{
vcpPort_t *port = container_of(instance, vcpPort_t, port);
port->buffering = false;
usbVcpFlush(port);
}
static const struct serialPortVTable usbVTable[] = {
{
.serialWrite = usbVcpWrite,
.serialTotalRxWaiting = usbVcpAvailable,
.serialTotalTxFree = usbTxBytesFree,
.serialRead = usbVcpRead,
.serialSetBaudRate = usbVcpSetBaudRate,
.isSerialTransmitBufferEmpty = isUsbVcpTransmitBufferEmpty,
.setMode = usbVcpSetMode,
.beginWrite = usbVcpBeginWrite,
.endWrite = usbVcpEndWrite,
.writeBuf = usbVcpWriteBuf
}
};
serialPort_t *usbVcpOpen(void)
{

View file

@ -22,6 +22,11 @@
typedef struct {
serialPort_t port;
// Buffer used during bulk writes.
uint8_t txBuf[20];
uint8_t txAt;
// Set if the port is in bulk write mode and can buffer.
bool buffering;
} vcpPort_t;
serialPort_t *usbVcpOpen(void);
@ -30,5 +35,4 @@ uint8_t usbVcpAvailable(serialPort_t *instance);
uint8_t usbVcpRead(serialPort_t *instance);
void usbVcpWrite(serialPort_t *instance, uint8_t ch);
void usbPrintStr(const char *str);

View file

@ -46,6 +46,7 @@
#include "drivers/timer.h"
#include "drivers/pwm_rx.h"
#include "drivers/buf_writer.h"
#include "io/escservo.h"
#include "io/gps.h"
@ -96,6 +97,8 @@ extern uint16_t cycleTime; // FIXME dependency on mw.c
void gpsEnablePassthrough(serialPort_t *gpsPassthroughPort);
static serialPort_t *cliPort;
static bufWriter_t *cliWriter;
static uint8_t cliWriteBuffer[sizeof(*cliWriter) + 16];
static void cliAux(char *cmdline);
static void cliRxFail(char *cmdline);
@ -772,11 +775,13 @@ typedef union {
static void cliSetVar(const clivalue_t *var, const int_float_value_t value);
static void cliPrintVar(const clivalue_t *var, uint32_t full);
static void cliPrint(const char *str);
static void cliPrintf(const char *fmt, ...);
static void cliWrite(uint8_t ch);
static void cliPrompt(void)
{
cliPrint("\r\n# ");
bufWriterFlush(cliWriter);
}
static void cliShowParseError(void)
@ -786,7 +791,7 @@ static void cliShowParseError(void)
static void cliShowArgumentRangeError(char *name, int min, int max)
{
printf("%s must be between %d and %d\r\n", name, min, max);
cliPrintf("%s must be between %d and %d\r\n", name, min, max);
}
static char *processChannelRangeArgs(char *ptr, channelRange_t *range, uint8_t *validArgumentCount)
@ -880,19 +885,19 @@ static void cliRxFail(char *cmdline)
char modeCharacter = rxFailsafeModeCharacters[channelFailsafeConfiguration->mode];
// triple use of printf below
// triple use of cliPrintf below
// 1. acknowledge interpretation on command,
// 2. query current setting on single item,
// 3. recursive use for full list.
if (requireValue) {
printf("rxfail %u %c %d\r\n",
cliPrintf("rxfail %u %c %d\r\n",
channel,
modeCharacter,
RXFAIL_STEP_TO_CHANNEL_VALUE(channelFailsafeConfiguration->step)
);
} else {
printf("rxfail %u %c\r\n",
cliPrintf("rxfail %u %c\r\n",
channel,
modeCharacter
);
@ -912,7 +917,7 @@ static void cliAux(char *cmdline)
// print out aux channel settings
for (i = 0; i < MAX_MODE_ACTIVATION_CONDITION_COUNT; i++) {
modeActivationCondition_t *mac = &currentProfile->modeActivationConditions[i];
printf("aux %u %u %u %u %u\r\n",
cliPrintf("aux %u %u %u %u %u\r\n",
i,
mac->modeId,
mac->auxChannelIndex,
@ -963,7 +968,7 @@ static void cliSerial(char *cmdline)
if (!serialIsPortAvailable(masterConfig.serialConfig.portConfigs[i].identifier)) {
continue;
};
printf("serial %d %d %ld %ld %ld %ld\r\n" ,
cliPrintf("serial %d %d %ld %ld %ld %ld\r\n" ,
masterConfig.serialConfig.portConfigs[i].identifier,
masterConfig.serialConfig.portConfigs[i].functionMask,
baudRates[masterConfig.serialConfig.portConfigs[i].msp_baudrateIndex],
@ -1059,7 +1064,7 @@ static void cliAdjustmentRange(char *cmdline)
// print out adjustment ranges channel settings
for (i = 0; i < MAX_ADJUSTMENT_RANGE_COUNT; i++) {
adjustmentRange_t *ar = &currentProfile->adjustmentRanges[i];
printf("adjrange %u %u %u %u %u %u %u\r\n",
cliPrintf("adjrange %u %u %u %u %u %u %u\r\n",
i,
ar->adjustmentIndex,
ar->auxChannelIndex,
@ -1139,11 +1144,11 @@ static void cliMotorMix(char *cmdline)
if (masterConfig.customMotorMixer[i].throttle == 0.0f)
break;
num_motors++;
printf("#%d:\t", i);
printf("%s\t", ftoa(masterConfig.customMotorMixer[i].throttle, buf));
printf("%s\t", ftoa(masterConfig.customMotorMixer[i].roll, buf));
printf("%s\t", ftoa(masterConfig.customMotorMixer[i].pitch, buf));
printf("%s\r\n", ftoa(masterConfig.customMotorMixer[i].yaw, buf));
cliPrintf("#%d:\t", i);
cliPrintf("%s\t", ftoa(masterConfig.customMotorMixer[i].throttle, buf));
cliPrintf("%s\t", ftoa(masterConfig.customMotorMixer[i].roll, buf));
cliPrintf("%s\t", ftoa(masterConfig.customMotorMixer[i].pitch, buf));
cliPrintf("%s\r\n", ftoa(masterConfig.customMotorMixer[i].yaw, buf));
}
return;
} else if (strncasecmp(cmdline, "reset", 5) == 0) {
@ -1161,7 +1166,7 @@ static void cliMotorMix(char *cmdline)
}
if (strncasecmp(ptr, mixerNames[i], len) == 0) {
mixerLoadMix(i, masterConfig.customMotorMixer);
printf("Loaded %s\r\n", mixerNames[i]);
cliPrintf("Loaded %s\r\n", mixerNames[i]);
cliMotorMix("");
break;
}
@ -1211,7 +1216,7 @@ static void cliRxRange(char *cmdline)
if (isEmpty(cmdline)) {
for (i = 0; i < NON_AUX_CHANNEL_COUNT; i++) {
rxChannelRangeConfiguration_t *channelRangeConfiguration = &masterConfig.rxConfig.channelRanges[i];
printf("rxrange %u %u %u\r\n", i, channelRangeConfiguration->min, channelRangeConfiguration->max);
cliPrintf("rxrange %u %u %u\r\n", i, channelRangeConfiguration->min, channelRangeConfiguration->max);
}
} else if (strcasecmp(cmdline, "reset") == 0) {
resetAllRxChannelRangeConfigurations(masterConfig.rxConfig.channelRanges);
@ -1258,7 +1263,7 @@ static void cliLed(char *cmdline)
if (isEmpty(cmdline)) {
for (i = 0; i < MAX_LED_STRIP_LENGTH; i++) {
generateLedConfig(i, ledConfigBuffer, sizeof(ledConfigBuffer));
printf("led %u %s\r\n", i, ledConfigBuffer);
cliPrintf("led %u %s\r\n", i, ledConfigBuffer);
}
} else {
ptr = cmdline;
@ -1281,7 +1286,7 @@ static void cliColor(char *cmdline)
if (isEmpty(cmdline)) {
for (i = 0; i < CONFIGURABLE_COLOR_COUNT; i++) {
printf("color %u %d,%u,%u\r\n",
cliPrintf("color %u %d,%u,%u\r\n",
i,
masterConfig.colors[i].h,
masterConfig.colors[i].s,
@ -1319,7 +1324,7 @@ static void cliServo(char *cmdline)
for (i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
servo = &currentProfile->servoConf[i];
printf("servo %u %d %d %d %d %d %d %d\r\n",
cliPrintf("servo %u %d %d %d %d %d %d %d\r\n",
i,
servo->min,
servo->max,
@ -1412,7 +1417,7 @@ static void cliServoMix(char *cmdline)
if (masterConfig.customServoMixer[i].rate == 0)
break;
printf("#%d:\t%d\t%d\t%d\t%d\t%d\t%d\t%d\r\n",
cliPrintf("#%d:\t%d\t%d\t%d\t%d\t%d\t%d\t%d\r\n",
i,
masterConfig.customServoMixer[i].targetChannel,
masterConfig.customServoMixer[i].inputSource,
@ -1423,7 +1428,7 @@ static void cliServoMix(char *cmdline)
masterConfig.customServoMixer[i].box
);
}
printf("\r\n");
cliPrintf("\r\n");
return;
} else if (strncasecmp(cmdline, "reset", 5) == 0) {
// erase custom mixer
@ -1437,12 +1442,12 @@ static void cliServoMix(char *cmdline)
len = strlen(++ptr);
for (i = 0; ; i++) {
if (mixerNames[i] == NULL) {
printf("Invalid name\r\n");
cliPrintf("Invalid name\r\n");
break;
}
if (strncasecmp(ptr, mixerNames[i], len) == 0) {
servoMixerLoadMix(i, masterConfig.customServoMixer);
printf("Loaded %s\r\n", mixerNames[i]);
cliPrintf("Loaded %s\r\n", mixerNames[i]);
cliServoMix("");
break;
}
@ -1455,16 +1460,16 @@ static void cliServoMix(char *cmdline)
len = strlen(ptr);
if (len == 0) {
printf("s");
cliPrintf("s");
for (inputSource = 0; inputSource < INPUT_SOURCE_COUNT; inputSource++)
printf("\ti%d", inputSource);
printf("\r\n");
cliPrintf("\ti%d", inputSource);
cliPrintf("\r\n");
for (servoIndex = 0; servoIndex < MAX_SUPPORTED_SERVOS; servoIndex++) {
printf("%d", servoIndex);
cliPrintf("%d", servoIndex);
for (inputSource = 0; inputSource < INPUT_SOURCE_COUNT; inputSource++)
printf("\t%s ", (currentProfile->servoConf[servoIndex].reversedSources & (1 << inputSource)) ? "r" : "n");
printf("\r\n");
cliPrintf("\t%s ", (currentProfile->servoConf[servoIndex].reversedSources & (1 << inputSource)) ? "r" : "n");
cliPrintf("\r\n");
}
return;
}
@ -1537,7 +1542,7 @@ static void cliFlashInfo(char *cmdline)
UNUSED(cmdline);
printf("Flash sectors=%u, sectorSize=%u, pagesPerSector=%u, pageSize=%u, totalSize=%u, usedSize=%u\r\n",
cliPrintf("Flash sectors=%u, sectorSize=%u, pagesPerSector=%u, pageSize=%u, totalSize=%u, usedSize=%u\r\n",
layout->sectors, layout->sectorSize, layout->pagesPerSector, layout->pageSize, layout->totalSize, flashfsGetOffset());
}
@ -1545,14 +1550,14 @@ static void cliFlashErase(char *cmdline)
{
UNUSED(cmdline);
printf("Erasing...\r\n");
cliPrintf("Erasing...\r\n");
flashfsEraseCompletely();
while (!flashfsIsReady()) {
delay(100);
}
printf("Done.\r\n");
cliPrintf("Done.\r\n");
}
#ifdef USE_FLASH_TOOLS
@ -1569,7 +1574,7 @@ static void cliFlashWrite(char *cmdline)
flashfsWrite((uint8_t*)text, strlen(text), true);
flashfsFlushSync();
printf("Wrote %u bytes at %u.\r\n", strlen(text), address);
cliPrintf("Wrote %u bytes at %u.\r\n", strlen(text), address);
}
}
@ -1588,7 +1593,7 @@ static void cliFlashRead(char *cmdline)
} else {
length = atoi(nextArg);
printf("Reading %u bytes at %u:\r\n", length, address);
cliPrintf("Reading %u bytes at %u:\r\n", length, address);
while (length > 0) {
int bytesRead;
@ -1607,7 +1612,7 @@ static void cliFlashRead(char *cmdline)
break;
}
}
printf("\r\n");
cliPrintf("\r\n");
}
}
@ -1625,7 +1630,7 @@ static void dumpValues(uint16_t valueSection)
continue;
}
printf("set %s = ", valueTable[i].name);
cliPrintf("set %s = ", valueTable[i].name);
cliPrintVar(value, 0);
cliPrint("\r\n");
}
@ -1642,7 +1647,7 @@ typedef enum {
static const char* const sectionBreak = "\r\n";
#define printSectionBreak() printf((char *)sectionBreak)
#define printSectionBreak() cliPrintf((char *)sectionBreak)
static void cliDump(char *cmdline)
{
@ -1677,9 +1682,9 @@ static void cliDump(char *cmdline)
cliPrint("\r\n# mixer\r\n");
#ifndef USE_QUAD_MIXER_ONLY
printf("mixer %s\r\n", mixerNames[masterConfig.mixerMode - 1]);
cliPrintf("mixer %s\r\n", mixerNames[masterConfig.mixerMode - 1]);
printf("mmix reset\r\n");
cliPrintf("mmix reset\r\n");
for (i = 0; i < MAX_SUPPORTED_MOTORS; i++) {
if (masterConfig.customMotorMixer[i].throttle == 0.0f)
@ -1688,30 +1693,30 @@ static void cliDump(char *cmdline)
roll = masterConfig.customMotorMixer[i].roll;
pitch = masterConfig.customMotorMixer[i].pitch;
yaw = masterConfig.customMotorMixer[i].yaw;
printf("mmix %d", i);
cliPrintf("mmix %d", i);
if (thr < 0)
cliWrite(' ');
printf("%s", ftoa(thr, buf));
cliPrintf("%s", ftoa(thr, buf));
if (roll < 0)
cliWrite(' ');
printf("%s", ftoa(roll, buf));
cliPrintf("%s", ftoa(roll, buf));
if (pitch < 0)
cliWrite(' ');
printf("%s", ftoa(pitch, buf));
cliPrintf("%s", ftoa(pitch, buf));
if (yaw < 0)
cliWrite(' ');
printf("%s\r\n", ftoa(yaw, buf));
cliPrintf("%s\r\n", ftoa(yaw, buf));
}
// print custom servo mixer if exists
printf("smix reset\r\n");
#ifdef USE_SERVOS
cliPrintf("smix reset\r\n");
for (i = 0; i < MAX_SERVO_RULES; i++) {
if (masterConfig.customServoMixer[i].rate == 0)
break;
printf("smix %d %d %d %d %d %d %d %d\r\n",
cliPrintf("smix %d %d %d %d %d %d %d %d\r\n",
i,
masterConfig.customServoMixer[i].targetChannel,
masterConfig.customServoMixer[i].inputSource,
@ -1723,7 +1728,6 @@ static void cliDump(char *cmdline)
);
}
#endif
#endif
cliPrint("\r\n\r\n# feature\r\n");
@ -1732,13 +1736,13 @@ static void cliDump(char *cmdline)
for (i = 0; ; i++) { // disable all feature first
if (featureNames[i] == NULL)
break;
printf("feature -%s\r\n", featureNames[i]);
cliPrintf("feature -%s\r\n", featureNames[i]);
}
for (i = 0; ; i++) { // reenable what we want.
if (featureNames[i] == NULL)
break;
if (mask & (1 << i))
printf("feature %s\r\n", featureNames[i]);
cliPrintf("feature %s\r\n", featureNames[i]);
}
cliPrint("\r\n\r\n# map\r\n");
@ -1746,7 +1750,7 @@ static void cliDump(char *cmdline)
for (i = 0; i < 8; i++)
buf[masterConfig.rxConfig.rcmap[i]] = rcChannelLetters[i];
buf[i] = '\0';
printf("map %s\r\n", buf);
cliPrintf("map %s\r\n", buf);
cliPrint("\r\n\r\n# serial\r\n");
cliSerial("");
@ -1779,7 +1783,7 @@ static void cliDump(char *cmdline)
cliAdjustmentRange("");
printf("\r\n# rxrange\r\n");
cliPrintf("\r\n# rxrange\r\n");
cliRxRange("");
@ -1794,7 +1798,7 @@ static void cliDump(char *cmdline)
for (i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
for (channel = 0; channel < INPUT_SOURCE_COUNT; channel++) {
if (servoDirection(i, channel) < 0) {
printf("smix reverse %d %d r\r\n", i , channel);
cliPrintf("smix reverse %d %d r\r\n", i , channel);
}
}
}
@ -1822,6 +1826,9 @@ void cliEnter(serialPort_t *serialPort)
cliMode = 1;
cliPort = serialPort;
setPrintfSerialPort(cliPort);
cliWriter = bufWriterInit(cliWriteBuffer, sizeof(cliWriteBuffer),
(bufWrite_t)serialWriteBufShim, serialPort);
cliPrint("\r\nEntering CLI Mode, type 'exit' to return, or 'help'\r\n");
cliPrompt();
ENABLE_ARMING_FLAG(PREVENT_ARMING);
@ -1832,6 +1839,8 @@ static void cliExit(char *cmdline)
UNUSED(cmdline);
cliPrint("\r\nLeaving CLI mode, unsaved changes lost.\r\n");
bufWriterFlush(cliWriter);
*cliBuffer = '\0';
bufferIndex = 0;
cliMode = 0;
@ -1839,7 +1848,7 @@ static void cliExit(char *cmdline)
mixerResetDisarmedMotors();
cliReboot();
cliPort = NULL;
cliWriter = NULL;
}
static void cliFeature(char *cmdline)
@ -1857,7 +1866,7 @@ static void cliFeature(char *cmdline)
if (featureNames[i] == NULL)
break;
if (mask & (1 << i))
printf("%s ", featureNames[i]);
cliPrintf("%s ", featureNames[i]);
}
cliPrint("\r\n");
} else if (strncasecmp(cmdline, "list", len) == 0) {
@ -1865,7 +1874,7 @@ static void cliFeature(char *cmdline)
for (i = 0; ; i++) {
if (featureNames[i] == NULL)
break;
printf("%s ", featureNames[i]);
cliPrintf("%s ", featureNames[i]);
}
cliPrint("\r\n");
return;
@ -1906,7 +1915,7 @@ static void cliFeature(char *cmdline)
featureSet(mask);
cliPrint("Enabled");
}
printf(" %s\r\n", featureNames[i]);
cliPrintf(" %s\r\n", featureNames[i]);
break;
}
}
@ -1932,10 +1941,10 @@ static void cliHelp(char *cmdline)
cliPrint(cmdTable[i].name);
#ifndef SKIP_CLI_COMMAND_HELP
if (cmdTable[i].description) {
printf(" - %s", cmdTable[i].description);
cliPrintf(" - %s", cmdTable[i].description);
}
if (cmdTable[i].args) {
printf("\r\n\t%s", cmdTable[i].args);
cliPrintf("\r\n\t%s", cmdTable[i].args);
}
#endif
cliPrint("\r\n");
@ -1966,7 +1975,7 @@ static void cliMap(char *cmdline)
for (i = 0; i < 8; i++)
out[masterConfig.rxConfig.rcmap[i]] = rcChannelLetters[i];
out[i] = '\0';
printf("%s\r\n", out);
cliPrintf("%s\r\n", out);
}
#ifndef USE_QUAD_MIXER_ONLY
@ -1978,14 +1987,14 @@ static void cliMixer(char *cmdline)
len = strlen(cmdline);
if (len == 0) {
printf("Mixer: %s\r\n", mixerNames[masterConfig.mixerMode - 1]);
cliPrintf("Mixer: %s\r\n", mixerNames[masterConfig.mixerMode - 1]);
return;
} else if (strncasecmp(cmdline, "list", len) == 0) {
cliPrint("Available mixers: ");
for (i = 0; ; i++) {
if (mixerNames[i] == NULL)
break;
printf("%s ", mixerNames[i]);
cliPrintf("%s ", mixerNames[i]);
}
cliPrint("\r\n");
return;
@ -2047,7 +2056,7 @@ static void cliMotor(char *cmdline)
}
}
printf("motor %d: %d\r\n", motor_index, motor_disarmed[motor_index]);
cliPrintf("motor %d: %d\r\n", motor_index, motor_disarmed[motor_index]);
}
static void cliPlaySound(char *cmdline)
@ -2068,7 +2077,7 @@ static void cliPlaySound(char *cmdline)
if ((name=beeperNameForTableIndex(i)) != NULL)
break; //if name OK then play sound below
if (i == lastSoundIdx + 1) { //prevent infinite loop
printf("Error playing sound\r\n");
cliPrintf("Error playing sound\r\n");
return;
}
}
@ -2076,13 +2085,13 @@ static void cliPlaySound(char *cmdline)
} else { //index value was given
i = atoi(cmdline);
if ((name=beeperNameForTableIndex(i)) == NULL) {
printf("No sound for index %d\r\n", i);
cliPrintf("No sound for index %d\r\n", i);
return;
}
}
lastSoundIdx = i;
beeperSilence();
printf("Playing sound %d: %s\r\n", i, name);
cliPrintf("Playing sound %d: %s\r\n", i, name);
beeper(beeperModeForTableIndex(i));
#endif
}
@ -2092,7 +2101,7 @@ static void cliProfile(char *cmdline)
int i;
if (isEmpty(cmdline)) {
printf("profile %d\r\n", getCurrentProfile());
cliPrintf("profile %d\r\n", getCurrentProfile());
return;
} else {
i = atoi(cmdline);
@ -2110,7 +2119,7 @@ static void cliRateProfile(char *cmdline)
int i;
if (isEmpty(cmdline)) {
printf("rateprofile %d\r\n", getCurrentControlRateProfile());
cliPrintf("rateprofile %d\r\n", getCurrentControlRateProfile());
return;
} else {
i = atoi(cmdline);
@ -2123,6 +2132,7 @@ static void cliRateProfile(char *cmdline)
static void cliReboot(void) {
cliPrint("\r\nRebooting");
bufWriterFlush(cliWriter);
waitForSerialPortToFinishTransmitting(cliPort);
stopMotors();
handleOneshotFeatureChangeOnRestart();
@ -2151,12 +2161,25 @@ static void cliDefaults(char *cmdline)
static void cliPrint(const char *str)
{
while (*str)
serialWrite(cliPort, *(str++));
bufWriterAppend(cliWriter, *str++);
}
static void cliPutp(void *p, char ch)
{
bufWriterAppend(p, ch);
}
static void cliPrintf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(cliWriter, cliPutp, fmt, va);
va_end(va);
}
static void cliWrite(uint8_t ch)
{
serialWrite(cliPort, ch);
bufWriterAppend(cliWriter, ch);
}
static void cliPrintVar(const clivalue_t *var, uint32_t full)
@ -2194,23 +2217,23 @@ static void cliPrintVar(const clivalue_t *var, uint32_t full)
break;
case VAR_FLOAT:
printf("%s", ftoa(*(float *)ptr, buf));
cliPrintf("%s", ftoa(*(float *)ptr, buf));
if (full && (var->type & VALUE_MODE_MASK) == MODE_DIRECT) {
printf(" %s", ftoa((float)var->config.minmax.min, buf));
printf(" %s", ftoa((float)var->config.minmax.max, buf));
cliPrintf(" %s", ftoa((float)var->config.minmax.min, buf));
cliPrintf(" %s", ftoa((float)var->config.minmax.max, buf));
}
return; // return from case for float only
}
switch(var->type & VALUE_MODE_MASK) {
case MODE_DIRECT:
printf("%d", value);
cliPrintf("%d", value);
if (full) {
printf(" %d %d", var->config.minmax.min, var->config.minmax.max);
cliPrintf(" %d %d", var->config.minmax.min, var->config.minmax.max);
}
break;
case MODE_LOOKUP:
printf(lookupTables[var->config.lookup.tableIndex].values[value]);
cliPrintf(lookupTables[var->config.lookup.tableIndex].values[value]);
break;
}
}
@ -2263,7 +2286,7 @@ static void cliSet(char *cmdline)
cliPrint("Current settings: \r\n");
for (i = 0; i < VALUE_COUNT; i++) {
val = &valueTable[i];
printf("%s = ", valueTable[i].name);
cliPrintf("%s = ", valueTable[i].name);
cliPrintVar(val, len); // when len is 1 (when * is passed as argument), it will print min/max values as well, for gui
cliPrint("\r\n");
}
@ -2326,7 +2349,7 @@ static void cliSet(char *cmdline)
if (changeValue) {
cliSetVar(val, tmp);
printf("%s set to ", valueTable[i].name);
cliPrintf("%s set to ", valueTable[i].name);
cliPrintVar(val, 0);
} else {
cliPrint("Invalid value\r\n");
@ -2351,7 +2374,7 @@ static void cliGet(char *cmdline)
for (i = 0; i < VALUE_COUNT; i++) {
if (strstr(valueTable[i].name, cmdline)) {
val = &valueTable[i];
printf("%s = ", valueTable[i].name);
cliPrintf("%s = ", valueTable[i].name);
cliPrintVar(val, 0);
cliPrint("\r\n");
@ -2371,10 +2394,10 @@ static void cliStatus(char *cmdline)
{
UNUSED(cmdline);
printf("System Uptime: %d seconds, Voltage: %d * 0.1V (%dS battery - %s), System load: %d.%02d\r\n",
cliPrintf("System Uptime: %d seconds, Voltage: %d * 0.1V (%dS battery - %s), System load: %d.%02d\r\n",
millis() / 1000, vbat, batteryCellCount, getBatteryStateString(), averageSystemLoadPercent / 100, averageSystemLoadPercent % 100);
printf("CPU Clock=%dMHz", (SystemCoreClock / 1000000));
cliPrintf("CPU Clock=%dMHz", (SystemCoreClock / 1000000));
#ifndef CJMCU
uint8_t i;
@ -2392,10 +2415,10 @@ static void cliStatus(char *cmdline)
uint8_t sensorHardwareIndex = detectedSensors[i];
sensorHardware = sensorHardwareNames[i][sensorHardwareIndex];
printf(", %s=%s", sensorTypeNames[i], sensorHardware);
cliPrintf(", %s=%s", sensorTypeNames[i], sensorHardware);
if (mask == SENSOR_ACC && acc.revisionCode) {
printf(".%c", acc.revisionCode);
cliPrintf(".%c", acc.revisionCode);
}
}
}
@ -2408,7 +2431,7 @@ static void cliStatus(char *cmdline)
uint16_t i2cErrorCounter = 0;
#endif
printf("Cycle Time: %d, I2C Errors: %d, config size: %d\r\n", cycleTime, i2cErrorCounter, sizeof(master_t));
cliPrintf("Cycle Time: %d, I2C Errors: %d, config size: %d\r\n", cycleTime, i2cErrorCounter, sizeof(master_t));
}
#ifndef SKIP_TASK_STATISTICS
@ -2419,11 +2442,11 @@ static void cliTasks(char *cmdline)
cfTaskId_e taskId;
cfTaskInfo_t taskInfo;
printf("Task list:\r\n");
cliPrintf("Task list:\r\n");
for (taskId = 0; taskId < TASK_COUNT; taskId++) {
getTaskInfo(taskId, &taskInfo);
if (taskInfo.isEnabled) {
printf("%d - %s, max = %d us, avg = %d us, total = %d ms\r\n", taskId, taskInfo.taskName, taskInfo.maxExecutionTime, taskInfo.averageExecutionTime, taskInfo.totalExecutionTime / 1000);
cliPrintf("%d - %s, max = %d us, avg = %d us, total = %d ms\r\n", taskId, taskInfo.taskName, taskInfo.maxExecutionTime, taskInfo.averageExecutionTime, taskInfo.totalExecutionTime / 1000);
}
}
}
@ -2433,7 +2456,7 @@ static void cliVersion(char *cmdline)
{
UNUSED(cmdline);
printf("# iNav/%s %s %s / %s (%s)",
cliPrintf("# iNav/%s %s %s / %s (%s)",
targetName,
FC_VERSION_STRING,
buildDate,
@ -2446,15 +2469,18 @@ static void cliPFlags(char *cmdline)
{
UNUSED(cmdline);
printf("# Persistent config flags: 0x%08x", masterConfig.persistentFlags );
cliPrintf("# Persistent config flags: 0x%08x", masterConfig.persistentFlags );
}
void cliProcess(void)
{
if (!cliPort) {
if (!cliWriter) {
return;
}
// Be a little bit tricky. Flush the last inputs buffer, if any.
bufWriterFlush(cliWriter);
while (serialRxBytesWaiting(cliPort)) {
uint8_t c = serialRead(cliPort);
if (c == '\t' || c == '?') {

View file

@ -42,6 +42,7 @@
#include "drivers/timer.h"
#include "drivers/pwm_rx.h"
#include "drivers/buf_writer.h"
#include "rx/rx.h"
#include "rx/msp.h"
@ -414,10 +415,11 @@ typedef struct mspPort_s {
static mspPort_t mspPorts[MAX_MSP_PORT_COUNT];
static mspPort_t *currentPort;
static bufWriter_t *writer;
static void serialize8(uint8_t a)
{
serialWrite(mspSerialPort, a);
bufWriterAppend(writer, a);
currentPort->checksum ^= a;
}
@ -454,6 +456,8 @@ static uint32_t read32(void)
static void headSerialResponse(uint8_t err, uint8_t responseBodySize)
{
serialBeginWrite(mspSerialPort);
serialize8('$');
serialize8('M');
serialize8(err ? '!' : '>');
@ -475,6 +479,7 @@ static void headSerialError(uint8_t responseBodySize)
static void tailSerialReply(void)
{
serialize8(currentPort->checksum);
serialEndWrite(mspSerialPort);
}
static void s_struct(uint8_t *cb, uint8_t siz)
@ -1858,6 +1863,8 @@ static bool processInCommand(void)
// proceed with a success reply first
headSerialReply(0);
tailSerialReply();
// flush the transmit buffer
bufWriterFlush(writer);
// wait for all data to send
waitForSerialPortToFinishTransmitting(currentPort->port);
// Start to activate here
@ -1953,7 +1960,7 @@ static bool mspProcessReceivedData(uint8_t c)
return true;
}
void setCurrentPort(mspPort_t *port)
static void setCurrentPort(mspPort_t *port)
{
currentPort = port;
mspSerialPort = currentPort->port;
@ -1971,6 +1978,10 @@ void mspProcess(void)
}
setCurrentPort(candidatePort);
// Big enough to fit a MSP_STATUS in one write.
uint8_t buf[sizeof(bufWriter_t) + 20];
writer = bufWriterInit(buf, sizeof(buf),
(bufWrite_t)serialWriteBufShim, currentPort->port);
while (serialRxBytesWaiting(mspSerialPort)) {
@ -1987,6 +1998,8 @@ void mspProcess(void)
}
}
bufWriterFlush(writer);
if (isRebootScheduled) {
waitForSerialPortToFinishTransmitting(candidatePort->port);
stopMotors();