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/>.
25 #ifdef USE_RX_REDPINE_SPI
27 #include "cc2500_redpine.h"
29 #include "build/build_config.h"
30 #include "build/debug.h"
31 #include "common/maths.h"
32 #include "common/utils.h"
33 #include "config/config.h"
34 #include "config/feature.h"
35 #include "drivers/adc.h"
36 #include "drivers/io.h"
37 #include "drivers/io_def.h"
38 #include "drivers/io_types.h"
39 #include "drivers/resource.h"
40 #include "drivers/rx/rx_cc2500.h"
41 #include "drivers/rx/rx_spi.h"
42 #include "drivers/system.h"
43 #include "drivers/time.h"
44 #include "fc/runtime_config.h"
47 #include "pg/rx_spi.h"
48 #include "pg/rx_spi_cc2500.h"
49 #include "rx/cc2500_common.h"
50 #include "rx/rx_spi_common.h"
51 #include "sensors/battery.h"
67 bool redpineFast
= true;
69 #define VTX_STATUS_FRAME 1
70 #define SCALE_REDPINE(channelValue) ((2 * channelValue + 2452) / 3)
71 #define SWITCH_REDPINE_SPEED_US 2000000
72 #define DEFAULT_PACKET_TIME_US 50000
73 #define REDPINE_HOP_CHANNELS 49
74 #define BIND_TUNE_STEP 2
76 static uint8_t calData
[255][3];
77 static timeMs_t start_time
;
78 static uint8_t protocolState
;
79 static uint32_t missingPackets
;
80 static timeDelta_t timeoutUs
;
81 static timeMs_t timeTunedMs
;
82 static int8_t bindOffset_max
= 0;
83 static int8_t bindOffset_min
= 0;
85 static void initialise(void);
86 static void initBindTuneRx(void);
87 static bool tuneRx1(uint8_t *packet
);
88 static bool tuneRx2(uint8_t *packet
);
89 static bool tuneRx3(uint8_t *packet
);
90 static void nextChannel();
91 static bool redpineRxPacketBind(uint8_t *packet
);
92 static bool isRedpineFast(void);
94 const cc2500RegisterConfigElement_t cc2500RedPineBaseConfig
[] =
96 { CC2500_02_IOCFG0
, 0x01 },
97 { CC2500_03_FIFOTHR
, 0x07 },
98 { CC2500_06_PKTLEN
, REDPINE_PACKET_SIZE
},
99 { CC2500_07_PKTCTRL1
, 0x0C },
100 { CC2500_08_PKTCTRL0
, 0x05 },
101 { CC2500_09_ADDR
, 0x00 }
104 const cc2500RegisterConfigElement_t cc2500RedPineFastConfig
[] =
106 { CC2500_0B_FSCTRL1
, 0x0A },
107 { CC2500_0C_FSCTRL0
, 0x00 },
108 { CC2500_0D_FREQ2
, 0x5D },
109 { CC2500_0E_FREQ1
, 0x93 },
110 { CC2500_0F_FREQ0
, 0xB1 },
111 { CC2500_10_MDMCFG4
, 0x2D },
112 { CC2500_11_MDMCFG3
, 0x3B },
113 { CC2500_12_MDMCFG2
, 0x73 },
114 { CC2500_13_MDMCFG1
, 0x23 },
115 { CC2500_14_MDMCFG0
, 0x56 },
116 { CC2500_15_DEVIATN
, 0x00 },
117 { CC2500_17_MCSM1
, 0x0C },
118 { CC2500_18_MCSM0
, 0x08 },
119 { CC2500_19_FOCCFG
, 0x1D },
120 { CC2500_1A_BSCFG
, 0x1C },
121 { CC2500_1B_AGCCTRL2
, 0xC7 },
122 { CC2500_1C_AGCCTRL1
, 0x00 },
123 { CC2500_1D_AGCCTRL0
, 0xB0 },
124 { CC2500_21_FREND1
, 0xB6 },
125 { CC2500_22_FREND0
, 0x10 },
126 { CC2500_23_FSCAL3
, 0xEA },
127 { CC2500_24_FSCAL2
, 0x0A },
128 { CC2500_25_FSCAL1
, 0x00 },
129 { CC2500_26_FSCAL0
, 0x11 },
130 { CC2500_29_FSTEST
, 0x59 },
131 { CC2500_2C_TEST2
, 0x88 },
132 { CC2500_2D_TEST1
, 0x31 },
133 { CC2500_2E_TEST0
, 0x0B },
134 { CC2500_3E_PATABLE
, 0xFF }
137 const cc2500RegisterConfigElement_t cc2500RedPineConfig
[] =
139 { CC2500_0B_FSCTRL1
, 0x06 },
140 { CC2500_0C_FSCTRL0
, 0x00 },
141 { CC2500_0D_FREQ2
, 0x5D },
142 { CC2500_0E_FREQ1
, 0x93 },
143 { CC2500_0F_FREQ0
, 0xB1 },
144 { CC2500_10_MDMCFG4
, 0x78 },
145 { CC2500_11_MDMCFG3
, 0x93 },
146 { CC2500_12_MDMCFG2
, 0x03 },
147 { CC2500_13_MDMCFG1
, 0x22 },
148 { CC2500_14_MDMCFG0
, 0xF8 },
149 { CC2500_15_DEVIATN
, 0x44 },
150 { CC2500_17_MCSM1
, 0x0C },
151 { CC2500_18_MCSM0
, 0x08 },
152 { CC2500_19_FOCCFG
, 0x16 },
153 { CC2500_1A_BSCFG
, 0x6C },
154 { CC2500_1B_AGCCTRL2
, 0x43 },
155 { CC2500_1C_AGCCTRL1
, 0x40 },
156 { CC2500_1D_AGCCTRL0
, 0x91 },
157 { CC2500_21_FREND1
, 0x56 },
158 { CC2500_22_FREND0
, 0x10 },
159 { CC2500_23_FSCAL3
, 0xA9 },
160 { CC2500_24_FSCAL2
, 0x0A },
161 { CC2500_25_FSCAL1
, 0x00 },
162 { CC2500_26_FSCAL0
, 0x11 },
163 { CC2500_29_FSTEST
, 0x59 },
164 { CC2500_2C_TEST2
, 0x88 },
165 { CC2500_2D_TEST1
, 0x31 },
166 { CC2500_2E_TEST0
, 0x0B },
167 { CC2500_3E_PATABLE
, 0xFF }
170 static void initialise()
174 cc2500ApplyRegisterConfig(cc2500RedPineBaseConfig
, sizeof(cc2500RedPineBaseConfig
));
176 if (isRedpineFast()) {
177 cc2500ApplyRegisterConfig(cc2500RedPineFastConfig
, sizeof(cc2500RedPineFastConfig
));
179 cc2500ApplyRegisterConfig(cc2500RedPineConfig
, sizeof(cc2500RedPineConfig
));
182 for (unsigned c
= 0; c
< 0xFF; c
++) { // calibrate all channels
183 cc2500Strobe(CC2500_SIDLE
);
184 cc2500WriteReg(CC2500_0A_CHANNR
, c
);
185 cc2500Strobe(CC2500_SCAL
);
186 delayMicroseconds(900); //
187 calData
[c
][0] = cc2500ReadReg(CC2500_23_FSCAL3
);
188 calData
[c
][1] = cc2500ReadReg(CC2500_24_FSCAL2
);
189 calData
[c
][2] = cc2500ReadReg(CC2500_25_FSCAL1
);
193 rx_spi_received_e
redpineSpiDataReceived(uint8_t *packet
)
195 rx_spi_received_e ret
= RX_SPI_RECEIVED_NONE
;
197 switch (protocolState
) {
199 if ((millis() - start_time
) > 10) {
202 protocolState
= STATE_BIND
;
207 if (rxSpiCheckBindRequested(true) || rxCc2500SpiConfig()->autoBind
) {
213 protocolState
= STATE_BIND_TUNING1
;
215 protocolState
= STATE_STARTING
;
219 case STATE_BIND_TUNING1
:
220 if (tuneRx1(packet
)) {
221 protocolState
= STATE_BIND_TUNING2
;
224 case STATE_BIND_TUNING2
:
225 if (tuneRx2(packet
)) {
226 protocolState
= STATE_BIND_TUNING3
;
229 case STATE_BIND_TUNING3
:
230 if (tuneRx3(packet
)) {
231 if (((int16_t)bindOffset_max
- (int16_t)bindOffset_min
) <= 10) {
233 protocolState
= STATE_BIND_TUNING1
; // retry
235 rxCc2500SpiConfigMutable()->bindOffset
= ((int16_t)bindOffset_max
+ (int16_t)bindOffset_min
) / 2;
236 protocolState
= STATE_BIND_COMPLETE
;
237 cc2500Strobe(CC2500_SIDLE
);
239 for (uint8_t i
= 0; i
< REDPINE_HOP_CHANNELS
; i
++) {
240 if (rxCc2500SpiConfigMutable()->bindHopData
[i
] == 0) {
241 protocolState
= STATE_BIND_TUNING1
; // retry
248 case STATE_BIND_COMPLETE
:
249 if (!rxCc2500SpiConfig()->autoBind
) {
259 ret
= RX_SPI_RECEIVED_BIND
;
260 protocolState
= STATE_STARTING
;
264 ret
= redpineHandlePacket(packet
, &protocolState
);
268 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 3, protocolState
);
273 static void initBindTuneRx(void)
275 timeTunedMs
= millis();
277 bindOffset_min
= -64;
278 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 1, bindOffset_min
);
279 cc2500WriteReg(CC2500_0C_FSCTRL0
, (uint8_t)bindOffset_min
);
280 cc2500WriteReg(CC2500_07_PKTCTRL1
, 0x0C);
281 cc2500WriteReg(CC2500_18_MCSM0
, 0x8);
283 cc2500Strobe(CC2500_SIDLE
);
284 cc2500WriteReg(CC2500_23_FSCAL3
, calData
[0][0]);
285 cc2500WriteReg(CC2500_24_FSCAL2
, calData
[0][1]);
286 cc2500WriteReg(CC2500_25_FSCAL1
, calData
[0][2]);
287 cc2500WriteReg(CC2500_0A_CHANNR
, 0);
288 cc2500Strobe(CC2500_SFRX
);
289 cc2500Strobe(CC2500_SRX
);
291 for (uint8_t i
= 0; i
< REDPINE_HOP_CHANNELS
; i
++) {
292 rxCc2500SpiConfigMutable()->bindHopData
[i
] = 0;
296 static bool tuneRx1(uint8_t *packet
)
298 if (bindOffset_min
>= 126) {
299 bindOffset_min
= -126;
300 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 1, bindOffset_min
);
302 if ((millis() - timeTunedMs
) > 220) { // 220ms
303 timeTunedMs
= millis();
304 bindOffset_min
+= BIND_TUNE_STEP
<< 3;
305 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 1, bindOffset_min
);
306 cc2500WriteReg(CC2500_0C_FSCTRL0
, (uint8_t)bindOffset_min
);
307 cc2500Strobe(CC2500_SRX
);
309 if (redpineRxPacketBind(packet
)) {
310 bindOffset_max
= bindOffset_min
;
311 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 2, bindOffset_max
);
312 cc2500Strobe(CC2500_SRX
);
313 timeTunedMs
= millis();
319 static bool tuneRx2(uint8_t *packet
)
322 if (((millis() - timeTunedMs
) > 880) || bindOffset_max
> (126 - BIND_TUNE_STEP
)) { // 220ms *4
323 timeTunedMs
= millis();
324 cc2500WriteReg(CC2500_0C_FSCTRL0
, (uint8_t)bindOffset_min
);
325 cc2500Strobe(CC2500_SRX
);
328 if (redpineRxPacketBind(packet
)) {
329 timeTunedMs
= millis();
330 bindOffset_max
+= BIND_TUNE_STEP
;
331 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 2, bindOffset_max
);
332 cc2500WriteReg(CC2500_0C_FSCTRL0
, (uint8_t)bindOffset_max
);
337 static bool tuneRx3(uint8_t *packet
)
340 if (((millis() - timeTunedMs
) > 880) || bindOffset_min
< (-126 + BIND_TUNE_STEP
)) { // 220ms *4
343 if (redpineRxPacketBind(packet
)) {
344 timeTunedMs
= millis();
345 bindOffset_min
-= BIND_TUNE_STEP
;
346 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 1, bindOffset_min
);
347 cc2500WriteReg(CC2500_0C_FSCTRL0
, (uint8_t)bindOffset_min
);
352 static bool redpineRxPacketBind(uint8_t *packet
)
354 if (rxSpiGetExtiState()) {
355 uint8_t ccLen
= cc2500ReadReg(CC2500_3B_RXBYTES
| CC2500_READ_BURST
) & 0x7F;
356 if (ccLen
!= REDPINE_PACKET_SIZE_W_ADDONS
) {
357 cc2500Strobe(CC2500_SFRX
);
359 cc2500ReadFifo(packet
, ccLen
);
360 if (packet
[1] == 0x03 && packet
[2] == 0x01) {
361 rxCc2500SpiConfigMutable()->bindTxId
[0] = packet
[3];
362 rxCc2500SpiConfigMutable()->bindTxId
[1] = packet
[4];
363 for (uint8_t n
= 0; n
< 5; n
++) {
364 rxCc2500SpiConfigMutable()->bindHopData
[packet
[5] + n
] = packet
[6 + n
];
373 static void nextChannel(void)
375 static uint8_t channr
= 0;
378 while (channr
>= REDPINE_HOP_CHANNELS
) {
379 channr
-= REDPINE_HOP_CHANNELS
;
381 cc2500Strobe(CC2500_SIDLE
);
382 cc2500WriteReg(CC2500_23_FSCAL3
, calData
[rxCc2500SpiConfig()->bindHopData
[channr
]][0]);
383 cc2500WriteReg(CC2500_24_FSCAL2
, calData
[rxCc2500SpiConfig()->bindHopData
[channr
]][1]);
384 cc2500WriteReg(CC2500_25_FSCAL1
, calData
[rxCc2500SpiConfig()->bindHopData
[channr
]][2]);
385 cc2500WriteReg(CC2500_0A_CHANNR
, rxCc2500SpiConfig()->bindHopData
[channr
]);
388 static bool isRedpineFast(void)
390 return (redpineFast
);
393 void switchRedpineMode(void)
395 redpineFast
= !redpineFast
;
398 #define CHANNEL_START 3
399 void redpineSetRcData(uint16_t *rcData
, const uint8_t *packet
)
401 if (packet
[CHANNEL_START
] == VTX_STATUS_FRAME
&& packet
[CHANNEL_START
+ 1] == 0) {
402 if (!ARMING_FLAG(ARMED
)) {
403 vtxSettingsConfigMutable()->band
= packet
[5] + 1;
404 vtxSettingsConfigMutable()->channel
= packet
[6];
405 vtxSettingsConfigMutable()->power
= packet
[7];
406 saveConfigAndNotify();
409 uint16_t channelValue
;
410 // 4 stick channels (11-bit)
411 channelValue
= (uint16_t)((packet
[CHANNEL_START
+ 1] << 8) & 0x700) | packet
[CHANNEL_START
];
412 rcData
[0] = SCALE_REDPINE(channelValue
);
414 channelValue
= (uint16_t)((packet
[CHANNEL_START
+ 2] << 4) & 0x7F0) | ((packet
[CHANNEL_START
+ 1] >> 4) & 0xF);
415 rcData
[1] = SCALE_REDPINE(channelValue
);
417 channelValue
= (uint16_t)((packet
[CHANNEL_START
+ 4] << 8) & 0x700) | packet
[CHANNEL_START
+ 3];
418 rcData
[2] = SCALE_REDPINE(channelValue
);
420 channelValue
= (uint16_t)((packet
[CHANNEL_START
+ 5] << 4) & 0x7F0) | ((packet
[CHANNEL_START
+ 4] >> 4) & 0xF);
421 rcData
[3] = SCALE_REDPINE(channelValue
);
423 // 12 1-bit aux channels - first 4 are interleaved with stick channels
424 rcData
[4] = (packet
[CHANNEL_START
+ 1] & 0x08) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
425 rcData
[5] = (packet
[CHANNEL_START
+ 2] & 0x80) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
426 rcData
[6] = (packet
[CHANNEL_START
+ 4] & 0x08) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
427 rcData
[7] = (packet
[CHANNEL_START
+ 5] & 0x80) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
428 rcData
[8] = (packet
[CHANNEL_START
+ 6] & 0x01) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
429 rcData
[9] = (packet
[CHANNEL_START
+ 6] & 0x02) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
430 rcData
[10] = (packet
[CHANNEL_START
+ 6] & 0x04) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
431 rcData
[11] = (packet
[CHANNEL_START
+ 6] & 0x08) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
432 rcData
[12] = (packet
[CHANNEL_START
+ 6] & 0x10) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
433 rcData
[13] = (packet
[CHANNEL_START
+ 6] & 0x20) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
434 rcData
[14] = (packet
[CHANNEL_START
+ 6] & 0x40) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
435 rcData
[15] = (packet
[CHANNEL_START
+ 6] & 0x80) ? PWM_RANGE_MAX
: PWM_RANGE_MIN
;
439 rx_spi_received_e
redpineHandlePacket(uint8_t *const packet
, uint8_t *const protocolState
)
441 static int32_t looptime
= DEFAULT_PACKET_TIME_US
;
442 static timeUs_t packetTimerUs
;
443 static timeUs_t totalTimerUs
;
444 static timeUs_t protocolTimerUs
;
446 rx_spi_received_e ret
= RX_SPI_RECEIVED_NONE
;
448 switch (*protocolState
) {
450 *protocolState
= STATE_UPDATE
;
452 cc2500Strobe(CC2500_SRX
);
453 #ifdef USE_RX_CC2500_SPI_PA_LNA
455 #endif // USE_RX_CC2500_SPI_PA_LNA
456 protocolTimerUs
= micros();
460 totalTimerUs
= micros();
462 *protocolState
= STATE_DATA
;
463 if (rxSpiCheckBindRequested(false)) {
466 *protocolState
= STATE_INIT
;
471 // here FS code could be
473 if (rxSpiGetExtiState()) {
474 uint8_t ccLen
= cc2500ReadReg(CC2500_3B_RXBYTES
| CC2500_READ_BURST
) & 0x7F;
475 if (ccLen
== REDPINE_PACKET_SIZE_W_ADDONS
) {
476 cc2500ReadFifo(packet
, ccLen
);
478 if ((packet
[1] == rxCc2500SpiConfig()->bindTxId
[0]) && (packet
[2] == rxCc2500SpiConfig()->bindTxId
[1])) {
479 if (isRedpineFast()) {
480 looptime
= packet
[CHANNEL_START
+ 7] * 100;
482 looptime
= packet
[CHANNEL_START
+ 7] * 1000;
484 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 0, looptime
);
485 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 1, packet
[ccLen
- 2]);
487 packetTimerUs
= micros() + looptime
/ 8; // add a buffer on the packet time incase tx and rx clocks are different
488 totalTimerUs
= micros();
489 protocolTimerUs
= micros();
491 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 2, missingPackets
);
495 cc2500setRssiDbm(packet
[ccLen
- 2]);
497 ret
= RX_SPI_RECEIVED_DATA
;
499 cc2500Strobe(CC2500_SRX
);
502 cc2500Strobe(CC2500_SFRX
);
506 if (cmpTimeUs(micros(), totalTimerUs
) > 50 * looptime
) {
507 // out of sync with packets - do a complete resysnc
509 setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL
);
511 cc2500Strobe(CC2500_SRX
);
512 *protocolState
= STATE_UPDATE
;
513 } else if ((cmpTimeUs(micros(), packetTimerUs
) > looptime
) && packetTimerUs
) {
515 packetTimerUs
= micros();
517 cc2500Strobe(CC2500_SRX
);
519 DEBUG_SET(DEBUG_RX_FRSKY_SPI
, 2, missingPackets
);
520 #if defined(USE_RX_CC2500_SPI_DIVERSITY)
521 if (missingPackets
>= 2) {
522 cc2500switchAntennae();
525 } else if (cmpTimeUs(micros(), protocolTimerUs
) > SWITCH_REDPINE_SPEED_US
) {
527 looptime
= DEFAULT_PACKET_TIME_US
;
528 protocolTimerUs
= micros();
529 *protocolState
= STATE_INIT
;
536 bool redpineSpiInit(const rxSpiConfig_t
*rxSpiConfig
, rxRuntimeState_t
*rxRuntimeState
, rxSpiExtiConfig_t
*extiConfig
)
540 rxSpiCommonIOInit(rxSpiConfig
);
541 if (!cc2500SpiInit()) {
545 rxRuntimeState
->channelCount
= RC_CHANNEL_COUNT_REDPINE
;
550 start_time
= millis();
551 protocolState
= STATE_INIT
;