/* * FrSky RX 2-way protocol */ #include #include #include #include #include "cc2500.h" #include "spi.h" #include "timer.h" // ---------------------------------------------------------------------------- #define TRUE 1 #define FALSE 0 #define CHANNELS 8 #define PPM_MIN 1000 #define PPM_MAX 2000 #define RSSI_OVER_PPM 7 #define HOP_DATA_LENGTH 60 #define EEPROM_BASE_ADDRESS 100 #define MISSING_PACKET_DELAY 9 #define SEEK_CHANNEL_SKIP 13 #define MAX_MISSING_PACKET 20 #define FAILSAFE_MISSING_PACKET 170 static uint8_t ccData[27]; static uint8_t ccLen; static uint8_t packet = FALSE; static uint8_t channr = 0; static uint8_t missingPackets = 0; uint8_t hopData[HOP_DATA_LENGTH]; uint8_t listLength; uint8_t txid[2]; static uint8_t counter = 0; volatile uint8_t failed = FALSE; int count = 0; uint16_t c[8]; int rssi; const int rssi_offset = 71; const int rssi_min = -103; const int rssi_max = -96; void getBind(void); void loop(void); void tuning(void); // ---------------------------------------------------------------------------- static uint16_t ppmBuffer[CHANNELS]; static long map(long x, long in_min, long in_max, long out_min, long out_max); static long constrain(long x, long min, long max); static void initialize(uint8_t bind); static void binding(void); static void nextChannel(uint8_t skip); static void readBindingData(void); static void writeBindingData(void); void rxInit(void) { initialize(1); // binding binding(); initialize(0); // data cc2500WriteReg(CC2500_0A_CHANNR, hopData[channr]);//0A-hop cc2500WriteReg(CC2500_23_FSCAL3, 0x89); //23-89 cc2500Strobe(CC2500_SRX); } static long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } static long constrain(long x, long min, long max) { if (x < min) { x = min; } if (x > max) { x = max; } return x; } static void initialize(uint8_t bind) { cc2500ResetChip(); cc2500WriteReg(CC2500_02_IOCFG0, 0x01); // RX complete interrupt(GDO0) cc2500WriteReg(CC2500_17_MCSM1, 0x0C); cc2500WriteReg(CC2500_18_MCSM0, 0x18); cc2500WriteReg(CC2500_06_PKTLEN, 0x19); // Leave room for appended status bytes cc2500WriteReg(CC2500_08_PKTCTRL0, 0x05); cc2500WriteReg(CC2500_3E_PATABLE, 0xFF); cc2500WriteReg(CC2500_0B_FSCTRL1, 0x08); cc2500WriteReg(CC2500_0C_FSCTRL0, 0x00); cc2500WriteReg(CC2500_0D_FREQ2, 0x5C); cc2500WriteReg(CC2500_0E_FREQ1, 0x76); cc2500WriteReg(CC2500_0F_FREQ0, 0x27); cc2500WriteReg(CC2500_10_MDMCFG4, 0xAA); cc2500WriteReg(CC2500_11_MDMCFG3, 0x39); cc2500WriteReg(CC2500_12_MDMCFG2, 0x11); cc2500WriteReg(CC2500_13_MDMCFG1, 0x23); cc2500WriteReg(CC2500_14_MDMCFG0, 0x7A); cc2500WriteReg(CC2500_15_DEVIATN, 0x42); cc2500WriteReg(CC2500_19_FOCCFG, 0x16); cc2500WriteReg(CC2500_1A_BSCFG, 0x6C); cc2500WriteReg(CC2500_1B_AGCCTRL2, 0x03); cc2500WriteReg(CC2500_1C_AGCCTRL1, 0x40); cc2500WriteReg(CC2500_1D_AGCCTRL0, 0x91); cc2500WriteReg(CC2500_21_FREND1, 0x56); cc2500WriteReg(CC2500_22_FREND0, 0x10); cc2500WriteReg(CC2500_23_FSCAL3, 0xA9); cc2500WriteReg(CC2500_24_FSCAL2, 0x05); cc2500WriteReg(CC2500_25_FSCAL1, 0x00); cc2500WriteReg(CC2500_26_FSCAL0, 0x11); cc2500WriteReg(CC2500_29_FSTEST, 0x59); cc2500WriteReg(CC2500_2C_TEST2, 0x88); cc2500WriteReg(CC2500_2D_TEST1, 0x31); cc2500WriteReg(CC2500_2E_TEST0, 0x0B); cc2500WriteReg(CC2500_03_FIFOTHR, 0x0F); cc2500WriteReg(CC2500_09_ADDR, bind ? 0x03 : txid[0]); cc2500Strobe(CC2500_SIDLE); // Go to idle... // hack: Append status, filter by address, auto-flush on bad crc, PQT=0 cc2500WriteReg(CC2500_07_PKTCTRL1, 0x0D); //cc2500WriteReg(CC2500_0C_FSCTRL0, 0); // Frequency offset cc2500WriteReg(CC2500_0C_FSCTRL0, bind ? 0x00 : count); // Frequency offset hack cc2500WriteReg(CC2500_0A_CHANNR, 0x00); } static void binding(void) { uint8_t jumper2 = 0; //bind_jumper(); while (1) { if (jumper2 == 0) { // bind complete or no bind readBindingData(); if ((txid[0] == 0xff) && (txid[1] == 0xff)) { // No valid txid, forcing bind jumper2 = 1; continue; } break; } else { tuning(); //count=0xC8;//for test cc2500WriteReg(CC2500_0C_FSCTRL0, count); eeprom_write_byte(EEPROM_BASE_ADDRESS + 101, count); getBind(); // TODO reset?! while (1) { } } } } static void nextChannel(uint8_t skip) { channr += skip; if (channr >= listLength) { channr -= listLength; } cc2500WriteReg(CC2500_0A_CHANNR, hopData[channr]); cc2500WriteReg(CC2500_23_FSCAL3, 0x89); } static void readBindingData() { for (uint8_t i = 0; i < 2; i++) { txid[i] = eeprom_read_byte(EEPROM_BASE_ADDRESS + i); } for (uint8_t i = 0; i < HOP_DATA_LENGTH; i++) { hopData[i] = eeprom_read_byte(EEPROM_BASE_ADDRESS + 10 + i); } listLength = eeprom_read_byte(EEPROM_BASE_ADDRESS + 100); count = eeprom_read_byte(EEPROM_BASE_ADDRESS + 101); } static void writeBindingData() { for (uint8_t i = 0; i < 2; i++) { eeprom_write_byte(EEPROM_BASE_ADDRESS + i, txid[i]); } for (uint8_t i = 0; i < HOP_DATA_LENGTH; i++) { eeprom_write_byte(EEPROM_BASE_ADDRESS + 10 + i, hopData[i]); } eeprom_write_byte(EEPROM_BASE_ADDRESS + 100, listLength); } // ---------------------------------------------------------------------------- // Receives complete bind setup void getBind(void) { cc2500Strobe(CC2500_SRX); // enter in rx mode while (1) { if (GDO_1) { ccLen = cc2500ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (ccLen) { cc2500ReadFifo((uint8_t *)ccData, ccLen); if ((ccData[ccLen - 1] & 0x80) && (ccData[2] == 0x01) && (ccData[5] == 0x00)) { txid[0] = ccData[3]; txid[1] = ccData[4]; for (uint8_t n = 0; n < 5; n++) { hopData[ccData[5] + n] = ccData[6 + n]; } break; } } } } listLength = 0; uint8_t eol = FALSE; for (uint8_t bindIdx = 0x05; bindIdx <= 120; bindIdx += 5) { while (1) { if (GDO_1) { ccLen = cc2500ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (ccLen) { cc2500ReadFifo((uint8_t *)ccData, ccLen); if ((ccData[ccLen - 1] & 0x80) && (ccData[2] == 0x01) && (ccData[3] == txid[0]) && (ccData[4] == txid[1]) && (ccData[5] == bindIdx)) { for (uint8_t n = 0; n < 5; n++) { //if (ccData[6 + n] == ccData[ccLen - 3]) { if (ccData[6 + n] <= 3) { eol = TRUE; listLength = ccData[5] + n; break; } hopData[ccData[5] + n] = ccData[6 + n]; } break; } } } } if (eol) { break; // End of list found, stop! } } writeBindingData(); cc2500Strobe(CC2500_SIDLE); // Back to idle } void loop() { time_t time = timerGet(); if (missingPackets > FAILSAFE_MISSING_PACKET) { failed = TRUE; missingPackets = 0; } while (1) { if ((timerGet() - time) > MISSING_PACKET_DELAY) { missingPackets++; cc2500Strobe(CC2500_SIDLE); if (missingPackets > MAX_MISSING_PACKET) { nextChannel(SEEK_CHANNEL_SKIP); counter++; //if (counter > (MAX_MISSING_PACKET << 1)) LED_ON; if (counter == (MAX_MISSING_PACKET << 2)) counter = 0; break; } else nextChannel(1); break; } if (GDO_1) { ccLen = cc2500ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (ccLen > 20) ccLen = 20; if (ccLen) { cc2500ReadFifo((uint8_t *)ccData, ccLen); if (ccData[ccLen - 1] & 0x80) { // Only if correct CRC missingPackets = 0; if (ccData[0] == 0x11) { // Correct length if ((ccData[1] == txid[0]) && (ccData[2] == txid[1])) { // Only if correct txid packet = TRUE; //sei(); #ifdef RSSI_OVER_PPM int rssi_dec = cc2500ReadReg(CC2500_34_RSSI | CC2500_READ_BURST); if (rssi_dec < 128) { rssi = ((rssi_dec / 2) - rssi_offset) & 0x7f; } else { rssi = (((rssi_dec - 256) / 2)) - rssi_offset; } rssi = constrain(rssi, rssi_min, rssi_max); #endif cc2500Strobe(CC2500_SIDLE); nextChannel(1); failed = FALSE; break; } } } } } } if (packet == TRUE) { packet = FALSE; //cli(); c[0] = (uint16_t)(ccData[10] & 0x0F) << 8 | ccData[6]; c[1] = (uint16_t)(ccData[10] & 0xF0) << 4 | ccData[7]; c[2] = (uint16_t)(ccData[11] & 0x0F) << 8 | ccData[8]; c[3] = (uint16_t)(ccData[11] & 0xF0) << 4 | ccData[9]; c[4] = (uint16_t)(ccData[16] & 0x0F) << 8 | ccData[12]; c[5] = (uint16_t)(ccData[16] & 0xF0) << 4 | ccData[13]; c[6] = (uint16_t)(ccData[17] & 0x0F) << 8 | ccData[14]; c[7] = (uint16_t)(ccData[17] & 0xF0) << 4 | ccData[15]; //sei(); for (int i = 0; i < CHANNELS; i++) { ppmBuffer[i] = 0.67 * c[i]; /* if (ppmBuffer[i] < 900) { ppmBuffer[i] = 1500; //ppmBuffer[2] = 1000; } */ } #ifdef RSSI_OVER_PPM ppmBuffer[RSSI_OVER_PPM] = map(rssi, rssi_min, rssi_max, PPM_MIN, PPM_MAX); #endif } cc2500Strobe(CC2500_SRX); } void tuning() { cc2500Strobe(CC2500_SRX); // enter in rx mode int count1 = 0; while (1) { count1++; if (count >= 250) { count = 0; } if (count1 > 3000) { count1 = 0; cc2500WriteReg(CC2500_0C_FSCTRL0, count); // Frequency offset hack count = count + 10; //cc2500Strobe(CC2500_SRX);// enter in rx mode } if (GDO_1) { ccLen = cc2500ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (ccLen) { cc2500ReadFifo((uint8_t *)ccData, ccLen); if (ccData[ccLen - 1] & 0x80) { if (ccData[2] == 0x01) { if (ccData[5] == 0x00) { break; } } } } } } }