diff --git a/src/protocol/Makefile.inc b/src/protocol/Makefile.inc index c9d32d23dd..0cd9450179 100644 --- a/src/protocol/Makefile.inc +++ b/src/protocol/Makefile.inc @@ -61,6 +61,7 @@ PROTO_MODULES += $(ODIR)/protocol/loli.mod PROTO_MODULES += $(ODIR)/protocol/e016h.mod PROTO_MODULES += $(ODIR)/protocol/sumd.mod PROTO_MODULES += $(ODIR)/protocol/scancyrf.mod +PROTO_MODULES += $(ODIR)/protocol/e010.mod ALL += $(PROTO_MODULES) else #BUILD_TARGET @@ -276,11 +277,11 @@ $(ODIR)/protocol/ncc1701.mod : $(ODIR)/ncc1701_nrf24l01.bin @echo Building 'ncc1701' module /bin/mkdir -p $(ODIR)/protocol/ 2>/dev/null; /bin/cp $< $@ -$(ODIR)/protocol/v911s.mod : $(ODIR)/v911s_nrf24l01.bin +$(ODIR)/protocol/v911s.mod : $(ODIR)/v911s_cc2500.bin @echo Building 'v911s' module /bin/mkdir -p $(ODIR)/protocol/ 2>/dev/null; /bin/cp $< $@ -$(ODIR)/protocol/gd00x.mod : $(ODIR)/gd00x_nrf24l01.bin +$(ODIR)/protocol/gd00x.mod : $(ODIR)/gd00x_cc2500.bin @echo Building 'gd00x' module /bin/mkdir -p $(ODIR)/protocol/ 2>/dev/null; /bin/cp $< $@ @@ -307,4 +308,9 @@ $(ODIR)/protocol/sumd.mod : $(ODIR)/sumd_uart.bin $(ODIR)/protocol/scancyrf.mod : $(ODIR)/scanner_cyrf6936.bin @echo Building 'scancyrf' module /bin/mkdir -p $(ODIR)/protocol/ 2>/dev/null; /bin/cp $< $@ + +$(ODIR)/protocol/e010.mod : $(ODIR)/e010_cc2500.bin + @echo Building 'e010' module + /bin/mkdir -p $(ODIR)/protocol/ 2>/dev/null; /bin/cp $< $@ + endif #BUILD_TARGET diff --git a/src/protocol/bayang_nrf24l01.c b/src/protocol/bayang_nrf24l01.c index cd2abc36f7..6822774e88 100644 --- a/src/protocol/bayang_nrf24l01.c +++ b/src/protocol/bayang_nrf24l01.c @@ -350,6 +350,7 @@ static int check_rx(void) static void bay_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); u8 bind_address[] = { 0, 0, 0, 0, 0 }; switch (Model.proto_opts[PROTOOPTS_FORMAT]) { case FORMAT_REGULAR: diff --git a/src/protocol/bugs3mini_nrf24l01.c b/src/protocol/bugs3mini_nrf24l01.c index f282eec56d..f78dad60d1 100644 --- a/src/protocol/bugs3mini_nrf24l01.c +++ b/src/protocol/bugs3mini_nrf24l01.c @@ -138,6 +138,7 @@ static u8 checksum() static void bugs3mini_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); tx_power = Model.tx_power; NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); diff --git a/src/protocol/cg023_nrf24l01.c b/src/protocol/cg023_nrf24l01.c index 7521746b9f..81d98a0a58 100644 --- a/src/protocol/cg023_nrf24l01.c +++ b/src/protocol/cg023_nrf24l01.c @@ -219,6 +219,7 @@ static void send_packet(u8 bind) static void cg023_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); diff --git a/src/protocol/cx10_nrf24l01.c b/src/protocol/cx10_nrf24l01.c index 859186f3d3..8afdef829f 100644 --- a/src/protocol/cx10_nrf24l01.c +++ b/src/protocol/cx10_nrf24l01.c @@ -293,6 +293,7 @@ static void send_packet(u8 bind) static void cx10_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); diff --git a/src/protocol/dm002_nrf24l01.c b/src/protocol/dm002_nrf24l01.c index 2fa552291e..72d8d8e697 100644 --- a/src/protocol/dm002_nrf24l01.c +++ b/src/protocol/dm002_nrf24l01.c @@ -175,6 +175,7 @@ static void DM002_send_packet(u8 bind) static void DM002_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", DM002_ADDRESS_SIZE); diff --git a/src/protocol/e010_cc2500.c b/src/protocol/e010_cc2500.c new file mode 100644 index 0000000000..efefdf0b00 --- /dev/null +++ b/src/protocol/e010_cc2500.c @@ -0,0 +1,293 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . + */ + +#include "common.h" +#include "interface.h" +#include "mixer.h" +#include "config/model.h" +#include "config/tx.h" + +#ifdef PROTO_HAS_CC2500 + +#ifdef EMULATOR + #define USE_FIXED_MFGID + #define BIND_COUNT 20 + #define PACKET_PERIOD 150 + #define dbgprintf printf +#else + #define BIND_COUNT 150 + #define PACKET_PERIOD 4000 // Timeout for callback in uSec + // printf inside an interrupt handler is really dangerous + // this shouldn't be enabled even in debug builds without explicitly + // turning it on + #define dbgprintf if (0) printf +#endif + +#define INITIAL_WAIT 500 +#define PACKET_SIZE 16 +#define RF_NUM_CHANNELS 4 +#define ADDRESS_LENGTH 5 + +const char * const e010_opts[] = { + _tr_noop("Format"), "E010", "Phoenix", NULL, + _tr_noop("Freq-Fine"), "-127", "127", NULL, + NULL +}; + +enum { + PROTOOPTS_FORMAT = 0, + PROTOOPTS_FREQFINE, + LAST_PROTO_OPT +}; +ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts); + +enum { + FORMAT_E010, + FORMAT_PHOENIX, +}; + +// For code readability +enum { + CHANNEL1 = 0, // Aileron + CHANNEL2, // Elevator + CHANNEL3, // Throttle + CHANNEL4, // Rudder + CHANNEL5, // Leds / Arm + CHANNEL6, // Flip + CHANNEL7, // Still camera + CHANNEL8, // Video camera + CHANNEL9, // Headless + CHANNEL10, // Return To Home +}; + +#define CHANNEL_LED CHANNEL5 +#define CHANNEL_ARM CHANNEL5 // TDR Phoenix mini +#define CHANNEL_FLIP CHANNEL6 +#define CHANNEL_PICTURE CHANNEL7 +#define CHANNEL_VIDEO CHANNEL8 +#define CHANNEL_HEADLESS CHANNEL9 +#define CHANNEL_RTH CHANNEL10 + +enum { + E010_BIND = 0, + E010_DATA +}; + +static u8 tx_power; +static s8 fine; +static u16 counter; +static u8 phase; +static u8 tx_power; +static u8 rf_chan; +static u8 txid[3]; +static u8 packet[PACKET_SIZE]; +static u8 hopping_frequency[RF_NUM_CHANNELS]; +static u8 calibration[RF_NUM_CHANNELS]; +static u8 calibration_fscal2; +static u8 calibration_fscal3; + +// dumped from E010 and H36 stock transmitters +static const struct { + u8 txid[2]; + u8 rfchan[RF_NUM_CHANNELS]; +} +e010_tx_rf_map[] = {{{0x4F, 0x1C}, {0x3A, 0x35, 0x4A, 0x45}}, + {{0x90, 0x1C}, {0x2E, 0x36, 0x3E, 0x46}}, + {{0x24, 0x36}, {0x32, 0x3E, 0x42, 0x4E}}, + {{0x7A, 0x40}, {0x2E, 0x3C, 0x3E, 0x4C}}, + {{0x61, 0x31}, {0x2F, 0x3B, 0x3F, 0x4B}}, + {{0x5D, 0x37}, {0x33, 0x3B, 0x43, 0x4B}}, + {{0xFD, 0x4F}, {0x33, 0x3B, 0x43, 0x4B}}, + {{0x86, 0x3C}, {0x34, 0x3E, 0x44, 0x4E}}}; + +// Bit vector from bit position +#define BV(bit) (1 << bit) + +static u8 checksum() +{ + u8 sum = packet[0]; + for (int i=1; i < PACKET_SIZE-1; i++) sum += packet[i]; + return sum; +} + +#define BABS(X) (((X) < 0) ? -(u8)(X) : (X)) +#define LIMIT_CHAN(X) (X < CHAN_MIN_VALUE ? CHAN_MIN_VALUE : (X > CHAN_MAX_VALUE ? CHAN_MAX_VALUE : X)) +// Channel values are sign + magnitude 8bit values +static u8 convert_channel(u8 num) +{ + s32 ch = LIMIT_CHAN(Channels[num]); + return (u8) ((ch < 0 ? 0x80 : 0) | BABS(ch * 127 / CHAN_MAX_VALUE)); +} + +#define GET_FLAG(ch, mask) (Channels[ch] > 0 ? mask : 0) +#define GET_FLAG_INV(ch, mask) (Channels[ch] < 0 ? mask : 0) +#define CHAN2TRIM(X) (((X) & 0x80 ? (X) : 0x7f - (X)) >> 1) +static void send_packet(u8 bind) +{ + packet[0] = convert_channel(CHANNEL3); // throttle + packet[0] = packet[0] & 0x80 ? 0xff - packet[0] : 0x80 + packet[0]; + packet[1] = convert_channel(CHANNEL4); // rudder + packet[4] = 0x40; // rudder does not work well with dyntrim + packet[2] = 0x80 ^ convert_channel(CHANNEL2); // elevator + // driven trims cause issues when headless is enabled + packet[5] = GET_FLAG(CHANNEL_HEADLESS, 1) ? 0x40 : CHAN2TRIM(packet[2]); // trim elevator + packet[3] = convert_channel(CHANNEL1); // aileron + packet[6] = GET_FLAG(CHANNEL_HEADLESS, 1) ? 0x40 : CHAN2TRIM(packet[3]); // trim aileron + packet[7] = txid[0]; + packet[8] = txid[1]; + packet[9] = txid[2]; + packet[10] = 0; // overwritten below for feature bits + packet[11] = 0; + packet[12] = 0; + packet[13] = 0; + packet[14] = 0xc0; // bind value + + packet[10] += GET_FLAG(CHANNEL_RTH, 0x02) + | GET_FLAG(CHANNEL_HEADLESS, 0x01); + if (!bind) { + packet[14] = 0x04 + | GET_FLAG(CHANNEL_FLIP, 0x01) + | GET_FLAG(CHANNEL_PICTURE, 0x08) + | GET_FLAG(CHANNEL_VIDEO, 0x10) + | GET_FLAG_INV(CHANNEL_LED, 0x20); // air/ground mode + if (Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_PHOENIX) { + packet[10] |= 0x20 // high rate + | GET_FLAG(CHANNEL_ARM, 0x80); + packet[14] &= ~0x24; + } + } + + packet[15] = checksum(); + + // channel hopping + rf_chan++; + CC2500_WriteReg(CC2500_23_FSCAL3, calibration_fscal3); + CC2500_WriteReg(CC2500_24_FSCAL2, calibration_fscal2); + CC2500_WriteReg(CC2500_25_FSCAL1, calibration[rf_chan / 2]); + XN297L_SetChannel(hopping_frequency[rf_chan / 2]); + rf_chan %= 2 * sizeof(hopping_frequency); // channels repeated + + XN297L_WritePayload(packet, PACKET_SIZE); + + // Check and adjust transmission power. We do this after + // transmission to not bother with timeout after power + // settings change - we have plenty of time until next + // packet. + if (tx_power != Model.tx_power) { + // Keep transmit power updated + tx_power = Model.tx_power; + CC2500_SetPower(tx_power); + } +} + +// calibrate used RF channels for faster hopping +static void calibrate_rf_chans() +{ + for (int c = 0; c < RF_NUM_CHANNELS; c++) { + CLOCK_ResetWatchdog(); + CC2500_Strobe(CC2500_SIDLE); + XN297L_SetChannel(hopping_frequency[c]); + CC2500_Strobe(CC2500_SCAL); + usleep(900); + calibration[c] = CC2500_ReadReg(CC2500_25_FSCAL1); + } + calibration_fscal3 = CC2500_ReadReg(CC2500_23_FSCAL3); // only needs to be done once + calibration_fscal2 = CC2500_ReadReg(CC2500_24_FSCAL2); // only needs to be done once + CC2500_Strobe(CC2500_SIDLE); +} + +static void e010_init() +{ + u8 rx_tx_addr[ADDRESS_LENGTH]; + // setup cc2500 for xn297L@250kbps emulation, scrambled, crc enabled + XN297L_Configure(XN297_SCRAMBLED, XN297_CRC); + CC2500_WriteReg(CC2500_0C_FSCTRL0, fine); + memcpy(rx_tx_addr, "\x6d\x6a\x77\x77\x77", sizeof(rx_tx_addr)); + memcpy(hopping_frequency, "\x36\x3e\x46\x2e", sizeof(hopping_frequency)); + XN297L_SetTXAddr(rx_tx_addr, sizeof(rx_tx_addr)); + CC2500_SetPower(tx_power); + calibrate_rf_chans(); + CC2500_SetTxRxMode(TX_EN); +} + +static u16 e010_callback() +{ + switch (phase) { + case E010_BIND: + if (counter == 0) { + memcpy(hopping_frequency, e010_tx_rf_map[Model.fixed_id % (sizeof(e010_tx_rf_map)/sizeof(e010_tx_rf_map[0]))].rfchan, sizeof(hopping_frequency)); + calibrate_rf_chans(); + phase = E010_DATA; + PROTOCOL_SetBindState(0); + } else { + send_packet(1); + counter -= 1; + } + break; + + case E010_DATA: + if (fine != (s8)Model.proto_opts[PROTOOPTS_FREQFINE]) { + fine = (s8)Model.proto_opts[PROTOOPTS_FREQFINE]; + CC2500_WriteReg(CC2500_0C_FSCTRL0, fine); + } + send_packet(0); + break; + } + return PACKET_PERIOD; +} + +static void initialize_txid() +{ + memcpy(txid, e010_tx_rf_map[Model.fixed_id % (sizeof(e010_tx_rf_map)/sizeof(e010_tx_rf_map[0]))].txid, 2); + txid[2] = 0x00; +} + +static void initialize() +{ + CLOCK_StopTimer(); + tx_power = Model.tx_power; + fine = (s8)Model.proto_opts[PROTOOPTS_FREQFINE]; + counter = BIND_COUNT; + initialize_txid(); + e010_init(); + phase = E010_BIND; + + PROTOCOL_SetBindState(BIND_COUNT * PACKET_PERIOD / 1000); + + CLOCK_StartTimer(INITIAL_WAIT, e010_callback); +} + +uintptr_t E010_Cmds(enum ProtoCmds cmd) +{ + switch (cmd) { + case PROTOCMD_INIT: initialize(0); return 0; + case PROTOCMD_DEINIT: + case PROTOCMD_RESET: + CLOCK_StopTimer(); + return (CC2500_Reset() ? 1 : -1); + case PROTOCMD_CHECK_AUTOBIND: return 1; + case PROTOCMD_BIND: initialize(1); return 0; + case PROTOCMD_NUMCHAN: return 8; + case PROTOCMD_DEFAULT_NUMCHAN: return 8; + case PROTOCMD_CURRENT_ID: return Model.fixed_id; + case PROTOCMD_GETOPTIONS: return (uintptr_t)e010_opts; + case PROTOCMD_TELEMETRYSTATE: return PROTO_TELEM_UNSUPPORTED; + case PROTOCMD_CHANNELMAP: return AETRG; + default: break; + } + return 0; +} + +#endif diff --git a/src/protocol/e016h_nrf24l01.c b/src/protocol/e016h_nrf24l01.c index 2a4bf0b558..ff3dcf2b18 100644 --- a/src/protocol/e016h_nrf24l01.c +++ b/src/protocol/e016h_nrf24l01.c @@ -101,6 +101,7 @@ static u16 scale_channel(s32 chanval, s32 inMin, s32 inMax, u16 destMin, u16 des static void init_e016h() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_FlushTx(); NRF24L01_FlushRx(); diff --git a/src/protocol/gd00x_cc2500.c b/src/protocol/gd00x_cc2500.c new file mode 100644 index 0000000000..30c17280c8 --- /dev/null +++ b/src/protocol/gd00x_cc2500.c @@ -0,0 +1,423 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . + */ + +#include "common.h" +#include "interface.h" +#include "mixer.h" +#include "config/model.h" +#include "config/tx.h" // for Transmitter + +#ifdef PROTO_HAS_CC2500 + +#ifdef EMULATOR +#define USE_FIXED_MFGID +#define GD00X_BIND_COUNT 20 +#define GD00X_PACKET_PERIOD 100 +#define dbgprintf printf +#else +#define GD00X_BIND_COUNT 857 // 3sec +#define GD00X_PACKET_PERIOD 3500 // Timeout for callback in uSec +// printf inside an interrupt handler is really dangerous +// this shouldn't be enabled even in debug builds without explicitly +// turning it on +#define dbgprintf if (0) printf +#endif + +#define FORCE_GD00X_ORIGINAL_ID + +#define GD00X_INITIAL_WAIT 500 +#define GD00X_RF_BIND_CHANNEL 2 +#define GD00X_PAYLOAD_SIZE 15 +#define GD00X_NUM_CHANNEL 4 + +#define GD00X_V2_BIND_PACKET_PERIOD 1700 +#define GD00X_V2_RF_BIND_CHANNEL 0x43 +#define GD00X_V2_PAYLOAD_SIZE 6 + + +static const char * const gd00x_opts[] = { + _tr_noop("Format"), "V1", "V2", NULL, + _tr_noop("Freq-Fine"), "-127", "127", NULL, + _tr_noop("Freq-Coarse"), "-127", "127", NULL, + NULL +}; +enum { + PROTOOPTS_FORMAT = 0, + PROTOOPTS_FREQFINE, + PROTOOPTS_FREQCOARSE, + LAST_PROTO_OPT, +}; +enum { + FORMAT_V1 = 0, + FORMAT_V2, +}; +ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts); + +static u8 tx_power; +static u8 packet[GD00X_PAYLOAD_SIZE]; +static u8 hopping_frequency_no; +static u8 rx_tx_addr[5]; +static u8 hopping_frequency[GD00X_NUM_CHANNEL]; +static u16 bind_counter; +static u16 packet_period; +static u8 packet_count; +static u8 packet_length; +static u8 phase; +static u8 len; +static s8 fine; +static s8 coarse; +static u8 calibration[GD00X_NUM_CHANNEL]; +static u8 calibration_fscal2; +static u8 calibration_fscal3; + +enum{ + GD00X_BIND, + GD00X_DATA +}; + +// flags going to packet[11] +#define GD00X_FLAG_DR 0x08 +#define GD00X_FLAG_LIGHT 0x04 + +// flags going to packet[4] +#define GD00X_V2_FLAG_DR 0x40 +#define GD00X_V2_FLAG_LIGHT 0x80 + +// For code readability +enum { + CHANNEL1 = 0, + CHANNEL2, + CHANNEL3, + CHANNEL4, + CHANNEL5, + CHANNEL6, +}; + +#define CHANNEL_LIGHT CHANNEL5 + +// Bit vector from bit position +#define BV(bit) (1 << bit) + +#define CHAN_RANGE (CHAN_MAX_VALUE - CHAN_MIN_VALUE) +static u16 scale_channel(u8 ch, u16 destMin, u16 destMax) +{ + s32 chanval = Channels[ch]; + s32 range = (s32) destMax - (s32) destMin; + + if (chanval < CHAN_MIN_VALUE) + chanval = CHAN_MIN_VALUE; + else if (chanval > CHAN_MAX_VALUE) + chanval = CHAN_MAX_VALUE; + return (range * (chanval - CHAN_MIN_VALUE)) / CHAN_RANGE + destMin; +} + +#define GET_FLAG(ch, mask) (Channels[ch] > 0 ? mask : 0) +static void GD00X_send_packet() +{ + static u8 prev_CH6 = 0; + switch (Model.proto_opts[PROTOOPTS_FORMAT]) { + case FORMAT_V1: + packet[0] = (phase == GD00X_BIND) ? 0xAA : 0x55; + memcpy(packet+1, rx_tx_addr, 4); + u16 channel = scale_channel(CHANNEL1, 2000, 1000); // aileron + packet[5 ] = channel; + packet[6 ] = channel>>8; + channel = scale_channel(CHANNEL3, 1000, 2000); // throttle + packet[7 ] = channel; + packet[8 ] = channel>>8; + // dynamically driven aileron trim + channel = scale_channel(CHANNEL1, 1000, 2000); // aileron + packet[9 ] = channel; + packet[10] = channel>>8; + packet[11] = GD00X_FLAG_DR // Force high rate + | GET_FLAG(CHANNEL5, GD00X_FLAG_LIGHT); + packet[12] = 0x00; + packet[13] = 0x00; + packet[14] = 0x00; + break; + case FORMAT_V2: + if (phase == GD00X_BIND) { + for (u8 i = 0; i < 5; i++) + packet[i] = rx_tx_addr[i]; + } + else + { + packet[0] = scale_channel(CHANNEL3, 0, 100); // throttle 0..100 + + #define CHANNEL_MAX_100 1844 + #define CHANNEL_MIN_100 204 + #define GD00X_V2_DB_MIN 1024-40 + #define GD00X_V2_DB_MAX 1024+40 + // Deadband is needed on aileron + u16 aileron = scale_channel(CHANNEL1, CHANNEL_MAX_100, CHANNEL_MIN_100); + if (aileron > GD00X_V2_DB_MIN && aileron < GD00X_V2_DB_MAX) { + packet[1] = 0x20; // Send the channel centered + } + else // Ail: 0x3F..0x20..0x00 + { + if (aileron > GD00X_V2_DB_MAX) + packet[1] = 0x1F-((aileron-GD00X_V2_DB_MAX)*(0x20)/(CHANNEL_MAX_100+1-GD00X_V2_DB_MAX)); // 1F..00 + else + packet[1] = 0x3F-((aileron-CHANNEL_MIN_100)*(0x1F)/(GD00X_V2_DB_MIN-CHANNEL_MIN_100)); // 3F..21 + } + // Trims must be in a seperate channel for this model + packet[2] = 0x3F-(scale_channel(CHANNEL5, 0, 255)>>2); // Trim: 0x3F..0x20..0x00 + + u8 seq = ((packet_count*3)/7)%5; + packet[4] = seq | GD00X_V2_FLAG_DR; + + if (GET_FLAG(CHANNEL6, 1) != prev_CH6) + { // LED switch is temporary + len = 43; + prev_CH6 = GET_FLAG(CHANNEL6, 1); + } + if (len) + { // Send the light flag for a couple of packets + packet[4] |= GD00X_V2_FLAG_LIGHT; + len--; + } + + packet[3] = (packet[0]+packet[1]+packet[2]+packet[4])^(rx_tx_addr[0]^rx_tx_addr[1]^rx_tx_addr[2]); + + if ((packet_count%12) == 0 ) + hopping_frequency_no ^= 1; // Toggle between the 2 frequencies + packet_count++; + if (packet_count > 34) packet_count = 0; // Full period + if ( seq == (((packet_count*3)/7)%5) ) + { + if (packet_period == 2700) + packet_period = 3000; + else + packet_period = 2700; + } + else + packet_period = 4300; + } + packet[5] = 'D'; + break; + } + + // channel hopping + if (phase == GD00X_DATA) { + CC2500_WriteReg(CC2500_23_FSCAL3, calibration_fscal3); + CC2500_WriteReg(CC2500_24_FSCAL2, calibration_fscal2); + CC2500_WriteReg(CC2500_25_FSCAL1, calibration[hopping_frequency_no]); + XN297L_SetChannel(hopping_frequency[hopping_frequency_no]); + if (Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_V1) { + hopping_frequency_no++; + hopping_frequency_no &= 3; // 4 RF channels + } + } + + XN297L_WritePayload(packet, packet_length); + + if (tx_power != Model.tx_power) { + // Keep transmit power updated + tx_power = Model.tx_power; + CC2500_SetPower(tx_power); + } +} + +// calibrate used RF channels for faster hopping +static void calibrate_rf_chans() +{ + for (int c = 0; c < GD00X_NUM_CHANNEL; c++) { + CLOCK_ResetWatchdog(); + CC2500_Strobe(CC2500_SIDLE); + XN297L_SetChannel(hopping_frequency[c]); + CC2500_Strobe(CC2500_SCAL); + usleep(900); + calibration[c] = CC2500_ReadReg(CC2500_25_FSCAL1); + } + calibration_fscal3 = CC2500_ReadReg(CC2500_23_FSCAL3); // only needs to be done once + calibration_fscal2 = CC2500_ReadReg(CC2500_24_FSCAL2); // only needs to be done once + CC2500_Strobe(CC2500_SIDLE); +} + +static void GD00X_init() +{ + // setup cc2500 for xn297L@250kbps emulation, scrambled, crc enabled + XN297L_Configure(XN297_SCRAMBLED, XN297_CRC); + CC2500_WriteReg(CC2500_0C_FSCTRL0, fine); + CC2500_WriteReg(CC2500_0E_FREQ1, 0x4e + coarse); + switch (Model.proto_opts[PROTOOPTS_FORMAT]) { + case FORMAT_V1: + XN297L_SetTXAddr((u8*)"\xcc\xcc\xcc\xcc\xcc", 5); + XN297L_SetChannel(GD00X_RF_BIND_CHANNEL); + break; + case FORMAT_V2: + XN297L_SetTXAddr((u8*)"GDKNx", 5); + XN297L_SetChannel(GD00X_V2_RF_BIND_CHANNEL); + break; + } + CC2500_SetPower(tx_power); + calibrate_rf_chans(); + CC2500_SetTxRxMode(TX_EN); +} + +static void GD00X_initialize_txid() +{ + u32 lfsr = 0xb2c54a2ful; + u8 i, j; + +#ifndef USE_FIXED_MFGID + u8 var[12]; + MCU_SerialNumber(var, 12); + dbgprintf("Manufacturer id: "); + for (i = 0; i < 12; ++i) { + dbgprintf("%02X", var[i]); + rand32_r(&lfsr, var[i]); + } + dbgprintf("\r\n"); +#endif + + if (Model.fixed_id) { + for (i = 0, j = 0; i < sizeof(Model.fixed_id); ++i, j += 8) + rand32_r(&lfsr, (Model.fixed_id >> j) & 0xff); + } + // Pump zero bytes for LFSR to diverge more + for (i = 0; i < sizeof(lfsr); ++i) rand32_r(&lfsr, 0); + + switch (Model.proto_opts[PROTOOPTS_FORMAT]) { + case FORMAT_V1: + // tx address + for (i = 0; i < 2; i++) + rx_tx_addr[i] = (lfsr >> (i*8)) & 0xff; + + rx_tx_addr[2] = 0x12; + rx_tx_addr[3] = 0x13; + + u8 start = 76+(rx_tx_addr[0]&0x03); + for (i = 0; i < 4; i++) + hopping_frequency[i] = start-(i << 1); + + #ifdef FORCE_GD00X_ORIGINAL_ID + rx_tx_addr[0] = 0x1F; + rx_tx_addr[1] = 0x39; + rx_tx_addr[2] = 0x12; + rx_tx_addr[3] = 0x13; + for (i = 0; i < 4; i++) + hopping_frequency[i] = 79-(i << 1); + #endif + break; + case FORMAT_V2: + // Generate 64 different IDs + rx_tx_addr[1] = 0x00; + rx_tx_addr[2] = 0x00; + rx_tx_addr[1+((lfsr&0x10)>>4)] = lfsr&0x8F; + rx_tx_addr[0] = 0x65; + rx_tx_addr[3] = 0x95; + rx_tx_addr[4] = 0x47; // 'G' + + // hopping calculation + hopping_frequency[0] = (0x15+(rx_tx_addr[0]^rx_tx_addr[1]^rx_tx_addr[2]^rx_tx_addr[3]))&0x1F; + if (hopping_frequency[0] == 0x0F) + hopping_frequency[0] = 0x0E; + else if ((hopping_frequency[0]&0xFE) == 0x10) + hopping_frequency[0] += 2; + hopping_frequency[1] = 0x20+hopping_frequency[0]; + + #ifdef FORCE_GD00X_ORIGINAL_ID + // ID 1 + rx_tx_addr[0] = 0x65; + rx_tx_addr[1] = 0x00; + rx_tx_addr[2] = 0x00; + rx_tx_addr[3] = 0x95; + rx_tx_addr[4] = 0x47; // 'G' + hopping_frequency[0] = 0x05; + hopping_frequency[1] = 0x25; + // ID 2 + rx_tx_addr[0] = 0xFD; + rx_tx_addr[1] = 0x09; + rx_tx_addr[2] = 0x00; + rx_tx_addr[3] = 0x65; + rx_tx_addr[4] = 0x47; // 'G' + hopping_frequency[0] = 0x06; + hopping_frequency[1] = 0x26; + // ID 3 + rx_tx_addr[0] = 0x67; + rx_tx_addr[1] = 0x0F; + rx_tx_addr[2] = 0x00; + rx_tx_addr[3] = 0x69; + rx_tx_addr[4] = 0x47; // 'G' + hopping_frequency[0] = 0x16; + hopping_frequency[1] = 0x36; + #endif + break; + } +} + +static u16 GD00X_callback() +{ + if (fine != (s8)Model.proto_opts[PROTOOPTS_FREQFINE]) { + fine = (s8)Model.proto_opts[PROTOOPTS_FREQFINE]; + CC2500_WriteReg(CC2500_0C_FSCTRL0, fine); + } + if (coarse != (s8)Model.proto_opts[PROTOOPTS_FREQCOARSE]) { + coarse = (s8)Model.proto_opts[PROTOOPTS_FREQCOARSE]; + CC2500_WriteReg(CC2500_0E_FREQ1, 0x4e + coarse); + } + if (phase == GD00X_BIND) { + if (--bind_counter == 0) { + PROTOCOL_SetBindState(0); + phase = GD00X_DATA; + } + } + GD00X_send_packet(); + return packet_period; +} + +static void initialize() +{ + CLOCK_StopTimer(); + tx_power = Model.tx_power; + fine = (s8)Model.proto_opts[PROTOOPTS_FREQFINE]; + coarse = (s8)Model.proto_opts[PROTOOPTS_FREQCOARSE]; + packet_period = Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_V1?GD00X_PACKET_PERIOD:GD00X_V2_BIND_PACKET_PERIOD; + packet_length = Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_V1?GD00X_PAYLOAD_SIZE:GD00X_V2_PAYLOAD_SIZE; + packet_count = 0; + len = 0; + hopping_frequency_no = 0; + bind_counter = GD00X_BIND_COUNT; + phase = GD00X_BIND; + PROTOCOL_SetBindState((GD00X_BIND_COUNT * packet_period)/1000); + GD00X_initialize_txid(); + GD00X_init(); + CLOCK_StartTimer(GD00X_INITIAL_WAIT, GD00X_callback); +} + +uintptr_t GD00X_Cmds(enum ProtoCmds cmd) +{ + switch (cmd) { + case PROTOCMD_INIT: initialize(); return 0; + case PROTOCMD_DEINIT: + case PROTOCMD_RESET: + CLOCK_StopTimer(); + return (CC2500_Reset() ? 1 : -1); + case PROTOCMD_CHECK_AUTOBIND: return 1; + case PROTOCMD_BIND: initialize(); return 0; + case PROTOCMD_NUMCHAN: return 5; + case PROTOCMD_DEFAULT_NUMCHAN: return 5; + case PROTOCMD_CURRENT_ID: return Model.fixed_id; + case PROTOCMD_GETOPTIONS: return (uintptr_t)gd00x_opts; + case PROTOCMD_TELEMETRYSTATE: return PROTO_TELEM_UNSUPPORTED; + case PROTOCMD_CHANNELMAP: return AETRG; + default: break; + } + return 0; +} + + +#endif diff --git a/src/protocol/gd00x_nrf24l01.c b/src/protocol/gd00x_nrf24l01.c deleted file mode 100644 index e55cc84383..0000000000 --- a/src/protocol/gd00x_nrf24l01.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - 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. - - Deviation 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 Deviation. If not, see . - */ - -#include "common.h" -#include "interface.h" -#include "mixer.h" -#include "config/model.h" -#include "config/tx.h" // for Transmitter - -#ifdef PROTO_HAS_NRF24L01 - -#ifdef EMULATOR -#define USE_FIXED_MFGID -#define GD00X_BIND_COUNT 20 -#define GD00X_PACKET_PERIOD 100 -#define dbgprintf printf -#else -#define GD00X_BIND_COUNT 857 //3sec -#define GD00X_PACKET_PERIOD 3500 // Timeout for callback in uSec -//printf inside an interrupt handler is really dangerous -//this shouldn't be enabled even in debug builds without explicitly -//turning it on -#define dbgprintf if(0) printf -#endif - -//#define FORCE_GD00X_ORIGINAL_ID - -#define GD00X_INITIAL_WAIT 500 -#define GD00X_RF_BIND_CHANNEL 2 -#define GD00X_PAYLOAD_SIZE 15 - -static u8 tx_power; -static u8 packet[GD00X_PAYLOAD_SIZE]; -static u8 hopping_frequency_no; -static u8 rx_tx_addr[5]; -static u8 hopping_frequency[4]; -static u16 bind_counter; -static u8 phase; - -enum{ - GD00X_BIND, - GD00X_DATA -}; - -// flags going to packet[11] -#define GD00X_FLAG_DR 0x08 -#define GD00X_FLAG_LIGHT 0x04 - -// For code readability -enum { - CHANNEL1 = 0, - CHANNEL2, - CHANNEL3, - CHANNEL4, - CHANNEL5, - CHANNEL6, -}; - -#define CHANNEL_LIGHT CHANNEL5 - -// Bit vector from bit position -#define BV(bit) (1 << bit) - -#define CHAN_RANGE (CHAN_MAX_VALUE - CHAN_MIN_VALUE) -static u16 scale_channel(u8 ch, u16 destMin, u16 destMax) -{ - s32 chanval = Channels[ch]; - s32 range = (s32) destMax - (s32) destMin; - - if (chanval < CHAN_MIN_VALUE) - chanval = CHAN_MIN_VALUE; - else if (chanval > CHAN_MAX_VALUE) - chanval = CHAN_MAX_VALUE; - return (range * (chanval - CHAN_MIN_VALUE)) / CHAN_RANGE + destMin; -} - -#define GET_FLAG(ch, mask) (Channels[ch] > 0 ? mask : 0) -static void GD00X_send_packet() -{ - packet[0] = (phase == GD00X_BIND) ? 0xAA : 0x55; - memcpy(packet+1,rx_tx_addr,4); - u16 channel=scale_channel(CHANNEL1, 2000, 1000); // aileron - packet[5 ] = channel; - packet[6 ] = channel>>8; - channel=scale_channel(CHANNEL3, 1000, 2000); // throttle - packet[7 ] = channel; - packet[8 ] = channel>>8; - // dynamically driven aileron trim - channel=scale_channel(CHANNEL1, 1000, 2000); // aileron - packet[9 ] = channel; - packet[10] = channel>>8; - packet[11] = GD00X_FLAG_DR // Force high rate - | GET_FLAG(CHANNEL5, GD00X_FLAG_LIGHT); - packet[12] = 0x00; - packet[13] = 0x00; - packet[14] = 0x00; - - // Power on, TX mode, CRC enabled - XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP)); - if(phase == GD00X_DATA) - { - NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]); - hopping_frequency_no &= 3; // 4 RF channels - } - - NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); - NRF24L01_FlushTx(); - XN297_WritePayload(packet, GD00X_PAYLOAD_SIZE); - - - if (tx_power != Model.tx_power) { - //Keep transmit power updated - tx_power = Model.tx_power; - NRF24L01_SetPower(tx_power); - } -} - -static void GD00X_init() -{ - NRF24L01_Initialize(); - NRF24L01_SetTxRxMode(TX_EN); - XN297_SetTXAddr((u8*)"\xcc\xcc\xcc\xcc\xcc", 5); - NRF24L01_WriteReg(NRF24L01_05_RF_CH, GD00X_RF_BIND_CHANNEL); // Bind channel - NRF24L01_FlushTx(); - NRF24L01_FlushRx(); - NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit - NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes - NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only - NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps - NRF24L01_SetPower(tx_power); -} - -static void GD00X_initialize_txid() -{ - u32 lfsr = 0xb2c54a2ful; - u8 i,j; - -#ifndef USE_FIXED_MFGID - u8 var[12]; - MCU_SerialNumber(var, 12); - dbgprintf("Manufacturer id: "); - for (i = 0; i < 12; ++i) { - dbgprintf("%02X", var[i]); - rand32_r(&lfsr, var[i]); - } - dbgprintf("\r\n"); -#endif - - if (Model.fixed_id) { - for (i = 0, j = 0; i < sizeof(Model.fixed_id); ++i, j += 8) - rand32_r(&lfsr, (Model.fixed_id >> j) & 0xff); - } - // Pump zero bytes for LFSR to diverge more - for (i = 0; i < sizeof(lfsr); ++i) rand32_r(&lfsr, 0); - - // tx address - for(i=0; i<2; i++) - rx_tx_addr[i] = (lfsr >> (i*8)) & 0xff; - - rx_tx_addr[2]=0x12; - rx_tx_addr[3]=0x13; - - u8 start=76+(rx_tx_addr[0]&0x03); - for(i=0; i<4;i++) - hopping_frequency[i]=start-(i<<1); - - #ifdef FORCE_GD00X_ORIGINAL_ID - rx_tx_addr[0]=0x1F; - rx_tx_addr[1]=0x39; - rx_tx_addr[2]=0x12; - rx_tx_addr[3]=0x13; - for(i=0; i<4;i++) - hopping_frequency[i]=79-(i<<1); - #endif -} - -static u16 GD00X_callback() -{ - if(phase == GD00X_BIND) { - if(--bind_counter==0) { - PROTOCOL_SetBindState(0); - phase = GD00X_DATA; - } - } - GD00X_send_packet(); - return GD00X_PACKET_PERIOD; -} - -static void initialize() -{ - CLOCK_StopTimer(); - tx_power = Model.tx_power; - PROTOCOL_SetBindState((GD00X_BIND_COUNT * GD00X_PACKET_PERIOD)/1000); - GD00X_initialize_txid(); - GD00X_init(); - hopping_frequency_no = 0; - bind_counter=GD00X_BIND_COUNT; - phase = GD00X_BIND; - CLOCK_StartTimer(GD00X_INITIAL_WAIT, GD00X_callback); -} - -uintptr_t GD00X_Cmds(enum ProtoCmds cmd) -{ - switch(cmd) { - case PROTOCMD_INIT: initialize(); return 0; - case PROTOCMD_DEINIT: - case PROTOCMD_RESET: - CLOCK_StopTimer(); - return (NRF24L01_Reset() ? 1 : -1); - case PROTOCMD_CHECK_AUTOBIND: return 1; - case PROTOCMD_BIND: initialize(); return 0; - case PROTOCMD_NUMCHAN: return 5; - case PROTOCMD_DEFAULT_NUMCHAN: return 5; - case PROTOCMD_CURRENT_ID: return Model.fixed_id; - case PROTOCMD_GETOPTIONS: return 0; - case PROTOCMD_TELEMETRYSTATE: return PROTO_TELEM_UNSUPPORTED; - case PROTOCMD_CHANNELMAP: return AETRG; - default: break; - } - return 0; -} - - -#endif diff --git a/src/protocol/gw008_nrf24l01.c b/src/protocol/gw008_nrf24l01.c index aaf7bb7599..c25f731410 100644 --- a/src/protocol/gw008_nrf24l01.c +++ b/src/protocol/gw008_nrf24l01.c @@ -127,6 +127,7 @@ static void send_packet(u8 bind) static void gw008_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); XN297_SetTXAddr((u8*)"\xcc\xcc\xcc\xcc\xcc", 5); diff --git a/src/protocol/h8_3d_nrf24l01.c b/src/protocol/h8_3d_nrf24l01.c index 0149c223df..225b6d6112 100644 --- a/src/protocol/h8_3d_nrf24l01.c +++ b/src/protocol/h8_3d_nrf24l01.c @@ -357,6 +357,7 @@ static void send_packet(u8 bind) static void h8_3d_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); diff --git a/src/protocol/hontai_nrf24l01.c b/src/protocol/hontai_nrf24l01.c index 7e1b6fadce..9e64fc95d1 100644 --- a/src/protocol/hontai_nrf24l01.c +++ b/src/protocol/hontai_nrf24l01.c @@ -274,6 +274,7 @@ static void send_packet(u8 bind) static void ht_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_Initialize(); NRF24L01_SetTxRxMode(TX_EN); diff --git a/src/protocol/iface_cc2500.h b/src/protocol/iface_cc2500.h index 4e8f645671..082d62a24b 100644 --- a/src/protocol/iface_cc2500.h +++ b/src/protocol/iface_cc2500.h @@ -135,4 +135,13 @@ void CC2500_WriteData(u8 *packet, u8 length); void CC2500_ReadData(u8 *dpbuffer, int len); void CC2500_SetTxRxMode(enum TXRX_State); void CC2500_SetPower(int power); + +//---------------------------------------------------------------------------------- +// XN297L emulation, used for XN297@250kbps, TX only for now +//---------------------------------------------------------------------------------- +void XN297L_Configure(u8 scramble_en, u8 crc_en); +void XN297L_SetTXAddr(const u8* addr, u8 len); +void XN297L_WritePayload(u8* msg, u8 len); +void XN297L_SetChannel(u8 ch); + #endif diff --git a/src/protocol/iface_nrf24l01.h b/src/protocol/iface_nrf24l01.h index d7e500eb9f..ce0b6398ae 100644 --- a/src/protocol/iface_nrf24l01.h +++ b/src/protocol/iface_nrf24l01.h @@ -137,10 +137,21 @@ int NRF24L01_Reset(); //void NRF24L01_PulseCE(); // XN297 emulation layer -enum { - XN297_UNSCRAMBLED = 0, - XN297_SCRAMBLED -}; +#define XN297_UNSCRAMBLED 0 +#define XN297_SCRAMBLED 1 +#define XN297_NOCRC 0 +#define XN297_CRC 1 + +extern const u8 xn297_scramble[]; +extern const u16 xn297_crc_xorout_scrambled[]; +extern const u16 xn297_crc_xorout[]; +extern u8 xn297_crc; +extern u8 xn297_scramble_enabled; +extern u8 xn297_addr_len; +extern u8 xn297_tx_addr[5]; +extern u8 xn297_rx_addr[5]; +u8 bit_reverse(u8 b_in); +u8 _xn297_write_payload(u8* msg, u8 len, u8* out); void XN297_SetTXAddr(const u8* addr, int len); void XN297_SetRXAddr(const u8* addr, int len); diff --git a/src/protocol/mjxq_nrf24l01.c b/src/protocol/mjxq_nrf24l01.c index 628801ffa1..47f0fe77f7 100644 --- a/src/protocol/mjxq_nrf24l01.c +++ b/src/protocol/mjxq_nrf24l01.c @@ -55,7 +55,7 @@ #define ADDRESS_LENGTH 5 static const char * const mjxq_opts[] = { - _tr_noop("Format"), "WLH08", "X600", "X800", "H26D", "H26WH", "E010", "Phoenix", NULL, + _tr_noop("Format"), "WLH08", "X600", "X800", "H26D", "H26WH", NULL, NULL }; enum { @@ -68,8 +68,6 @@ enum { FORMAT_X800, FORMAT_H26D, FORMAT_H26WH, - FORMAT_E010, - FORMAT_PHOENIX, }; ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts); @@ -90,7 +88,7 @@ enum { CHANNEL12, // Camera tilt }; #define CHANNEL_LED CHANNEL5 -#define CHANNEL_ARM CHANNEL5 // H26WH - TDR Phoenix mini +#define CHANNEL_ARM CHANNEL5 // H26WH #define CHANNEL_FLIP CHANNEL6 #define CHANNEL_PICTURE CHANNEL7 #define CHANNEL_VIDEO CHANNEL8 @@ -123,20 +121,6 @@ static const struct { {{0xC8, 0x6E, 0x02}, {0x0A, 0x3C, 0x36, 0x3F}}, {{0x48, 0x6A, 0x40}, {0x0A, 0x43, 0x36, 0x3F}}}; -// captured from E010 and H36 stock transmitters -static const struct { - u8 txid[2]; - u8 rfchan[RF_NUM_CHANNELS]; -} -e010_tx_rf_map[] = {{{0x4F, 0x1C}, {0x3A, 0x35, 0x4A, 0x45}}, - {{0x90, 0x1C}, {0x2E, 0x36, 0x3E, 0x46}}, - {{0x24, 0x36}, {0x32, 0x3E, 0x42, 0x4E}}, - {{0x7A, 0x40}, {0x2E, 0x3C, 0x3E, 0x4C}}, - {{0x61, 0x31}, {0x2F, 0x3B, 0x3F, 0x4B}}, - {{0x5D, 0x37}, {0x33, 0x3B, 0x43, 0x4B}}, - {{0xFD, 0x4F}, {0x33, 0x3B, 0x43, 0x4B}}, - {{0x86, 0x3C}, {0x34, 0x3E, 0x44, 0x4E}}}; - // Bit vector from bit position #define BV(bit) (1 << bit) @@ -212,8 +196,6 @@ static void send_packet(u8 bind) packet[10] = pan_tilt_value(); /* FALLTHROUGH */ case FORMAT_WLH08: - case FORMAT_E010: - case FORMAT_PHOENIX: packet[10] += GET_FLAG(CHANNEL_RTH, 0x02) | GET_FLAG(CHANNEL_HEADLESS, 0x01); if (!bind) { @@ -222,11 +204,6 @@ static void send_packet(u8 bind) | GET_FLAG(CHANNEL_PICTURE, 0x08) | GET_FLAG(CHANNEL_VIDEO, 0x10) | GET_FLAG_INV(CHANNEL_LED, 0x20); // air/ground mode - if (Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_PHOENIX) { - packet[10] |= 0x20 // high rate - | GET_FLAG(CHANNEL_ARM, 0x80); - packet[14] &= ~0x24; - } if (Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_H26WH) { packet[10] |= 0x40; // high rate packet[14] &= ~0x24; // unset air/ground & arm flags @@ -297,6 +274,7 @@ static void send_packet(u8 bind) static void mjxq_init() { + XN297_SetScrambledMode(XN297_SCRAMBLED); u8 rx_tx_addr[ADDRESS_LENGTH]; NRF24L01_Initialize(); @@ -309,8 +287,6 @@ static void mjxq_init() break; case FORMAT_H26D: case FORMAT_H26WH: - case FORMAT_E010: - case FORMAT_PHOENIX: memcpy(rf_channels, "\x36\x3e\x46\x2e", sizeof(rf_channels)); break; default: @@ -342,11 +318,7 @@ static void mjxq_init() NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, PACKET_SIZE); - if (Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_E010 - || Model.proto_opts[PROTOOPTS_FORMAT] == FORMAT_PHOENIX) - NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250kbps - else - NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps + NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps NRF24L01_SetPower(Model.tx_power); NRF24L01_Activate(0x73); // Activate feature register NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes @@ -360,10 +332,6 @@ static void mjxq_init2() case FORMAT_H26WH: memcpy(rf_channels, "\x47\x42\x37\x32", sizeof(rf_channels)); break; - case FORMAT_E010: - case FORMAT_PHOENIX: - memcpy(rf_channels, e010_tx_rf_map[Model.fixed_id % (sizeof(e010_tx_rf_map)/sizeof(e010_tx_rf_map[0]))].rfchan, sizeof(rf_channels)); - break; case FORMAT_H26D: memcpy(rf_channels, "\x32\x3e\x42\x4e", sizeof(rf_channels)); break; @@ -428,11 +396,6 @@ static void initialize_txid() case FORMAT_H26WH: memcpy(txid, "\xa4\x03\x00", sizeof(txid)); break; - case FORMAT_E010: - case FORMAT_PHOENIX: - memcpy(txid, e010_tx_rf_map[Model.fixed_id % (sizeof(e010_tx_rf_map)/sizeof(e010_tx_rf_map[0]))].txid, 2); - txid[2] = 0x00; - break; case FORMAT_WLH08: // txid must be multiple of 8 txid[0] = (lfsr >> 16) & 0xf8; diff --git a/src/protocol/mt99xx_nrf24l01.c b/src/protocol/mt99xx_nrf24l01.c index 3873fed14e..8c16d597cf 100644 --- a/src/protocol/mt99xx_nrf24l01.c +++ b/src/protocol/mt99xx_nrf24l01.c @@ -287,6 +287,8 @@ static void mt99xx_init() NRF24L01_Initialize(); if( Model.proto_opts[PROTOOPTS_FORMAT] == PROTOOPTS_FORMAT_YZ) XN297_SetScrambledMode(XN297_UNSCRAMBLED); + else + XN297_SetScrambledMode(XN297_SCRAMBLED); NRF24L01_SetTxRxMode(TX_EN); NRF24L01_FlushTx(); for(u8 i=0; i<5; i++) diff --git a/src/protocol/protocol.c b/src/protocol/protocol.c index addad87c2e..60a16f0b95 100644 --- a/src/protocol/protocol.c +++ b/src/protocol/protocol.c @@ -165,7 +165,7 @@ void PROTOCOL_Load(int no_dlg) //printf("Loading %s: %08lx\n", file, fh); if(! fh) { if(! no_dlg) { - sprintf(tempstring, "Misisng protocol:\n%s", file); + sprintf(tempstring, "Missing protocol:\n%s", file); PAGE_ShowWarning(NULL, tempstring); } return; diff --git a/src/protocol/protocol.h b/src/protocol/protocol.h index e7a089cc98..ed7a3fc850 100644 --- a/src/protocol/protocol.h +++ b/src/protocol/protocol.h @@ -28,6 +28,9 @@ PROTODEF(PROTOCOL_SKYARTEC, CC2500, AETRG, SKYARTEC_Cmds, "Skyartec") PROTODEF(PROTOCOL_SFHSS, CC2500, AETRG, SFHSS_Cmds, "S-FHSS") PROTODEF(PROTOCOL_CORONA, CC2500, AETRG, Corona_Cmds, "Corona") PROTODEF(PROTOCOL_HITEC, CC2500, AETRG, Hitec_Cmds, "Hitec") +PROTODEF(PROTOCOL_E010, CC2500, AETRG, E010_Cmds, "E010") +PROTODEF(PROTOCOL_GD00X, CC2500, AETRG, GD00X_Cmds, "GD00X") +PROTODEF(PROTOCOL_V911S, CC2500, AETRG, V911S_Cmds, "V911S") #endif //PROTO_HAS_CC2500 #ifdef PROTO_HAS_NRF24L01 PROTODEF(PROTOCOL_V202, NRF24L01, AETRG, V202_Cmds, "V202") @@ -61,8 +64,6 @@ PROTODEF(PROTOCOL_BUGS3MINI, NRF24L01, AETRG, BUGS3MINI_Cmds, "Bugs3Mini") PROTODEF(PROTOCOL_E012, NRF24L01, AETRG, E012_Cmds, "E012") PROTODEF(PROTOCOL_E015, NRF24L01, AETRG, E015_Cmds, "E015") PROTODEF(PROTOCOL_NCC1701, NRF24L01, AETRG, NCC1701_Cmds, "NCC1701") -PROTODEF(PROTOCOL_V911S, NRF24L01, AETRG, V911S_Cmds, "V911S") -PROTODEF(PROTOCOL_GD00X, NRF24L01, AETRG, GD00X_Cmds, "GD00X") PROTODEF(PROTOCOL_LOLI, NRF24L01, AETRG, LOLI_Cmds, "LOLI") PROTODEF(PROTOCOL_E016H, NRF24L01, AETRG, E016H_Cmds, "E016H") #endif //PROTO_HAS_NRF24L01 diff --git a/src/protocol/spi/cc2500.c b/src/protocol/spi/cc2500.c index 9d0028d16a..131159bfd4 100644 --- a/src/protocol/spi/cc2500.c +++ b/src/protocol/spi/cc2500.c @@ -140,4 +140,95 @@ int CC2500_Reset() CC2500_SetTxRxMode(TXRX_OFF); return CC2500_ReadReg(CC2500_0E_FREQ1) == 0xC4; } + +// xn297 emulation + +// setup CC2500 for XN297L @ 250 kbps emulation +void XN297L_Configure(u8 scramble_en, u8 crc_en) +{ + // Address Config = No address check + // Base Frequency = 2400 + // CRC Autoflush = false + // CRC Enable = false + // Carrier Frequency = 2400 + // Channel Number = 0 + // Channel Spacing = 333.251953 + // Data Format = Normal mode + // Data Rate = 249.939 + // Deviation = 126.953125 + // Device Address = 0 + // Manchester Enable = false + // Modulated = true + // Modulation Format = GFSK + // Packet Length = 255 + // Packet Length Mode = Variable packet length mode. Packet length configured by the first byte after sync word + // Preamble Count = 4 + // RX Filter BW = 203.125000 + // Sync Word Qualifier Mode = No preamble/sync + // TX Power = 0 + // Whitening = false + + CC2500_Reset(); + CC2500_Strobe(CC2500_SIDLE); + CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x01); // Packet Automation Control + CC2500_WriteReg(CC2500_0B_FSCTRL1, 0x0A); // Frequency Synthesizer Control + CC2500_WriteReg(CC2500_0C_FSCTRL0, 0x00); // Frequency Synthesizer Control + CC2500_WriteReg(CC2500_0D_FREQ2, 0x5C); // Frequency Control Word, High Byte + CC2500_WriteReg(CC2500_0E_FREQ1, 0x4E); // Frequency Control Word, Middle Byte + CC2500_WriteReg(CC2500_0F_FREQ0, 0xC5); // Frequency Control Word, Low Byte + CC2500_WriteReg(CC2500_10_MDMCFG4, 0x8D); // Modem Configuration + CC2500_WriteReg(CC2500_11_MDMCFG3, 0x3B); // Modem Configuration + CC2500_WriteReg(CC2500_12_MDMCFG2, 0x10); // Modem Configuration + CC2500_WriteReg(CC2500_13_MDMCFG1, 0x23); // Modem Configuration + CC2500_WriteReg(CC2500_14_MDMCFG0, 0xA4); // Modem Configuration + CC2500_WriteReg(CC2500_15_DEVIATN, 0x62); // Modem Deviation Setting + CC2500_WriteReg(CC2500_18_MCSM0, 0x18); // Main Radio Control State Machine Configuration + CC2500_WriteReg(CC2500_19_FOCCFG, 0x1D); // Frequency Offset Compensation Configuration + CC2500_WriteReg(CC2500_1A_BSCFG, 0x1C); // Bit Synchronization Configuration + CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0xC7); // AGC Control + CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0x00); // AGC Control + CC2500_WriteReg(CC2500_1D_AGCCTRL0, 0xB0); // AGC Control + CC2500_WriteReg(CC2500_21_FREND1, 0xB6); // Front End RX Configuration + CC2500_WriteReg(CC2500_23_FSCAL3, 0xEA); // Frequency Synthesizer Calibration + CC2500_WriteReg(CC2500_25_FSCAL1, 0x00); // Frequency Synthesizer Calibration + CC2500_WriteReg(CC2500_26_FSCAL0, 0x11); // Frequency Synthesizer Calibration + + XN297_SetScrambledMode(scramble_en); + xn297_crc = crc_en; +} + +void XN297L_SetTXAddr(const u8* addr, u8 len) +{ + if (len > 5) len = 5; + if (len < 3) len = 3; + xn297_addr_len = len; + memcpy(xn297_tx_addr, addr, len); +} + +void XN297L_WritePayload(u8* msg, u8 len) +{ + u8 buf[36]; + u8 count = _xn297_write_payload(msg, len, buf); + // halt Tx/Rx + CC2500_Strobe(CC2500_SIDLE); + // flush tx FIFO + CC2500_Strobe(CC2500_SFTX); + // set cc2500 packet length + CC2500_WriteReg(CC2500_3F_TXFIFO, count + 3); + // XN297L preamble + CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (u8*)"\x71\x0f\x55", 3); + // packet + CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buf, count); + // transmit + CC2500_Strobe(CC2500_STX); +} + +void XN297L_SetChannel(u8 ch) +{ + if (ch > 85) + ch = 85; + // channel spacing is 333.25 MHz + CC2500_WriteReg(CC2500_0A_CHANNR, ch * 3); +} + #endif diff --git a/src/protocol/spi/nrf24l01.c b/src/protocol/spi/nrf24l01.c index a590a095e0..63867ed51e 100644 --- a/src/protocol/spi/nrf24l01.c +++ b/src/protocol/spi/nrf24l01.c @@ -272,75 +272,7 @@ int NRF24L01_Reset() // // XN297 emulation layer ////////////////////////// -static u8 xn297_scramble_enabled; -static int xn297_addr_len; -static u8 xn297_tx_addr[5]; -static u8 xn297_rx_addr[5]; -static u8 xn297_crc = 0; - -static const uint8_t xn297_scramble[] = { - 0xe3, 0xb1, 0x4b, 0xea, 0x85, 0xbc, 0xe5, 0x66, - 0x0d, 0xae, 0x8c, 0x88, 0x12, 0x69, 0xee, 0x1f, - 0xc7, 0x62, 0x97, 0xd5, 0x0b, 0x79, 0xca, 0xcc, - 0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f, - 0x8e, 0xc5, 0x2f}; - -FLASHWORDTABLE xn297_crc_xorout_scrambled[] = { - 0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, - 0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x814B, - 0xD461, 0xF494, 0x2503, 0x691D, 0xFE8B, 0x9BA7, - 0x8B17, 0x2920, 0x8B5F, 0x61B1, 0xD391, 0x7401, - 0x2138, 0x129F, 0xB3A0, 0x2988}; - -FLASHWORDTABLE xn297_crc_xorout[] = { - 0x0000, 0x3d5f, 0xa6f1, 0x3a23, 0xaa16, 0x1caf, - 0x62b2, 0xe0eb, 0x0821, 0xbe07, 0x5f1a, 0xaf15, - 0x4f0a, 0xad24, 0x5e48, 0xed34, 0x068c, 0xf2c9, - 0x1852, 0xdf36, 0x129d, 0xb17c, 0xd5f5, 0x70d7, - 0xb798, 0x5133, 0x67db, 0xd94e}; - - -#if defined(__GNUC__) && defined(__ARM_ARCH_ISA_THUMB) && (__ARM_ARCH_ISA_THUMB==2) -// rbit instruction works on cortex m3 -uint32_t __RBIT_(uint32_t in) -{ - uint32_t out=0; - __asm volatile ("rbit %0, %1" : "=r" (out) : "r" (in) ); - return(out); -} - -static uint8_t bit_reverse(uint8_t a) -{ - return __RBIT_( (unsigned int) a)>>24; -} -#else -static uint8_t bit_reverse(uint8_t b_in) -{ - uint8_t b_out = 0; - for (int i = 0; i < 8; ++i) { - b_out = (b_out << 1) | (b_in & 1); - b_in >>= 1; - } - return b_out; -} -#endif - -static const uint16_t polynomial = 0x1021; -static const uint16_t initial = 0xb5d2; - -u16 crc16_update(u16 crc, u8 a, u8 bits) -{ - crc ^= a << 8; - while (bits--) { - if (crc & 0x8000) { - crc = (crc << 1) ^ polynomial; - } else { - crc = crc << 1; - } - } - return crc; -} - +static u8 xn297_packet[32]; void XN297_SetTXAddr(const u8* addr, int len) { @@ -355,11 +287,6 @@ void XN297_SetTXAddr(const u8* addr, int len) } NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2); NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, buf, 5); - // Receive address is complicated. We need to use scrambled actual address as a receive address - // but the TX code now assumes fixed 4-byte transmit address for preamble. We need to adjust it - // first. Also, if the scrambled address begings with 1 nRF24 will look for preamble byte 0xAA - // instead of 0x55 to ensure enough 0-1 transitions to tune the receiver. Still need to experiment - // with receiving signals. memcpy(xn297_tx_addr, addr, len); } @@ -389,116 +316,82 @@ void XN297_Configure(u8 flags) NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags & 0xff); } -void XN297_SetScrambledMode(const u8 mode) -{ - xn297_scramble_enabled = mode; -} - u8 XN297_WritePayload(u8* msg, int len) { - u8 packet[32]; u8 res; - int last = 0; if (xn297_addr_len < 4) { // If address length (which is defined by receive address length) // is less than 4 the TX address can't fit the preamble, so the last // byte goes here - packet[last++] = 0x55; - } - for (int i = 0; i < xn297_addr_len; ++i) { - packet[last] = xn297_tx_addr[xn297_addr_len-i-1]; - if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[i]; - last++; - } - - for (int i = 0; i < len; ++i) { - // bit-reverse bytes in packet - u8 b_out = bit_reverse(msg[i]); - packet[last] = b_out; - if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[xn297_addr_len+i]; - last++; + xn297_packet[last++] = 0x55; } - if (xn297_crc) { - int offset = xn297_addr_len < 4 ? 1 : 0; - u16 crc = initial; - for (int i = offset; i < last; ++i) { - crc = crc16_update(crc, packet[i], 8); - } - if(xn297_scramble_enabled) - crc ^= pgm_read_word(&xn297_crc_xorout_scrambled[xn297_addr_len - 3 + len]); - else - crc ^= pgm_read_word(&xn297_crc_xorout[xn297_addr_len - 3 + len]); - packet[last++] = crc >> 8; - packet[last++] = crc & 0xff; - } - res = NRF24L01_WritePayload(packet, last); + last += _xn297_write_payload(msg, len, &xn297_packet[last]); + res = NRF24L01_WritePayload(xn297_packet, last); return res; } u8 XN297_WriteEnhancedPayload(u8* msg, int len, int noack, u16 crc_xorout) { - u8 packet[32]; u8 scramble_index=0; u8 res; int last = 0; static int pid=0; + uint16_t initial = 0xb5d2; // address if (xn297_addr_len < 4) { // If address length (which is defined by receive address length) // is less than 4 the TX address can't fit the preamble, so the last // byte goes here - packet[last++] = 0x55; + xn297_packet[last++] = 0x55; } for (int i = 0; i < xn297_addr_len; ++i) { - packet[last] = xn297_tx_addr[xn297_addr_len-i-1]; - if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[scramble_index++]; + xn297_packet[last] = xn297_tx_addr[xn297_addr_len-i-1]; + if (xn297_scramble_enabled) + xn297_packet[last] ^= xn297_scramble[scramble_index++]; last++; } // pcf - packet[last] = (len << 1) | (pid>>1); + xn297_packet[last] = (len << 1) | (pid>>1); if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[scramble_index++]; + xn297_packet[last] ^= xn297_scramble[scramble_index++]; last++; - packet[last] = (pid << 7) | (noack << 6); + xn297_packet[last] = (pid << 7) | (noack << 6); // payload - packet[last]|= bit_reverse(msg[0]) >> 2; // first 6 bit of payload + xn297_packet[last]|= bit_reverse(msg[0]) >> 2; // first 6 bit of payload if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[scramble_index++]; + xn297_packet[last] ^= xn297_scramble[scramble_index++]; for (int i = 0; i < len-1; ++i) { last++; - packet[last] = (bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2); + xn297_packet[last] = (bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2); if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[scramble_index++]; + xn297_packet[last] ^= xn297_scramble[scramble_index++]; } last++; - packet[last] = bit_reverse(msg[len-1]) << 6; // last 2 bit of payload + xn297_packet[last] = bit_reverse(msg[len-1]) << 6; // last 2 bit of payload if(xn297_scramble_enabled) - packet[last] ^= xn297_scramble[scramble_index++] & 0xc0; + xn297_packet[last] ^= xn297_scramble[scramble_index++] & 0xc0; // crc if (xn297_crc) { int offset = xn297_addr_len < 4 ? 1 : 0; u16 crc = initial; for (int i = offset; i < last; ++i) { - crc = crc16_update(crc, packet[i], 8); + crc = crc16_update(crc, xn297_packet[i], 8); } - crc = crc16_update(crc, packet[last] & 0xc0, 2); + crc = crc16_update(crc, xn297_packet[last] & 0xc0, 2); crc ^= crc_xorout; - packet[last++] |= (crc >> 8) >> 2; - packet[last++] = ((crc >> 8) << 6) | ((crc & 0xff) >> 2); - packet[last++] = (crc & 0xff) << 6; + xn297_packet[last++] |= (crc >> 8) >> 2; + xn297_packet[last++] = ((crc >> 8) << 6) | ((crc & 0xff) >> 2); + xn297_packet[last++] = (crc & 0xff) << 6; } - res = NRF24L01_WritePayload(packet, last); + res = NRF24L01_WritePayload(xn297_packet, last); pid++; if(pid>3) @@ -521,8 +414,8 @@ u8 XN297_ReadPayload(u8* msg, int len) u8 XN297_ReadEnhancedPayload(u8* msg, int len) { u8 buffer[32]; - u8 pcf_size; // pcf payload size - NRF24L01_ReadPayload(buffer, len+2); // pcf + payload + u8 pcf_size; // pcf payload size + NRF24L01_ReadPayload(buffer, len+2); // pcf + payload pcf_size = buffer[0]; if(xn297_scramble_enabled) pcf_size ^= xn297_scramble[xn297_addr_len]; @@ -548,8 +441,8 @@ static u8 hs6200_tx_addr[5]; static u8 hs6200_address_length; static const u8 hs6200_scramble[] = { - 0x80,0xf5,0x3b,0x0d,0x6d,0x2a,0xf9,0xbc, - 0x51,0x8e,0x4c,0xfd,0xc1,0x65,0xd0}; // todo: find all 32 bytes ... + 0x80, 0xf5, 0x3b, 0x0d, 0x6d, 0x2a, 0xf9, 0xbc, + 0x51, 0x8e, 0x4c, 0xfd, 0xc1, 0x65, 0xd0}; // todo: find all 32 bytes ... void HS6200_SetTXAddr(const u8* addr, u8 len) { diff --git a/src/protocol/spi/xn297_emu.c b/src/protocol/spi/xn297_emu.c new file mode 100644 index 0000000000..26ac721f53 --- /dev/null +++ b/src/protocol/spi/xn297_emu.c @@ -0,0 +1,125 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . +*/ + +#include "common.h" + +u8 xn297_crc; +u8 xn297_scramble_enabled; +u8 xn297_addr_len; +u8 xn297_tx_addr[5]; +u8 xn297_rx_addr[5]; + +const u8 xn297_scramble[] = { + 0xe3, 0xb1, 0x4b, 0xea, 0x85, 0xbc, 0xe5, 0x66, + 0x0d, 0xae, 0x8c, 0x88, 0x12, 0x69, 0xee, 0x1f, + 0xc7, 0x62, 0x97, 0xd5, 0x0b, 0x79, 0xca, 0xcc, + 0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f, + 0x8e, 0xc5, 0x2f}; + +const u16 xn297_crc_xorout_scrambled[] = { + 0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, + 0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x814B, + 0xD461, 0xF494, 0x2503, 0x691D, 0xFE8B, 0x9BA7, + 0x8B17, 0x2920, 0x8B5F, 0x61B1, 0xD391, 0x7401, + 0x2138, 0x129F, 0xB3A0, 0x2988}; + +const u16 xn297_crc_xorout[] = { + 0x0000, 0x3d5f, 0xa6f1, 0x3a23, 0xaa16, 0x1caf, + 0x62b2, 0xe0eb, 0x0821, 0xbe07, 0x5f1a, 0xaf15, + 0x4f0a, 0xad24, 0x5e48, 0xed34, 0x068c, 0xf2c9, + 0x1852, 0xdf36, 0x129d, 0xb17c, 0xd5f5, 0x70d7, + 0xb798, 0x5133, 0x67db, 0xd94e}; + +#if defined(__GNUC__) && defined(__ARM_ARCH_ISA_THUMB) && (__ARM_ARCH_ISA_THUMB == 2) +// rbit instruction works on cortex m3 +static u32 __RBIT_(u32 in) +{ + u32 out = 0; + __asm volatile ("rbit %0, %1" : "=r" (out) : "r" (in) ); + return(out); +} + +u8 bit_reverse(u8 b_in) +{ + return __RBIT_( (unsigned int) b_in)>>24; +} +#else +u8 bit_reverse(u8 b_in) +{ + u8 b_out = 0; + for (int i = 0; i < 8; ++i) { + b_out = (b_out << 1) | (b_in & 1); + b_in >>= 1; + } + return b_out; +} +#endif + +static const uint16_t polynomial = 0x1021; + +u16 crc16_update(u16 crc, u8 a, u8 bits) +{ + crc ^= a << 8; + while (bits--) { + if (crc & 0x8000) { + crc = (crc << 1) ^ polynomial; + } else { + crc = crc << 1; + } + } + return crc; +} + +void XN297_SetScrambledMode(const u8 mode) +{ + xn297_scramble_enabled = mode; +} + +u8 _xn297_write_payload(const u8* msg, u8 len, u8* out) +{ + u8 last = 0; + u8 i; + const uint16_t initial = 0xb5d2; + + for (i = 0; i < xn297_addr_len; ++i) { + out[last] = xn297_tx_addr[xn297_addr_len-i-1]; + if (xn297_scramble_enabled) + out[last] ^= xn297_scramble[i]; + last++; + } + + for (i = 0; i < len; ++i) { + // bit-reverse bytes in packet + u8 b_out = bit_reverse(msg[i]); + out[last] = b_out; + if (xn297_scramble_enabled) + out[last] ^= xn297_scramble[xn297_addr_len+i]; + last++; + } + + if (xn297_crc) { + u16 crc = initial; + for (i = 0; i < last; ++i) { + crc = crc16_update(crc, out[i], 8); + } + if (xn297_scramble_enabled) + crc ^= xn297_crc_xorout_scrambled[xn297_addr_len - 3 + len]; + else + crc ^= xn297_crc_xorout[xn297_addr_len - 3 + len]; + out[last++] = crc >> 8; + out[last++] = crc & 0xff; + } + return last; +} diff --git a/src/protocol/v911s_nrf24l01.c b/src/protocol/v911s_cc2500.c similarity index 79% rename from src/protocol/v911s_nrf24l01.c rename to src/protocol/v911s_cc2500.c index 99ce8fa54b..a970dd899f 100644 --- a/src/protocol/v911s_nrf24l01.c +++ b/src/protocol/v911s_cc2500.c @@ -19,7 +19,7 @@ #include "config/model.h" #include "config/tx.h" // for Transmitter -#ifdef PROTO_HAS_NRF24L01 +#ifdef PROTO_HAS_CC2500 #ifdef EMULATOR #define USE_FIXED_MFGID @@ -44,6 +44,16 @@ #define V911S_RF_BIND_CHANNEL 35 #define V911S_NUM_RF_CHANNELS 8 +static const char * const v911s_opts[] = { + _tr_noop("Freq-Fine"), "-127", "127", NULL, + NULL +}; +enum { + PROTOOPTS_FREQFINE = 0, + LAST_PROTO_OPT, +}; +ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts); + static u8 tx_power; static u8 packet[V911S_PACKET_SIZE]; static u8 rf_ch_num; @@ -53,6 +63,9 @@ static u8 hopping_frequency[V911S_NUM_RF_CHANNELS]; static u16 bind_counter; static u16 packet_period; static u8 phase; +static u8 calibration[V911S_NUM_RF_CHANNELS]; +static u8 calibration_fscal2; +static u8 calibration_fscal3; enum{ V911S_BIND, @@ -132,40 +145,54 @@ static void V911S_send_packet(u8 bind) packet[11]|= ch<<3; packet[12] = ch>>5; } - - // Power on, TX mode, 2byte CRC - XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP)); + if (!bind) { - NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[channel]); + // channel hopping + CC2500_WriteReg(CC2500_23_FSCAL3, calibration_fscal3); + CC2500_WriteReg(CC2500_24_FSCAL2, calibration_fscal2); + CC2500_WriteReg(CC2500_25_FSCAL1, calibration[channel]); + XN297L_SetChannel(hopping_frequency[channel]); hopping_frequency_no++; hopping_frequency_no&=7; // 8 RF channels } - // clear packet status bits and TX FIFO - NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); - NRF24L01_FlushTx(); - XN297_WritePayload(packet, V911S_PACKET_SIZE); + + XN297L_WritePayload(packet, V911S_PACKET_SIZE); if (tx_power != Model.tx_power) { //Keep transmit power updated tx_power = Model.tx_power; - NRF24L01_SetPower(tx_power); + CC2500_SetPower(tx_power); + } +} + +// calibrate used RF channels for faster hopping +static void calibrate_rf_chans() +{ + for (int c = 0; c < V911S_NUM_RF_CHANNELS; c++) { + CLOCK_ResetWatchdog(); + CC2500_Strobe(CC2500_SIDLE); + XN297L_SetChannel(hopping_frequency[c]); + CC2500_Strobe(CC2500_SCAL); + usleep(900); + calibration[c] = CC2500_ReadReg(CC2500_25_FSCAL1); } + calibration_fscal3 = CC2500_ReadReg(CC2500_23_FSCAL3); // only needs to be done once + calibration_fscal2 = CC2500_ReadReg(CC2500_24_FSCAL2); // only needs to be done once + CC2500_Strobe(CC2500_SIDLE); } static void V911S_init() { - NRF24L01_Initialize(); - NRF24L01_SetTxRxMode(TX_EN); - XN297_SetTXAddr((u8 *)"\x4B\x4E\x42\x4E\x44", 5); // Bind address - NRF24L01_WriteReg(NRF24L01_05_RF_CH, V911S_RF_BIND_CHANNEL); // Bind channel - NRF24L01_FlushTx(); - NRF24L01_FlushRx(); - NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit - NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes - NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only - NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps - NRF24L01_SetPower(tx_power); + // setup cc2500 for xn297L@250kbps emulation, scrambled, crc enabled + XN297L_Configure(XN297_SCRAMBLED, XN297_CRC); + // Bind address + XN297L_SetTXAddr((u8 *)"\x4B\x4E\x42\x4E\x44", 5); + // Bind channel + XN297L_SetChannel(V911S_RF_BIND_CHANNEL); + CC2500_SetPower(tx_power); + calibrate_rf_chans(); + CC2500_SetTxRxMode(TX_EN); } static void V911S_initialize_txid() @@ -214,7 +241,7 @@ static u16 V911S_callback() if (bind_counter == 0) { PROTOCOL_SetBindState(0); - XN297_SetTXAddr(rx_tx_addr, 5); + XN297L_SetTXAddr(rx_tx_addr, 5); packet_period=V911S_PACKET_PERIOD; phase = V911S_DATA; } @@ -262,7 +289,7 @@ static void initialize(u8 bind) } else { - XN297_SetTXAddr(rx_tx_addr, 5); + XN297L_SetTXAddr(rx_tx_addr, 5); packet_period= V911S_PACKET_PERIOD; phase = V911S_DATA; } @@ -278,12 +305,13 @@ uintptr_t V911S_Cmds(enum ProtoCmds cmd) case PROTOCMD_DEINIT: case PROTOCMD_RESET: CLOCK_StopTimer(); - return (NRF24L01_Reset() ? 1 : -1); + return (CC2500_Reset() ? 1 : -1); case PROTOCMD_CHECK_AUTOBIND: return 0; case PROTOCMD_BIND: initialize(1); return 0; case PROTOCMD_NUMCHAN: return 5; case PROTOCMD_DEFAULT_NUMCHAN: return 5; case PROTOCMD_CURRENT_ID: return Model.fixed_id; + case PROTOCMD_GETOPTIONS: return (uintptr_t)v911s_opts; case PROTOCMD_TELEMETRYSTATE: return PROTO_TELEM_UNSUPPORTED; case PROTOCMD_CHANNELMAP: return AETRG; default: break;