1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-23 00:05:33 +03:00

Combine ibus serial rx and ibus telemetry on the same fc uart on a single pin.

```
+---------+
| FS-iA6B |
|         |
| Ser RX  |---|<---\       +------------+
|         |        |       | FC         |
| Sensor  |---[R]--*-------| Uart TX    |
+---------+                +------------+
```
R = 10Kohm, Diode 1N4148 or similar.

Both uart tx and rx channels are used so it's not possible to use the spare pin for rx of something else.

Configure the serial port like this to get both serial rx and ibus telemetry on the same port:
```
serial 1 4160 115200 57600 115200 115200
```

It is still possible to run the serial rx and ibus telemetry on two uarts like before, an example:
```
serial 1 64 115200 57600 0 115200
serial 2 4096 115200 57600 115200 115200
```
This commit is contained in:
Magnus Ivarsson 2017-03-08 21:44:46 +01:00
parent 355c7cc215
commit b13d4694bb
12 changed files with 640 additions and 347 deletions

View file

@ -25,13 +25,25 @@ extern "C" {
#include "io/serial.h"
#include "rx/rx.h"
#include "rx/ibus.h"
#include "telemetry/ibus_shared.h"
#include "telemetry/telemetry.h"
#include "fc/rc_controls.h"
#include "sensors/barometer.h"
#include "sensors/battery.h"
}
#include "unittest_macros.h"
#include "gtest/gtest.h"
extern "C" {
uint8_t batteryCellCount = 3;
int16_t rcCommand[4] = {0, 0, 0, 0};
int16_t telemTemperature1 = 0;
baro_t baro = { .baroTemperature = 50 };
telemetryConfig_t telemetryConfig_System;
}
bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
{
@ -42,6 +54,11 @@ bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
serialPort_t * telemetrySharedPort = NULL;
static uint16_t vbat = 100;
uint16_t getVbat(void)
{
return vbat;
}
uint32_t microseconds_stub_value = 0;
uint32_t micros(void)
@ -49,8 +66,15 @@ uint32_t micros(void)
return microseconds_stub_value;
}
#define SERIAL_BUFFER_SIZE 256
#define SERIAL_PORT_DUMMY_IDENTIFIER (serialPortIdentifier_e)0x1234
typedef struct serialPortStub_s {
uint8_t buffer[SERIAL_BUFFER_SIZE];
int pos = 0;
int end = 0;
} serialPortStub_t;
static serialPort_t serialTestInstance;
static serialPortConfig_t serialTestInstanceConfig = {
.identifier = SERIAL_PORT_DUMMY_IDENTIFIER,
@ -60,7 +84,18 @@ static serialPortConfig_t serialTestInstanceConfig = {
static serialReceiveCallbackPtr stub_serialRxCallback;
static serialPortConfig_t *findSerialPortConfig_stub_retval;
static bool openSerial_called = false;
static serialPortStub_t serialWriteStub;
static bool portIsShared = false;
bool isSerialPortShared(const serialPortConfig_t *portConfig,
uint16_t functionMask,
serialPortFunction_e sharedWithFunction)
{
EXPECT_EQ(portConfig, findSerialPortConfig_stub_retval);
EXPECT_EQ(functionMask, FUNCTION_RX_SERIAL);
EXPECT_EQ(sharedWithFunction, FUNCTION_TELEMETRY_IBUS);
return portIsShared;
}
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function)
{
@ -68,6 +103,8 @@ serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function)
return findSerialPortConfig_stub_retval;
}
static portMode_t serialExpectedMode = MODE_RX;
static portOptions_t serialExpectedOptions = SERIAL_UNIDIR;
serialPort_t *openSerialPort(
serialPortIdentifier_e identifier,
@ -81,22 +118,71 @@ serialPort_t *openSerialPort(
openSerial_called = true;
EXPECT_FALSE(NULL == callback);
EXPECT_EQ(identifier, SERIAL_PORT_DUMMY_IDENTIFIER);
EXPECT_EQ(options, SERIAL_UNIDIR);
EXPECT_EQ(options, serialExpectedOptions);
EXPECT_EQ(function, FUNCTION_RX_SERIAL);
EXPECT_EQ(baudrate, 115200);
EXPECT_EQ(mode, MODE_RX);
EXPECT_EQ(mode, serialExpectedMode);
stub_serialRxCallback = callback;
return &serialTestInstance;
}
void serialWrite(serialPort_t *instance, uint8_t ch)
{
EXPECT_EQ(instance, &serialTestInstance);
EXPECT_LT(serialWriteStub.pos, sizeof(serialWriteStub.buffer));
serialWriteStub.buffer[serialWriteStub.pos++] = ch;
//TODO serialReadStub.buffer[serialReadStub.end++] = ch; //characters echoes back on the shared wire
//printf("w: %02d 0x%02x\n", serialWriteStub.pos, ch);
}
void serialTestResetPort()
{
openSerial_called = false;
stub_serialRxCallback = NULL;
portIsShared = false;
serialExpectedMode = MODE_RX;
serialExpectedOptions = SERIAL_UNIDIR;
}
static bool isChecksumOkReturnValue = true;
bool isChecksumOkIa6b(const uint8_t *ibusPacket, const uint8_t length)
{
(void) ibusPacket;
(void) length;
return isChecksumOkReturnValue;
}
static bool initSharedIbusTelemetryCalled = false;
void initSharedIbusTelemetry(serialPort_t * port)
{
EXPECT_EQ(port, &serialTestInstance);
initSharedIbusTelemetryCalled = true;
}
static bool stubTelemetryCalled = false;
static uint8_t stubTelemetryPacket[100];
static uint8_t stubTelemetryIgnoreRxChars = 0;
uint8_t respondToIbusRequest(uint8_t const * const ibusPacket) {
uint8_t len = ibusPacket[0];
EXPECT_LT(len, sizeof(stubTelemetryPacket));
memcpy(stubTelemetryPacket, ibusPacket, len);
stubTelemetryCalled = true;
return stubTelemetryIgnoreRxChars;
}
void resetStubTelemetry(void)
{
memset(stubTelemetryPacket, 0, sizeof(stubTelemetryPacket));
stubTelemetryCalled = false;
stubTelemetryIgnoreRxChars = 0;
initSharedIbusTelemetryCalled = false;
isChecksumOkReturnValue = true;
}
class IbusRxInitUnitTest : public ::testing::Test
{
@ -154,23 +240,36 @@ protected:
virtual void SetUp()
{
serialTestResetPort();
resetStubTelemetry();
portIsShared = true;
serialExpectedOptions = SERIAL_BIDIR;
serialExpectedMode = MODE_RXTX;
const rxConfig_t initialRxConfig = {};
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
EXPECT_TRUE(ibusInit(&initialRxConfig, &rxRuntimeConfig));
EXPECT_TRUE(initSharedIbusTelemetryCalled);
//handle that internal ibus position is not set to zero at init
microseconds_stub_value += 5000;
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
}
virtual void receivePacket(uint8_t const * const packet, const size_t length)
{
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
for (size_t i=0; i < length; i++) {
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
stub_serialRxCallback(packet[i]);
}
}
};
TEST_F(IbusRxProtocollUnitTest, Test_InitialFrameState)
{
//TODO: ibusFrameStatus should return rxFrameState_t not uint8_t
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
//TODO: is it ok to have undefined channel values after init?
@ -209,6 +308,7 @@ TEST_F(IbusRxProtocollUnitTest, Test_IA6B_OnePacketReceivedWithBadCrc)
0x0a, 0x33, 0x0b, 0x33, 0x0c, 0x33, 0x0d, 0x33, //channel 11..14
0x00, 0x00}; //checksum
isChecksumOkReturnValue = false;
for (size_t i=0; i < sizeof(packet); i++) {
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
stub_serialRxCallback(packet[i]);
@ -304,3 +404,110 @@ TEST_F(IbusRxProtocollUnitTest, Test_IA6_OnePacketReceivedBadCrc)
ASSERT_NE(i + (0x33 << 8), rxRuntimeConfig.rcReadRawFn(&rxRuntimeConfig, i));
}
}
TEST_F(IbusRxProtocollUnitTest, Test_IA6B_OnePacketReceived_not_shared_port)
{
uint8_t packet[] = {0x20, 0x00, //length and reserved (unknown) bits
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, //channel 1..5
0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, //channel 6..10
0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, //channel 11..14
0x84, 0xff}; //checksum
{
serialTestResetPort();
resetStubTelemetry();
portIsShared = false;
serialExpectedOptions = SERIAL_NOT_INVERTED;
serialExpectedMode = MODE_RX;
const rxConfig_t initialRxConfig = {};
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
EXPECT_TRUE(ibusInit(&initialRxConfig, &rxRuntimeConfig));
EXPECT_FALSE(initSharedIbusTelemetryCalled);
//handle that internal ibus position is not set to zero at init
microseconds_stub_value += 5000;
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
}
for (size_t i=0; i < sizeof(packet); i++) {
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
stub_serialRxCallback(packet[i]);
}
//report frame complete once
EXPECT_EQ(RX_FRAME_COMPLETE, rxRuntimeConfig.rcFrameStatusFn());
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
//check that channel values have been updated
for (int i=0; i<14; i++) {
ASSERT_EQ(i, rxRuntimeConfig.rcReadRawFn(&rxRuntimeConfig, i));
}
}
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryPacketReceived)
{
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
resetStubTelemetry();
receivePacket(packet, sizeof(packet));
//no frame complete signal to rx system, but telemetry system is called
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
EXPECT_TRUE(stubTelemetryCalled);
EXPECT_TRUE( 0 == memcmp( stubTelemetryPacket, packet, sizeof(packet)));
}
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryIgnoreTxEchoToRx)
{
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
resetStubTelemetry();
stubTelemetryIgnoreRxChars = 4;
//given one packet received, that will respond with four characters to be ignored
receivePacket(packet, sizeof(packet));
rxRuntimeConfig.rcFrameStatusFn();
EXPECT_TRUE(stubTelemetryCalled);
//when those four bytes are sent and looped back
resetStubTelemetry();
rxRuntimeConfig.rcFrameStatusFn();
receivePacket(packet, sizeof(packet));
//then they are ignored
EXPECT_FALSE(stubTelemetryCalled);
//and then next packet can be received
receivePacket(packet, sizeof(packet));
rxRuntimeConfig.rcFrameStatusFn();
EXPECT_TRUE(stubTelemetryCalled);
}
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryShouldNotIgnoreTxEchoAfterInterFrameGap)
{
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
resetStubTelemetry();
stubTelemetryIgnoreRxChars = 4;
//given one packet received, that will respond with four characters to be ignored
receivePacket(packet, sizeof(packet));
rxRuntimeConfig.rcFrameStatusFn();
EXPECT_TRUE(stubTelemetryCalled);
//when there is an interPacketGap
microseconds_stub_value += 5000;
resetStubTelemetry();
rxRuntimeConfig.rcFrameStatusFn();
//then next packet can be received
receivePacket(packet, sizeof(packet));
rxRuntimeConfig.rcFrameStatusFn();
EXPECT_TRUE(stubTelemetryCalled);
}

