/* This project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Multiprotocol is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Multiprotocol. If not, see . */ #if defined(FRSKY_RX_CC2500_INO) #include "iface_cc2500.h" #define FRSKY_RX_D16FCC_LENGTH 0x1D+1 #define FRSKY_RX_D16LBT_LENGTH 0x20+1 #define FRSKY_RX_D16v2_LENGTH 0x1D+1 #define FRSKY_RX_D8_LENGTH 0x11+1 #define FRSKY_RX_FORMATS 5 enum { FRSKY_RX_D8 =0, FRSKY_RX_D16FCC =1, FRSKY_RX_D16LBT =2, FRSKY_RX_D16v2FCC =3, FRSKY_RX_D16v2LBT =4, }; enum { FRSKY_RX_TUNE_START, FRSKY_RX_TUNE_LOW, FRSKY_RX_TUNE_HIGH, FRSKY_RX_BIND, FRSKY_RX_DATA, }; const PROGMEM uint8_t FRSKY_RX_common_reg[][2] = { {CC2500_02_IOCFG0, 0x01}, {CC2500_18_MCSM0, 0x18}, {CC2500_07_PKTCTRL1, 0x05}, {CC2500_3E_PATABLE, 0xFF}, {CC2500_0C_FSCTRL0, 0}, {CC2500_0D_FREQ2, 0x5C}, {CC2500_13_MDMCFG1, 0x23}, {CC2500_14_MDMCFG0, 0x7A}, {CC2500_19_FOCCFG, 0x16}, {CC2500_1A_BSCFG, 0x6C}, {CC2500_1B_AGCCTRL2, 0x03}, {CC2500_1C_AGCCTRL1, 0x40}, {CC2500_1D_AGCCTRL0, 0x91}, {CC2500_21_FREND1, 0x56}, {CC2500_22_FREND0, 0x10}, {CC2500_23_FSCAL3, 0xA9}, {CC2500_24_FSCAL2, 0x0A}, {CC2500_25_FSCAL1, 0x00}, {CC2500_26_FSCAL0, 0x11}, {CC2500_29_FSTEST, 0x59}, {CC2500_2C_TEST2, 0x88}, {CC2500_2D_TEST1, 0x31}, {CC2500_2E_TEST0, 0x0B}, {CC2500_03_FIFOTHR, 0x07}, {CC2500_09_ADDR, 0x03}, }; const PROGMEM uint8_t FRSKY_RX_d16fcc_reg[][2] = { {CC2500_17_MCSM1, 0x0C}, {CC2500_0E_FREQ1, 0x76}, {CC2500_0F_FREQ0, 0x27}, {CC2500_06_PKTLEN, 0x1E}, {CC2500_08_PKTCTRL0, 0x01}, {CC2500_0B_FSCTRL1, 0x0A}, {CC2500_10_MDMCFG4, 0x7B}, {CC2500_11_MDMCFG3, 0x61}, {CC2500_12_MDMCFG2, 0x13}, {CC2500_15_DEVIATN, 0x51}, }; const PROGMEM uint8_t FRSKY_RX_d16lbt_reg[][2] = { {CC2500_17_MCSM1, 0x0E}, {CC2500_0E_FREQ1, 0x80}, {CC2500_0F_FREQ0, 0x00}, {CC2500_06_PKTLEN, 0x23}, {CC2500_08_PKTCTRL0, 0x01}, {CC2500_0B_FSCTRL1, 0x08}, {CC2500_10_MDMCFG4, 0x7B}, {CC2500_11_MDMCFG3, 0xF8}, {CC2500_12_MDMCFG2, 0x03}, {CC2500_15_DEVIATN, 0x53}, }; const PROGMEM uint8_t FRSKY_RX_d8_reg[][2] = { {CC2500_17_MCSM1, 0x0C}, {CC2500_0E_FREQ1, 0x76}, {CC2500_0F_FREQ0, 0x27}, {CC2500_06_PKTLEN, 0x19}, {CC2500_08_PKTCTRL0, 0x05}, {CC2500_0B_FSCTRL1, 0x08}, {CC2500_10_MDMCFG4, 0xAA}, {CC2500_11_MDMCFG3, 0x39}, {CC2500_12_MDMCFG2, 0x11}, {CC2500_15_DEVIATN, 0x42}, }; static uint8_t FRSKY_RX_chanskip; static int8_t FRSKY_RX_finetune; static uint8_t FRSKY_RX_format; static void __attribute__((unused)) FRSKY_RX_strobe_rx() { CC2500_Strobe(CC2500_SIDLE); CC2500_Strobe(CC2500_SFRX); CC2500_Strobe(CC2500_SRX); } static void __attribute__((unused)) FRSKY_RX_initialise_cc2500() { const uint8_t FRSKY_RX_length[] = { FRSKY_RX_D8_LENGTH, FRSKY_RX_D16FCC_LENGTH, FRSKY_RX_D16LBT_LENGTH, FRSKY_RX_D16v2_LENGTH, FRSKY_RX_D16v2_LENGTH }; packet_length = FRSKY_RX_length[FRSKY_RX_format]; CC2500_Reset(); CC2500_Strobe(CC2500_SIDLE); for (uint8_t i = 0; i < sizeof(FRSKY_RX_common_reg) / 2; i++) CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_common_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_common_reg[i][1])); switch (FRSKY_RX_format) { case FRSKY_RX_D16v2FCC: case FRSKY_RX_D16FCC: for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16fcc_reg) / 2; i++) CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][1])); if(FRSKY_RX_format==FRSKY_RX_D16v2FCC) { CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC CC2500_WriteReg(CC2500_17_MCSM1, 0x0E); // Go/Stay in RX mode CC2500_WriteReg(CC2500_11_MDMCFG3, 0x84); // bitrate 70K->77K } break; case FRSKY_RX_D16v2LBT: case FRSKY_RX_D16LBT: for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16lbt_reg) / 2; i++) CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][1])); if(FRSKY_RX_format==FRSKY_RX_D16v2LBT) CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC break; case FRSKY_RX_D8: for (uint8_t i = 0; i < sizeof(FRSKY_RX_d8_reg) / 2; i++) CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d8_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d8_reg[i][1])); CC2500_WriteReg(CC2500_23_FSCAL3, 0x89); break; } CC2500_WriteReg(CC2500_0A_CHANNR, 0); // bind channel rx_disable_lna = IS_POWER_FLAG_on; CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN); // lna disable / enable FRSKY_RX_strobe_rx(); delayMicroseconds(1000); // wait for RX to activate } static void __attribute__((unused)) FRSKY_RX_set_channel(uint8_t channel) { CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[channel]); if(FRSKY_RX_format == FRSKY_RX_D8) CC2500_WriteReg(CC2500_23_FSCAL3, 0x89); CC2500_WriteReg(CC2500_25_FSCAL1, calData[channel]); FRSKY_RX_strobe_rx(); } static void __attribute__((unused)) FRSKY_RX_calibrate() { FRSKY_RX_strobe_rx(); for (unsigned c = 0; c < 47; c++) { CC2500_Strobe(CC2500_SIDLE); CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[c]); CC2500_Strobe(CC2500_SCAL); delayMicroseconds(900); calData[c] = CC2500_ReadReg(CC2500_25_FSCAL1); } } static uint8_t __attribute__((unused)) frskyx_rx_check_crc_id(bool bind,bool init) { /*debugln("RX"); for(uint8_t i=0; i> 4); raw_channel[2] = ((packet[13] << 8) & 0xF00) | packet[12]; raw_channel[3] = ((packet[14] << 4) & 0xFF0) | (packet[13] >> 4); raw_channel[4] = ((packet[16] << 8) & 0xF00) | packet[15]; raw_channel[5] = ((packet[17] << 4) & 0xFF0) | (packet[16] >> 4); raw_channel[6] = ((packet[19] << 8) & 0xF00) | packet[18]; raw_channel[7] = ((packet[20] << 4) & 0xFF0) | (packet[19] >> 4); for (i = 0; i < 8; i++) { // ignore failsafe channels if(packet[7] != 0x10+(i<<1)) { uint8_t shifted = (raw_channel[i] & 0x800)>0; uint16_t channel_value = raw_channel[i] & 0x7FF; if (channel_value < 64) rx_rc_chan[shifted ? i + 8 : i] = 0; else rx_rc_chan[shifted ? i + 8 : i] = min(((channel_value - 64) << 4) / 15, 2047); } } } // buid telemetry packet packet_in[idx++] = RX_LQI; packet_in[idx++] = RX_RSSI; packet_in[idx++] = 0; // start channel packet_in[idx++] = FRSKY_RX_format == FRSKY_RX_D8 ? 8 : 16; // number of channels in packet // pack channels for (i = 0; i < packet_in[3]; i++) { bits |= ((uint32_t)rx_rc_chan[i]) << bitsavailable; bitsavailable += 11; while (bitsavailable >= 8) { packet_in[idx++] = bits & 0xff; bits >>= 8; bitsavailable -= 8; } } } static void __attribute__((unused)) FRSKY_RX_data() { uint16_t temp = FRSKY_RX_EEPROM_OFFSET; FRSKY_RX_format = eeprom_read_byte((EE_ADDR)temp++) % FRSKY_RX_FORMATS; rx_tx_addr[3] = eeprom_read_byte((EE_ADDR)temp++); rx_tx_addr[2] = eeprom_read_byte((EE_ADDR)temp++); rx_tx_addr[1] = eeprom_read_byte((EE_ADDR)temp++); rx_tx_addr[0] = RX_num; FRSKY_RX_finetune = eeprom_read_byte((EE_ADDR)temp++); debug("format=%d, ", FRSKY_RX_format); debug("addr[3]=%02X, ", rx_tx_addr[3]); debug("addr[2]=%02X, ", rx_tx_addr[2]); debug("addr[1]=%02X, ", rx_tx_addr[1]); debug("rx_num=%02X, ", rx_tx_addr[0]); debugln("tune=%d", (int8_t)FRSKY_RX_finetune); if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC) {//D8 & D16v1 for (uint8_t ch = 0; ch < 47; ch++) hopping_frequency[ch] = eeprom_read_byte((EE_ADDR)temp++); } else { FrSkyFormat=FRSKY_RX_format == FRSKY_RX_D16v2FCC?0:2; FrSkyX2_init_hop(); } debug("ch:"); for (uint8_t ch = 0; ch < 47; ch++) debug(" %02X", hopping_frequency[ch]); debugln(""); FRSKY_RX_initialise_cc2500(); FRSKY_RX_calibrate(); CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // FS_AUTOCAL = manual CC2500_WriteReg(CC2500_09_ADDR, rx_tx_addr[3]); // set address CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); // check address if (option == 0) CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); else CC2500_WriteReg(CC2500_0C_FSCTRL0, option); FRSKY_RX_set_channel(hopping_frequency_no); phase = FRSKY_RX_DATA; } void FRSKY_RX_init() { if(sub_protocol == FRSKY_ERASE) { if(IS_BIND_IN_PROGRESS) {// Clear all cloned addresses uint16_t addr[]={ FRSKYD_CLONE_EEPROM_OFFSET+1, FRSKYX_CLONE_EEPROM_OFFSET+1, FRSKYX2_CLONE_EEPROM_OFFSET+1 }; for(uint8_t i=0; i<3;i++) for(uint8_t j=0; j<3;j++) eeprom_write_byte((EE_ADDR)(addr[i]+j), 0xFF); packet_count = 100; } } else { FRSKY_RX_chanskip = 1; hopping_frequency_no = 0; rx_data_started = false; FRSKY_RX_finetune = 0; telemetry_link = 0; packet_count = 0; if (IS_BIND_IN_PROGRESS) { FRSKY_RX_format = FRSKY_RX_D8; FRSKY_RX_initialise_cc2500(); phase = FRSKY_RX_TUNE_START; debugln("FRSKY_RX_TUNE_START"); } else FRSKY_RX_data(); } } uint16_t FRSKY_RX_callback() { static int8_t read_retry = 0; static int8_t tune_low, tune_high; uint8_t len, ch; if(sub_protocol == FRSKY_ERASE) { if(packet_count) packet_count--; else BIND_DONE; return 10000; // Nothing to do... } if(IS_BIND_DONE && phase != FRSKY_RX_DATA) FRSKY_RX_init(); // Abort bind if ((prev_option != option) && (phase >= FRSKY_RX_DATA)) { if (option == 0) CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); else CC2500_WriteReg(CC2500_0C_FSCTRL0, option); prev_option = option; } if (rx_disable_lna != IS_POWER_FLAG_on) { rx_disable_lna = IS_POWER_FLAG_on; CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN); } len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; switch(phase) { case FRSKY_RX_TUNE_START: if (len == packet_length + 2) //+2=RSSI+LQI+CRC { CC2500_ReadData(packet, len); if(frskyx_rx_check_crc_id(true,true)) { FRSKY_RX_finetune = -127; CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); phase = FRSKY_RX_TUNE_LOW; debugln("FRSKY_RX_TUNE_LOW"); FRSKY_RX_strobe_rx(); state = 0; return 1000; } } FRSKY_RX_format = (FRSKY_RX_format + 1) % FRSKY_RX_FORMATS; // switch to next format (D8, D16FCC, D16LBT, D16v2FCC, D16v2LBT) FRSKY_RX_initialise_cc2500(); FRSKY_RX_finetune += 10; CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); FRSKY_RX_strobe_rx(); return 18000; case FRSKY_RX_TUNE_LOW: if (len == packet_length + 2) //+2=RSSI+LQI+CRC { CC2500_ReadData(packet, len); if(frskyx_rx_check_crc_id(true,false)) { tune_low = FRSKY_RX_finetune; FRSKY_RX_finetune = 127; CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); phase = FRSKY_RX_TUNE_HIGH; debugln("FRSKY_RX_TUNE_HIGH"); FRSKY_RX_strobe_rx(); return 1000; } } FRSKY_RX_finetune += 1; CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); FRSKY_RX_strobe_rx(); return 18000; case FRSKY_RX_TUNE_HIGH: if (len == packet_length + 2) //+2=RSSI+LQI+CRC { CC2500_ReadData(packet, len); if(frskyx_rx_check_crc_id(true,false)) { tune_high = FRSKY_RX_finetune; FRSKY_RX_finetune = (tune_low + tune_high) / 2; CC2500_WriteReg(CC2500_0C_FSCTRL0, (int8_t)FRSKY_RX_finetune); if(tune_low < tune_high) { phase = FRSKY_RX_BIND; debugln("FRSKY_RX_TUNE_HIGH"); } else { phase = FRSKY_RX_TUNE_START; debugln("FRSKY_RX_TUNE_START"); } FRSKY_RX_strobe_rx(); return 1000; } } FRSKY_RX_finetune -= 1; CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune); FRSKY_RX_strobe_rx(); return 18000; case FRSKY_RX_BIND: if (len == packet_length + 2) //+2=RSSI+LQI+CRC { CC2500_ReadData(packet, len); if(frskyx_rx_check_crc_id(true,false)) { if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC) {// D8 & D16v1 if(packet[5] <= 0x2D) { for (ch = 0; ch < 5; ch++) hopping_frequency[packet[5]+ch] = packet[6+ch]; state |= 1 << (packet[5] / 5); } } else state = 0x3FF; //No hop table for D16v2 if (state == 0x3FF) { debugln("Bind complete"); BIND_DONE; // store format, finetune setting, txid, channel list uint16_t temp = FRSKY_RX_EEPROM_OFFSET; if(sub_protocol==FRSKY_CLONE) { if(FRSKY_RX_format==FRSKY_RX_D8) temp=FRSKYD_CLONE_EEPROM_OFFSET; else if(FRSKY_RX_format == FRSKY_RX_D16FCC || FRSKY_RX_format == FRSKY_RX_D16LBT) temp=FRSKYX_CLONE_EEPROM_OFFSET; else temp=FRSKYX2_CLONE_EEPROM_OFFSET; } eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_format); eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[3]); eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[2]); eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[1]); if(sub_protocol == FRSKY_RX || sub_protocol == FRSKY_CPPM) // FRSKY_RX, FRSKY_CPPM eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_finetune); if(FRSKY_RX_format != FRSKY_RX_D16v2FCC && FRSKY_RX_format != FRSKY_RX_D16v2LBT) for (ch = 0; ch < 47; ch++) eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[ch]); FRSKY_RX_data(); debugln("FRSKY_RX_DATA"); } } FRSKY_RX_strobe_rx(); } return 1000; case FRSKY_RX_DATA: if (len == packet_length + 2) //+2=RSSI+LQI+CRC { CC2500_ReadData(packet, len); if(frskyx_rx_check_crc_id(false,false)) { RX_RSSI = packet[len-2]; if(RX_RSSI >= 128) RX_RSSI -= 128; else RX_RSSI += 128; bool chanskip_valid=true; // hop to next channel if (FRSKY_RX_format != FRSKY_RX_D8) {//D16v1 & D16v2 if(rx_data_started) { if(FRSKY_RX_chanskip != (((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2))) { chanskip_valid=false; // chanskip value has changed which surely indicates a bad frame packet_count++; if(packet_count>5) // the TX must have changed chanskip... FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init } else packet_count=0; } else FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init } hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47; FRSKY_RX_set_channel(hopping_frequency_no); if(chanskip_valid) { if ((telemetry_link & 0x7F) == 0) { // send channels to TX FRSKY_RX_build_telemetry_packet(); telemetry_link = 1; #ifdef SEND_CPPM if(sub_protocol == FRSKY_CPPM) telemetry_link |= 0x80; // Disable telemetry output #endif } pps_counter++; } rx_data_started = true; read_retry = 0; } } // packets per second if (millis() - pps_timer >= 1000) { pps_timer = millis(); debugln("%d pps", pps_counter); RX_LQI = pps_counter; if(pps_counter==0) // no packets for 1 sec or more... {// restart the search rx_data_started=false; packet_count=0; } pps_counter = 0; } // skip channel if no packet received in time if (read_retry++ >= 9) { hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47; FRSKY_RX_set_channel(hopping_frequency_no); if(rx_data_started) read_retry = 0; else read_retry = -50; // retry longer until first packet is catched } break; } return 1000; } #endif