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/>.
23 #include "build/build_config.h"
24 #include "build/debug.h"
26 #include "common/maths.h"
27 #include "common/utils.h"
29 #include "drivers/io.h"
30 #include "drivers/rx/rx_a7105.h"
31 #include "drivers/system.h"
32 #include "drivers/time.h"
34 #include "fc/config.h"
36 #include "rx/flysky_defs.h"
38 #include "rx/rx_spi.h"
41 #include "pg/pg_ids.h"
43 #include "sensors/battery.h"
47 #if FLYSKY_CHANNEL_COUNT > MAX_FLYSKY_CHANNEL_COUNT
48 #error "FlySky AFHDS protocol support 8 channel max"
51 #if FLYSKY_2A_CHANNEL_COUNT > MAX_FLYSKY_2A_CHANNEL_COUNT
52 #error "FlySky AFHDS 2A protocol support 14 channel max"
55 PG_REGISTER_WITH_RESET_TEMPLATE(flySkyConfig_t
, flySkyConfig
, PG_FLYSKY_CONFIG
, 0);
56 PG_RESET_TEMPLATE(flySkyConfig_t
, flySkyConfig
, .txId
= 0, .rfChannelMap
= {0}, .protocol
= 0);
58 static const uint8_t flySkyRegs
[] = {
59 0xff, 0x42, 0x00, 0x14, 0x00, 0xff, 0xff, 0x00,
60 0x00, 0x00, 0x00, 0x03, 0x19, 0x05, 0x00, 0x50,
61 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00,
62 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f,
63 0x13, 0xc3, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00,
64 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00,
68 static const uint8_t flySky2ARegs
[] = {
69 0xff, 0x62, 0x00, 0x25, 0x00, 0xff, 0xff, 0x00,
70 0x00, 0x00, 0x00, 0x03, 0x19, 0x05, 0x00, 0x50,
71 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x4f,
72 0x62, 0x80, 0xff, 0xff, 0x2a, 0x32, 0xc3, 0x1f,
73 0x1e, 0xff, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00,
74 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00,
78 static const uint8_t flySky2ABindChannels
[] = {
82 static const uint8_t flySkyRfChannels
[16][16] = {
83 { 0x0a, 0x5a, 0x14, 0x64, 0x1e, 0x6e, 0x28, 0x78, 0x32, 0x82, 0x3c, 0x8c, 0x46, 0x96, 0x50, 0xa0},
84 { 0xa0, 0x50, 0x96, 0x46, 0x8c, 0x3c, 0x82, 0x32, 0x78, 0x28, 0x6e, 0x1e, 0x64, 0x14, 0x5a, 0x0a},
85 { 0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x46, 0x96, 0x1e, 0x6e, 0x3c, 0x8c, 0x28, 0x78, 0x32, 0x82},
86 { 0x82, 0x32, 0x78, 0x28, 0x8c, 0x3c, 0x6e, 0x1e, 0x96, 0x46, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a},
87 { 0x28, 0x78, 0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96},
88 { 0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a, 0x78, 0x28},
89 { 0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96, 0x14, 0x64},
90 { 0x64, 0x14, 0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50},
91 { 0x50, 0xa0, 0x46, 0x96, 0x3c, 0x8c, 0x28, 0x78, 0x0a, 0x5a, 0x32, 0x82, 0x1e, 0x6e, 0x14, 0x64},
92 { 0x64, 0x14, 0x6e, 0x1e, 0x82, 0x32, 0x5a, 0x0a, 0x78, 0x28, 0x8c, 0x3c, 0x96, 0x46, 0xa0, 0x50},
93 { 0x46, 0x96, 0x3c, 0x8c, 0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64},
94 { 0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50, 0x8c, 0x3c, 0x96, 0x46},
95 { 0x46, 0x96, 0x0a, 0x5a, 0x3c, 0x8c, 0x14, 0x64, 0x50, 0xa0, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82},
96 { 0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0xa0, 0x50, 0x64, 0x14, 0x8c, 0x3c, 0x5a, 0x0a, 0x96, 0x46},
97 { 0x46, 0x96, 0x0a, 0x5a, 0x50, 0xa0, 0x3c, 0x8c, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64},
98 { 0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0x8c, 0x3c, 0xa0, 0x50, 0x5a, 0x0a, 0x96, 0x46}
101 const timings_t flySkyTimings
= {.packet
= 1500, .firstPacket
= 1900, .syncPacket
= 2250, .telemetry
= 0xFFFFFFFF};
102 const timings_t flySky2ATimings
= {.packet
= 3850, .firstPacket
= 4850, .syncPacket
= 5775, .telemetry
= 57000};
104 static rx_spi_protocol_e protocol
= RX_SPI_A7105_FLYSKY_2A
;
105 static const timings_t
*timings
= &flySky2ATimings
;
106 static uint32_t timeout
= 0;
107 static uint32_t timeLastPacket
= 0;
108 static uint32_t timeLastBind
= 0;
109 static uint32_t timeTxRequest
= 0;
110 static uint32_t countTimeout
= 0;
111 static uint32_t countPacket
= 0;
112 static uint32_t txId
= 0;
113 static uint32_t rxId
= 0;
114 static bool bound
= false;
115 static bool sendTelemetry
= false;
116 static bool waitTx
= false;
117 static uint16_t errorRate
= 0;
118 static uint16_t rssi_dBm
= 0;
119 static uint8_t rfChannelMap
[FLYSKY_FREQUENCY_COUNT
] = {0};
122 static uint8_t getNextChannel (uint8_t step
)
124 static uint8_t channel
= 0;
125 channel
= (channel
+ step
) & 0x0F;
126 return rfChannelMap
[channel
];
129 static void flySkyCalculateRfChannels (void)
131 uint32_t channelRow
= txId
& 0x0F;
132 uint32_t channelOffset
= ((txId
& 0xF0) >> 4) + 1;
134 if (channelOffset
> 9) {
135 channelOffset
= 9; // from sloped soarer findings, bug in flysky protocol
138 for (uint32_t i
= 0; i
< FLYSKY_FREQUENCY_COUNT
; i
++) {
139 rfChannelMap
[i
] = flySkyRfChannels
[channelRow
][i
] - channelOffset
;
143 static void resetTimeout (const uint32_t timeStamp
)
145 timeLastPacket
= timeStamp
;
146 timeout
= timings
->firstPacket
;
151 static void checkTimeout (void)
153 static uint32_t timeMeasuareErrRate
= 0;
154 static uint32_t timeLastTelemetry
= 0;
155 uint32_t time
= micros();
157 if ((time
- timeMeasuareErrRate
) > (101 * timings
->packet
)) {
158 errorRate
= (countPacket
>= 100) ? (0) : (100 - countPacket
);
160 timeMeasuareErrRate
= time
;
163 if ((time
- timeLastTelemetry
) > timings
->telemetry
) {
164 timeLastTelemetry
= time
;
165 sendTelemetry
= true;
168 if ((time
- timeLastPacket
) > timeout
) {
169 uint32_t stepOver
= (time
- timeLastPacket
) / timings
->packet
;
171 timeLastPacket
= (stepOver
> 1) ? (time
) : (timeLastPacket
+ timeout
);
173 A7105Strobe(A7105_STANDBY
);
174 A7105WriteReg(A7105_0F_CHANNEL
, getNextChannel(stepOver
% FLYSKY_FREQUENCY_COUNT
));
175 A7105Strobe(A7105_RX
);
177 if(countTimeout
> 31) {
178 timeout
= timings
->syncPacket
;
179 setRssiFiltered(0, RSSI_SOURCE_RX_PROTOCOL
);
181 timeout
= timings
->packet
;
187 static void checkRSSI (void)
189 static uint8_t buf
[FLYSKY_RSSI_SAMPLE_COUNT
] = {0};
190 static int16_t sum
= 0;
191 static uint16_t currentIndex
= 0;
193 uint8_t adcValue
= A7105ReadReg(A7105_1D_RSSI_THOLD
);
196 sum
-= buf
[currentIndex
];
197 buf
[currentIndex
] = adcValue
;
198 currentIndex
= (currentIndex
+ 1) % FLYSKY_RSSI_SAMPLE_COUNT
;
200 rssi_dBm
= 50 + sum
/ (3 * FLYSKY_RSSI_SAMPLE_COUNT
); // range about [95...52], -dBm
202 int16_t tmp
= 2280 - 24 * rssi_dBm
;// convert to [0...1023]
203 setRssiFiltered(constrain(tmp
, 0, 1023), RSSI_SOURCE_RX_PROTOCOL
);
206 static bool isValidPacket (const uint8_t *packet
) {
207 const flySky2ARcDataPkt_t
*rcPacket
= (const flySky2ARcDataPkt_t
*) packet
;
208 return (rcPacket
->rxId
== rxId
&& rcPacket
->txId
== txId
);
211 static void buildAndWriteTelemetry (uint8_t *packet
)
214 static uint8_t bytesToWrite
= FLYSKY_2A_PAYLOAD_SIZE
; // first time write full packet to buffer a7105
215 flySky2ATelemetryPkt_t
*telemertyPacket
= (flySky2ATelemetryPkt_t
*) packet
;
216 uint16_t voltage
= 10 * getBatteryVoltage();
218 telemertyPacket
->type
= FLYSKY_2A_PACKET_TELEMETRY
;
220 telemertyPacket
->sens
[0].type
= SENSOR_INT_V
;
221 telemertyPacket
->sens
[0].number
= 0;
222 telemertyPacket
->sens
[0].valueL
= voltage
& 0xFF;
223 telemertyPacket
->sens
[0].valueH
= (voltage
>> 8) & 0xFF;
225 telemertyPacket
->sens
[1].type
= SENSOR_RSSI
;
226 telemertyPacket
->sens
[1].number
= 0;
227 telemertyPacket
->sens
[1].valueL
= rssi_dBm
& 0xFF;
228 telemertyPacket
->sens
[1].valueH
= (rssi_dBm
>> 8) & 0xFF;
230 telemertyPacket
->sens
[2].type
= SENSOR_ERR_RATE
;
231 telemertyPacket
->sens
[2].number
= 0;
232 telemertyPacket
->sens
[2].valueL
= errorRate
& 0xFF;
233 telemertyPacket
->sens
[2].valueH
= (errorRate
>> 8) & 0xFF;
235 memset (&telemertyPacket
->sens
[3], 0xFF, 4 * sizeof(flySky2ASens_t
));
237 A7105WriteFIFO(packet
, bytesToWrite
);
239 bytesToWrite
= 9 + 3 * sizeof(flySky2ASens_t
);// next time write only bytes that could change, the others are already set as 0xFF in buffer a7105
243 static rx_spi_received_e
flySky2AReadAndProcess (uint8_t *payload
, const uint32_t timeStamp
)
245 rx_spi_received_e result
= RX_SPI_RECEIVED_NONE
;
246 uint8_t packet
[FLYSKY_2A_PAYLOAD_SIZE
];
248 uint8_t bytesToRead
= (bound
) ? (9 + 2*FLYSKY_2A_CHANNEL_COUNT
) : (11 + FLYSKY_FREQUENCY_COUNT
);
249 A7105ReadFIFO(packet
, bytesToRead
);
252 case FLYSKY_2A_PACKET_RC_DATA
:
253 case FLYSKY_2A_PACKET_FS_SETTINGS
: // failsafe settings
254 case FLYSKY_2A_PACKET_SETTINGS
: // receiver settings
255 if (isValidPacket(packet
)) {
257 resetTimeout(timeStamp
);
259 const flySky2ARcDataPkt_t
*rcPacket
= (const flySky2ARcDataPkt_t
*) packet
;
261 if (rcPacket
->type
== FLYSKY_2A_PACKET_RC_DATA
) {
263 memcpy(payload
, rcPacket
->data
, 2*FLYSKY_2A_CHANNEL_COUNT
);
267 buildAndWriteTelemetry(packet
);
268 sendTelemetry
= false;
269 timeTxRequest
= timeStamp
;
273 result
= RX_SPI_RECEIVED_DATA
;
277 A7105WriteReg(A7105_0F_CHANNEL
, getNextChannel(1));
282 case FLYSKY_2A_PACKET_BIND1
:
283 case FLYSKY_2A_PACKET_BIND2
:
285 resetTimeout(timeStamp
);
287 flySky2ABindPkt_t
*bindPacket
= (flySky2ABindPkt_t
*) packet
;
289 if (bindPacket
->rfChannelMap
[0] != 0xFF) {
290 memcpy(rfChannelMap
, bindPacket
->rfChannelMap
, FLYSKY_FREQUENCY_COUNT
); // get TX channels
293 txId
= bindPacket
->txId
;
294 bindPacket
->rxId
= rxId
;
295 memset(bindPacket
->rfChannelMap
, 0xFF, 26); // erase channelMap and 10 bytes after it
297 timeTxRequest
= timeLastBind
= timeStamp
;
300 A7105WriteFIFO(packet
, FLYSKY_2A_PAYLOAD_SIZE
);
309 A7105Strobe(A7105_RX
);
314 static rx_spi_received_e
flySkyReadAndProcess (uint8_t *payload
, const uint32_t timeStamp
)
316 rx_spi_received_e result
= RX_SPI_RECEIVED_NONE
;
317 uint8_t packet
[FLYSKY_PAYLOAD_SIZE
];
319 uint8_t bytesToRead
= (bound
) ? (5 + 2*FLYSKY_CHANNEL_COUNT
) : (5);
320 A7105ReadFIFO(packet
, bytesToRead
);
322 const flySkyRcDataPkt_t
*rcPacket
= (const flySkyRcDataPkt_t
*) packet
;
324 if (bound
&& rcPacket
->type
== FLYSKY_PACKET_RC_DATA
&& rcPacket
->txId
== txId
) {
326 resetTimeout(timeStamp
);
329 memcpy(payload
, rcPacket
->data
, 2*FLYSKY_CHANNEL_COUNT
);
332 A7105WriteReg(A7105_0F_CHANNEL
, getNextChannel(1));
334 result
= RX_SPI_RECEIVED_DATA
;
337 if (!bound
&& rcPacket
->type
== FLYSKY_PACKET_BIND
) {
338 resetTimeout(timeStamp
);
340 txId
= rcPacket
->txId
;
341 flySkyCalculateRfChannels();
343 A7105WriteReg(A7105_0F_CHANNEL
, getNextChannel(0));
345 timeLastBind
= timeStamp
;
348 A7105Strobe(A7105_RX
);
352 bool flySkyInit (const struct rxConfig_s
*rxConfig
, struct rxRuntimeConfig_s
*rxRuntimeConfig
)
354 protocol
= rxConfig
->rx_spi_protocol
;
356 if (protocol
!= flySkyConfig()->protocol
) {
357 PG_RESET(flySkyConfig
);
360 IO_t bindPin
= IOGetByTag(IO_TAG(BINDPLUG_PIN
));
361 IOInit(bindPin
, OWNER_RX_BIND
, 0);
362 IOConfigGPIO(bindPin
, IOCFG_IPU
);
364 uint8_t startRxChannel
;
366 if (protocol
== RX_SPI_A7105_FLYSKY_2A
) {
367 rxRuntimeConfig
->channelCount
= FLYSKY_2A_CHANNEL_COUNT
;
368 timings
= &flySky2ATimings
;
369 rxId
= U_ID_0
^ U_ID_1
^ U_ID_2
;
370 startRxChannel
= flySky2ABindChannels
[0];
371 A7105Init(0x5475c52A);
372 A7105Config(flySky2ARegs
, sizeof(flySky2ARegs
));
374 rxRuntimeConfig
->channelCount
= FLYSKY_CHANNEL_COUNT
;
375 timings
= &flySkyTimings
;
377 A7105Init(0x5475c52A);
378 A7105Config(flySkyRegs
, sizeof(flySkyRegs
));
381 if ( !IORead(bindPin
) || flySkyConfig()->txId
== 0) {
385 txId
= flySkyConfig()->txId
; // load TXID
386 memcpy (rfChannelMap
, flySkyConfig()->rfChannelMap
, FLYSKY_FREQUENCY_COUNT
);// load channel map
387 startRxChannel
= getNextChannel(0);
390 if (rssiSource
== RSSI_SOURCE_NONE
) {
391 rssiSource
= RSSI_SOURCE_RX_PROTOCOL
;
394 A7105WriteReg(A7105_0F_CHANNEL
, startRxChannel
);
395 A7105Strobe(A7105_RX
); // start listening
397 resetTimeout(micros());
402 void flySkySetRcDataFromPayload (uint16_t *rcData
, const uint8_t *payload
)
404 if (rcData
&& payload
) {
405 uint32_t channelCount
;
406 channelCount
= (protocol
== RX_SPI_A7105_FLYSKY_2A
) ? (FLYSKY_2A_CHANNEL_COUNT
) : (FLYSKY_CHANNEL_COUNT
);
408 for (uint8_t i
= 0; i
< channelCount
; i
++) {
409 rcData
[i
] = payload
[2 * i
+ 1] << 8 | payload
[2 * i
+ 0];
414 rx_spi_received_e
flySkyDataReceived (uint8_t *payload
)
416 rx_spi_received_e result
= RX_SPI_RECEIVED_NONE
;
419 if (A7105RxTxFinished(&timeStamp
)) {
420 uint8_t modeReg
= A7105ReadReg(A7105_00_MODE
);
422 if (((modeReg
& A7105_MODE_TRSR
) != 0) && ((modeReg
& A7105_MODE_TRER
) == 0)) { // TX complete
424 A7105WriteReg(A7105_0F_CHANNEL
, getNextChannel(1));
426 A7105Strobe(A7105_RX
);
427 } else if ((modeReg
& (A7105_MODE_CRCF
|A7105_MODE_TRER
)) == 0) { // RX complete, CRC pass
428 if (protocol
== RX_SPI_A7105_FLYSKY_2A
) {
429 result
= flySky2AReadAndProcess(payload
, timeStamp
);
431 result
= flySkyReadAndProcess(payload
, timeStamp
);
434 A7105Strobe(A7105_RX
);
438 if (waitTx
&& (micros() - timeTxRequest
) > TX_DELAY
) {
439 A7105Strobe(A7105_TX
);
446 if ((micros() - timeLastBind
) > BIND_TIMEOUT
&& rfChannelMap
[0] != 0 && txId
!= 0) {
447 result
= RX_SPI_RECEIVED_BIND
;
449 flySkyConfigMutable()->txId
= txId
; // store TXID
450 memcpy (flySkyConfigMutable()->rfChannelMap
, rfChannelMap
, FLYSKY_FREQUENCY_COUNT
);// store channel map
451 flySkyConfigMutable()->protocol
= protocol
;
459 #endif /* USE_RX_FLYSKY */