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"
38 #include "telemetry/smartport.h"
42 #include "rx/sbus_channels.h"
46 #define FPORT_TIME_NEEDED_PER_FRAME_US 3000
47 #define FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US 2000
48 #define FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US 500
49 #define FPORT_MAX_TELEMETRY_AGE_MS 500
52 #define FPORT_FRAME_MARKER 0x7E
54 #define FPORT_ESCAPE_CHAR 0x7D
55 #define FPORT_ESCAPE_MASK 0x20
57 #define FPORT_CRC_VALUE 0xFF
59 #define FPORT_BAUDRATE 115200
61 #define FPORT_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO)
64 DEBUG_FPORT_FRAME_INTERVAL
= 0,
65 DEBUG_FPORT_FRAME_ERRORS
,
66 DEBUG_FPORT_FRAME_LAST_ERROR
,
67 DEBUG_FPORT_TELEMETRY_DELAY
,
71 DEBUG_FPORT_NO_ERROR
= 0,
72 DEBUG_FPORT_ERROR_TIMEOUT
,
73 DEBUG_FPORT_ERROR_OVERSIZE
,
74 DEBUG_FPORT_ERROR_SIZE
,
75 DEBUG_FPORT_ERROR_CHECKSUM
,
76 DEBUG_FPORT_ERROR_TYPE
,
77 DEBUG_FPORT_ERROR_TYPE_SIZE
,
81 FPORT_FRAME_TYPE_CONTROL
= 0x00,
82 FPORT_FRAME_TYPE_TELEMETRY_REQUEST
= 0x01,
83 FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
= 0x81,
88 FPORT_FRAME_ID_NULL
= 0x00, // (master/slave)
89 FPORT_FRAME_ID_DATA
= 0x10, // (master/slave)
90 FPORT_FRAME_ID_READ
= 0x30, // (master)
91 FPORT_FRAME_ID_WRITE
= 0x31, // (master)
92 FPORT_FRAME_ID_RESPONSE
= 0x32, // (slave)
95 typedef struct fportControlData_s
{
96 sbusChannels_t channels
;
100 typedef union fportData_s
{
101 fportControlData_t controlData
;
102 smartPortPayload_t telemetryData
;
105 typedef struct fportFrame_s
{
110 static const smartPortPayload_t emptySmartPortFrame
= { .frameId
= 0, .valueId
= 0, .data
= 0 };
112 #define FPORT_REQUEST_FRAME_LENGTH sizeof(fportFrame_t)
113 #define FPORT_RESPONSE_FRAME_LENGTH (sizeof(uint8_t) + sizeof(smartPortPayload_t))
115 #define FPORT_FRAME_PAYLOAD_LENGTH_CONTROL (sizeof(uint8_t) + sizeof(fportControlData_t))
116 #define FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST (sizeof(uint8_t) + sizeof(smartPortPayload_t))
118 #define NUM_RX_BUFFERS 3
119 #define BUFFER_SIZE (FPORT_REQUEST_FRAME_LENGTH + 2 * sizeof(uint8_t))
121 typedef struct fportBuffer_s
{
122 uint8_t data
[BUFFER_SIZE
];
126 static fportBuffer_t rxBuffer
[NUM_RX_BUFFERS
];
128 static volatile uint8_t rxBufferWriteIndex
= 0;
129 static volatile uint8_t rxBufferReadIndex
= 0;
131 static volatile timeUs_t lastTelemetryFrameReceivedUs
;
132 static volatile bool clearToSend
= false;
134 static volatile uint8_t framePosition
= 0;
136 static smartPortPayload_t
*mspPayload
= NULL
;
137 static timeUs_t lastRcFrameReceivedMs
= 0;
139 static serialPort_t
*fportPort
;
140 static bool telemetryEnabled
= false;
142 static void reportFrameError(uint8_t errorReason
) {
143 static volatile uint16_t frameErrors
= 0;
145 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_ERRORS
, ++frameErrors
);
146 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_LAST_ERROR
, errorReason
);
149 // Receive ISR callback
150 static void fportDataReceive(uint16_t c
, void *data
)
154 static timeUs_t frameStartAt
= 0;
155 static bool escapedCharacter
= false;
156 static timeUs_t lastFrameReceivedUs
= 0;
157 static bool telemetryFrame
= false;
159 const timeUs_t currentTimeUs
= micros();
163 if (framePosition
> 1 && cmpTimeUs(currentTimeUs
, frameStartAt
) > FPORT_TIME_NEEDED_PER_FRAME_US
+ 500) {
164 reportFrameError(DEBUG_FPORT_ERROR_TIMEOUT
);
169 uint8_t val
= (uint8_t)c
;
171 if (val
== FPORT_FRAME_MARKER
) {
172 if (framePosition
> 1) {
173 const uint8_t nextWriteIndex
= (rxBufferWriteIndex
+ 1) % NUM_RX_BUFFERS
;
174 if (nextWriteIndex
!= rxBufferReadIndex
) {
175 rxBuffer
[rxBufferWriteIndex
].length
= framePosition
- 1;
176 rxBufferWriteIndex
= nextWriteIndex
;
179 if (telemetryFrame
) {
181 lastTelemetryFrameReceivedUs
= currentTimeUs
;
182 telemetryFrame
= false;
185 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_FRAME_INTERVAL
, currentTimeUs
- lastFrameReceivedUs
);
186 lastFrameReceivedUs
= currentTimeUs
;
188 escapedCharacter
= false;
191 frameStartAt
= currentTimeUs
;
193 } else if (framePosition
> 0) {
194 if (framePosition
>= BUFFER_SIZE
+ 1) {
197 reportFrameError(DEBUG_FPORT_ERROR_OVERSIZE
);
199 if (escapedCharacter
) {
200 val
= val
^ FPORT_ESCAPE_MASK
;
201 escapedCharacter
= false;
202 } else if (val
== FPORT_ESCAPE_CHAR
) {
203 escapedCharacter
= true;
208 if (framePosition
== 2 && val
== FPORT_FRAME_TYPE_TELEMETRY_REQUEST
) {
209 telemetryFrame
= true;
212 rxBuffer
[rxBufferWriteIndex
].data
[framePosition
- 1] = val
;
213 framePosition
= framePosition
+ 1;
218 #if defined(USE_TELEMETRY_SMARTPORT)
219 static void smartPortWriteFrameFport(const smartPortPayload_t
*payload
)
223 uint16_t checksum
= 0;
224 smartPortSendByte(FPORT_RESPONSE_FRAME_LENGTH
, &checksum
, fportPort
);
225 smartPortSendByte(FPORT_FRAME_TYPE_TELEMETRY_RESPONSE
, &checksum
, fportPort
);
226 smartPortWriteFrameSerial(payload
, fportPort
, checksum
);
230 static bool checkChecksum(uint8_t *data
, uint8_t length
)
232 uint16_t checksum
= 0;
233 for (unsigned i
= 0; i
< length
; i
++) {
234 checksum
= checksum
+ *(uint8_t *)(data
+ i
);
237 checksum
= (checksum
& 0xff) + (checksum
>> 8);
239 return checksum
== FPORT_CRC_VALUE
;
242 static uint8_t fportFrameStatus(rxRuntimeConfig_t
*rxRuntimeConfig
)
244 static smartPortPayload_t payloadBuffer
;
245 static bool hasTelemetryRequest
= false;
247 uint8_t result
= RX_FRAME_PENDING
;
249 if (rxBufferReadIndex
!= rxBufferWriteIndex
) {
250 uint8_t bufferLength
= rxBuffer
[rxBufferReadIndex
].length
;
251 uint8_t frameLength
= rxBuffer
[rxBufferReadIndex
].data
[0];
252 if (frameLength
!= bufferLength
- 2) {
253 reportFrameError(DEBUG_FPORT_ERROR_SIZE
);
255 if (!checkChecksum(&rxBuffer
[rxBufferReadIndex
].data
[0], bufferLength
)) {
256 reportFrameError(DEBUG_FPORT_ERROR_CHECKSUM
);
258 fportFrame_t
*frame
= (fportFrame_t
*)&rxBuffer
[rxBufferReadIndex
].data
[1];
260 switch (frame
->type
) {
261 case FPORT_FRAME_TYPE_CONTROL
:
262 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_CONTROL
) {
263 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
265 result
|= sbusChannelsDecode(rxRuntimeConfig
, &frame
->data
.controlData
.channels
);
267 setRssiUnfiltered(scaleRange(constrain(frame
->data
.controlData
.rssi
, 0, 100), 0, 100, 0, 1024), RSSI_SOURCE_RX_PROTOCOL
);
269 lastRcFrameReceivedMs
= millis();
273 case FPORT_FRAME_TYPE_TELEMETRY_REQUEST
:
274 if (frameLength
!= FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST
) {
275 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE
);
277 #if defined(USE_TELEMETRY_SMARTPORT)
278 if (!telemetryEnabled
) {
282 switch(frame
->data
.telemetryData
.frameId
) {
283 case FPORT_FRAME_ID_NULL
:
284 case FPORT_FRAME_ID_DATA
: // never used
285 hasTelemetryRequest
= true;
288 case FPORT_FRAME_ID_READ
:
289 case FPORT_FRAME_ID_WRITE
: // never used
290 memcpy(&payloadBuffer
, &frame
->data
.telemetryData
, sizeof(smartPortPayload_t
));
291 mspPayload
= &payloadBuffer
;
303 reportFrameError(DEBUG_FPORT_ERROR_TYPE
);
311 rxBufferReadIndex
= (rxBufferReadIndex
+ 1) % NUM_RX_BUFFERS
;
314 if ((mspPayload
|| hasTelemetryRequest
) && cmpTimeUs(micros(), lastTelemetryFrameReceivedUs
) >= FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US
) {
315 hasTelemetryRequest
= false;
317 result
= result
| RX_FRAME_PROCESSING_REQUIRED
;
320 if (lastRcFrameReceivedMs
&& ((millis() - lastRcFrameReceivedMs
) > FPORT_MAX_TELEMETRY_AGE_MS
)) {
321 setRssiFiltered(0, RSSI_SOURCE_RX_PROTOCOL
);
322 lastRcFrameReceivedMs
= 0;
328 static bool fportProcessFrame(const rxRuntimeConfig_t
*rxRuntimeConfig
)
330 UNUSED(rxRuntimeConfig
);
332 #if defined(USE_TELEMETRY_SMARTPORT)
333 timeUs_t currentTimeUs
= micros();
334 if (cmpTimeUs(currentTimeUs
, lastTelemetryFrameReceivedUs
) > FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US
) {
339 DEBUG_SET(DEBUG_FPORT
, DEBUG_FPORT_TELEMETRY_DELAY
, currentTimeUs
- lastTelemetryFrameReceivedUs
);
341 processSmartPortTelemetry(mspPayload
, &clearToSend
, NULL
);
344 smartPortWriteFrameFport(&emptySmartPortFrame
);
356 bool fportRxInit(const rxConfig_t
*rxConfig
, rxRuntimeConfig_t
*rxRuntimeConfig
)
358 static uint16_t sbusChannelData
[SBUS_MAX_CHANNEL
];
359 rxRuntimeConfig
->channelData
= sbusChannelData
;
360 sbusChannelsInit(rxConfig
, rxRuntimeConfig
);
362 rxRuntimeConfig
->channelCount
= SBUS_MAX_CHANNEL
;
363 rxRuntimeConfig
->rxRefreshRate
= 11000;
365 rxRuntimeConfig
->rcFrameStatusFn
= fportFrameStatus
;
366 rxRuntimeConfig
->rcProcessFrameFn
= fportProcessFrame
;
368 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_RX_SERIAL
);
373 fportPort
= openSerialPort(portConfig
->identifier
,
379 FPORT_PORT_OPTIONS
| (rxConfig
->serialrx_inverted
? SERIAL_INVERTED
: 0) | (rxConfig
->halfDuplex
? SERIAL_BIDIR
: 0)
383 #if defined(USE_TELEMETRY_SMARTPORT)
384 telemetryEnabled
= initSmartPortTelemetryExternal(smartPortWriteFrameFport
);
387 if (rssiSource
== RSSI_SOURCE_NONE
) {
388 rssiSource
= RSSI_SOURCE_RX_PROTOCOL
;
392 return fportPort
!= NULL
;