2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 #if defined(USE_SERIAL_RX)
27 #include "build/debug.h"
29 #include "common/maths.h"
30 #include "common/utils.h"
32 #include "drivers/time.h"
34 #include "io/serial.h"
37 #include "telemetry/telemetry.h"
39 #include "telemetry/smartport.h"
41 #include "rx/frsky_crc.h"
43 #include "rx/sbus_channels.h"
47 #define FPORT_TIME_NEEDED_PER_FRAME_US 3000
48 #define FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US 2000
49 #define FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US 500
50 #define FPORT_MAX_TELEMETRY_AGE_MS 500
52 #define FPORT_TELEMETRY_MAX_CONSECUTIVE_TELEMETRY_FRAMES 2
55 #define FPORT_FRAME_MARKER 0x7E
57 #define FPORT_ESCAPE_CHAR 0x7D
58 #define FPORT_ESCAPE_MASK 0x20
60 #define FPORT_BAUDRATE 115200
62 #define FPORT_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO)
65 DEBUG_FPORT_FRAME_INTERVAL
= 0,
66 DEBUG_FPORT_FRAME_ERRORS
,
67 DEBUG_FPORT_FRAME_LAST_ERROR
,
68 DEBUG_FPORT_TELEMETRY_INTERVAL
,
72 DEBUG_FPORT_NO_ERROR
= 0,
73 DEBUG_FPORT_ERROR_TIMEOUT
,
74 DEBUG_FPORT_ERROR_OVERSIZE
,
75 DEBUG_FPORT_ERROR_SIZE
,
76 DEBUG_FPORT_ERROR_CHECKSUM
,
77 DEBUG_FPORT_ERROR_TYPE
,
78 DEBUG_FPORT_ERROR_TYPE_SIZE
,
82 FPORT_FRAME_TYPE_CONTROL
= 0x00,
83 FPORT_FRAME_TYPE_TELEMETRY_REQUEST
= 0x01,
84 FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
= 0x81,
89 FPORT_FRAME_ID_NULL
= 0x00, // (master/slave)
90 FPORT_FRAME_ID_DATA
= 0x10, // (master/slave)
91 FPORT_FRAME_ID_READ
= 0x30, // (master)
92 FPORT_FRAME_ID_WRITE
= 0x31, // (master)
93 FPORT_FRAME_ID_RESPONSE
= 0x32, // (slave)
96 typedef struct fportControlData_s
{
97 sbusChannels_t channels
;
101 typedef union fportData_s
{
102 fportControlData_t controlData
;
103 smartPortPayload_t telemetryData
;
106 typedef struct fportFrame_s
{
111 #if defined(USE_SERIALRX_FPORT)
113 #if defined(USE_TELEMETRY_SMARTPORT)
115 static bool telemetryEnabled
= false;
117 static const smartPortPayload_t emptySmartPortFrame
= { .frameId
= 0, .valueId
= 0, .data
= 0 };
119 #endif // USE_TELEMETRY_SMARTPORT
121 #define FPORT_REQUEST_FRAME_LENGTH sizeof(fportFrame_t)
122 #define FPORT_RESPONSE_FRAME_LENGTH (sizeof(uint8_t) + sizeof(smartPortPayload_t))
124 #define FPORT_FRAME_PAYLOAD_LENGTH_CONTROL (sizeof(uint8_t) + sizeof(fportControlData_t))
125 #define FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST (sizeof(uint8_t) + sizeof(smartPortPayload_t))
127 #define NUM_RX_BUFFERS 3
128 #define BUFFER_SIZE (FPORT_REQUEST_FRAME_LENGTH + 2 * sizeof(uint8_t))
130 typedef struct fportBuffer_s
{
131 uint8_t data
[BUFFER_SIZE
];
135 static fportBuffer_t rxBuffer
[NUM_RX_BUFFERS
];
137 static volatile uint8_t rxBufferWriteIndex
= 0;
138 static volatile uint8_t rxBufferReadIndex
= 0;
140 static volatile timeUs_t lastTelemetryFrameReceivedUs
;
141 static volatile bool clearToSend
= false;
143 static volatile uint8_t framePosition
= 0;
145 static smartPortPayload_t
*mspPayload
= NULL
;
146 static timeUs_t lastRcFrameReceivedMs
= 0;
148 static serialPort_t
*fportPort
;
150 static void reportFrameError(uint8_t errorReason
) {
152 static volatile uint16_t frameErrors
= 0;
156 // Receive ISR callback
157 static void fportDataReceive(uint16_t c
, void *data
)
161 static timeUs_t frameStartAt
= 0;
162 static bool escapedCharacter
= false;
163 static bool telemetryFrame
= false;
165 const timeUs_t currentTimeUs
= micros();
169 if (framePosition
> 1 && cmpTimeUs(currentTimeUs
, frameStartAt
) > FPORT_TIME_NEEDED_PER_FRAME_US
+ 500) {
170 reportFrameError(DEBUG_FPORT_ERROR_TIMEOUT
);
175 uint8_t val
= (uint8_t)c
;
177 if (val
== FPORT_FRAME_MARKER
) {
178 if (framePosition
> 1) {
179 const uint8_t nextWriteIndex
= (rxBufferWriteIndex
+ 1) % NUM_RX_BUFFERS
;
180 if (nextWriteIndex
!= rxBufferReadIndex
) {
181 rxBuffer
[rxBufferWriteIndex
].length
= framePosition
- 1;
182 rxBufferWriteIndex
= nextWriteIndex
;
185 if (telemetryFrame
) {
187 lastTelemetryFrameReceivedUs
= currentTimeUs
;
188 telemetryFrame
= false;
190 escapedCharacter
= false;
193 frameStartAt
= currentTimeUs
;
195 } else if (framePosition
> 0) {
196 if (framePosition
>= BUFFER_SIZE
+ 1) {
199 reportFrameError(DEBUG_FPORT_ERROR_OVERSIZE
);
201 if (escapedCharacter
) {
202 val
= val
^ FPORT_ESCAPE_MASK
;
203 escapedCharacter
= false;
204 } else if (val
== FPORT_ESCAPE_CHAR
) {
205 escapedCharacter
= true;
210 if (framePosition
== 2 && val
== FPORT_FRAME_TYPE_TELEMETRY_REQUEST
) {
211 telemetryFrame
= true;
214 rxBuffer
[rxBufferWriteIndex
].data
[framePosition
- 1] = val
;
215 framePosition
= framePosition
+ 1;
220 #if defined(USE_TELEMETRY_SMARTPORT)
221 static void smartPortWriteFrameFport(const smartPortPayload_t
*payload
)
225 uint16_t checksum
= 0;
226 smartPortSendByte(FPORT_RESPONSE_FRAME_LENGTH
, &checksum
, fportPort
);
227 smartPortSendByte(FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
, &checksum
, fportPort
);
228 smartPortWriteFrameSerial(payload
, fportPort
, checksum
);
232 static uint8_t fportFrameStatus(rxRuntimeConfig_t
*rxRuntimeConfig
)
234 #if defined(USE_TELEMETRY_SMARTPORT)
235 static smartPortPayload_t payloadBuffer
;
236 static bool rxDrivenFrameRate
= false;
237 static uint8_t consecutiveTelemetryFrameCount
= 0;
239 static bool hasTelemetryRequest
= false;
241 uint8_t result
= RX_FRAME_PENDING
;
243 if (rxBufferReadIndex
!= rxBufferWriteIndex
) {
244 uint8_t bufferLength
= rxBuffer
[rxBufferReadIndex
].length
;
245 uint8_t frameLength
= rxBuffer
[rxBufferReadIndex
].data
[0];
246 if (frameLength
!= bufferLength
- 2) {
247 reportFrameError(DEBUG_FPORT_ERROR_SIZE
);
249 if (!frskyCheckSumIsGood(&rxBuffer
[rxBufferReadIndex
].data
[0], bufferLength
)) {
250 reportFrameError(DEBUG_FPORT_ERROR_CHECKSUM
);
252 fportFrame_t
*frame
= (fportFrame_t
*)&rxBuffer
[rxBufferReadIndex
].data
[1];
254 switch (frame
->type
) {
255 case FPORT_FRAME_TYPE_CONTROL
:
256 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_CONTROL
) {
257 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
259 result
= sbusChannelsDecode(rxRuntimeConfig
, &frame
->data
.controlData
.channels
);
260 lqTrackerSet(rxRuntimeConfig
->lqTracker
, scaleRange(frame
->data
.controlData
.rssi
, 0, 100, 0, RSSI_MAX_VALUE
));
261 lastRcFrameReceivedMs
= millis();
265 case FPORT_FRAME_TYPE_TELEMETRY_REQUEST
:
266 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST
) {
267 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
269 #if defined(USE_TELEMETRY_SMARTPORT)
270 if (!telemetryEnabled
) {
274 switch(frame
->data
.telemetryData
.frameId
) {
275 case FPORT_FRAME_ID_DATA
:
276 if (!rxDrivenFrameRate
) {
277 rxDrivenFrameRate
= true;
280 hasTelemetryRequest
= true;
283 case FPORT_FRAME_ID_NULL
:
284 if (!rxDrivenFrameRate
) {
285 if (consecutiveTelemetryFrameCount
>= FPORT_TELEMETRY_MAX_CONSECUTIVE_TELEMETRY_FRAMES
&& !(mspPayload
&& smartPortPayloadContainsMSP(mspPayload
))) {
286 consecutiveTelemetryFrameCount
= 0;
288 hasTelemetryRequest
= true;
290 consecutiveTelemetryFrameCount
++;
295 case FPORT_FRAME_ID_READ
:
296 case FPORT_FRAME_ID_WRITE
: // never used
297 memcpy(&payloadBuffer
, &frame
->data
.telemetryData
, sizeof(smartPortPayload_t
));
298 mspPayload
= &payloadBuffer
;
310 reportFrameError(DEBUG_FPORT_ERROR_TYPE
);
318 rxBufferReadIndex
= (rxBufferReadIndex
+ 1) % NUM_RX_BUFFERS
;
321 if ((mspPayload
|| hasTelemetryRequest
) && cmpTimeUs(micros(), lastTelemetryFrameReceivedUs
) >= FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US
) {
322 hasTelemetryRequest
= false;
323 result
= (result
& ~RX_FRAME_PENDING
) | RX_FRAME_PROCESSING_REQUIRED
;
326 if (lastRcFrameReceivedMs
&& ((millis() - lastRcFrameReceivedMs
) > FPORT_MAX_TELEMETRY_AGE_MS
)) {
327 lqTrackerSet(rxRuntimeConfig
->lqTracker
, 0);
328 lastRcFrameReceivedMs
= 0;
334 static bool fportProcessFrame(const rxRuntimeConfig_t
*rxRuntimeConfig
)
336 UNUSED(rxRuntimeConfig
);
338 #if defined(USE_TELEMETRY_SMARTPORT)
340 timeUs_t currentTimeUs
= micros();
341 if (cmpTimeUs(currentTimeUs
, lastTelemetryFrameReceivedUs
) > FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US
) {
346 processSmartPortTelemetry(mspPayload
, &clearToSend
, NULL
);
349 smartPortWriteFrameFport(&emptySmartPortFrame
);
362 bool fportRxInit(const rxConfig_t
*rxConfig
, rxRuntimeConfig_t
*rxRuntimeConfig
)
364 static uint16_t sbusChannelData
[SBUS_MAX_CHANNEL
];
365 rxRuntimeConfig
->channelData
= sbusChannelData
;
366 sbusChannelsInit(rxRuntimeConfig
);
368 rxRuntimeConfig
->channelCount
= SBUS_MAX_CHANNEL
;
369 rxRuntimeConfig
->rcFrameStatusFn
= fportFrameStatus
;
370 rxRuntimeConfig
->rcProcessFrameFn
= fportProcessFrame
;
372 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_RX_SERIAL
);
377 fportPort
= openSerialPort(portConfig
->identifier
,
384 (rxConfig
->serialrx_inverted
? 0 : SERIAL_INVERTED
) |
385 (tristateWithDefaultOnIsActive(rxConfig
->halfDuplex
) ? SERIAL_BIDIR
: 0)
389 #if defined(USE_TELEMETRY_SMARTPORT)
390 telemetryEnabled
= initSmartPortTelemetryExternal(smartPortWriteFrameFport
);
394 return fportPort
!= NULL
;