View file

@ -26,7 +26,7 @@ extern "C" {
#include "fc/rc_controls.h"
#include "telemetry/telemetry.h"
#include "telemetry/ibus.h"
#include "sensors/barometer.h"
#include "sensors/gyro.h"
#include "sensors/battery.h"
#include "scheduler/scheduler.h"
#include "fc/fc_tasks.h"
@ -39,11 +39,19 @@ extern "C" {
extern "C" {
uint8_t batteryCellCount = 3;
int16_t rcCommand[4] = {0, 0, 0, 0};
int16_t telemTemperature1 = 0;
baro_t baro = { .baroTemperature = 50 };
telemetryConfig_t telemetryConfig_System;
}
static int16_t gyroTemperature;
int16_t gyroGetTemperature(void) {
return gyroTemperature;
}
static uint16_t vbat = 100;
uint16_t getVbat(void)
{
return vbat;
}
#define SERIAL_BUFFER_SIZE 256
@ -54,12 +62,6 @@ typedef struct serialPortStub_s {
} serialPortStub_t;
static uint16_t vbat = 100;
uint16_t getVbat(void)
{
return vbat;
}
static serialPortStub_t serialWriteStub;
static serialPortStub_t serialReadStub;
@ -72,6 +74,7 @@ serialPortConfig_t serialTestInstanceConfig = {
static serialPortConfig_t *findSerialPortConfig_stub_retval;
static portSharing_e determinePortSharing_stub_retval;
static bool portIsShared = false;
static bool openSerial_called = false;
static bool telemetryDetermineEnabledState_stub_retval;
@ -105,6 +108,17 @@ bool telemetryDetermineEnabledState(portSharing_e portSharing)
}
bool isSerialPortShared(const serialPortConfig_t *portConfig,
uint16_t functionMask,
serialPortFunction_e sharedWithFunction)
{
EXPECT_EQ(portConfig, findSerialPortConfig_stub_retval);
EXPECT_EQ(functionMask, FUNCTION_RX_SERIAL);
EXPECT_EQ(sharedWithFunction, FUNCTION_TELEMETRY_IBUS);
return portIsShared;
}
serialPortConfig_t *findSerialPortConfig(uint16_t mask)
{
EXPECT_EQ(mask, FUNCTION_TELEMETRY_IBUS);
@ -179,6 +193,7 @@ void serialTestResetBuffers()
void serialTestResetPort()
{
portIsShared = false;
openSerial_called = false;
determinePortSharing_stub_retval = PORTSHARING_UNUSED;
telemetryDetermineEnabledState_stub_retval = true;
@ -235,11 +250,31 @@ TEST_F(IbusTelemteryInitUnitTest, Test_IbusInitEnabled)
}
TEST_F(IbusTelemteryInitUnitTest, Test_IbusInitSerialRxAndTelemetryEnabled)
{
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
//given stuff in serial read
serialReadStub.end++;
//and serial rx enabled too
portIsShared = true;
//when initializing and polling ibus
initIbusTelemetry();
checkIbusTelemetryState();
handleIbusTelemetry();
//then all is read from serial port
EXPECT_NE(serialReadStub.pos, serialReadStub.end);
EXPECT_FALSE(openSerial_called);
}
class IbusTelemetryProtocolUnitTestBase : public ::testing::Test
{
protected:
virtual void SetUp()
{
serialTestResetPort();
telemetryConfigMutable()->report_cell_voltage = false;
serialTestResetBuffers();
initIbusTelemetry();
@ -373,33 +408,20 @@ TEST_F(IbusTelemteryProtocolUnitTest, Test_IbusRespondToGetMeasurementVbattPackV
TEST_F(IbusTelemteryProtocolUnitTest, Test_IbusRespondToGetMeasurementTemperature)
{
#ifdef BARO
//Given ibus command: Sensor at address 2, please send your measurement
//then we respond
baro.baroTemperature = 50;
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x95\x01\xc1\xFE", 6);
gyroTemperature = 50;
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x84\x03\xd0\xfe", 6);
//Given ibus command: Sensor at address 2, please send your measurement
//then we respond
baro.baroTemperature = 59; //test integer rounding
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x96\x01\xc0\xFE", 6);
gyroTemperature = 59; //test integer rounding
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\xde\x03\x76\xfe", 6);
//Given ibus command: Sensor at address 2, please send your measurement
//then we respond
baro.baroTemperature = 150;
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x9f\x01\xb7\xFE", 6);
#else
#error not tested, may be obsolete
// //Given ibus command: Sensor at address 2, please send your measurement
// //then we respond with: I'm reading 0 degrees + constant offset 0x190
// telemTemperature1 = 0;
// checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x90\x01\xC6\xFE", 6);
// //Given ibus command: Sensor at address 2, please send your measurement
// //then we respond with: I'm reading 100 degrees + constant offset 0x190
// telemTemperature1 = 100;
// checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\xF4\x01\x62\xFE", 6);
#endif
gyroTemperature = 150;
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x6c\x07\xe4\xfe", 6);
}
@ -468,19 +490,12 @@ TEST_F(IbusTelemteryProtocolUnitTestDaisyChained, Test_IbusRespondToGetMeasureme
//then we respond with: I'm reading 0.1 volts
batteryCellCount = 1;
vbat = 10;
checkResponseToCommand("\x04\xA3\x58\xff", 4, "\x06\xA3\x64\x00\xf2\xFe", 6);
checkResponseToCommand("\x04\xA3\x58\xff", 4, "\x06\xA3\x64\x00\xf2\xfe", 6);
#ifdef BARO
//Given ibus command: Sensor at address 4, please send your measurement
//then we respond
baro.baroTemperature = 150;
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\x9f\x01\xb5\xFE", 6);
#else
//Given ibus command: Sensor at address 4, please send your measurement
//then we respond with: I'm reading 100 degrees + constant offset 0x190
telemTemperature1 = 100;
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\xF4\x01\x60\xFE", 6);
#endif
gyroTemperature = 150;
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\x6c\x07\xe2\xfe", 6);
//Given ibus command: Sensor at address 5, please send your measurement
//then we respond with: I'm reading 100 rpm