diff --git a/Multiprotocol/FrSkyDVX_common.ino b/Multiprotocol/FrSkyDVX_common.ino index 1674440..7bfc346 100644 --- a/Multiprotocol/FrSkyDVX_common.ino +++ b/Multiprotocol/FrSkyDVX_common.ino @@ -17,7 +17,7 @@ /** FrSky D and X routines **/ /******************************/ -#if defined(FRSKYX_CC2500_INO) || defined(FRSKY_RX_CC2500_INO) +#if defined(FRSKYX_CC2500_INO) || defined(FRSKY_RX_CC2500_INO) || defined(FRSKYR9_SX1276_INO) //**CRC** const uint16_t PROGMEM FrSkyX_CRC_Short[]={ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, diff --git a/Multiprotocol/FrSkyR9_sx1276.ino b/Multiprotocol/FrSkyR9_sx1276.ino new file mode 100644 index 0000000..177ff90 --- /dev/null +++ b/Multiprotocol/FrSkyR9_sx1276.ino @@ -0,0 +1,220 @@ +#if defined(FRSKYR9_SX1276_INO) +#include "iface_sx1276.h" + +#define FREQ_MAP_SIZE 29 + +// TODO the channel spacing is equal, consider calculating the new channel instead of using lookup tables (first_chan + index * step) + +static uint32_t _freq_map_915[FREQ_MAP_SIZE] = +{ + 914472960, + 914972672, + 915472384, + 915972096, + 916471808, + 916971520, + 917471232, + 917970944, + 918470656, + 918970368, + 919470080, + 919969792, + 920469504, + 920969216, + 921468928, + 921968640, + 922468352, + 922968064, + 923467776, + 923967488, + 924467200, + 924966912, + 925466624, + 925966336, + 926466048, + 926965760, + 927465472, + + // last two determined by _step + 0, + 0 +}; + +static uint32_t _freq_map_868[FREQ_MAP_SIZE] = +{ + 859504640, + 860004352, + 860504064, + 861003776, + 861503488, + 862003200, + 862502912, + 863002624, + 863502336, + 864002048, + 864501760, + 865001472, + 865501184, + 866000896, + 866500608, + 867000320, + 867500032, + 867999744, + 868499456, + 868999168, + 869498880, + 869998592, + 870498304, + 870998016, + 871497728, + 871997440, + 872497152, + + // last two determined by _step + 0, + 0 +}; + +static uint8_t _step = 1; +static uint32_t* _freq_map = _freq_map_915; + +uint16_t initFrSkyR9() +{ + set_rx_tx_addr(MProtocol_id_master); + + if(sub_protocol == 0) // 915MHz + _freq_map = _freq_map_915; + else if(sub_protocol == 1) // 868MHz + _freq_map = _freq_map_868; + + _step = 1 + (random(0xfefefefe) % 24); + _freq_map[27] = _freq_map[_step]; + _freq_map[28] = _freq_map[_step+1]; + + SX1276_SetMode(true, false, SX1276_OPMODE_SLEEP); + SX1276_SetMode(true, false, SX1276_OPMODE_STDBY); + + // uint8_t buffer[2]; + // buffer[0] = 0x00; + // buffer[1] = 0x00; + // SX1276_WriteRegisterMulti(SX1276_40_DIOMAPPING1, buffer, 2); + + SX1276_SetDetectOptimize(true, SX1276_DETECT_OPTIMIZE_SF6); + SX1276_ConfigModem1(SX1276_MODEM_CONFIG1_BW_500KHZ, SX1276_MODEM_CONFIG1_CODING_RATE_4_5, true); + SX1276_ConfigModem2(6, false, false); + SX1276_ConfigModem3(false, false); + SX1276_SetPreambleLength(9); + SX1276_SetDetectionThreshold(SX1276_MODEM_DETECTION_THRESHOLD_SF6); + SX1276_SetLna(1, true); + SX1276_SetHopPeriod(0); // 0 = disabled, we hope frequencies manually + SX1276_SetPaDac(true); + + // TODO this can probably be shorter + return 20000; // start calling FrSkyR9_callback in 20 milliseconds +} + +uint16_t FrSkyR9_callback() +{ + static uint16_t freq_hop_index = 0; + + SX1276_SetMode(true, false, SX1276_OPMODE_STDBY); + + //SX1276_WriteReg(SX1276_11_IRQFLAGSMASK, 0xbf); // use only RxDone interrupt + + // uint8_t buffer[2]; + // buffer[0] = 0x00; + // buffer[1] = 0x00; + // SX1276_WriteRegisterMulti(SX1276_40_DIOMAPPING1, buffer, 2); // RxDone interrupt mapped to DIO0 (the rest are not used because of the REG_IRQ_FLAGS_MASK) + + // SX1276_WriteReg(REG_PAYLOAD_LENGTH, 13); + + // SX1276_WriteReg(REG_FIFO_ADDR_PTR, 0x00); + + // SX1276_WriteReg(SX1276_01_OPMODE, 0x85); // RXCONTINUOUS + // delay(10); // 10 ms + + // SX1276_WriteReg(SX1276_01_OPMODE, 0x81); // STDBY + + //SX1276_WriteReg(SX1276_09_PACONFIG, 0xF0); + + // max power: 15dBm (10.8 + 0.6 * MaxPower [dBm]) + // output_power: 2 dBm (17-(15-OutputPower) (if pa_boost_pin == true)) + SX1276_SetPaConfig(true, 7, 0); + SX1276_SetFrequency(_freq_map[freq_hop_index]); // set current center frequency + + delayMicroseconds(500); + + uint8_t payload[26]; + + payload[0] = 0x3C; // ???? + payload[1] = rx_tx_addr[3]; // unique radio id + payload[2] = rx_tx_addr[2]; // unique radio id + payload[3] = freq_hop_index; // current channel index + payload[4] = _step; // step size and last 2 channels start index + payload[5] = RX_num; // receiver number from OpenTX + + // binding mode: 0x00 regular / 0x41 bind? + if(IS_BIND_IN_PROGRESS) + payload[6] = 0x41; + else + payload[6] = 0x00; + + // TODO + payload[7] = 0x00; // fail safe related (looks like the same sequence of numbers as FrskyX protocol) + + // two channel are spread over 3 bytes. + // each channel is 11 bit + 1 bit (msb) that states whether + // it's part of the upper channels (9-16) or lower (1-8) (0 - lower 1 - upper) + + const int payload_offset = 8; + const bool is_upper = false; + + int chan_index = 0; + + for(int i = 0; i < 8; i += 3) + { + // map channel values (0-2047) to (64-1984) + //uint16_t ch1 = 64 + (uint16_t)((1920.0f / 2047.0f) * Channel_data[chan_index]); + //uint16_t ch2 = 64 + (uint16_t)((1920.0f / 2047.0f) * Channel_data[chan_index + 1]); + + uint16_t ch1 = FrSkyX_scaleForPXX(chan_index); + uint16_t ch2 = FrSkyX_scaleForPXX(chan_index + 1); + + chan_index += 2; + + payload[payload_offset + i] = ch1; + + if(is_upper) + { + payload[payload_offset + i + 1] = ((ch1 >> 8) | 0b1000) | (ch2 << 4); + payload[payload_offset + i + 2] = (ch2 >> 4) | 0b10000000; + } + else + { + payload[payload_offset + i + 1] = ((ch1 >> 8) & 0b0111) | (ch2 << 4); + payload[payload_offset + i + 2] = (ch2 >> 4) & 0b01111111; + } + } + + payload[20] = 0x08; // ???? + payload[21] = 0x00; // ???? + payload[22] = 0x00; // ???? + payload[23] = 0x00; // ???? + + uint16_t crc = FrSkyX_crc(payload, 24); + + payload[24] = crc; // low byte + payload[25] = crc >> 8; // high byte + + SX1276_WritePayloadToFifo(payload, 26); + + freq_hop_index = (freq_hop_index + _step) % FREQ_MAP_SIZE; + + SX1276_SetMode(true, false, SX1276_OPMODE_TX); + + // need to clear RegIrqFlags? + + return 19400; +} + +#endif \ No newline at end of file diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index 759906d..e4887ba 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -78,6 +78,7 @@ const char STR_PELIKAN[] ="Pelikan"; const char STR_TIGER[] ="Tiger"; const char STR_XK[] ="XK"; const char STR_XN297DUMP[] ="XN297DP"; +const char STR_FRSKYR9[] ="FrSkyR9"; const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20"; const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501"; @@ -118,6 +119,7 @@ const char STR_SUBTYPE_XN297DUMP[] = "\x07""250Kbps""1Mbps\0 ""2Mbps\0 ""Auto\0 const char STR_SUBTYPE_ESKY150[] = "\x03""4CH""7CH"; const char STR_SUBTYPE_V911S[] = "\x04""Std\0""E119"; const char STR_SUBTYPE_XK[] = "\x04""X450""X420"; +const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz"; enum { @@ -324,6 +326,9 @@ const mm_protocol_definition multi_protocols[] = { #endif #if defined(XN297DUMP_NRF24L01_INO) {PROTO_XN297DUMP, STR_XN297DUMP, 4, STR_SUBTYPE_XN297DUMP, OPTION_RFCHAN }, +#endif +#if defined(FRSKYR9_SX1276_INO) + {PROTO_FRSKY_R9, STR_FRSKYR9, 2, STR_SUBTYPE_FRSKYR9, OPTION_NONE }, #endif {0x00, nullptr, 0, nullptr, 0 } }; diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index ffbf423..7f56565 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -91,6 +91,7 @@ enum PROTOCOLS PROTO_XK = 62, // =>NRF24L01 PROTO_XN297DUMP = 63, // =>NRF24L01 PROTO_FRSKYX2 = 64, // =>CC2500 + PROTO_FRSKY_R9 = 65 // =>SX1276 }; enum Flysky diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index e9e2cef..233916f 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -1504,6 +1504,14 @@ static void protocol_init() break; #endif #endif + #ifdef SX1276_INSTALLED + #if defined(FRSKYR9_SX1276_INO) + case PROTO_FRSKY_R9: + next_callback = initFrSkyR9(); + remote_callback = FrSkyR9_callback; + break; + #endif + #endif } debugln("Protocol selected: %d, sub proto %d, rxnum %d, option %d", protocol, sub_protocol, RX_num, option); #ifdef MULTI_NAMES @@ -1723,8 +1731,8 @@ void update_serial_data() else if( ((rx_ok_buff[1]&0x80)==0) && ((cur_protocol[1]&0x80)!=0) ) // Bind flag has been reset { // Request protocol to end bind - #if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO) || defined(AFHDS2A_A7105_INO) - if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A ) + #if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO) || defined(AFHDS2A_A7105_INO) || defined(FRSKYR9_SX1276_INO) + if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A || protocol==PROTO_FRSKY_R9 ) BIND_DONE; else #endif @@ -1871,6 +1879,9 @@ void modules_reset() #ifdef NRF24L01_INSTALLED NRF24L01_Reset(); #endif + #ifdef SX1276_INSTALLED + SX1276_Reset(); + #endif //Wait for every component to reset delayMilliseconds(100); diff --git a/Multiprotocol/SX1276_SPI.ino b/Multiprotocol/SX1276_SPI.ino new file mode 100644 index 0000000..3b6c49c --- /dev/null +++ b/Multiprotocol/SX1276_SPI.ino @@ -0,0 +1,177 @@ +#ifdef SX1276_INSTALLED +#include "iface_sx1276.h" + +void SX1276_WriteReg(uint8_t address, uint8_t data) +{ + SPI_CSN_off; + SPI_Write(address | 0x80); // MSB 1 = write + NOP(); + SPI_Write(data); + SPI_CSN_on; +} + +uint8_t SX1276_ReadReg(uint8_t address) +{ + SPI_CSN_off; + SPI_Write(address & 0x7F); + uint8_t result = SPI_Read(); + SPI_CSN_on; + + return result; +} + +void SX1276_WriteRegisterMulti(uint8_t address, const uint8_t* data, uint8_t length) +{ + SPI_CSN_off; + SPI_Write(address | 0x80); // MSB 1 = write + + for(uint8_t i = 0; i < length; i++) + SPI_Write(data[i]); + + SPI_CSN_on; +} + +uint8_t SX1276_Reset() +{ + //TODO + + return 0; +} + +void SX1276_SetFrequency(uint32_t frequency) +{ + uint32_t f = frequency / 61; + uint8_t data[3]; + data[0] = (f & (0xFF << 16)) >> 16; + data[1] = (f & (0xFF << 8)) >> 8; + data[2] = f & 0xFF; + + SX1276_WriteRegisterMulti(SX1276_06_FRFMSB, data, 3); +} + +void SX1276_SetMode(bool lora, bool low_freq_mode, uint8_t mode) +{ + uint8_t data = 0x00; + + if(lora) + { + data = data | (1 << 7); + data = data & ~(1 << 6); + } + else + { + data = data & ~(1 << 7); + data = data | (1 << 6); + } + + if(low_freq_mode) + data = data | (1 << 3); + + data = data | mode; + + SX1276_WriteReg(SX1276_01_OPMODE, data); +} + +void SX1276_SetDetectOptimize(bool auto_if, uint8_t detect_optimize) +{ + uint8_t data = SX1276_ReadReg(SX1276_31_DETECTOPTIMIZE); + data = (data & 0b01111000) | detect_optimize; + data = data | (auto_if << 7); + + SX1276_WriteReg(SX1276_31_DETECTOPTIMIZE, data); +} + +void SX1276_ConfigModem1(uint8_t bandwidth, uint8_t coding_rate, bool implicit_header_mode) +{ + uint8_t data = 0x00; + data = data | (bandwidth << 4); + data = data | (coding_rate << 1); + data = data | implicit_header_mode; + + SX1276_WriteReg(SX1276_1D_MODEMCONFIG1, data); +} + +void SX1276_ConfigModem2(uint8_t spreading_factor, bool tx_continuous_mode, bool rx_payload_crc_on) +{ + uint8_t data = SX1276_ReadReg(SX1276_1E_MODEMCONFIG2); + data = data & 0b11; // preserve the last 2 bits + data = data | (spreading_factor << 4); + data = data | (tx_continuous_mode << 3); + data = data | (rx_payload_crc_on << 2); + + SX1276_WriteReg(SX1276_1E_MODEMCONFIG2, data); +} + +void SX1276_ConfigModem3(bool low_data_rate_optimize, bool agc_auto_on) +{ + uint8_t data = SX1276_ReadReg(SX1276_26_MODEMCONFIG3); + data = data & 0b11; // preserve the last 2 bits + data = data | (low_data_rate_optimize << 3); + data = data | (agc_auto_on << 2); + + SX1276_WriteReg(SX1276_26_MODEMCONFIG3, data); +} + +void SX1276_SetPreambleLength(uint16_t length) +{ + uint8_t data[2]; + data[0] = (length >> 8) & 0xFF; // high byte + data[1] = length & 0xFF; // low byte + + SX1276_WriteRegisterMulti(SX1276_20_PREAMBLEMSB, data, 2); +} + +void SX1276_SetDetectionThreshold(uint8_t threshold) +{ + SX1276_WriteReg(SX1276_37_DETECTIONTHRESHOLD, threshold); +} + +void SX1276_SetLna(uint8_t gain, bool high_freq_lna_boost) +{ + uint8_t data = SX1276_ReadReg(SX1276_0C_LNA); + data = data & 0b100; // preserve the third bit + data = data | (gain << 5); + + if(high_freq_lna_boost) + data = data | 0b11; + + SX1276_WriteReg(SX1276_0C_LNA, data); +} + +void SX1276_SetHopPeriod(uint8_t freq_hop_period) +{ + SX1276_WriteReg(SX1276_24_HOPPERIOD, freq_hop_period); +} + +void SX1276_SetPaDac(bool on) +{ + uint8_t data = SX1276_ReadReg(SX1276_4D_PADAC); + data = data & 0b11111000; // preserve the upper 5 bits + + if(on) + data = data | 0x07; + else + data = data | 0x04; + + SX1276_WriteReg(SX1276_4D_PADAC, data); +} + +void SX1276_SetPaConfig(bool pa_boost_pin, uint8_t max_power, uint8_t output_power) +{ + uint8_t data = 0x00; + data = data | (pa_boost_pin << 7); + data = data | (max_power << 4); + data = data | output_power; + + SX1276_WriteReg(SX1276_09_PACONFIG, data); +} + +void SX1276_WritePayloadToFifo(uint8_t* payload, uint8_t length) +{ + SX1276_WriteReg(SX1276_22_PAYLOAD_LENGTH, length); + SX1276_WriteReg(SX1276_0E_FIFOTXBASEADDR, 0x00); + SX1276_WriteReg(SX1276_0D_FIFOADDRPTR, 0x00); + SX1276_WriteRegisterMulti(SX1276_00_FIFO, payload, length); +} + +#endif \ No newline at end of file diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index 588bae8..5c6e6c6 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -77,6 +77,7 @@ #define CYRF6936_INSTALLED #define CC2500_INSTALLED #define NRF24L01_INSTALLED +//#define SX1276_INSTALLED /** OrangeRX TX **/ //If you compile for the OrangeRX TX module you need to select the correct board type. @@ -222,6 +223,8 @@ #define YD717_NRF24L01_INO #define ZSX_NRF24L01_INO +//The protocols below need a SX1276 to be installed +//#define FRSKYR9_SX1276_INO /***************************/ /*** PROTOCOLS SETTINGS ***/ diff --git a/Multiprotocol/iface_sx1276.h b/Multiprotocol/iface_sx1276.h new file mode 100644 index 0000000..bd69896 --- /dev/null +++ b/Multiprotocol/iface_sx1276.h @@ -0,0 +1,90 @@ +/* + 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 . + */ + +#ifndef _IFACE_SX1276_H_ +#define _IFACE_SX1276_H_ + +enum +{ + SX1276_00_FIFO = 0x00, + SX1276_01_OPMODE = 0x01, + SX1276_06_FRFMSB = 0x06, + SX1276_09_PACONFIG = 0x09, + SX1276_0B_OCP = 0x0B, + SX1276_0C_LNA = 0x0C, + SX1276_0D_FIFOADDRPTR = 0x0D, + SX1276_0E_FIFOTXBASEADDR = 0x0E, + SX1276_11_IRQFLAGSMASK = 0x11, + SX1276_1D_MODEMCONFIG1 = 0x1D, + SX1276_1E_MODEMCONFIG2 = 0x1E, + SX1276_20_PREAMBLEMSB = 0x20, + SX1276_22_PAYLOAD_LENGTH = 0x22, + SX1276_24_HOPPERIOD = 0x24, + SX1276_26_MODEMCONFIG3 = 0x26, + SX1276_31_DETECTOPTIMIZE = 0x31, + SX1276_37_DETECTIONTHRESHOLD = 0x37, + SX1276_40_DIOMAPPING1 = 0x40, + SX1276_42_VERSION = 0x42, + SX1276_4D_PADAC = 0x4D +}; + +enum +{ + SX1276_OPMODE_SLEEP = 0, + SX1276_OPMODE_STDBY, + SX1276_OPMODE_FSTX, + SX1276_OPMODE_TX, + SX1276_OPMODE_FSRX, + SX1276_OPMODE_RXCONTINUOUS, + SX1276_OPMODE_RXSINGLE, + SX1276_OPMODE_CAD +}; + +enum +{ + SX1276_DETECT_OPTIMIZE_SF7_TO_SF12 = 0x03, + SX1276_DETECT_OPTIMIZE_SF6 = 0x05 +}; + +enum +{ + SX1276_MODEM_CONFIG1_BW_7_8KHZ = 0, + SX1276_MODEM_CONFIG1_BW_10_4KHZ, + SX1276_MODEM_CONFIG1_BW_15_6KHZ, + SX1276_MODEM_CONFIG1_BW_20_8KHZ, + SX1276_MODEM_CONFIG1_BW_31_25KHZ, + SX1276_MODEM_CONFIG1_BW_41_7KHZ, + SX1276_MODEM_CONFIG1_BW_62_5KHZ, + SX1276_MODEM_CONFIG1_BW_125KHZ, + SX1276_MODEM_CONFIG1_BW_250KHZ, + SX1276_MODEM_CONFIG1_BW_500KHZ +}; + +enum +{ + SX1276_MODEM_CONFIG1_CODING_RATE_4_5 = 1, + SX1276_MODEM_CONFIG1_CODING_RATE_4_6, + SX1276_MODEM_CONFIG1_CODING_RATE_4_7, + SX1276_MODEM_CONFIG1_CODING_RATE_4_8 +}; + +enum +{ + SX1276_MODEM_DETECTION_THRESHOLD_SF7_TO_SF12 = 0x0A, + SX1276_MODEM_DETECTION_THRESHOLD_SF6 = 0x0C, + +}; + +#endif