2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #ifdef USE_SERIALRX_FPORT
30 #include "build/debug.h"
32 #include "common/maths.h"
33 #include "common/utils.h"
35 #include "drivers/time.h"
37 #include "io/serial.h"
40 #include "telemetry/telemetry.h"
41 #include "telemetry/smartport.h"
46 #include "rx/frsky_crc.h"
48 #include "rx/sbus_channels.h"
52 #define FPORT_TIME_NEEDED_PER_FRAME_US 3000
53 #define FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US 2000
54 #define FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US 500
55 #define FPORT_MAX_TELEMETRY_AGE_MS 500
57 #define FPORT_TELEMETRY_MAX_CONSECUTIVE_TELEMETRY_FRAMES 2
60 #define FPORT_FRAME_MARKER 0x7E
62 #define FPORT_ESCAPE_CHAR 0x7D
63 #define FPORT_ESCAPE_MASK 0x20
65 #define FPORT_BAUDRATE 115200
67 #define FPORT_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO)
70 DEBUG_FPORT_FRAME_INTERVAL
= 0,
71 DEBUG_FPORT_FRAME_ERRORS
,
72 DEBUG_FPORT_FRAME_LAST_ERROR
,
73 DEBUG_FPORT_TELEMETRY_INTERVAL
,
77 DEBUG_FPORT_NO_ERROR
= 0,
78 DEBUG_FPORT_ERROR_TIMEOUT
,
79 DEBUG_FPORT_ERROR_OVERSIZE
,
80 DEBUG_FPORT_ERROR_SIZE
,
81 DEBUG_FPORT_ERROR_CHECKSUM
,
82 DEBUG_FPORT_ERROR_TYPE
,
83 DEBUG_FPORT_ERROR_TYPE_SIZE
,
87 FPORT_FRAME_TYPE_CONTROL
= 0x00,
88 FPORT_FRAME_TYPE_TELEMETRY_REQUEST
= 0x01,
89 FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
= 0x81,
94 FPORT_FRAME_ID_NULL
= 0x00, // (master/slave)
95 FPORT_FRAME_ID_DATA
= 0x10, // (master/slave)
96 FPORT_FRAME_ID_READ
= 0x30, // (master)
97 FPORT_FRAME_ID_WRITE
= 0x31, // (master)
98 FPORT_FRAME_ID_RESPONSE
= 0x32, // (slave)
101 typedef struct fportControlData_s
{
102 sbusChannels_t channels
;
104 } fportControlData_t
;
106 typedef union fportData_s
{
107 fportControlData_t controlData
;
108 smartPortPayload_t telemetryData
;
111 typedef struct fportFrame_s
{
116 #ifdef USE_TELEMETRY_SMARTPORT
117 static const smartPortPayload_t emptySmartPortFrame
= { .frameId
= 0, .valueId
= 0, .data
= 0 };
120 #define FPORT_REQUEST_FRAME_LENGTH sizeof(fportFrame_t)
121 #define FPORT_RESPONSE_FRAME_LENGTH (sizeof(uint8_t) + sizeof(smartPortPayload_t))
123 #define FPORT_FRAME_PAYLOAD_LENGTH_CONTROL (sizeof(uint8_t) + sizeof(fportControlData_t))
124 #define FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST (sizeof(uint8_t) + sizeof(smartPortPayload_t))
126 #define NUM_RX_BUFFERS 3
127 #define BUFFER_SIZE (FPORT_REQUEST_FRAME_LENGTH + 2 * sizeof(uint8_t))
129 typedef struct fportBuffer_s
{
130 uint8_t data
[BUFFER_SIZE
];
132 timeUs_t frameStartTimeUs
;
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
;
149 #ifdef USE_TELEMETRY_SMARTPORT
150 static bool telemetryEnabled
= false;
153 static void reportFrameError(uint8_t errorReason
) {
154 static volatile uint16_t frameErrors
= 0;
158 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_ERRORS
, frameErrors
);
159 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_LAST_ERROR
, errorReason
);
162 // Receive ISR callback
163 static void fportDataReceive(uint16_t c
, void *data
)
167 static timeUs_t frameStartAt
= 0;
168 static bool escapedCharacter
= false;
169 static timeUs_t lastFrameReceivedUs
= 0;
170 static bool telemetryFrame
= false;
172 const timeUs_t currentTimeUs
= microsISR();
176 if (framePosition
> 1 && cmpTimeUs(currentTimeUs
, frameStartAt
) > FPORT_TIME_NEEDED_PER_FRAME_US
+ 500) {
177 reportFrameError(DEBUG_FPORT_ERROR_TIMEOUT
);
182 uint8_t val
= (uint8_t)c
;
184 if (val
== FPORT_FRAME_MARKER
) {
185 if (framePosition
> 1) {
186 const uint8_t nextWriteIndex
= (rxBufferWriteIndex
+ 1) % NUM_RX_BUFFERS
;
187 if (nextWriteIndex
!= rxBufferReadIndex
) {
188 rxBuffer
[rxBufferWriteIndex
].length
= framePosition
- 1;
189 rxBufferWriteIndex
= nextWriteIndex
;
192 if (telemetryFrame
) {
194 lastTelemetryFrameReceivedUs
= currentTimeUs
;
195 telemetryFrame
= false;
198 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_INTERVAL
, currentTimeUs
- lastFrameReceivedUs
);
199 lastFrameReceivedUs
= currentTimeUs
;
201 escapedCharacter
= false;
204 frameStartAt
= currentTimeUs
;
207 rxBuffer
[rxBufferWriteIndex
].frameStartTimeUs
= currentTimeUs
;
208 } else if (framePosition
> 0) {
209 if (framePosition
>= BUFFER_SIZE
+ 1) {
212 reportFrameError(DEBUG_FPORT_ERROR_OVERSIZE
);
214 if (escapedCharacter
) {
215 val
= val
^ FPORT_ESCAPE_MASK
;
216 escapedCharacter
= false;
217 } else if (val
== FPORT_ESCAPE_CHAR
) {
218 escapedCharacter
= true;
223 if (framePosition
== 2 && val
== FPORT_FRAME_TYPE_TELEMETRY_REQUEST
) {
224 telemetryFrame
= true;
227 rxBuffer
[rxBufferWriteIndex
].data
[framePosition
- 1] = val
;
228 framePosition
= framePosition
+ 1;
233 #if defined(USE_TELEMETRY_SMARTPORT)
234 static void smartPortWriteFrameFport(const smartPortPayload_t
*payload
)
238 uint16_t checksum
= 0;
239 smartPortSendByte(FPORT_RESPONSE_FRAME_LENGTH
, &checksum
, fportPort
);
240 smartPortSendByte(FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
, &checksum
, fportPort
);
241 smartPortWriteFrameSerial(payload
, fportPort
, checksum
);
245 static uint8_t fportFrameStatus(rxRuntimeState_t
*rxRuntimeState
)
247 static bool hasTelemetryRequest
= false;
249 #ifdef USE_TELEMETRY_SMARTPORT
250 static smartPortPayload_t payloadBuffer
;
251 static bool rxDrivenFrameRate
= false;
252 static uint8_t consecutiveTelemetryFrameCount
= 0;
255 uint8_t result
= RX_FRAME_PENDING
;
258 if (rxBufferReadIndex
!= rxBufferWriteIndex
) {
259 uint8_t bufferLength
= rxBuffer
[rxBufferReadIndex
].length
;
260 uint8_t frameLength
= rxBuffer
[rxBufferReadIndex
].data
[0];
261 if (frameLength
!= bufferLength
- 2) {
262 reportFrameError(DEBUG_FPORT_ERROR_SIZE
);
264 if (!frskyCheckSumIsGood(&rxBuffer
[rxBufferReadIndex
].data
[0], bufferLength
)) {
265 reportFrameError(DEBUG_FPORT_ERROR_CHECKSUM
);
267 fportFrame_t
*frame
= (fportFrame_t
*)&rxBuffer
[rxBufferReadIndex
].data
[1];
269 switch (frame
->type
) {
270 case FPORT_FRAME_TYPE_CONTROL
:
271 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_CONTROL
) {
272 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
274 result
= sbusChannelsDecode(rxRuntimeState
, &frame
->data
.controlData
.channels
);
276 setRssi(scaleRange(frame
->data
.controlData
.rssi
, 0, 100, 0, RSSI_MAX_VALUE
), RSSI_SOURCE_RX_PROTOCOL
);
278 lastRcFrameReceivedMs
= millis();
280 if (!(result
& (RX_FRAME_FAILSAFE
| RX_FRAME_DROPPED
))) {
281 rxRuntimeState
->lastRcFrameTimeUs
= rxBuffer
[rxBufferReadIndex
].frameStartTimeUs
;
286 case FPORT_FRAME_TYPE_TELEMETRY_REQUEST
:
287 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST
) {
288 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
290 #if defined(USE_TELEMETRY_SMARTPORT)
291 if (!telemetryEnabled
) {
295 switch(frame
->data
.telemetryData
.frameId
) {
296 case FPORT_FRAME_ID_DATA
:
297 if (!rxDrivenFrameRate
) {
298 rxDrivenFrameRate
= true;
301 hasTelemetryRequest
= true;
304 case FPORT_FRAME_ID_NULL
:
305 if (!rxDrivenFrameRate
) {
306 if (consecutiveTelemetryFrameCount
>= FPORT_TELEMETRY_MAX_CONSECUTIVE_TELEMETRY_FRAMES
&& !(mspPayload
&& smartPortPayloadContainsMSP(mspPayload
))) {
307 consecutiveTelemetryFrameCount
= 0;
309 hasTelemetryRequest
= true;
311 consecutiveTelemetryFrameCount
++;
316 case FPORT_FRAME_ID_READ
:
317 case FPORT_FRAME_ID_WRITE
: // never used
318 memcpy(&payloadBuffer
, &frame
->data
.telemetryData
, sizeof(smartPortPayload_t
));
319 mspPayload
= &payloadBuffer
;
331 reportFrameError(DEBUG_FPORT_ERROR_TYPE
);
339 rxBufferReadIndex
= (rxBufferReadIndex
+ 1) % NUM_RX_BUFFERS
;
342 if ((mspPayload
|| hasTelemetryRequest
) && cmpTimeUs(micros(), lastTelemetryFrameReceivedUs
) >= FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US
) {
343 hasTelemetryRequest
= false;
345 result
= (result
& ~RX_FRAME_PENDING
) | RX_FRAME_PROCESSING_REQUIRED
;
348 if (lastRcFrameReceivedMs
&& ((millis() - lastRcFrameReceivedMs
) > FPORT_MAX_TELEMETRY_AGE_MS
)) {
349 setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL
);
350 lastRcFrameReceivedMs
= 0;
356 static bool fportProcessFrame(const rxRuntimeState_t
*rxRuntimeState
)
358 UNUSED(rxRuntimeState
);
360 #if defined(USE_TELEMETRY_SMARTPORT)
361 static timeUs_t lastTelemetryFrameSentUs
;
363 timeUs_t currentTimeUs
= micros();
364 if (cmpTimeUs(currentTimeUs
, lastTelemetryFrameReceivedUs
) > FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US
) {
369 processSmartPortTelemetry(mspPayload
, &clearToSend
, NULL
);
372 smartPortWriteFrameFport(&emptySmartPortFrame
);
377 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_TELEMETRY_INTERVAL
, currentTimeUs
- lastTelemetryFrameSentUs
);
378 lastTelemetryFrameSentUs
= currentTimeUs
;
387 bool fportRxInit(const rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
)
389 static uint16_t sbusChannelData
[SBUS_MAX_CHANNEL
];
390 rxRuntimeState
->channelData
= sbusChannelData
;
391 sbusChannelsInit(rxConfig
, rxRuntimeState
);
393 rxRuntimeState
->channelCount
= SBUS_MAX_CHANNEL
;
394 rxRuntimeState
->rxRefreshRate
= 11000;
396 rxRuntimeState
->rcFrameStatusFn
= fportFrameStatus
;
397 rxRuntimeState
->rcProcessFrameFn
= fportProcessFrame
;
398 rxRuntimeState
->rcFrameTimeUsFn
= rxFrameTimeUs
;
400 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_RX_SERIAL
);
405 fportPort
= openSerialPort(portConfig
->identifier
,
411 FPORT_PORT_OPTIONS
| (rxConfig
->serialrx_inverted
? SERIAL_INVERTED
: 0) | (rxConfig
->halfDuplex
? SERIAL_BIDIR
: 0)
415 #if defined(USE_TELEMETRY_SMARTPORT)
416 telemetryEnabled
= initSmartPortTelemetryExternal(smartPortWriteFrameFport
);
419 if (rssiSource
== RSSI_SOURCE_NONE
) {
420 rssiSource
= RSSI_SOURCE_RX_PROTOCOL
;
424 return fportPort
!= NULL
;