Merge pull request #11198 from SteveCEvans/sce_rc2
[betaflight.git] / src / main / rx / cc2500_redpine.c
blob2a8ad0a97d62ac20f31fc92fb7701ca10f56dea2
1 /*
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)
8 * any later version.
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/>.
21 #include <string.h>
23 #include "platform.h"
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"
45 #include "io/vtx.h"
46 #include "pg/rx.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"
53 enum {
54 STATE_INIT = 0,
55 STATE_BIND,
56 STATE_BIND_TUNING1,
57 STATE_BIND_TUNING2,
58 STATE_BIND_TUNING3,
59 STATE_BIND_COMPLETE,
60 STATE_STARTING,
61 STATE_UPDATE,
62 STATE_DATA,
63 STATE_TELEMETRY,
64 STATE_RESUME,
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()
172 cc2500Reset();
174 cc2500ApplyRegisterConfig(cc2500RedPineBaseConfig, sizeof(cc2500RedPineBaseConfig));
176 if (isRedpineFast()) {
177 cc2500ApplyRegisterConfig(cc2500RedPineFastConfig, sizeof(cc2500RedPineFastConfig));
178 } else {
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) {
198 case STATE_INIT:
199 if ((millis() - start_time) > 10) {
200 initialise();
202 protocolState = STATE_BIND;
205 break;
206 case STATE_BIND:
207 if (rxSpiCheckBindRequested(true) || rxCc2500SpiConfig()->autoBind) {
208 redpineFast = true;
209 initialise();
210 rxSpiLedOn();
211 initBindTuneRx();
213 protocolState = STATE_BIND_TUNING1;
214 } else {
215 protocolState = STATE_STARTING;
218 break;
219 case STATE_BIND_TUNING1:
220 if (tuneRx1(packet)) {
221 protocolState = STATE_BIND_TUNING2;
223 break;
224 case STATE_BIND_TUNING2:
225 if (tuneRx2(packet)) {
226 protocolState = STATE_BIND_TUNING3;
228 break;
229 case STATE_BIND_TUNING3:
230 if (tuneRx3(packet)) {
231 if (((int16_t)bindOffset_max - (int16_t)bindOffset_min) <= 10) {
232 initBindTuneRx();
233 protocolState = STATE_BIND_TUNING1; // retry
234 } else {
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
242 break;
247 break;
248 case STATE_BIND_COMPLETE:
249 if (!rxCc2500SpiConfig()->autoBind) {
250 writeEEPROM();
251 } else {
252 uint8_t ctr = 80;
253 while (ctr--) {
254 rxSpiLedToggle();
255 delay(50);
259 ret = RX_SPI_RECEIVED_BIND;
260 protocolState = STATE_STARTING;
262 break;
263 default:
264 ret = redpineHandlePacket(packet, &protocolState);
266 break;
268 DEBUG_SET(DEBUG_RX_FRSKY_SPI, 3, protocolState);
270 return ret;
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();
314 return true;
316 return false;
319 static bool tuneRx2(uint8_t *packet)
321 rxSpiLedBlink(100);
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);
326 return true;
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);
334 return false;
337 static bool tuneRx3(uint8_t *packet)
339 rxSpiLedBlink(100);
340 if (((millis() - timeTunedMs) > 880) || bindOffset_min < (-126 + BIND_TUNE_STEP)) { // 220ms *4
341 return true;
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);
349 return false;
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);
358 } else {
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];
366 return true;
370 return false;
373 static void nextChannel(void)
375 static uint8_t channr = 0;
377 channr += 1;
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();
408 } else {
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) {
449 case STATE_STARTING:
450 *protocolState = STATE_UPDATE;
451 nextChannel();
452 cc2500Strobe(CC2500_SRX);
453 #ifdef USE_RX_CC2500_SPI_PA_LNA
454 cc2500TxDisable();
455 #endif // USE_RX_CC2500_SPI_PA_LNA
456 protocolTimerUs = micros();
457 break;
458 case STATE_UPDATE:
459 packetTimerUs = 0;
460 totalTimerUs = micros();
462 *protocolState = STATE_DATA;
463 if (rxSpiCheckBindRequested(false)) {
464 packetTimerUs = 0;
465 missingPackets = 0;
466 *protocolState = STATE_INIT;
467 break;
470 FALLTHROUGH;
471 // here FS code could be
472 case STATE_DATA:
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;
481 } else {
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();
490 missingPackets = 0;
491 DEBUG_SET(DEBUG_RX_FRSKY_SPI, 2, missingPackets);
493 rxSpiLedOn();
495 cc2500setRssiDbm(packet[ccLen - 2]);
497 ret = RX_SPI_RECEIVED_DATA;
498 nextChannel();
499 cc2500Strobe(CC2500_SRX);
501 } else {
502 cc2500Strobe(CC2500_SFRX);
506 if (cmpTimeUs(micros(), totalTimerUs) > 50 * looptime) {
507 // out of sync with packets - do a complete resysnc
508 rxSpiLedToggle();
509 setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL);
510 nextChannel();
511 cc2500Strobe(CC2500_SRX);
512 *protocolState = STATE_UPDATE;
513 } else if ((cmpTimeUs(micros(), packetTimerUs) > looptime) && packetTimerUs) {
514 // missed a packet
515 packetTimerUs = micros();
516 nextChannel();
517 cc2500Strobe(CC2500_SRX);
518 missingPackets++;
519 DEBUG_SET(DEBUG_RX_FRSKY_SPI, 2, missingPackets);
520 #if defined(USE_RX_CC2500_SPI_DIVERSITY)
521 if (missingPackets >= 2) {
522 cc2500switchAntennae();
524 #endif
525 } else if (cmpTimeUs(micros(), protocolTimerUs) > SWITCH_REDPINE_SPEED_US) {
526 switchRedpineMode();
527 looptime = DEFAULT_PACKET_TIME_US;
528 protocolTimerUs = micros();
529 *protocolState = STATE_INIT;
531 break;
533 return ret;
536 bool redpineSpiInit(const rxSpiConfig_t *rxSpiConfig, rxRuntimeState_t *rxRuntimeState, rxSpiExtiConfig_t *extiConfig)
538 UNUSED(extiConfig);
540 rxSpiCommonIOInit(rxSpiConfig);
541 if (!cc2500SpiInit()) {
542 return false;
545 rxRuntimeState->channelCount = RC_CHANNEL_COUNT_REDPINE;
547 missingPackets = 0;
548 timeoutUs = 50;
550 start_time = millis();
551 protocolState = STATE_INIT;
553 return true;
556 #endif