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;