Merge 89bea4c38b3ab47eaa01bf1197179fe4732b0d49 into 8f4bffa9509ea607ea841f1892e4a174eaa79096

This commit is contained in:
tipouic 2016-10-04 21:28:08 +00:00 committed by GitHub
commit d19f56f549
41 changed files with 7443 additions and 33 deletions

Binary file not shown.

View File

@ -0,0 +1,164 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined JOYSWAY_A7105_INO
#include "iface_a7105.h"
#define EVEN_ODD 0x00
//#define EVEN_ODD 0x01
static const uint8_t A7105_regs[] = {
0x00, 0x62, -1, 0x0f, 0x00, -1 , -1 , 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0xf5, 0x00, 0x15,
0x9e, 0x4b, 0x00, 0x03, 0x56, 0x2b, 0x12, 0x4a, 0x02, 0x80, 0x80, 0x00, 0x0e, 0x91, 0x03, 0x0f,
0x16, 0x2a, 0x00, -1, -1, -1, 0x3a, 0x06, 0x1f, 0x47, 0x80, 0x01, 0x05, 0x45, 0x18, 0x00,
0x01, 0x0f, 0x00
};
static uint8_t next_ch;
static int joysway_init()
{
int i;
uint8_t if_calibration1;
//uint8_t vco_calibration0;
//uint8_t vco_calibration1;
phase = 0;
next_ch = 0x30;
for (i = 0; i < 0x33; i++)
if((uint8_t)A7105_regs[i] != -1)
A7105_WriteReg(i, A7105_regs[i]);
A7105_WriteID(0x5475c52a);
A7105_Strobe(A7105_PLL);
//IF Filter Bank Calibration
A7105_WriteReg(0x02, 1);
A7105_ReadReg(0x02);
uint32_t ms = micros();
while(micros() - ms < 500) {
if(! A7105_ReadReg(0x02))
break;
}
if (micros() - ms >= 500)
return 0;
A7105_Strobe(A7105_STANDBY);
if_calibration1 = A7105_ReadReg(0x22);
if(if_calibration1 & A7105_MASK_FBCF) {
//Calibration failed...what do we do?
return 0;
}
//VCO Current Calibration
A7105_WriteReg(0x24, 0x13); //Recomended calibration from A7105 Datasheet
A7105_WriteReg(0x25, 0x09); //Recomended calibration from A7105 Datasheet
A7105_WriteID(MProtocol_id_master);
A7105_Strobe(A7105_PLL);
A7105_WriteReg(0x02, 1);
ms = micros();
while(micros() - ms < 500) {
if(! A7105_ReadReg(0x02))
break;
}
if (micros() - ms >= 500)
return 0;
A7105_Strobe(A7105_STANDBY);
if_calibration1 = A7105_ReadReg(0x22);
if(if_calibration1 & A7105_MASK_FBCF) {
//Calibration failed...what do we do?
return 0;
}
A7105_WriteReg(0x24, 0x13); //Recomended calibration from A7105 Datasheet
A7105_WriteReg(0x25, 0x09); //Recomended calibration from A7105 Datasheet
A7105_SetTxRxMode(TX_EN);
A7105_SetPower();
A7105_Strobe(A7105_STANDBY);
return 1;
}
static void joysway_build_packet()
{
int i;
//-100% =~ 0x03e8
//+100% =~ 0x07ca
//Calculate:
//Center = 0x5d9
//1 % = 5
packet[0] = phase == 0 ? 0xdd : 0xff;
packet[1] = (MProtocol_id_master >> 24) & 0xff;
packet[2] = (MProtocol_id_master >> 16) & 0xff;
packet[3] = (MProtocol_id_master >> 8) & 0xff;
packet[4] = (MProtocol_id_master >> 0) & 0xff;
packet[5] = 0x00;
static const int chmap[4] = {6, 7, 10, 11};
for (i = 0; i < 4; i++) {
// if (i >= Model.num_channels) { packet[chmap[i]] = 0x64; continue; }
uint32_t value = (uint32_t)Servo_data[i] * 0x66 / PPM_MAX + 0x66;
if (value < 0) { value = 0; }
if (value > 0xff) { value = 0xff; }
packet[chmap[i]] = value;
}
packet[8] = 0x64;
packet[9] = 0x64;
packet[12] = 0x64;
packet[13] = 0x64;
packet[14] = phase == 0 ? 0x30 : 0xaa;
uint8_t value = 0;
for (int i = 0; i < 15; i++) { value += packet[i]; }
packet[15] = value;
}
static uint16_t joysway_cb()
{
uint8_t ch;
if (phase == 254) {
phase = 0;
A7105_WriteID(0x5475c52a);
ch = 0x0a;
} else if (phase == 2) {
A7105_WriteID(MProtocol_id_master);
ch = 0x30;
} else {
if ((phase & 0x01) ^ EVEN_ODD) {
ch = 0x30;
} else {
ch = next_ch;
}
}
if (! ((phase & 0x01) ^ EVEN_ODD)) {
next_ch++;
if (next_ch == 0x45)
next_ch = 0x30;
}
joysway_build_packet();
A7105_Strobe(A7105_STANDBY);
A7105_WriteData(16, ch);
phase++;
return 6000;
}
static uint16_t JOYSWAY_Setup() {
while(1) {
A7105_Reset();
if (joysway_init())
break;
}
return 2400;
}
#endif

View File

@ -25,6 +25,10 @@
#define Q282_PACKET_SIZE 21
#define CX10_PACKET_PERIOD 1316 // Timeout for callback in uSec
#define CX10A_PACKET_PERIOD 6000
<<<<<<< HEAD
#define CX10A_BIND_COUNT 400 // 2 seconds
=======
>>>>>>> refs/remotes/pascallanger/master
#define CX10_INITIAL_WAIT 500
@ -197,15 +201,30 @@ uint16_t CX10_callback()
}
break;
case CX10_BIND2:
<<<<<<< HEAD
bind_counter--;
if(bind_counter==0)
{ // Needed for some CX-10A to properly finish the bind
CX10_init();
bind_counter=CX10A_BIND_COUNT;
}
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR))
=======
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
>>>>>>> refs/remotes/pascallanger/master
{ // RX fifo data ready
XN297_ReadPayload(packet, packet_length);
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
if(packet[9] == 1)
{
<<<<<<< HEAD
phase = CX10_BIND1;
bind_counter=0;
=======
BIND_DONE;
phase = CX10_DATA;
>>>>>>> refs/remotes/pascallanger/master
}
}
else
@ -215,7 +234,11 @@ uint16_t CX10_callback()
NRF24L01_FlushTx();
NRF24L01_SetTxRxMode(TX_EN);
CX10_Write_Packet(1);
<<<<<<< HEAD
delayMicroseconds(400); // 300µs in deviation but not working so using 400µs instead
=======
delayMicroseconds(400);
>>>>>>> refs/remotes/pascallanger/master
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
@ -265,6 +288,10 @@ uint16_t initCX10(void)
packet_period = CX10A_PACKET_PERIOD;
phase = CX10_BIND2;
<<<<<<< HEAD
bind_counter=CX10A_BIND_COUNT;
=======
>>>>>>> refs/remotes/pascallanger/master
for(uint8_t i=0; i<4; i++)
packet[5+i] = 0xff; // clear aircraft id

View File

@ -192,11 +192,18 @@ static void CYRF_StartReceive()
CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, 0x10);
}
*/
<<<<<<< HEAD
static void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length)
=======
void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length)
>>>>>>> refs/remotes/pascallanger/master
{
CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, length);
}
<<<<<<< HEAD
=======
>>>>>>> refs/remotes/pascallanger/master
static void CYRF_WriteDataPacketLen(const uint8_t dpbuffer[], uint8_t len)
{
CYRF_WriteRegister(CYRF_01_TX_LENGTH, len);

View File

@ -0,0 +1,277 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(J6PRO_CYRF6936_INO)
#include "iface_cyrf6936.h"
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
//For Debug
//#define NO_SCRAMBLE
enum J6ProState {
J6PRO_BIND,
J6PRO_BIND_01,
J6PRO_BIND_03_START,
J6PRO_BIND_03_CHECK,
J6PRO_BIND_05_1,
J6PRO_BIND_05_2,
J6PRO_BIND_05_3,
J6PRO_BIND_05_4,
J6PRO_BIND_05_5,
J6PRO_BIND_05_6,
J6PRO_CHANSEL,
J6PRO_CHAN_1,
J6PRO_CHAN_2,
J6PRO_CHAN_3,
J6PRO_CHAN_4,
};
static const uint8_t J6Pro_sopcodes[][8] = {
/* Note these are in order transmitted (LSB 1st) */
{0x3C, 0x37, 0xCC, 0x91, 0xE2, 0xF8, 0xCC, 0x91},
{0x9B, 0xC5, 0xA1, 0x0F, 0xAD, 0x39, 0xA2, 0x0F},
{0xEF, 0x64, 0xB0, 0x2A, 0xD2, 0x8F, 0xB1, 0x2A},
{0x66, 0xCD, 0x7C, 0x50, 0xDD, 0x26, 0x7C, 0x50},
{0x5C, 0xE1, 0xF6, 0x44, 0xAD, 0x16, 0xF6, 0x44},
{0x5A, 0xCC, 0xAE, 0x46, 0xB6, 0x31, 0xAE, 0x46},
{0xA1, 0x78, 0xDC, 0x3C, 0x9E, 0x82, 0xDC, 0x3C},
{0xB9, 0x8E, 0x19, 0x74, 0x6F, 0x65, 0x18, 0x74},
{0xDF, 0xB1, 0xC0, 0x49, 0x62, 0xDF, 0xC1, 0x49},
{0x97, 0xE5, 0x14, 0x72, 0x7F, 0x1A, 0x14, 0x72},
{0x82, 0xC7, 0x90, 0x36, 0x21, 0x03, 0xFF, 0x17},
{0xE2, 0xF8, 0xCC, 0x91, 0x3C, 0x37, 0xCC, 0x91}, //Note: the '03' was '9E' in the Cypress recommended table
{0xAD, 0x39, 0xA2, 0x0F, 0x9B, 0xC5, 0xA1, 0x0F}, //The following are the same as the 1st 8 above,
{0xD2, 0x8F, 0xB1, 0x2A, 0xEF, 0x64, 0xB0, 0x2A}, //but with the upper and lower word swapped
{0xDD, 0x26, 0x7C, 0x50, 0x66, 0xCD, 0x7C, 0x50},
{0xAD, 0x16, 0xF6, 0x44, 0x5C, 0xE1, 0xF6, 0x44},
{0xB6, 0x31, 0xAE, 0x46, 0x5A, 0xCC, 0xAE, 0x46},
{0x9E, 0x82, 0xDC, 0x3C, 0xA1, 0x78, 0xDC, 0x3C},
{0x6F, 0x65, 0x18, 0x74, 0xB9, 0x8E, 0x19, 0x74},
};
const uint8_t bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49};
const uint8_t data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f};
static uint8_t stateJ6P;
static uint8_t radio_ch[4];
static uint8_t num_channels;
void J6Pro_build_bind_packet()
{
packet[0] = 0x01; //Packet type
packet[1] = 0x01; //FIXME: What is this? Model number maybe?
packet[2] = 0x56; //FIXME: What is this?
packet[3] = cyrfmfg_id[0];
packet[4] = cyrfmfg_id[1];
packet[5] = cyrfmfg_id[2];
packet[6] = cyrfmfg_id[3];
packet[7] = cyrfmfg_id[4];
packet[8] = cyrfmfg_id[5];
}
void J6Pro_build_data_packet()
{
uint8_t i;
uint32_t upperbits = 0;
packet[0] = 0xaa; //FIXME what is this?
for (i = 0; i < 12; i++) {
if (i >= num_channels) {
packet[i+1] = 0xff;
continue;
}
uint32_t value = (uint32_t)Servo_data[i] * 0x200 / PPM_MAX + 0x200;
if (value < 0)
value = 0;
if (value > 0x3ff)
value = 0x3ff;
packet[i+1] = value & 0xff;
upperbits |= (value >> 8) << (i * 2);
}
packet[13] = upperbits & 0xff;
packet[14] = (upperbits >> 8) & 0xff;
packet[15] = (upperbits >> 16) & 0xff;
}
static void J6Pro_cyrf_init()
{
/* Initialise CYRF chip */
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3c);
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25);
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x05 | CYRF_BIND_POWER);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x8a);
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_BIND_POWER);
CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0e);
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xee);
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x00);
CYRF_ConfigDataCode(data_code, 16);
CYRF_WritePreamble(0x023333);
}
static void J6Pro_cyrf_bindinit()
{
/* Use when binding */
//0.060470# 03 2f
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | 0x07); //Use max power for binding in case there is no telem module
CYRF_ConfigRFChannel(0x52);
CYRF_ConfigSOPCode(bind_sop_code);
CYRF_ConfigCRCSeed(0x0000);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83);
//0.061511# 13 20
CYRF_ConfigRFChannel(0x52);
//0.062684# 0f 05
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25);
//0.062792# 0f 05
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40);
J6Pro_build_bind_packet(); //01 01 e9 49 ec a9 c4 c1 ff
//CYRF_WriteDataPacketLen(packet, 0x09);
}
static void J6Pro_cyrf_datainit()
{
/* Use when already bound */
//0.094007# 0f 05
uint8_t sop_idx = (0xff & (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[5])) % 19;
uint16_t crc = (0xff & (cyrfmfg_id[1] - cyrfmfg_id[4] + cyrfmfg_id[5])) |
((0xff & (cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[4] + cyrfmfg_id[5])) << 8);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25);
CYRF_ConfigSOPCode(J6Pro_sopcodes[sop_idx]);
CYRF_ConfigCRCSeed(crc);
}
static void J6Pro_set_radio_channels()
{
//FIXME: Query free channels
//lowest channel is 0x08, upper channel is 0x4d?
CYRF_FindBestChannels(radio_ch, 3, 5, 8, 77);
radio_ch[3] = radio_ch[0];
}
static uint16_t j6pro_cb()
{
switch(stateJ6P) {
case J6PRO_BIND:
J6Pro_cyrf_bindinit();
stateJ6P = J6PRO_BIND_01;
//no break because we want to send the 1st bind packet now
case J6PRO_BIND_01:
CYRF_ConfigRFChannel(0x52);
CYRF_SetTxRxMode(TX_EN);
//0.062684# 0f 05
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25);
//0.062684# 0f 05
CYRF_WriteDataPacketLen(packet, 0x09);
stateJ6P = J6PRO_BIND_03_START;
return 3000; //3msec
case J6PRO_BIND_03_START:
{
int i = 0;
while (! (CYRF_ReadRegister(0x04) & 0x06))
if(++i > NUM_WAIT_LOOPS)
break;
}
CYRF_ConfigRFChannel(0x53);
CYRF_SetTxRxMode(RX_EN);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83);
stateJ6P = J6PRO_BIND_03_CHECK;
return 30000; //30msec
case J6PRO_BIND_03_CHECK:
{
uint8_t rx = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
if((rx & 0x1a) == 0x1a) {
rx = CYRF_ReadRegister(CYRF_0A_RX_LENGTH);
if(rx == 0x0f) {
rx = CYRF_ReadRegister(CYRF_09_RX_COUNT);
if(rx == 0x0f) {
//Expected and actual length are both 15
CYRF_ReadDataPacketLen(packet, rx);
if (packet[0] == 0x03 &&
packet[3] == cyrfmfg_id[0] &&
packet[4] == cyrfmfg_id[1] &&
packet[5] == cyrfmfg_id[2] &&
packet[6] == cyrfmfg_id[3] &&
packet[7] == cyrfmfg_id[4] &&
packet[8] == cyrfmfg_id[5])
{
//Send back Ack
packet[0] = 0x05;
CYRF_ConfigRFChannel(0x54);
CYRF_SetTxRxMode(TX_EN);
stateJ6P = J6PRO_BIND_05_1;
return 2000; //2msec
}
}
}
}
stateJ6P = J6PRO_BIND_01;
return 500;
}
case J6PRO_BIND_05_1:
case J6PRO_BIND_05_2:
case J6PRO_BIND_05_3:
case J6PRO_BIND_05_4:
case J6PRO_BIND_05_5:
case J6PRO_BIND_05_6:
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25);
CYRF_WriteDataPacketLen(packet, 0x0f);
stateJ6P = stateJ6P + 1;
return 4600; //4.6msec
case J6PRO_CHANSEL:
BIND_DONE;
J6Pro_set_radio_channels();
J6Pro_cyrf_datainit();
stateJ6P = J6PRO_CHAN_1;
case J6PRO_CHAN_1:
//Keep transmit power updated
CYRF_SetPower(CYRF_HIGH_POWER);
J6Pro_build_data_packet();
//return 3400;
case J6PRO_CHAN_2:
//return 3500;
case J6PRO_CHAN_3:
//return 3750
case J6PRO_CHAN_4:
CYRF_ConfigRFChannel(radio_ch[stateJ6P - J6PRO_CHAN_1]);
CYRF_SetTxRxMode(TX_EN);
CYRF_WriteDataPacket(packet);
if (stateJ6P == J6PRO_CHAN_4) {
stateJ6P = J6PRO_CHAN_1;
return 13900;
}
stateJ6P = stateJ6P + 1;
return 3550;
}
return 0;
}
static uint16_t j6pro_setup()
{
CYRF_Reset();
J6Pro_cyrf_init();
num_channels = 8;
if (IS_AUTOBIND_FLAG_on) {
stateJ6P = J6PRO_BIND;
BIND_IN_PROGRESS;
} else {
stateJ6P = J6PRO_CHANSEL;
}
return 2400;
}
#endif

View File

@ -0,0 +1,183 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(SKYARTEC_CC2500_INO)
#include "iface_cc2500.h"
#define TX_ADDR ((binding_idx >> 16) & 0xff)
#define TX_CHANNEL ((binding_idx >> 24) & 0xff)
enum {
SKYARTEC_PKT1 = 0,
SKYARTEC_SLEEP1,
SKYARTEC_PKT2,
SKYARTEC_SLEEP2,
SKYARTEC_PKT3,
SKYARTEC_SLEEP3,
SKYARTEC_PKT4,
SKYARTEC_SLEEP4,
SKYARTEC_PKT5,
SKYARTEC_SLEEP5,
SKYARTEC_PKT6,
SKYARTEC_LAST,
};
static void skyartec_init() {
CC2500_Reset();
cc2500_writeReg(CC2500_16_MCSM2, 0x07);
cc2500_writeReg(CC2500_17_MCSM1, 0x30);
cc2500_writeReg(CC2500_1E_WOREVT1, 0x87);
cc2500_writeReg(CC2500_1F_WOREVT0, 0x6b);
cc2500_writeReg(CC2500_20_WORCTRL, 0xf8);
cc2500_writeReg(CC2500_2A_PTEST, 0x7f);
cc2500_writeReg(CC2500_2B_AGCTEST, 0x3f);
cc2500_writeReg(CC2500_0B_FSCTRL1, 0x09);
cc2500_writeReg(CC2500_0C_FSCTRL0, 0x00);
cc2500_writeReg(CC2500_0D_FREQ2, 0x5d);
cc2500_writeReg(CC2500_0E_FREQ1, 0x93);
cc2500_writeReg(CC2500_0F_FREQ0, 0xb1);
cc2500_writeReg(CC2500_10_MDMCFG4, 0x2d);
cc2500_writeReg(CC2500_11_MDMCFG3, 0x20);
cc2500_writeReg(CC2500_12_MDMCFG2, 0x73);
cc2500_writeReg(CC2500_13_MDMCFG1, 0x22);
cc2500_writeReg(CC2500_14_MDMCFG0, 0xf8);
cc2500_writeReg(CC2500_0A_CHANNR, 0xcd);
cc2500_writeReg(CC2500_15_DEVIATN, 0x50);
cc2500_writeReg(CC2500_21_FREND1, 0xb6);
cc2500_writeReg(CC2500_22_FREND0, 0x10);
cc2500_writeReg(CC2500_18_MCSM0, 0x18);
cc2500_writeReg(CC2500_19_FOCCFG, 0x1d);
cc2500_writeReg(CC2500_1A_BSCFG, 0x1c);
cc2500_writeReg(CC2500_1B_AGCCTRL2, 0xc7);
cc2500_writeReg(CC2500_1C_AGCCTRL1, 0x00);
cc2500_writeReg(CC2500_1D_AGCCTRL0, 0xb2);
cc2500_writeReg(CC2500_23_FSCAL3, 0xea);
cc2500_writeReg(CC2500_24_FSCAL2, 0x0a);
cc2500_writeReg(CC2500_25_FSCAL1, 0x00);
cc2500_writeReg(CC2500_26_FSCAL0, 0x11);
cc2500_writeReg(CC2500_29_FSTEST, 0x59);
cc2500_writeReg(CC2500_2C_TEST2, 0x88);
cc2500_writeReg(CC2500_2D_TEST1, 0x31);
cc2500_writeReg(CC2500_2E_TEST0, 0x0b);
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x05);
cc2500_writeReg(CC2500_08_PKTCTRL0, 0x05);
cc2500_writeReg(CC2500_09_ADDR, 0x43);
cc2500_writeReg(CC2500_06_PKTLEN, 0xff);
cc2500_writeReg(CC2500_04_SYNC1, 0x13);
cc2500_writeReg(CC2500_05_SYNC0, 0x18);
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
cc2500_strobe(CC2500_SFTX);
cc2500_strobe(CC2500_SFRX);
cc2500_strobe(CC2500_SXOFF);
cc2500_strobe(CC2500_SIDLE);
}
static void add_pkt_suffix() {
int xor1 = 0;
int xor2 = 0;
for(int i = 3; i <= 16; i++) { xor1 ^= packet[i]; }
for(int i = 3; i <= 14; i++) { xor2 ^= packet[i]; }
int sum = packet[3] + packet[5] + packet[7] + packet[9] + packet[11] + packet[13];
packet[17] = xor1;
packet[18] = xor2;
packet[19] = sum & 0xff;
}
static void send_data_packet() {
//13 c5 01 0259 0168 0000 0259 030c 021a 0489 f3 7e 0a
packet[0] = 0x13; //Length
packet[1] = TX_ADDR; //Tx Addr?
packet[2] = 0x01; //???
for(int i = 0; i < 7; i++) {
uint32_t value = (uint32_t)Servo_data[i] * 0x280 / PPM_MAX + 0x280;
if(value < 0) { value = 0; }
if(value > 0x500) { value = 0x500; }
packet[3+2*i] = value >> 8;
packet[4+2*i] = value & 0xff;
}
add_pkt_suffix();
//for(int i = 0; i < 20; i++) printf("%02x ", packet[i]); printf("\n");
cc2500_writeReg(CC2500_04_SYNC1, ((binding_idx >> 0) & 0xff));
cc2500_writeReg(CC2500_05_SYNC0, ((binding_idx >> 8) & 0xff));
cc2500_writeReg(CC2500_09_ADDR, TX_ADDR);
cc2500_writeReg(CC2500_0A_CHANNR, TX_CHANNEL);
cc2500_writeFifo(packet, 20);
}
static void send_bind_packet() {
//0b 7d 01 01 b2 c5 4a 2f 00 00 c5 d6
packet[0] = 0x0b; //Length
packet[1] = 0x7d;
packet[2] = 0x01;
packet[3] = 0x01;
packet[4] = (binding_idx >> 24) & 0xff;
packet[5] = (binding_idx >> 16) & 0xff;
packet[6] = (binding_idx >> 8) & 0xff;
packet[7] = (binding_idx >> 0) & 0xff;
packet[8] = 0x00;
packet[9] = 0x00;
packet[10] = TX_ADDR;
uint8_t xore = 0;
for(int i = 3; i < 11; i++) { xore ^= packet[i]; }
packet[11] = xore;
cc2500_writeReg(CC2500_04_SYNC1, 0x7d);
cc2500_writeReg(CC2500_05_SYNC0, 0x7d);
cc2500_writeReg(CC2500_09_ADDR, 0x7d);
cc2500_writeReg(CC2500_0A_CHANNR, 0x7d);
cc2500_writeFifo(packet, 12);
}
static uint16_t skyartec_cb() {
if (state & 0x01) {
cc2500_strobe(CC2500_SIDLE);
if (state == SKYARTEC_LAST) { CC2500_SetPower(); state = SKYARTEC_PKT1; }
else { state++; }
return 3000;
}
if (state == SKYARTEC_PKT1 && bind_phase) {
send_bind_packet();
bind_phase--;
if(bind_phase == 0) { printf("Done binding\n"); }
} else { send_data_packet(); }
state++;
return 3000;
}
static uint8_t skyartec_setup() {
skyartec_init();
/* binding_idx = 0xb2c54a2f;
if (Model.fixed_id) {
binding_idx ^= Model.binding_idx + (Model.fixed_id << 16);
} else {
int partnum = CC2500_ReadReg(0xF0);
int vernum = CC2500_ReadReg(0xF1);
binding_idx ^= partnum << 24;
binding_idx ^= vernum << 16;
binding_idx ^= (vernum << 4 | partnum >> 4) << 8;
binding_idx ^= (partnum << 4 | vernum >> 4) << 8;
}
*/
binding_idx = MProtocol_id;
if (0 == (binding_idx & 0xff000000)) { binding_idx |= 0xb2; }
if (0 == (binding_idx & 0x00ff0000)) { binding_idx |= 0xc5; }
if (0 == (binding_idx & 0x0000ff00)) { binding_idx |= 0x4a; }
if (0 == (binding_idx & 0x000000ff)) { binding_idx |= 0x2f; }
bind_phase = 10000;
state = SKYARTEC_PKT1;
}
#endif

View File

@ -0,0 +1,449 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(WK2x01_CYRF6936_INO)
#include "iface_cyrf6936.h"
#define PKTS_PER_CHANNEL 4
//Fewer bind packets in the emulator so we can get right to the important bits
#define WK_BIND_COUNT 2980
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
#define WK_BIND 0
#define WK_BOUND_1 1
#define WK_BOUND_2 2
#define WK_BOUND_3 3
#define WK_BOUND_4 4
#define WK_BOUND_5 5
#define WK_BOUND_6 6
#define WK_BOUND_7 7
#define WK_BOUND_8 8
static const uint8_t sopcode[8] = {
/* Note these are in order transmitted (LSB 1st) */
0xDF,0xB1,0xC0,0x49,0x62,0xDF,0xC1,0x49 //0x49C1DF6249C0B1DF
};
static const uint8_t fail_map[8] = {2, 1, 0, 3, 4, 5, 6, 7};
static uint8_t wk_pkt_num;
static uint8_t *radio_ch_ptr;
static uint16_t WK_BIND_COUNTer;
static uint8_t last_beacon;
/*
static const char * const wk2601_opts[] = {
_tr_noop("Chan mode"), _tr_noop("5+1"), _tr_noop("Heli"), _tr_noop("6+1"), NULL,
_tr_noop("COL Inv"), _tr_noop("Normal"), _tr_noop("Inverted"), NULL,
_tr_noop("COL Limit"), "-100", "100", NULL,
NULL
};
#define WK2601_OPT_CHANMODE 0
#define WK2601_OPT_PIT_INV 1
#define WK2601_OPT_PIT_LIMIT 2
#define LAST_PROTO_OPT 3
*/
static void add_pkt_crc(uint8_t init) {
uint8_t add = init;
uint8_t xou = init;
int i;
for (i = 0; i < 14; i++) { add += packet[i]; xou ^= packet[i]; }
packet[14] = xou;
packet[15] = add & 0xff;
}
static const char init_2801[] = {0xc5, 0x34, 0x60, 0x00, 0x25};
static const char init_2601[] = {0xb9, 0x45, 0xb0, 0xf1, 0x3a};
static const char init_2401[] = {0xa5, 0x23, 0xd0, 0xf0, 0x00};
static void build_bind_pkt(const char *init) {
packet[0] = init[0];
packet[1] = init[1];
packet[2] = rx_tx_addr[0];
packet[3] = rx_tx_addr[1];
packet[4] = init[2];
packet[5] = rx_tx_addr[2];
packet[6] = 0xff;
packet[7] = 0x00;
packet[8] = 0x00;
packet[9] = 0x32;
if (sub_protocol == WK2401) { packet[10] = 0x10 | ((fixed_id >> 0) & 0x0e); }
else { packet[10] = (fixed_id >> 0) & 0xff; }
packet[11] = (fixed_id >> 8) & 0xff;
packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num;
packet[13] = init[3];
add_pkt_crc(init[4]);
}
static uint16_t get_channel(uint8_t ch, uint32_t scale, uint32_t center, uint32_t range) {
uint32_t value = (uint32_t)Servo_data[ch] * scale / PPM_MAX + center;
if (value < center - range) { value = center - range; }
if (value > center + range) { value = center + range; }
return value;
}
static void build_data_pkt_2401() {
uint8_t i;
uint16_t msb = 0;
uint8_t offset = 0;
for (i = 0; i < 4; i++) {
if (i == 2) { offset = 1; }
uint16_t value = get_channel(i, 0x800, 0, 0xA00); //12 bits, allow value to go to 125%
uint16_t base = abs(value) >> 2; //10 bits is the base value
uint16_t trim = abs(value) & 0x03; //lowest 2 bits represent trim
if (base >= 0x200) { //if value is > 100%, remainder goes to trim
trim = 4 *(base - 0x200);
base = 0x1ff;
}
base = (value >= 0) ? 0x200 + base : 0x200 - base;
trim = (value >= 0) ? 0x200 + trim : 0x200 - trim;
packet[2*i+offset] = base & 0xff;
packet[2*i+offset+1] = trim & 0xff;
msb = (msb << 4) | ((base >> 6) & 0x0c) | ((trim >> 8) & 0x03);
}
packet[4] = msb >> 8; //Ele/Ail MSB
packet[9] = msb & 0xff; //Thr/Rud MSB
packet[10] = 0xe0 | ((fixed_id >> 0) & 0x0e);
packet[11] = (fixed_id >> 8) & 0xff;
packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num;
packet[13] = 0xf0; //FIXME - What is this?
add_pkt_crc(0x00);
}
#define PCT(pct, max) (((max) * (pct) + 1L) / 1000)
#define MAXTHR 426 //Measured to provide equal value at +/-0
static void channels_6plus1_2601(int frame, int *_v1, int *_v2) {
uint16_t thr = get_channel(2, 1000, 0, 1000);
int v1;
int thr_rev = 0, pitch_rev = 0;
if(thr > 0) {
if(thr >= 780) { //78%
v1 = 0; //thr = 60% * (x - 78%) / 22% + 40%
thr = PCT(1000-MAXTHR,512) * (thr-PCT(780,1000)) / PCT(220,1000) + PCT(MAXTHR,512);
} else {
v1 = 1023 - 1023 * thr / 780;
thr = PCT(MAXTHR, 512); //40%
}
}
else {
thr = -thr;
thr_rev = 1;
if(thr >= 780) { //78%
v1 = 1023; //thr = 60% * (x - 78%) / 22% + 40%
thr = PCT(1000-MAXTHR,512) * (thr-PCT(780,1000)) / PCT(220,1000) + PCT(MAXTHR,512);
if (thr >= 512) { thr = 511; }
}
else {
v1 = 1023 * thr / 780;
thr = PCT(MAXTHR, 512); //40%
}
}
if (thr >= 512) { thr = 511; }
packet[2] = thr & 0xff;
packet[4] = (packet[4] & 0xF3) | ((thr >> 6) & 0x04);
uint16_t pitch= get_channel(5, 0x400, 0, 0x400);
if (pitch < 0) {
pitch_rev = 1;
pitch = -pitch;
}
if (frame == 1) {
//Pitch curve and range
if (thr > PCT(MAXTHR, 512)) { *_v2 = pitch - pitch * 16 * (thr - PCT(MAXTHR, 512)) / PCT(1000 - MAXTHR, 512) / 100; }
else { *_v2 = pitch; }
*_v1 = 0;
}
else if (frame == 2) {
//Throttle curve & Expo
*_v1 = v1;
*_v2 = 512;
}
packet[7] = (thr_rev << 5) | (pitch_rev << 2); //reverse bits
packet[8] = 0;
}
static void channels_5plus1_2601(int frame, int *v1, int *v2) {
(void)v1;
//Zero out pitch, provide ail, ele, thr, rud, gyr + gear
if (frame == 1) { *v2 = 0; } //Pitch curve and range
packet[7] = 0;
packet[8] = 0;
}
static void channels_heli_2601(int frame, int *v1, int *v2) {
(void)frame;
//pitch is controlled by rx
//we can only control fmode, pit-reverse and pit/thr rate
int pit_rev = 0;
if ((option/10)%10) { pit_rev = 1; }
uint16_t pit_rate = get_channel(5, 0x400, 0, 0x400);
int fmode = 1;
if (pit_rate < 0) { pit_rate = -pit_rate; fmode = 0; }
if (frame == 1) {
//Pitch curve and range
*v1 = pit_rate;
*v2 = ((option/100) ? -100 : 100) * 0x400 / 100 + 0x400;
}
packet[7] = (pit_rev << 2); //reverse bits
packet[8] = fmode ? 0x02 : 0x00;
}
static void build_data_pkt_2601() {
uint8_t i;
uint8_t msb = 0;
uint8_t frame = (wk_pkt_num % 3);
for (i = 0; i < 4; i++) {
uint16_t value = get_channel(i, 0x190, 0, 0x1FF);
uint16_t mag = value < 0 ? -value : value;
packet[i] = mag & 0xff;
msb = (msb << 2) | ((mag >> 8) & 0x01) | (value < 0 ? 0x02 : 0x00);
}
packet[4] = msb;
int v1 = 0x200, v2 = 0x200;
if (frame == 0) {
//Gyro & Rudder mix
v1 = get_channel(6, 0x200, 0x200, 0x200);
v2 = 0;
}
if (option%10 == 1) { channels_heli_2601(frame, &v1, &v2); }
else if (option%10 == 2) { channels_6plus1_2601(frame, &v1, &v2); }
else { channels_5plus1_2601(frame, &v1, &v2); }
if (v1 > 1023) { v1 = 1023; }
if (v2 > 1023) { v2 = 1023; }
packet[5] = v2 & 0xff;
packet[6] = v1 & 0xff;
//packet[7] handled by channel code
packet[8] |= (get_channel(4, 0x190, 0, 0x1FF) > 0 ? 1 : 0);
packet[9] = ((v1 >> 4) & 0x30) | ((v2 >> 2) & 0xc0) | 0x04 | frame;
packet[10] = (fixed_id >> 0) & 0xff;
packet[11] = (fixed_id >> 8) & 0xff;
packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num;
packet[13] = 0xff;
add_pkt_crc(0x3A);
}
static void build_data_pkt_2801() {
uint8_t i;
uint16_t msb = 0;
uint8_t offset = 0;
uint8_t sign = 0;
for (i = 0; i < 8; i++) {
if (i == 4) { offset = 1; }
uint16_t value = get_channel(i, 0x190, 0, 0x3FF);
uint16_t mag = value < 0 ? -value : value;
packet[i+offset] = mag & 0xff;
msb = (msb << 2) | ((mag >> 8) & 0x03);
if (value < 0) { sign |= 1 << i; }
}
packet[4] = msb >> 8;
packet[9] = msb & 0xff;
packet[10] = (fixed_id >> 0) & 0xff;
packet[11] = (fixed_id >> 8) & 0xff;
packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num;
packet[13] = sign;
add_pkt_crc(0x25);
}
static void build_beacon_pkt_2801() {
last_beacon ^= 1;
uint8_t i;
uint8_t en = 0;
uint8_t bind_state;
if (WK_BIND_COUNTer) { bind_state = 0xe4; }
else { bind_state = 0x1b; }
for (i = 0; i < 4; i++) {
/* if (Model.limits[fail_map[i + last_beacon * 4]].flags & CH_FAILSAFE_EN) {
uint32_t value = Model.limits[fail_map[i + last_beacon * 4]].failsafe + 128;
if (value > 255) { value = 255; }
if (value < 0) { value = 0; }
packet[i+1] = value;
en |= 1 << i;
} else
*/ { packet[i+1] = 0; }
}
packet[0] = en;
packet[5] = packet[4];
packet[4] = last_beacon << 6;
packet[6] = rx_tx_addr[0];
packet[7] = rx_tx_addr[1];
packet[8] = rx_tx_addr[2];
packet[9] = bind_state;
packet[10] = (fixed_id >> 0) & 0xff;
packet[11] = (fixed_id >> 8) & 0xff;
packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num;
packet[13] = 0x00; //Does this matter? in the docs it is the same as the data packet
add_pkt_crc(0x1C);
}
static void wk2x01_cyrf_init() {
/* Initialise CYRF chip */
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4A);
CYRF_WriteRegister(CYRF_0B_PWR_CTRL, 0x00);
CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0);
CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C);
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xEE);
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x18);
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C);
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x90);
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
CYRF_WriteRegister(CYRF_01_TX_LENGTH, 0x10);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C);
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
CYRF_WriteRegister(CYRF_27_CLK_OVERRIDE, 0x02);
CYRF_ConfigSOPCode(sopcode);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x28);
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x10);
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL, 0x20);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C);
}
void WK_BuildPacket_2801() {
switch(phase) {
case WK_BIND:
build_bind_pkt(init_2801);
// if ((--WK_BIND_COUNTer == 0) || PROTOCOL_SticksMoved(0)) {
if ((--WK_BIND_COUNTer == 0)) {
WK_BIND_COUNTer = 0;
BIND_DONE;
phase = WK_BOUND_1;
}
break;
case WK_BOUND_1:
case WK_BOUND_2:
case WK_BOUND_3:
case WK_BOUND_4:
case WK_BOUND_5:
case WK_BOUND_6:
case WK_BOUND_7:
build_data_pkt_2801();
phase++;
break;
case WK_BOUND_8:
build_beacon_pkt_2801();
phase = WK_BOUND_1;
if (WK_BIND_COUNTer) {
WK_BIND_COUNTer--;
if (WK_BIND_COUNTer == 0) { BIND_DONE; }
}
break;
}
wk_pkt_num = (wk_pkt_num + 1) % 12;
}
void WK_BuildPacket_2601() {
if (WK_BIND_COUNTer) {
WK_BIND_COUNTer--;
build_bind_pkt(init_2601);
if ((WK_BIND_COUNTer == 0)) {
WK_BIND_COUNTer = 0;
BIND_DONE;
}
}
else { build_data_pkt_2601(); }
wk_pkt_num = (wk_pkt_num + 1) % 12;
}
void WK_BuildPacket_2401() {
if (WK_BIND_COUNTer) {
WK_BIND_COUNTer--;
build_bind_pkt(init_2401);
if ((WK_BIND_COUNTer == 0)) {
WK_BIND_COUNTer = 0;
BIND_DONE;
}
}
else { build_data_pkt_2401(); }
wk_pkt_num = (wk_pkt_num + 1) % 12;
}
static uint16_t wk_cb() {
if (packet_sent == 0) {
packet_sent = 1;
if(sub_protocol == WK2801) { WK_BuildPacket_2801(); }
else if(sub_protocol == WK2601) { WK_BuildPacket_2601(); }
else if(sub_protocol == WK2401) { WK_BuildPacket_2401(); }
CYRF_WriteDataPacket(packet);
return 1600;
}
packet_sent = 0;
int i = 0;
while (! (CYRF_ReadRegister(0x04) & 0x02)) { if(++i > NUM_WAIT_LOOPS) { break; } }
if((wk_pkt_num & 0x03) == 0) {
radio_ch_ptr = radio_ch_ptr == &rx_tx_addr[2] ? rx_tx_addr : radio_ch_ptr + 1;
CYRF_ConfigRFChannel(*radio_ch_ptr);
//Keep transmit power updated
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER);
}
return 1200;
}
static void wk_bind() {
if((sub_protocol != WK2801)) { return; }
fixed_id = ((MProtocol_id_master << 2) & 0x0ffc00) | ((MProtocol_id_master >> 10) & 0x000300) | ((MProtocol_id_master) & 0x0000ff);
WK_BIND_COUNTer = WK_BIND_COUNT / 8 + 1;
BIND_IN_PROGRESS;
}
static uint16_t wk_setup() {
CYRF_Reset();
wk2x01_cyrf_init();
CYRF_SetTxRxMode(TX_EN);
CYRF_FindBestChannels(rx_tx_addr, 3, 4, 4, 80);
radio_ch_ptr = rx_tx_addr;
CYRF_ConfigRFChannel(*radio_ch_ptr);
wk_pkt_num = 0;
packet_sent = 0;
last_beacon = 0;
fixed_id = ((MProtocol_id_master << 2) & 0x0ffc00) | ((MProtocol_id_master >> 10) & 0x000300) | ((MProtocol_id_master) & 0x0000ff);
if (sub_protocol == WK2401) { fixed_id |= 0x01; } //Fixed ID must be odd for 2401
if(sub_protocol != WK2801) {
WK_BIND_COUNTer = WK_BIND_COUNT;
phase = WK_BIND;
BIND_IN_PROGRESS;
}
else {
phase = WK_BOUND_1;
WK_BIND_COUNTer = 0;
}
CYRF_ConfigRFChannel(*radio_ch_ptr);
return 2800;
}
/*
const void *WK2x01_Cmds(enum ProtoCmds cmd) {
switch(cmd) {
case PROTOCMD_INIT: initialize(); return 0;
case PROTOCMD_DEINIT: return 0;
case PROTOCMD_CHECK_AUTOBIND:
return (Model.protocol == WK2801 && Model.fixed_id) ? 0 : (void *)1L;
case PROTOCMD_BIND: wk_bind(); return 0;
case PROTOCMD_DEFAULT_NUMCHAN: return (Model.protocol == WK2801)
? (void *)8L
: (Model.protocol == WK2601)
? (void *)6L
: (void *)4L;
case PROTOCMD_NUMCHAN: return (Model.protocol == WK2801)
? (void *)8L
: (Model.protocol == WK2601)
? (void *)7L
: (void *)4L;
case PROTOCMD_GETOPTIONS:
if(Model.protocol == WK2601)
return wk2601_opts;
break;
case PROTOCMD_TELEMETRYSTATE: return (void *)(long)PROTO_TELEM_UNSUPPORTED;
default: break;
}
return 0;
}
*/
#endif

View File

@ -0,0 +1,538 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(DSM2_CYRF6936_INO)
#include "iface_cyrf6936.h"
#define RANDOM_CHANNELS 0 // disabled
//#define RANDOM_CHANNELS 1 // enabled
#define BIND_CHANNEL 0x0d //13 This can be any odd channel
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
//During binding we will send BIND_COUNT/2 packets
//One packet each 10msec
#define BIND_COUNT1 600
enum {
DSM2_BIND = 0,
DSM2_CHANSEL = BIND_COUNT1 + 0,
DSM2_CH1_WRITE_A = BIND_COUNT1 + 1,
DSM2_CH1_CHECK_A = BIND_COUNT1 + 2,
DSM2_CH2_WRITE_A = BIND_COUNT1 + 3,
DSM2_CH2_CHECK_A = BIND_COUNT1 + 4,
DSM2_CH2_READ_A = BIND_COUNT1 + 5,
DSM2_CH1_WRITE_B = BIND_COUNT1 + 6,
DSM2_CH1_CHECK_B = BIND_COUNT1 + 7,
DSM2_CH2_WRITE_B = BIND_COUNT1 + 8,
DSM2_CH2_CHECK_B = BIND_COUNT1 + 9,
DSM2_CH2_READ_B = BIND_COUNT1 + 10,
};
const uint8_t PROGMEM pncodes[5][9][8] = {
/* Note these are in order transmitted (LSB 1st) */
{ /* Row 0 */
/* Col 0 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8},
/* Col 1 */ {0x88, 0x17, 0x13, 0x3B, 0x2D, 0xBF, 0x06, 0xD6},
/* Col 2 */ {0xF1, 0x94, 0x30, 0x21, 0xA1, 0x1C, 0x88, 0xA9},
/* Col 3 */ {0xD0, 0xD2, 0x8E, 0xBC, 0x82, 0x2F, 0xE3, 0xB4},
/* Col 4 */ {0x8C, 0xFA, 0x47, 0x9B, 0x83, 0xA5, 0x66, 0xD0},
/* Col 5 */ {0x07, 0xBD, 0x9F, 0x26, 0xC8, 0x31, 0x0F, 0xB8},
/* Col 6 */ {0xEF, 0x03, 0x95, 0x89, 0xB4, 0x71, 0x61, 0x9D},
/* Col 7 */ {0x40, 0xBA, 0x97, 0xD5, 0x86, 0x4F, 0xCC, 0xD1},
/* Col 8 */ {0xD7, 0xA1, 0x54, 0xB1, 0x5E, 0x89, 0xAE, 0x86}
},
{ /* Row 1 */
/* Col 0 */ {0x83, 0xF7, 0xA8, 0x2D, 0x7A, 0x44, 0x64, 0xD3},
/* Col 1 */ {0x3F, 0x2C, 0x4E, 0xAA, 0x71, 0x48, 0x7A, 0xC9},
/* Col 2 */ {0x17, 0xFF, 0x9E, 0x21, 0x36, 0x90, 0xC7, 0x82},
/* Col 3 */ {0xBC, 0x5D, 0x9A, 0x5B, 0xEE, 0x7F, 0x42, 0xEB},
/* Col 4 */ {0x24, 0xF5, 0xDD, 0xF8, 0x7A, 0x77, 0x74, 0xE7},
/* Col 5 */ {0x3D, 0x70, 0x7C, 0x94, 0xDC, 0x84, 0xAD, 0x95},
/* Col 6 */ {0x1E, 0x6A, 0xF0, 0x37, 0x52, 0x7B, 0x11, 0xD4},
/* Col 7 */ {0x62, 0xF5, 0x2B, 0xAA, 0xFC, 0x33, 0xBF, 0xAF},
/* Col 8 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97}
},
{ /* Row 2 */
/* Col 0 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97},
/* Col 1 */ {0x8E, 0x4A, 0xD0, 0xA9, 0xA7, 0xFF, 0x20, 0xCA},
/* Col 2 */ {0x4C, 0x97, 0x9D, 0xBF, 0xB8, 0x3D, 0xB5, 0xBE},
/* Col 3 */ {0x0C, 0x5D, 0x24, 0x30, 0x9F, 0xCA, 0x6D, 0xBD},
/* Col 4 */ {0x50, 0x14, 0x33, 0xDE, 0xF1, 0x78, 0x95, 0xAD},
/* Col 5 */ {0x0C, 0x3C, 0xFA, 0xF9, 0xF0, 0xF2, 0x10, 0xC9},
/* Col 6 */ {0xF4, 0xDA, 0x06, 0xDB, 0xBF, 0x4E, 0x6F, 0xB3},
/* Col 7 */ {0x9E, 0x08, 0xD1, 0xAE, 0x59, 0x5E, 0xE8, 0xF0},
/* Col 8 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E}
},
{ /* Row 3 */
/* Col 0 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E},
/* Col 1 */ {0x80, 0x69, 0x26, 0x80, 0x08, 0xF8, 0x49, 0xE7},
/* Col 2 */ {0x7D, 0x2D, 0x49, 0x54, 0xD0, 0x80, 0x40, 0xC1},
/* Col 3 */ {0xB6, 0xF2, 0xE6, 0x1B, 0x80, 0x5A, 0x36, 0xB4},
/* Col 4 */ {0x42, 0xAE, 0x9C, 0x1C, 0xDA, 0x67, 0x05, 0xF6},
/* Col 5 */ {0x9B, 0x75, 0xF7, 0xE0, 0x14, 0x8D, 0xB5, 0x80},
/* Col 6 */ {0xBF, 0x54, 0x98, 0xB9, 0xB7, 0x30, 0x5A, 0x88},
/* Col 7 */ {0x35, 0xD1, 0xFC, 0x97, 0x23, 0xD4, 0xC9, 0x88},
/* Col 8 */ {0x88, 0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40}
},
{ /* Row 4 */
/* Col 0 */ {0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40, 0x93},
/* Col 1 */ {0xDC, 0x68, 0x08, 0x99, 0x97, 0xAE, 0xAF, 0x8C},
/* Col 2 */ {0xC3, 0x0E, 0x01, 0x16, 0x0E, 0x32, 0x06, 0xBA},
/* Col 3 */ {0xE0, 0x83, 0x01, 0xFA, 0xAB, 0x3E, 0x8F, 0xAC},
/* Col 4 */ {0x5C, 0xD5, 0x9C, 0xB8, 0x46, 0x9C, 0x7D, 0x84},
/* Col 5 */ {0xF1, 0xC6, 0xFE, 0x5C, 0x9D, 0xA5, 0x4F, 0xB7},
/* Col 6 */ {0x58, 0xB5, 0xB3, 0xDD, 0x0E, 0x28, 0xF1, 0xB0},
/* Col 7 */ {0x5F, 0x30, 0x3B, 0x56, 0x96, 0x45, 0xF4, 0xA1},
/* Col 8 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8}
},
};
static void __attribute__((unused)) read_code(uint8_t *buf, uint8_t row, uint8_t col, uint8_t len)
{
for(uint8_t i=0;i<len;i++)
buf[i]=pgm_read_byte_near( &pncodes[row][col][i] );
}
//
uint8_t chidx;
uint8_t sop_col;
uint8_t data_col;
uint16_t cyrf_state;
uint8_t crcidx;
uint8_t binding;
static void __attribute__((unused)) build_bind_packet()
{
uint8_t i;
uint16_t sum = 384 - 0x10;//
packet[0] = crc >> 8;
packet[1] = crc & 0xff;
packet[2] = 0xff ^ cyrfmfg_id[2];
packet[3] = (0xff ^ cyrfmfg_id[3]) + RX_num;
packet[4] = packet[0];
packet[5] = packet[1];
packet[6] = packet[2];
packet[7] = packet[3];
for(i = 0; i < 8; i++)
sum += packet[i];
packet[8] = sum >> 8;
packet[9] = sum & 0xff;
packet[10] = 0x01; //???
packet[11] = option>3?option:option+4;
if(sub_protocol==DSMX) //DSMX type
packet[12] = 0xb2; // Telemetry off: packet[12] = num_channels < 8 && Model.proto_opts[PROTOOPTS_TELEMETRY] == TELEM_OFF ? 0xa2 : 0xb2;
else
packet[12] = option<8?0x01:0x02;
packet[13] = 0x00; //???
for(i = 8; i < 14; i++)
sum += packet[i];
packet[14] = sum >> 8;
packet[15] = sum & 0xff;
}
static uint8_t __attribute__((unused)) PROTOCOL_SticksMoved(uint8_t init)
{
#define STICK_MOVEMENT 15*(PPM_MAX-PPM_MIN)/100 // defines when the bind dialog should be interrupted (stick movement STICK_MOVEMENT %)
static uint16_t ele_start, ail_start;
uint16_t ele = Servo_data[ELEVATOR];//CHAN_ReadInput(MIXER_MapChannel(INP_ELEVATOR));
uint16_t ail = Servo_data[AILERON];//CHAN_ReadInput(MIXER_MapChannel(INP_AILERON));
if(init) {
ele_start = ele;
ail_start = ail;
return 0;
}
uint16_t ele_diff = ele_start - ele;//abs(ele_start - ele);
uint16_t ail_diff = ail_start - ail;//abs(ail_start - ail);
return ((ele_diff + ail_diff) > STICK_MOVEMENT);//
}
static void __attribute__((unused)) build_data_packet(uint8_t upper)//
{
uint8_t i;
uint8_t bits;
uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //9 Channels - DM9 TX
switch(option>3?option:option+4) // Create channel map based on number of channels
{
case 12:
ch_map[11]=11; // 12 channels
case 11:
ch_map[10]=10; // 11 channels
case 10:
ch_map[9]=9; // 10 channels
break;
case 8:
memcpy(ch_map,"\x01\x05\x02\x03\x06\xFF\xFF\x04\x00\x07",10); // 8 channels - DX8 TX
break;
case 7:
memcpy(ch_map,"\x01\x05\x02\x04\x03\x06\x00",7); // 7 channels - DX6i TX
break;
case 6:
memcpy(ch_map,"\x01\x05\x02\x03\x00\x04\xFF",7); // 6 channels - HP6DSM TX
break;
case 4:
case 5:
memcpy(ch_map,"\x00\x01\x02\x03\xFF\xFF\xFF",7); // 4 channels - Guess
if(option&0x01)
ch_map[4]=4; // 5 channels - Guess
break;
}
//
if( binding && PROTOCOL_SticksMoved(0) )
binding = 0;
if (sub_protocol==DSMX)
{
packet[0] = cyrfmfg_id[2];
packet[1] = cyrfmfg_id[3] + RX_num;
bits=11;
}
else
{
packet[0] = (0xff ^ cyrfmfg_id[2]);
packet[1] = (0xff ^ cyrfmfg_id[3]) + RX_num;
bits=10;
}
//
uint16_t max = 1 << bits;//max=2048 for DSMX & 1024 for DSM2 less than 8 ch and 2048 otherwise
//uint16_t pct_100 = (uint32_t)max * 100 / 150;//682 1024*100/150
//
for (i = 0; i < 7; i++)
{
uint8_t idx = ch_map[upper * 7 + i];//1,5,2,3,0,4
uint16_t value;
if (idx == 0xff)
value = 0xffff;
else
{
if (binding)
{ // Failsafe position during binding
value=max/2; //all channels to middle
if(idx==0)
value=1; //except throttle
}
else
{
switch(idx)
{
case 0:
value=Servo_data[THROTTLE];//85.75-938.25=125%//171-853=100%
break;
case 1:
value=Servo_data[AILERON];
break;
case 2:
value=Servo_data[ELEVATOR];
break;
case 3:
value=Servo_data[RUDDER];
break;
case 4:
value=Servo_data[AUX1];
break;
case 5:
value=Servo_data[AUX2];
break;
case 6:
value=Servo_data[AUX3];
break;
case 7:
value=Servo_data[AUX4];
break;
}
value=map(value,PPM_MIN,PPM_MAX,0,max-1);
}
value |= (upper && i == 0 ? 0x8000 : 0) | (idx << bits);
}
packet[i*2+2] = (value >> 8) & 0xff;
packet[i*2+3] = (value >> 0) & 0xff;
}
}
static uint8_t __attribute__((unused)) get_pn_row(uint8_t channel)
{
return (sub_protocol == DSMX ? (channel - 2) % 5 : channel % 5);
}
const uint8_t init_vals[][2] = {
{CYRF_02_TX_CTRL, 0x00},
{CYRF_05_RX_CTRL, 0x00},
{CYRF_28_CLK_EN, 0x02},
{CYRF_32_AUTO_CAL_TIME, 0x3c},
{CYRF_35_AUTOCAL_OFFSET, 0x14},
{CYRF_06_RX_CFG, 0x4A},
{CYRF_1B_TX_OFFSET_LSB, 0x55},
{CYRF_1C_TX_OFFSET_MSB, 0x05},
{CYRF_0F_XACT_CFG, 0x24}, // Force Idle
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER}, //Set 64chip, SDR mode
{CYRF_12_DATA64_THOLD, 0x0a},
{CYRF_0F_XACT_CFG, 0x04}, // Idle
{CYRF_39_ANALOG_CTRL, 0x01},
{CYRF_0F_XACT_CFG, 0x24}, //Force IDLE
{CYRF_29_RX_ABORT, 0x00}, //Clear RX abort
{CYRF_12_DATA64_THOLD, 0x0a}, //set pn correlation threshold
{CYRF_10_FRAMING_CFG, 0x4a}, //set sop len and threshold
{CYRF_29_RX_ABORT, 0x0f}, //Clear RX abort?
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER}, //Set 64chip, SDR mode
{CYRF_10_FRAMING_CFG, 0x4a}, //set sop len and threshold
{CYRF_1F_TX_OVERRIDE, 0x04}, //disable tx CRC
{CYRF_1E_RX_OVERRIDE, 0x14}, //disable rx crc
{CYRF_14_EOP_CTRL, 0x02}, //set EOP sync == 2
{CYRF_01_TX_LENGTH, 0x10}, //16byte packet
};
static void __attribute__((unused)) cyrf_config()
{
for(uint8_t i = 0; i < sizeof(init_vals) / 2; i++)
CYRF_WriteRegister(init_vals[i][0], init_vals[i][1]);
CYRF_WritePreamble(0x333304);
CYRF_ConfigRFChannel(0x61);
}
static void __attribute__((unused)) initialize_bind_state()
{
uint8_t code[32];
CYRF_ConfigRFChannel(BIND_CHANNEL); //This seems to be random?
uint8_t pn_row = get_pn_row(BIND_CHANNEL);
//printf("Ch: %d Row: %d SOP: %d Data: %d\n", BIND_CHANNEL, pn_row, sop_col, data_col);
CYRF_ConfigCRCSeed(crc);
read_code(code,pn_row,sop_col,8);
CYRF_ConfigSOPCode(code);
read_code(code,pn_row,data_col,16);
read_code(code+16,0,8,8);
memcpy(code + 24, "\xc6\x94\x22\xfe\x48\xe6\x57\x4e", 8);
CYRF_ConfigDataCode(code, 32);
build_bind_packet();
}
const uint8_t data_vals[][2] = {
{CYRF_05_RX_CTRL, 0x83}, //Initialize for reading RSSI
{CYRF_29_RX_ABORT, 0x20},
{CYRF_0F_XACT_CFG, 0x24},
{CYRF_29_RX_ABORT, 0x00},
{CYRF_03_TX_CFG, 0x08 | CYRF_HIGH_POWER},
{CYRF_10_FRAMING_CFG, 0xea},
{CYRF_1F_TX_OVERRIDE, 0x00},
{CYRF_1E_RX_OVERRIDE, 0x00},
{CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER},
{CYRF_12_DATA64_THOLD, 0x3f},
{CYRF_10_FRAMING_CFG, 0xff},
{CYRF_0F_XACT_CFG, 0x24}, //Switch from reading RSSI to Writing
{CYRF_29_RX_ABORT, 0x00},
{CYRF_12_DATA64_THOLD, 0x0a},
{CYRF_10_FRAMING_CFG, 0xea},
};
static void __attribute__((unused)) cyrf_configdata()
{
for(uint8_t i = 0; i < sizeof(data_vals) / 2; i++)
CYRF_WriteRegister(data_vals[i][0], data_vals[i][1]);
}
static void __attribute__((unused)) set_sop_data_crc()
{
uint8_t code[16];
uint8_t pn_row = get_pn_row(hopping_frequency[chidx]);
//printf("Ch: %d Row: %d SOP: %d Data: %d\n", ch[chidx], pn_row, sop_col, data_col);
CYRF_ConfigRFChannel(hopping_frequency[chidx]);
CYRF_ConfigCRCSeed(crcidx ? ~crc : crc);
read_code(code,pn_row,sop_col,8);
CYRF_ConfigSOPCode(code);
read_code(code,pn_row,data_col,16);
CYRF_ConfigDataCode(code, 16);
if(sub_protocol == DSMX)
chidx = (chidx + 1) % 23;
else
chidx = (chidx + 1) % 2;
crcidx = !crcidx;
}
static void __attribute__((unused)) calc_dsmx_channel()
{
uint8_t idx = 0;
uint32_t id = ~(((uint32_t)cyrfmfg_id[0] << 24) | ((uint32_t)cyrfmfg_id[1] << 16) | ((uint32_t)cyrfmfg_id[2] << 8) | (cyrfmfg_id[3] << 0));
uint32_t id_tmp = id;
while(idx < 23)
{
uint8_t i;
uint8_t count_3_27 = 0, count_28_51 = 0, count_52_76 = 0;
id_tmp = id_tmp * 0x0019660D + 0x3C6EF35F; // Randomization
uint8_t next_ch = ((id_tmp >> 8) % 0x49) + 3; // Use least-significant byte and must be larger than 3
if (((next_ch ^ id) & 0x01 )== 0)
continue;
for (i = 0; i < idx; i++)
{
if(hopping_frequency[i] == next_ch)
break;
if(hopping_frequency[i] <= 27)
count_3_27++;
else
if (hopping_frequency[i] <= 51)
count_28_51++;
else
count_52_76++;
}
if (i != idx)
continue;
if ((next_ch < 28 && count_3_27 < 8)
||(next_ch >= 28 && next_ch < 52 && count_28_51 < 7)
||(next_ch >= 52 && count_52_76 < 8))
hopping_frequency[idx++] = next_ch;
}
}
uint16_t ReadDsm2()
{
#define CH1_CH2_DELAY 4010 // Time between write of channel 1 and channel 2
#define WRITE_DELAY 1650 // 1550 original, Time after write to verify write complete
#define READ_DELAY 400 // Time before write to check read state, and switch channels
uint8_t i = 0;
switch(cyrf_state)
{
default:
//Binding
cyrf_state++;
if(cyrf_state & 1)
{
//Send packet on even states
//Note state has already incremented,
// so this is actually 'even' state
CYRF_WriteDataPacket(packet);
return 8500;
}
else
{
//Check status on odd states
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS);
return 1500;
}
case DSM2_CHANSEL:
BIND_DONE;
//Select channels and configure for writing data
//CYRF_FindBestChannels(ch, 2, 10, 1, 79);
cyrf_configdata();
CYRF_SetTxRxMode(TX_EN);
chidx = 0;
crcidx = 0;
cyrf_state = DSM2_CH1_WRITE_A; // in fact cyrf_state++
set_sop_data_crc();
return 10000;
case DSM2_CH1_WRITE_A:
case DSM2_CH1_WRITE_B:
build_data_packet(cyrf_state == DSM2_CH1_WRITE_B);//compare state and DSM2_CH1_WRITE_B return 0 or 1
case DSM2_CH2_WRITE_A:
case DSM2_CH2_WRITE_B:
CYRF_WriteDataPacket(packet);
cyrf_state++; // change from WRITE to CHECK mode
return WRITE_DELAY;
case DSM2_CH1_CHECK_A:
case DSM2_CH1_CHECK_B:
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
if(++i > NUM_WAIT_LOOPS)
break;
set_sop_data_crc();
cyrf_state++; // change from CH1_CHECK to CH2_WRITE
return CH1_CH2_DELAY - WRITE_DELAY;
case DSM2_CH2_CHECK_A:
case DSM2_CH2_CHECK_B:
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
if(++i > NUM_WAIT_LOOPS)
break;
if (cyrf_state == DSM2_CH2_CHECK_A)
CYRF_SetPower(0x28); //Keep transmit power in sync
// No telemetry...
set_sop_data_crc();
if (cyrf_state == DSM2_CH2_CHECK_A)
{
if(option < 8)
{
cyrf_state = DSM2_CH1_WRITE_A; // change from CH2_CHECK_A to CH1_WRITE_A (ie no upper)
if(option>3)
return 11000 - CH1_CH2_DELAY - WRITE_DELAY ; // force 11ms if option>3 ie 4,5,6,7 channels @11ms
else
return 22000 - CH1_CH2_DELAY - WRITE_DELAY ; // normal 22ms mode if option<=3 ie 4,5,6,7 channels @22ms
}
else
cyrf_state = DSM2_CH1_WRITE_B; // change from CH2_CHECK_A to CH1_WRITE_A (to transmit upper)
}
else
cyrf_state = DSM2_CH1_WRITE_A; // change from CH2_CHECK_B to CH1_WRITE_A (upper already transmitted so transmit lower)
return 11000 - CH1_CH2_DELAY - WRITE_DELAY;
}
return 0;
}
uint16_t initDsm2()
{
CYRF_Reset();
CYRF_GetMfgData(cyrfmfg_id);//
cyrf_config();
if (sub_protocol ==DSMX)
calc_dsmx_channel();
else
{
#if RANDOM_CHANNELS == 1
uint8_t tmpch[10];
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75);
//
randomSeed((uint32_t)analogRead(A6)<<10|analogRead(A7));//seed
uint8_t idx = random(0xfefefefe) % 10;
hopping_frequency[0] = tmpch[idx];
while(1)
{
idx = random(0xfefefefe) % 10;
if (tmpch[idx] != hopping_frequency[0])
break;
}
hopping_frequency[1] = tmpch[idx];
#else
hopping_frequency[0] = (cyrfmfg_id[0] + cyrfmfg_id[2] + cyrfmfg_id[4]) % 39 + 1;
hopping_frequency[1] = (cyrfmfg_id[1] + cyrfmfg_id[3] + cyrfmfg_id[5]) % 40 + 40;
#endif
}
///}
crc = ~((cyrfmfg_id[0] << 8) + cyrfmfg_id[1]); //The crc for channel 'a' is NOT(mfgid[1] << 8 + mfgid[0])
crcidx = 0;//The crc for channel 'b' is (mfgid[1] << 8 + mfgid[0])
//
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;//Ok
data_col = 7 - sop_col;//ok
CYRF_SetTxRxMode(TX_EN);
//
if(IS_AUTOBIND_FLAG_on)
{
cyrf_state = DSM2_BIND;
PROTOCOL_SticksMoved(1); //Initialize Stick position
initialize_bind_state();
binding = 1;
}
else
{
cyrf_state = DSM2_CHANSEL;//
binding = 0;
}
return 10000;
}
#endif

View File

@ -43,7 +43,31 @@ enum {
DEVO_BOUND_10,
};
<<<<<<< HEAD
const uint8_t sopcodes[][8] = {
/* Note these are in order transmitted (LSB 1st) */
/* 0 */ {0x3C,0x37,0xCC,0x91,0xE2,0xF8,0xCC,0x91}, //0x91CCF8E291CC373C
/* 1 */ {0x9B,0xC5,0xA1,0x0F,0xAD,0x39,0xA2,0x0F}, //0x0FA239AD0FA1C59B
/* 2 */ {0xEF,0x64,0xB0,0x2A,0xD2,0x8F,0xB1,0x2A}, //0x2AB18FD22AB064EF
/* 3 */ {0x66,0xCD,0x7C,0x50,0xDD,0x26,0x7C,0x50}, //0x507C26DD507CCD66
/* 4 */ {0x5C,0xE1,0xF6,0x44,0xAD,0x16,0xF6,0x44}, //0x44F616AD44F6E15C
/* 5 */ {0x5A,0xCC,0xAE,0x46,0xB6,0x31,0xAE,0x46}, //0x46AE31B646AECC5A
/* 6 */ {0xA1,0x78,0xDC,0x3C,0x9E,0x82,0xDC,0x3C}, //0x3CDC829E3CDC78A1
/* 7 */ {0xB9,0x8E,0x19,0x74,0x6F,0x65,0x18,0x74}, //0x7418656F74198EB9
/* 8 */ {0xDF,0xB1,0xC0,0x49,0x62,0xDF,0xC1,0x49}, //0x49C1DF6249C0B1DF
/* 9 */ {0x97,0xE5,0x14,0x72,0x7F,0x1A,0x14,0x72}, //0x72141A7F7214E597
};
uint8_t txState;
uint8_t pkt_num;
uint8_t ch_idx;
uint8_t use_fixed_id;
uint8_t failsafe_pkt;
static void __attribute__((unused)) scramble_pkt()
=======
static void __attribute__((unused)) DEVO_scramble_pkt()
>>>>>>> refs/remotes/pascallanger/master
{
#ifdef NO_SCRAMBLE
return;
@ -53,7 +77,11 @@ static void __attribute__((unused)) DEVO_scramble_pkt()
#endif
}
<<<<<<< HEAD
static void __attribute__((unused)) add_pkt_suffix()
=======
static void __attribute__((unused)) DEVO_add_pkt_suffix()
>>>>>>> refs/remotes/pascallanger/master
{
uint8_t bind_state;
#ifdef ENABLE_PPM
@ -91,7 +119,11 @@ static void __attribute__((unused)) DEVO_add_pkt_suffix()
packet[15] = (MProtocol_id >> 16) & 0xff;
}
<<<<<<< HEAD
static void __attribute__((unused)) build_beacon_pkt(uint8_t upper)
=======
static void __attribute__((unused)) DEVO_build_beacon_pkt(uint8_t upper)
>>>>>>> refs/remotes/pascallanger/master
{
packet[0] = (DEVO_NUM_CHANNELS << 4) | 0x07;
uint8_t max = 8;
@ -106,7 +138,11 @@ static void __attribute__((unused)) DEVO_build_beacon_pkt(uint8_t upper)
DEVO_add_pkt_suffix();
}
<<<<<<< HEAD
static void __attribute__((unused)) build_bind_pkt()
=======
static void __attribute__((unused)) DEVO_build_bind_pkt()
>>>>>>> refs/remotes/pascallanger/master
{
packet[0] = (DEVO_NUM_CHANNELS << 4) | 0x0a;
packet[1] = bind_counter & 0xff;
@ -126,7 +162,11 @@ static void __attribute__((unused)) DEVO_build_bind_pkt()
packet[15] ^= cyrfmfg_id[2];
}
<<<<<<< HEAD
static void __attribute__((unused)) build_data_pkt()
=======
static void __attribute__((unused)) DEVO_build_data_pkt()
>>>>>>> refs/remotes/pascallanger/master
{
static uint8_t ch_idx=0;
@ -150,7 +190,11 @@ static void __attribute__((unused)) DEVO_build_data_pkt()
DEVO_add_pkt_suffix();
}
<<<<<<< HEAD
static void __attribute__((unused)) cyrf_set_bound_sop_code()
=======
static void __attribute__((unused)) DEVO_cyrf_set_bound_sop_code()
>>>>>>> refs/remotes/pascallanger/master
{
/* crc == 0 isn't allowed, so use 1 if the math results in 0 */
uint8_t crc = (cyrfmfg_id[0] + (cyrfmfg_id[1] >> 6) + cyrfmfg_id[2]);
@ -163,6 +207,9 @@ static void __attribute__((unused)) DEVO_cyrf_set_bound_sop_code()
CYRF_SetPower(0x08);
}
<<<<<<< HEAD
static void __attribute__((unused)) cyrf_init()
=======
const uint8_t PROGMEM DEVO_init_vals[][2] = {
{ CYRF_1D_MODE_OVERRIDE, 0x38 },
{ CYRF_03_TX_CFG, 0x08 },
@ -186,13 +233,18 @@ const uint8_t PROGMEM DEVO_init_vals[][2] = {
};
static void __attribute__((unused)) DEVO_cyrf_init()
>>>>>>> refs/remotes/pascallanger/master
{
/* Initialise CYRF chip */
for(uint8_t i = 0; i < sizeof(DEVO_init_vals) / 2; i++)
CYRF_WriteRegister(pgm_read_byte( &DEVO_init_vals[i][0]), pgm_read_byte( &DEVO_init_vals[i][1]) );
}
<<<<<<< HEAD
static void __attribute__((unused)) set_radio_channels()
=======
static void __attribute__((unused)) DEVO_set_radio_channels()
>>>>>>> refs/remotes/pascallanger/master
{
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80);
hopping_frequency[3] = hopping_frequency[0];
@ -285,6 +337,35 @@ uint16_t devo_callback()
return 1200;
}
<<<<<<< HEAD
/*static void __attribute__((unused)) devo_bind()
{
fixed_id = Model_fixed_id;
bind_counter = DEVO_BIND_COUNT;
use_fixed_id = 1;
//PROTOCOL_SetBindState(0x1388 * 2400 / 1000); //msecs 12000ms
}
static void __attribute__((unused)) generate_fixed_id_bind(){
if(BIND_0){
//randomSeed((uint32_t)analogRead(A6)<<10|analogRead(A7));//seed
uint8_t txid[4];
//Model_fixed_id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
Model_fixed_id=0x332211;
txid[0]= (id &0xFF);
txid[1] = ((id >> 8) & 0xFF);
txid[2] = ((id >> 16) & 0xFF);
//txid[3] = ((id >> 24) & 0xFF);
eeprom_write_block((const void*)txid,(void*)40,3);
devo_bind();
}
}
*/
=======
>>>>>>> refs/remotes/pascallanger/master
uint16_t DevoInit()
{
DEVO_cyrf_init();

View File

@ -15,21 +15,38 @@
// Last sync with hexfet new_protocols/fy326_nrf24l01.c dated 2015-07-29
#if defined(FY326_NRF24L01_INO)
<<<<<<< HEAD
=======
>>>>>>> refs/remotes/pascallanger/master
#include "iface_nrf24l01.h"
#define FY326_INITIAL_WAIT 500
#define FY326_PACKET_PERIOD 1500
#define FY326_PACKET_CHKTIME 300
#define FY326_PACKET_SIZE 15
<<<<<<< HEAD
#define FY326_BIND_COUNT 16
=======
#define FY326_BIND_COUNT 16
>>>>>>> refs/remotes/pascallanger/master
#define FY326_RF_BIND_CHANNEL 0x17
#define FY326_NUM_RF_CHANNELS 5
enum {
<<<<<<< HEAD
FY326_INIT1 = 0,
FY326_BIND1,
FY326_BIND2,
FY326_DATA,
FY319_INIT1,
FY319_BIND1,
FY319_BIND2,
=======
FY326_BIND1=0,
FY326_BIND2,
FY326_DATA
>>>>>>> refs/remotes/pascallanger/master
};
#define rxid channel
@ -39,7 +56,11 @@ static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
{
packet[0] = rx_tx_addr[3];
if(bind)
<<<<<<< HEAD
packet[1] = 0x55;
=======
packet[1] = 0x55;
>>>>>>> refs/remotes/pascallanger/master
else
packet[1] = GET_FLAG(Servo_AUX3, 0x80) // Headless
| GET_FLAG(Servo_AUX2, 0x40) // RTH
@ -50,6 +71,23 @@ static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
packet[3] = convert_channel_8b_scale(ELEVATOR, 0, 200); // elevator
packet[4] = 200 - convert_channel_8b_scale(RUDDER, 0, 200); // rudder
packet[5] = convert_channel_8b_scale(THROTTLE, 0, 200); // throttle
<<<<<<< HEAD
if(sub_protocol == FY319) {
packet[6] = 255 - scale_channel(AILERON, 0, 255);
packet[7] = scale_channel(ELEVATOR, 0, 255);
packet[8] = 255 - scale_channel(RUDDER, 0, 255);
}
else {
packet[6] = rx_tx_addr[0];
packet[7] = rx_tx_addr[1];
packet[8] = rx_tx_addr[2];
}
packet[9] = CHAN_TO_TRIM(packet[2]); // aileron_trim;
packet[10] = CHAN_TO_TRIM(packet[3]); // elevator_trim;
packet[11] = CHAN_TO_TRIM(packet[4]); // rudder_trim;
packet[12] = 0; // throttle_trim;
packet[13] = rxid;
=======
packet[6] = rx_tx_addr[0];
packet[7] = rx_tx_addr[1];
packet[8] = rx_tx_addr[2];
@ -58,6 +96,7 @@ static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
packet[11] = CHAN_TO_TRIM(packet[4]); // rudder_trim;
packet[12] = 0; // throttle_trim;
packet[13] = rxid;
>>>>>>> refs/remotes/pascallanger/master
packet[14] = rx_tx_addr[4];
if (bind)
@ -66,11 +105,20 @@ static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no %= FY326_NUM_RF_CHANNELS;
<<<<<<< HEAD
}
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
=======
}
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WritePayload(packet, FY326_PACKET_SIZE);
@ -79,9 +127,18 @@ static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
static void __attribute__((unused)) FY326_init()
{
<<<<<<< HEAD
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
if(sub_protocol == FY319)
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // Five-byte rx/tx address
else
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // Three-byte rx/tx address
=======
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // Three-byte rx/tx address
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x15\x59\x23\xc6\x29", 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x15\x59\x23\xc6\x29", 5);
NRF24L01_FlushTx();
@ -93,13 +150,112 @@ static void __attribute__((unused)) FY326_init()
NRF24L01_WriteReg(NRF24L01_05_RF_CH, FY326_RF_BIND_CHANNEL);
NRF24L01_SetBitrate(NRF24L01_BR_250K);
NRF24L01_SetPower();
<<<<<<< HEAD
NRF24L01_Activate(0x73);
=======
NRF24L01_Activate(0x73);
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f);
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
NRF24L01_Activate(0x73);
}
<<<<<<< HEAD
uint16_t fy326_callback()
{
uint8_t i;
switch (phase) {
case FY319_INIT1:
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
phase = FY319_BIND1;
BIND_IN_PROGRESS;
return FY326_CHKTIME;
break;
case FY319_BIND1:
if(NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR)) {
NRF24L01_ReadPayload(packet, FY326_SIZE);
rxid = packet[13];
packet[0] = txid[3];
packet[1] = 0x80;
packet[14]= txid[4];
bind_counter = FY326_BIND_COUNT;
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
bind_counter = 255;
for(i=2; i<6; i++)
packet[i] = rf_chans[0];
phase = FY319_BIND2;
}
return FY326_CHKTIME;
break;
case FY319_BIND2:
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, FY326_SIZE);
if(bind_counter == 250)
packet[1] = 0x40;
if(--bind_counter == 0) {
BIND_DONE;
phase = FY326_DATA;
}
break;
case FY326_INIT1:
bind_counter = FY326_BIND_COUNT;
phase = FY326_BIND2;
send_packet(1);
return FY326_CHKTIME;
break;
case FY326_BIND1:
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR))
{ // RX fifo data ready
NRF24L01_ReadPayload(packet, FY326_PACKET_SIZE);
rxid = packet[13];
rx_tx_addr[0] = 0xAA;
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
BIND_DONE;
phase = FY326_DATA;
}
else
if (bind_counter-- == 0)
{
bind_counter = FY326_BIND_COUNT;
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
FY326_send_packet(1);
phase = FY326_BIND2;
return FY326_PACKET_CHKTIME;
}
break;
case FY326_BIND2:
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_TX_DS))
{ // TX data sent -> switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
phase = FY326_BIND1;
}
else
return FY326_PACKET_CHKTIME;
break;
case FY326_DATA:
FY326_send_packet(0);
break;
}
=======
uint16_t FY326_callback()
{
switch (phase)
@ -141,26 +297,54 @@ uint16_t FY326_callback()
FY326_send_packet(0);
break;
}
>>>>>>> refs/remotes/pascallanger/master
return FY326_PACKET_PERIOD;
}
static void __attribute__((unused)) FY326_initialize_txid()
{
<<<<<<< HEAD
if(sub_protocol == FY319) {
hopping_frequency[0] = (rx_tx_addr[0]&0x0f) & ~0x80;
hopping_frequency[1] = (rx_tx_addr[0] >> 4) & ~0x80;
hopping_frequency[2] = (rx_tx_addr[1]&0x0f) & ~0x80;
hopping_frequency[3] = (rx_tx_addr[1] >> 4) & ~0x80;
hopping_frequency[4] = (rx_tx_addr[2] >> 4) & ~0x80;
} else {
hopping_frequency[0] = (rx_tx_addr[0]&0x0f);
hopping_frequency[1] = 0x10 + (rx_tx_addr[0] >> 4);
hopping_frequency[2] = 0x20 + (rx_tx_addr[1]&0x0f);
hopping_frequency[3] = 0x30 + (rx_tx_addr[1] >> 4);
hopping_frequency[4] = 0x40 + (rx_tx_addr[2] >> 4);
}
=======
hopping_frequency[0] = (rx_tx_addr[0]&0x0f);
hopping_frequency[1] = 0x10 + (rx_tx_addr[0] >> 4);
hopping_frequency[2] = 0x20 + (rx_tx_addr[1]&0x0f);
hopping_frequency[3] = 0x30 + (rx_tx_addr[1] >> 4);
hopping_frequency[4] = 0x40 + (rx_tx_addr[2] >> 4);
>>>>>>> refs/remotes/pascallanger/master
}
uint16_t initFY326(void)
{
BIND_IN_PROGRESS; // autobind protocol
<<<<<<< HEAD
rxid = 0xaa;
bind_counter = 0;
FY326_initialize_txid();
fy326_init();
if(sub_protocol == FY319)
phase = FY319_INIT1;
else
phase = FY326_INIT1;
=======
rxid = 0xAA;
bind_counter = 0;
FY326_initialize_txid();
FY326_init();
phase=FY326_BIND1;
>>>>>>> refs/remotes/pascallanger/master
return FY326_INITIAL_WAIT;
}

View File

@ -21,6 +21,34 @@
//FlySky constants & variables
#define FLYSKY_BIND_COUNT 2500
<<<<<<< HEAD
const uint8_t PROGMEM tx_channels[] = {
0x0a, 0x5a, 0x14, 0x64, 0x1e, 0x6e, 0x28, 0x78, 0x32, 0x82, 0x3c, 0x8c, 0x46, 0x96, 0x50, 0xa0,
0xa0, 0x50, 0x96, 0x46, 0x8c, 0x3c, 0x82, 0x32, 0x78, 0x28, 0x6e, 0x1e, 0x64, 0x14, 0x5a, 0x0a,
0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x46, 0x96, 0x1e, 0x6e, 0x3c, 0x8c, 0x28, 0x78, 0x32, 0x82,
0x82, 0x32, 0x78, 0x28, 0x8c, 0x3c, 0x6e, 0x1e, 0x96, 0x46, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a,
0x28, 0x78, 0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96,
0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a, 0x78, 0x28,
0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96, 0x14, 0x64,
0x64, 0x14, 0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50,
0x50, 0xa0, 0x46, 0x96, 0x3c, 0x8c, 0x28, 0x78, 0x0a, 0x5a, 0x32, 0x82, 0x1e, 0x6e, 0x14, 0x64,
0x64, 0x14, 0x6e, 0x1e, 0x82, 0x32, 0x5a, 0x0a, 0x78, 0x28, 0x8c, 0x3c, 0x96, 0x46, 0xa0, 0x50,
0x46, 0x96, 0x3c, 0x8c, 0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64,
0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50, 0x8c, 0x3c, 0x96, 0x46,
0x46, 0x96, 0x0a, 0x5a, 0x3c, 0x8c, 0x14, 0x64, 0x50, 0xa0, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82,
0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0xa0, 0x50, 0x64, 0x14, 0x8c, 0x3c, 0x5a, 0x0a, 0x96, 0x46,
0x46, 0x96, 0x0a, 0x5a, 0x50, 0xa0, 0x3c, 0x8c, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64,
0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0x8c, 0x3c, 0xa0, 0x50, 0x5a, 0x0a, 0x96, 0x46
};
enum {
// flags going to byte 10
FLAG_V9X9_VIDEO = 0x40,
FLAG_V9X9_CAMERA= 0x80,
// flags going to byte 12
FLAG_V9X9_FLIP = 0x10,
FLAG_V9X9_LED = 0x20,
=======
enum {
// flags going to byte 10
FLAG_V9X9_VIDEO = 0x40,
@ -28,26 +56,27 @@ enum {
// flags going to byte 12
FLAG_V9X9_FLIP = 0x10,
FLAG_V9X9_LED = 0x20,
>>>>>>> refs/remotes/pascallanger/master
};
enum {
// flags going to byte 13
FLAG_V6X6_HLESS1= 0x80,
// flags going to byte 14
FLAG_V6X6_VIDEO = 0x01,
FLAG_V6X6_YCAL = 0x02,
FLAG_V6X6_XCAL = 0x04,
FLAG_V6X6_RTH = 0x08,
FLAG_V6X6_CAMERA= 0x10,
FLAG_V6X6_HLESS2= 0x20,
FLAG_V6X6_LED = 0x40,
FLAG_V6X6_FLIP = 0x80,
// flags going to byte 13
FLAG_V6X6_HLESS1= 0x80,
// flags going to byte 14
FLAG_V6X6_VIDEO = 0x01,
FLAG_V6X6_YCAL = 0x02,
FLAG_V6X6_XCAL = 0x04,
FLAG_V6X6_RTH = 0x08,
FLAG_V6X6_CAMERA= 0x10,
FLAG_V6X6_HLESS2= 0x20,
FLAG_V6X6_LED = 0x40,
FLAG_V6X6_FLIP = 0x80,
};
enum {
// flags going to byte 14
FLAG_V912_TOPBTN= 0x40,
FLAG_V912_BTMBTN= 0x80,
// flags going to byte 14
FLAG_V912_TOPBTN= 0x40,
FLAG_V912_BTMBTN= 0x80,
};
const uint8_t PROGMEM V912_X17_SEQ[10] = { 0x14, 0x31, 0x40, 0x49, 0x49, // sometime first byte is 0x15 ?
@ -55,6 +84,79 @@ const uint8_t PROGMEM V912_X17_SEQ[10] = { 0x14, 0x31, 0x40, 0x49, 0x49, //
static void __attribute__((unused)) flysky_apply_extension_flags()
{
<<<<<<< HEAD
const uint8_t V912_X17_SEQ[10] = { 0x14, 0x31, 0x40, 0x49, 0x49, // sometime first byte is 0x15 ?
0x49, 0x49, 0x49, 0x49, 0x49, };
static uint8_t seq_counter;
switch(sub_protocol)
{
case V9X9:
if(Servo_AUX1)
packet[12] |= FLAG_V9X9_FLIP;
if(Servo_AUX2)
packet[12] |= FLAG_V9X9_LED;
if(Servo_AUX3)
packet[10] |= FLAG_V9X9_CAMERA;
if(Servo_AUX4)
packet[10] |= FLAG_V9X9_VIDEO;
break;
case V6X6:
packet[13] = 0x03; // 3 = 100% rate (0=40%, 1=60%, 2=80%)
packet[14] = 0x00;
if(Servo_AUX1)
packet[14] |= FLAG_V6X6_FLIP;
if(Servo_AUX2)
packet[14] |= FLAG_V6X6_LED;
if(Servo_AUX3)
packet[14] |= FLAG_V6X6_CAMERA;
if(Servo_AUX4)
packet[14] |= FLAG_V6X6_VIDEO;
if(Servo_AUX5)
{
packet[13] |= FLAG_V6X6_HLESS1;
packet[14] |= FLAG_V6X6_HLESS2;
}
if(Servo_AUX6) //use option to manipulate these bytes
packet[14] |= FLAG_V6X6_RTH;
if(Servo_AUX7)
packet[14] |= FLAG_V6X6_XCAL;
if(Servo_AUX8)
packet[14] |= FLAG_V6X6_YCAL;
packet[15] = 0x10; // unknown
packet[16] = 0x10; // unknown
packet[17] = 0xAA; // unknown
packet[18] = 0xAA; // unknown
packet[19] = 0x60; // unknown, changes at irregular interval in stock TX
packet[20] = 0x02; // unknown
break;
case V912:
seq_counter++;
if( seq_counter > 9)
seq_counter = 0;
packet[12] |= 0x20; // bit 6 is always set ?
packet[13] = 0x00; // unknown
packet[14] = 0x00;
if(Servo_AUX1)
packet[14] = FLAG_V912_BTMBTN;
if(Servo_AUX2)
packet[14] |= FLAG_V912_TOPBTN;
packet[15] = 0x27; // [15] and [16] apparently hold an analog channel with a value lower than 1000
packet[16] = 0x03; // maybe it's there for a pitch channel for a CP copter ?
packet[17] = V912_X17_SEQ[seq_counter]; // not sure what [17] & [18] are for
if(seq_counter == 0) // V912 Rx does not even read those bytes... [17-20]
packet[18] = 0x02;
else
packet[18] = 0x00;
packet[19] = 0x00; // unknown
packet[20] = 0x00; // unknown
break;
default:
break;
}
=======
static uint8_t seq_counter;
switch(sub_protocol)
{
@ -124,39 +226,78 @@ static void __attribute__((unused)) flysky_apply_extension_flags()
default:
break;
}
>>>>>>> refs/remotes/pascallanger/master
}
static void __attribute__((unused)) flysky_build_packet(uint8_t init)
{
uint8_t i;
//servodata timing range for flysky.
////-100% =~ 0x03e8//=1000us(min)
//+100% =~ 0x07ca//=1994us(max)
//Center = 0x5d9//=1497us(center)
//channel order AIL;ELE;THR;RUD;AUX1;AUX2;AUX3;AUX4
//servodata timing range for flysky.
////-100% =~ 0x03e8//=1000us(min)
//+100% =~ 0x07ca//=1994us(max)
//Center = 0x5d9//=1497us(center)
//channel order AIL;ELE;THR;RUD;AUX1;AUX2;AUX3;AUX4
packet[0] = init ? 0xaa : 0x55;
packet[1] = rx_tx_addr[3];
packet[2] = rx_tx_addr[2];
packet[3] = rx_tx_addr[1];
packet[4] = rx_tx_addr[0];
<<<<<<< HEAD
const uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
for(i = 0; i < 8; i++)
{
packet[5+2*i]=lowByte(Servo_data[ch[i]]); //low byte of servo timing(1000-2000us)
packet[6+2*i]=highByte(Servo_data[ch[i]]); //high byte of servo timing(1000-2000us)
}
=======
for(i = 0; i < 8; i++)
{
packet[5 + i*2]=Servo_data[CH_AETR[i]]&0xFF; //low byte of servo timing(1000-2000us)
packet[6 + i*2]=(Servo_data[CH_AETR[i]]>>8)&0xFF; //high byte of servo timing(1000-2000us)
}
>>>>>>> refs/remotes/pascallanger/master
flysky_apply_extension_flags();
}
uint16_t ReadFlySky()
{
if (bind_counter)
{
{
flysky_build_packet(1);
A7105_WriteData(21, 1);
bind_counter--;
if (! bind_counter)
BIND_DONE;
}
<<<<<<< HEAD
else
{
flysky_build_packet(0);
A7105_WriteData(21, pgm_read_byte_near(&tx_channels[chanrow*16+chancol])-chanoffset);
chancol = (chancol + 1) % 16;
if (! chancol) //Keep transmit power updated
A7105_SetPower();
}
return 1460;
}
uint16_t initFlySky() {
//A7105_Reset();
A7105_Init(INIT_FLYSKY); //flysky_init();
if (rx_tx_addr[3] > 0x90) // limit offset to 9 as higher values don't work with some RX (ie V912)
rx_tx_addr[3] = rx_tx_addr[3] - 0x70;
chanrow=rx_tx_addr[3] % 16;
chancol=0;
chanoffset=rx_tx_addr[3] / 16;
if(IS_AUTOBIND_FLAG_on)
bind_counter = FLYSKY_BIND_COUNT;
else
bind_counter = 0;
return 2400;
=======
else
{
flysky_build_packet(0);
@ -211,5 +352,7 @@ uint16_t initFlySky()
else
bind_counter = 0;
return 2400;
>>>>>>> refs/remotes/pascallanger/master
}
#endif

View File

@ -17,9 +17,297 @@
*/
#if defined(FRSKYX_CC2500_INO)
#include "iface_cc2500.h"
uint8_t chanskip;
uint8_t calData[48][3];
uint8_t channr;
uint8_t pass_ = 1 ;
uint8_t counter_rst;
uint8_t ctr;
uint8_t FS_flag=0;
// uint8_t ptr[4]={0x01,0x12,0x23,0x30};
//uint8_t ptr[4]={0x00,0x11,0x22,0x33};
const PROGMEM uint8_t hop_data[]={
0x02, 0xD4, 0xBB, 0xA2, 0x89,
0x70, 0x57, 0x3E, 0x25, 0x0C,
0xDE, 0xC5, 0xAC, 0x93, 0x7A,
0x61, 0x48, 0x2F, 0x16, 0xE8,
0xCF, 0xB6, 0x9D, 0x84, 0x6B,
0x52, 0x39, 0x20, 0x07, 0xD9,
0xC0, 0xA7, 0x8E, 0x75, 0x5C,
0x43, 0x2A, 0x11, 0xE3, 0xCA,
0xB1, 0x98, 0x7F, 0x66, 0x4D,
0x34, 0x1B, 0x00, 0x1D, 0x03
};
#include "iface_cc2500.h"
static uint8_t __attribute__((unused)) hop(uint8_t byte)
{
return pgm_read_byte_near(&hop_data[byte]);
}
static void __attribute__((unused)) set_start(uint8_t ch )
{
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_23_FSCAL3, calData[ch][0]);
cc2500_writeReg(CC2500_24_FSCAL2, calData[ch][1]);
cc2500_writeReg(CC2500_25_FSCAL1, calData[ch][2]);
cc2500_writeReg(CC2500_0A_CHANNR, ch==47?0:pgm_read_word(&hop_data[ch]));
}
static void __attribute__((unused)) frskyX_init()
{
CC2500_Reset();
for(uint8_t i=0;i<36;i++)
{
uint8_t reg=pgm_read_byte_near(&cc2500_conf[i][0]);
uint8_t val=pgm_read_byte_near(&cc2500_conf[i][1]);
if(reg==CC2500_06_PKTLEN)
val=0x1E;
else
if(reg==CC2500_08_PKTCTRL0)
val=0x01;
else
if(reg==CC2500_0B_FSCTRL1)
val=0x0A;
else
if(reg==CC2500_10_MDMCFG4)
val=0x7B;
else
if(reg==CC2500_11_MDMCFG3)
val=0x61;
else
if(reg==CC2500_12_MDMCFG2)
val=0x13;
else
if(reg==CC2500_15_DEVIATN)
val=0x51;
cc2500_writeReg(reg,val);
}
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x04);
cc2500_writeReg(CC2500_0C_FSCTRL0, option);
cc2500_strobe(CC2500_SIDLE);
//
for(uint8_t c=0;c < 47;c++){//calibrate hop channels
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_0A_CHANNR,pgm_read_word(&hop_data[c]));
cc2500_strobe(CC2500_SCAL);
delayMicroseconds(900);//
calData[c][0] = cc2500_readReg(CC2500_23_FSCAL3);
calData[c][1] = cc2500_readReg(CC2500_24_FSCAL2);
calData[c][2] = cc2500_readReg(CC2500_25_FSCAL1);
}
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_0A_CHANNR,0x00);
cc2500_strobe(CC2500_SCAL);
delayMicroseconds(900);
calData[47][0] = cc2500_readReg(CC2500_23_FSCAL3);
calData[47][1] = cc2500_readReg(CC2500_24_FSCAL2);
calData[47][2] = cc2500_readReg(CC2500_25_FSCAL1);
//#######END INIT########
}
static void __attribute__((unused)) initialize_data(uint8_t adr)
{
cc2500_writeReg(CC2500_0C_FSCTRL0,option); // Frequency offset hack
cc2500_writeReg(CC2500_18_MCSM0, 0x8);
cc2500_writeReg(CC2500_09_ADDR, adr ? 0x03 : rx_tx_addr[3]);
cc2500_writeReg(CC2500_07_PKTCTRL1,0x05);
}
static uint8_t __attribute__((unused)) crc_Byte( uint8_t byte )
{
crc = (crc<<8) ^ pgm_read_word(&CRCTable[((uint8_t)(crc>>8) ^ byte) & 0xFF]);
return byte;
}
static uint16_t __attribute__((unused)) scaleForPXX( uint8_t i )
{ //mapped 860,2140(125%) range to 64,1984(PXX values);
return (uint16_t)(((Servo_data[i]-PPM_MIN)*3)>>1)+64;
}
static void __attribute__((unused)) frskyX_build_bind_packet()
{
crc=0;
packet[0] = 0x1D;
packet[1] = 0x03;
packet[2] = 0x01;
//
packet[3] = crc_Byte(rx_tx_addr[3]);
packet[4] = crc_Byte(rx_tx_addr[2]);
int idx = ((state -FRSKY_BIND) % 10) * 5;
packet[5] = crc_Byte(idx);
packet[6] = crc_Byte(pgm_read_word(&hop_data[idx++]));
packet[7] = crc_Byte(pgm_read_word(&hop_data[idx++]));
packet[8] = crc_Byte(pgm_read_word(&hop_data[idx++]));
packet[9] = crc_Byte(pgm_read_word(&hop_data[idx++]));
packet[10] = crc_Byte(pgm_read_word(&hop_data[idx++]));
packet[11] = crc_Byte(0x02);
packet[12] = crc_Byte(RX_num);
//
for(uint8_t i=13;i<28;i++)
packet[i]=crc_Byte(0);
//
packet[28]=highByte(crc);
packet[29]=lowByte(crc);
//
}
static void __attribute__((unused)) frskyX_data_frame()
{
//0x1D 0xB3 0xFD 0x02 0x56 0x07 0x15 0x00 0x00 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x96 0x12
//
uint8_t lpass = pass_ ;
uint16_t chan_0 ;
uint16_t chan_1 ;
uint8_t flag2 = 0;
uint8_t startChan = 0;
crc = 0;
//static uint8_t p = 0;
//
packet[0] = 0x1D;
packet[1] = rx_tx_addr[3];
packet[2] = rx_tx_addr[2];
packet[3] = crc_Byte(0x02);
//
packet[4] = crc_Byte((ctr<<6)+channr); //*64
packet[5] = crc_Byte(counter_rst);
packet[6] = crc_Byte(RX_num);
// FLAGS 00 - standard packet
//10, 12, 14, 16, 18, 1A, 1C, 1E - failsafe packet
//20 - range check packet
packet[7] = crc_Byte(FS_flag);
packet[8] = crc_Byte(flag2);
//
if ( lpass & 1 )
startChan += 8 ;
for(uint8_t i = 0; i <12 ; i+=3)
{//12 bytes
chan_0 = scaleForPXX(startChan);
if(lpass & 1 )
chan_0+=2048;
packet[9+i] = crc_Byte(lowByte(chan_0));//3 bytes*4
startChan++;
chan_1 = scaleForPXX(startChan);
if(lpass & 1 )
chan_1+= 2048;
startChan++;
packet[9+i+1]=crc_Byte((((chan_0>>8) & 0x0F)|(chan_1 << 4)));
packet[9+i+2]=crc_Byte(chan_1>>4);
}
//packet[21]=crc_Byte(0x08);//first
packet[21]=crc_Byte(0x80);//??? when received first telemetry frame is changed to 0x80
//packet[21]=crc_Byte(ptr[p]);//???
//p=(p+1)%4;//repeating 4 bytes sequence pattern every 4th frame.
pass_=lpass+1;
for (uint8_t i=22;i<28;i++)
packet[i]=crc_Byte(0);
packet[28]=highByte(crc);
packet[29]=lowByte(crc);
}
<<<<<<< HEAD
uint16_t ReadFrSkyX()
{
switch(state)
{
default:
set_start(47);
CC2500_SetPower();
cc2500_strobe(CC2500_SFRX);
//
frskyX_build_bind_packet();
cc2500_strobe(CC2500_SIDLE);
cc2500_writeFifo(packet, packet[0]+1);
state++;
return 9000;
case FRSKY_BIND_DONE:
initialize_data(0);
channr=0;
BIND_DONE;
state++;
break;
case FRSKY_DATA1:
LED_ON;
CC2500_SetTxRxMode(TX_EN);
set_start(channr);
CC2500_SetPower();
cc2500_strobe(CC2500_SFRX);
channr = (channr+chanskip)%47;
cc2500_strobe(CC2500_SIDLE);
cc2500_writeFifo(packet, packet[0]+1);
//
frskyX_data_frame();
state++;
return 5500;
case FRSKY_DATA2:
CC2500_SetTxRxMode(RX_EN);
cc2500_strobe(CC2500_SIDLE);
state++;
return 200;
case FRSKY_DATA3:
cc2500_strobe(CC2500_SRX);
state++;
return 3000;
case FRSKY_DATA4:
len = cc2500_readReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
if (len &&(len<MAX_PKT))
{
cc2500_readFifo(pkt, len);
#if defined TELEMETRY
frsky_check_telemetry(pkt,len); //check if valid telemetry packets
//parse telemetry packets here
//The same telemetry function used by FrSky(D8).
#endif
}
state = FRSKY_DATA1;
return 300;
}
return 1;
}
uint16_t initFrSkyX()
{
while(!chanskip)
{
randomSeed((uint32_t)analogRead(A6) << 10 | analogRead(A7));
chanskip=random(0xfefefefe)%47;
}
while((chanskip-ctr)%4)
ctr=(ctr+1)%4;
counter_rst=(chanskip-ctr)>>2;
//for test***************
//rx_tx_addr[3]=0xB3;
//rx_tx_addr[2]=0xFD;
//************************
frskyX_init();
//
if(IS_AUTOBIND_FLAG_on)
{
state = FRSKY_BIND;
initialize_data(1);
}
else
{
state = FRSKY_DATA1;
initialize_data(0);
}
return 10000;
}
=======
uint8_t chanskip;
uint8_t counter_rst;
uint8_t ctr;
@ -329,4 +617,5 @@ uint16_t initFrSkyX()
seq_last_rcvd = 8;
return 10000;
}
>>>>>>> refs/remotes/pascallanger/master
#endif

View File

@ -0,0 +1,219 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(FRSKY_CC2500_INO)
#include "iface_cc2500.h"
//##########Variables########
//uint32_t state;
//uint8_t len;
/*
enum {
FRSKY_BIND = 0,
FRSKY_BIND_DONE = 1000,
FRSKY_DATA1,
FRSKY_DATA2,
FRSKY_DATA3,
FRSKY_DATA4,
FRSKY_DATA5
};
*/
static void __attribute__((unused)) frsky2way_init(uint8_t bind)
{
// Configure cc2500 for tx mode
CC2500_Reset();
//
for(uint8_t i=0;i<36;i++)
{
uint8_t reg=pgm_read_byte_near(&cc2500_conf[i][0]);
uint8_t val=pgm_read_byte_near(&cc2500_conf[i][1]);
if(reg==CC2500_0C_FSCTRL0)
val=option;
else
if(reg==CC2500_1B_AGCCTRL2)
val=bind ? 0x43 : 0x03;
cc2500_writeReg(reg,val);
}
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_09_ADDR, bind ? 0x03 : rx_tx_addr[3]);
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x05);
cc2500_strobe(CC2500_SIDLE); // Go to idle...
//
cc2500_writeReg(CC2500_0A_CHANNR, 0x00);
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
cc2500_strobe(CC2500_SFRX);
//#######END INIT########
}
static uint8_t __attribute__((unused)) get_chan_num(uint16_t idx)
{
uint8_t ret = (idx * 0x1e) % 0xeb;
if(idx == 3 || idx == 23 || idx == 47)
ret++;
if(idx > 47)
return 0;
return ret;
}
static void __attribute__((unused)) frsky2way_build_bind_packet()
{
//11 03 01 d7 2d 00 00 1e 3c 5b 78 00 00 00 00 00 00 01
//11 03 01 19 3e 00 02 8e 2f bb 5c 00 00 00 00 00 00 01
packet[0] = 0x11;
packet[1] = 0x03;
packet[2] = 0x01;
packet[3] = rx_tx_addr[3];
packet[4] = rx_tx_addr[2];
uint16_t idx = ((state -FRSKY_BIND) % 10) * 5;
packet[5] = idx;
packet[6] = get_chan_num(idx++);
packet[7] = get_chan_num(idx++);
packet[8] = get_chan_num(idx++);
packet[9] = get_chan_num(idx++);
packet[10] = get_chan_num(idx++);
packet[11] = 0x00;
packet[12] = 0x00;
packet[13] = 0x00;
packet[14] = 0x00;
packet[15] = 0x00;
packet[16] = 0x00;
packet[17] = 0x01;
}
static void __attribute__((unused)) frsky2way_data_frame()
{//pachet[4] is telemetry user frame counter(hub)
//11 d7 2d 22 00 01 c9 c9 ca ca 88 88 ca ca c9 ca 88 88
//11 57 12 00 00 01 f2 f2 f2 f2 06 06 ca ca ca ca 18 18
packet[0] = 0x11; //Length
packet[1] = rx_tx_addr[3];
packet[2] = rx_tx_addr[2];
packet[3] = counter;//
#if defined TELEMETRY
packet[4] = telemetry_counter;
#else
packet[4] = 0x00;
#endif
packet[5] = 0x01;
//
packet[10] = 0;
packet[11] = 0;
packet[16] = 0;
packet[17] = 0;
for(uint8_t i = 0; i < 8; i++)
{
uint16_t value;
value = convert_channel_frsky(i);
if(i < 4)
{
packet[6+i] = value & 0xff;
packet[10+(i>>1)] |= ((value >> 8) & 0x0f) << (4 *(i & 0x01));
}
else
{
packet[8+i] = value & 0xff;
packet[16+((i-4)>>1)] |= ((value >> 8) & 0x0f) << (4 * ((i-4) & 0x01));
}
}
}
uint16_t initFrSky_2way()
{
if(IS_AUTOBIND_FLAG_on)
{
frsky2way_init(1);
state = FRSKY_BIND;//
}
else
{
frsky2way_init(0);
state = FRSKY_DATA2;
}
return 10000;
}
uint16_t ReadFrSky_2way()
{
if (state < FRSKY_BIND_DONE)
{
frsky2way_build_bind_packet();
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_0A_CHANNR, 0x00);
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
cc2500_strobe(CC2500_SFRX);//0x3A
cc2500_writeFifo(packet, packet[0]+1);
state++;
return 9000;
}
if (state == FRSKY_BIND_DONE)
{
state = FRSKY_DATA2;
frsky2way_init(0);
counter = 0;
BIND_DONE;
}
else
if (state == FRSKY_DATA5)
{
cc2500_strobe(CC2500_SRX);//0x34 RX enable
state = FRSKY_DATA1;
return 9200;
}
counter = (counter + 1) % 188;
if (state == FRSKY_DATA4)
{ //telemetry receive
CC2500_SetTxRxMode(RX_EN);
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_0A_CHANNR, get_chan_num(counter % 47));
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
state++;
return 1300;
}
else
{
if (state == FRSKY_DATA1)
{
len = cc2500_readReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
if (len<=MAX_PKT)//27 bytes
{
cc2500_readFifo(pkt, len); //received telemetry packets
#if defined(TELEMETRY)
//parse telemetry packet here
frsky_check_telemetry(pkt,len); //check if valid telemetry packets and buffer them.
#endif
}
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower(); // Set tx_power
}
cc2500_strobe(CC2500_SIDLE);
cc2500_writeReg(CC2500_0A_CHANNR, get_chan_num(counter % 47));
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
cc2500_strobe(CC2500_SFRX);
frsky2way_data_frame();
cc2500_writeFifo(packet, packet[0]+1);
state++;
}
return state == FRSKY_DATA4 ? 7500 : 9000;
}
#endif

View File

@ -246,7 +246,11 @@ static void __attribute__((unused)) kn_init()
NRF24L01_Initialize();
<<<<<<< HEAD
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
=======
NRF24L01_WriteReg(NRF24L01_00_CONFIG, _BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO));
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
@ -259,7 +263,11 @@ static void __attribute__((unused)) kn_init()
NRF24L01_Activate(0x73);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
// Enable: Dynamic Payload Length to enable PCF
<<<<<<< HEAD
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, BV(NRF2401_1D_EN_DPL));
=======
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, _BV(NRF2401_1D_EN_DPL));
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_SetPower();

View File

@ -12,7 +12,11 @@
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
<<<<<<< HEAD
// compatible with MJX WLH08, X600, X800, H26D
=======
// compatible with MJX WLH08, X600, X800, H26D, Eachine E010
>>>>>>> refs/remotes/pascallanger/master
// Last sync with hexfet new_protocols/mjxq_nrf24l01.c dated 2016-01-17
#if defined(MJXQ_NRF24L01_INO)
@ -26,6 +30,8 @@
#define MJXQ_RF_NUM_CHANNELS 4
#define MJXQ_ADDRESS_LENGTH 5
<<<<<<< HEAD
=======
// haven't figured out txid<-->rf channel mapping for MJX models
const uint8_t PROGMEM MJXQ_map_rfchan[][4] = {
{0x0A, 0x46, 0x3A, 0x42},
@ -37,6 +43,7 @@ const uint8_t PROGMEM MJXQ_map_txid[][3] = {
{0x48, 0x6A, 0x40} };
>>>>>>> refs/remotes/pascallanger/master
#define MJXQ_PAN_TILT_COUNT 16 // for H26D - match stock tx timing
#define MJXQ_PAN_DOWN 0x08
#define MJXQ_PAN_UP 0x04
@ -50,6 +57,16 @@ static uint8_t __attribute__((unused)) MJXQ_pan_tilt_value()
packet_count++;
if(packet_count & MJXQ_PAN_TILT_COUNT)
{
<<<<<<< HEAD
if(Servo_AUX8)
pan=MJXQ_PAN_UP;
if(Servo_data[AUX8]<PPM_MIN_COMMAND)
pan=MJXQ_PAN_DOWN;
if(Servo_data[AUX9]>PPM_MIN_COMMAND)
pan=MJXQ_TILT_UP;
if(Servo_data[AUX9]<PPM_MIN_COMMAND)
pan=MJXQ_TILT_DOWN;
=======
if(Servo_data[AUX8]>PPM_MAX_COMMAND)
pan=MJXQ_PAN_UP;
if(Servo_data[AUX8]<PPM_MIN_COMMAND)
@ -58,6 +75,7 @@ static uint8_t __attribute__((unused)) MJXQ_pan_tilt_value()
pan+=MJXQ_TILT_UP;
if(Servo_data[AUX9]<PPM_MIN_COMMAND)
pan+=MJXQ_TILT_DOWN;
>>>>>>> refs/remotes/pascallanger/master
}
return pan;
}
@ -68,10 +86,17 @@ static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_s8b(RUDDER);
packet[4] = 0x40; // rudder does not work well with dyntrim
<<<<<<< HEAD
packet[2] = convert_channel_s8b(ELEVATOR);
packet[5] = MJXQ_CHAN2TRIM(packet[2]); // trim elevator
packet[3] = convert_channel_s8b(AILERON);
packet[6] = MJXQ_CHAN2TRIM(packet[3]); // trim aileron
=======
packet[2] = 0x80 ^ convert_channel_s8b(ELEVATOR);
packet[5] = GET_FLAG(Servo_AUX5, 1) ? 0x40 : MJXQ_CHAN2TRIM(packet[2]); // trim elevator
packet[3] = convert_channel_s8b(AILERON);
packet[6] = GET_FLAG(Servo_AUX5, 1) ? 0x40 : MJXQ_CHAN2TRIM(packet[3]); // trim aileron
>>>>>>> refs/remotes/pascallanger/master
packet[7] = rx_tx_addr[0];
packet[8] = rx_tx_addr[1];
packet[9] = rx_tx_addr[2];
@ -96,7 +121,10 @@ static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
packet[10]=MJXQ_pan_tilt_value();
// fall through on purpose - no break
case WLH08:
<<<<<<< HEAD
=======
case E010:
>>>>>>> refs/remotes/pascallanger/master
packet[10] += GET_FLAG(Servo_AUX6, 0x02) //RTH
| GET_FLAG(Servo_AUX5, 0x01); //HEADLESS
if (!bind)
@ -109,6 +137,14 @@ static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
}
break;
case X600:
<<<<<<< HEAD
if(Servo_AUX5) //HEADLESS
{ // driven trims cause issues when headless is enabled
packet[5] = 0x40;
packet[6] = 0x40;
}
=======
>>>>>>> refs/remotes/pascallanger/master
packet[10] = GET_FLAG(!Servo_AUX2, 0x02); //LED
packet[11] = GET_FLAG(Servo_AUX6, 0x01); //RTH
if (!bind)
@ -142,7 +178,11 @@ static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
if (sub_protocol == H26D)
NRF24L01_SetTxRxMode(TX_EN);
else
<<<<<<< HEAD
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
=======
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++ / 2]);
hopping_frequency_no %= 2 * MJXQ_RF_NUM_CHANNELS; // channels repeated
@ -165,12 +205,20 @@ static void __attribute__((unused)) MJXQ_init()
if (sub_protocol == WLH08)
memcpy(hopping_frequency, "\x12\x22\x32\x42", MJXQ_RF_NUM_CHANNELS);
else
<<<<<<< HEAD
if (sub_protocol == H26D)
=======
if (sub_protocol == H26D || sub_protocol == E010)
>>>>>>> refs/remotes/pascallanger/master
memcpy(hopping_frequency, "\x36\x3e\x46\x2e", MJXQ_RF_NUM_CHANNELS);
else
{
memcpy(hopping_frequency, "\x0a\x35\x42\x3d", MJXQ_RF_NUM_CHANNELS);
<<<<<<< HEAD
memcpy(addr, "\x6d\x6a\x73\x73\x73", MJXQ_RF_NUM_CHANNELS);
=======
memcpy(addr, "\x6d\x6a\x73\x73\x73", MJXQ_ADDRESS_LENGTH);
>>>>>>> refs/remotes/pascallanger/master
}
@ -189,25 +237,53 @@ static void __attribute__((unused)) MJXQ_init()
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, MJXQ_PACKET_SIZE); // rx pipe 0 (used only for blue board)
<<<<<<< HEAD
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
=======
if (sub_protocol == E010)
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250K
else
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_SetPower();
}
static void __attribute__((unused)) MJXQ_init2()
{
<<<<<<< HEAD
// haven't figured out txid<-->rf channel mapping for MJX models
static const uint8_t rf_map[][4] = {
{0x0A, 0x46, 0x3A, 0x42},
{0x0A, 0x3C, 0x36, 0x3F},
{0x0A, 0x43, 0x36, 0x3F} };
if (sub_protocol == H26D)
memcpy(hopping_frequency, "\x32\x3e\x42\x4e", MJXQ_RF_NUM_CHANNELS);
else
if (sub_protocol == WLH08)
memcpy(hopping_frequency, rf_map[rx_tx_addr[0]%3], MJXQ_RF_NUM_CHANNELS);
=======
if (sub_protocol == H26D)
memcpy(hopping_frequency, "\x32\x3e\x42\x4e", MJXQ_RF_NUM_CHANNELS);
else
if (sub_protocol != WLH08 && sub_protocol != E010)
for(uint8_t i=0;i<MJXQ_RF_NUM_CHANNELS;i++)
hopping_frequency[i]=pgm_read_byte_near( &MJXQ_map_rfchan[rx_tx_addr[4]%3][i] );
>>>>>>> refs/remotes/pascallanger/master
}
static void __attribute__((unused)) MJXQ_initialize_txid()
{
<<<<<<< HEAD
// haven't figured out txid<-->rf channel mapping for MJX models
static const uint8_t tx_map[][3]={
{0xF8, 0x4F, 0x1C},
{0xC8, 0x6E, 0x02},
{0x48, 0x6A, 0x40} };
if (sub_protocol == WLH08)
rx_tx_addr[0]&=0xF8; // txid must be multiple of 8
else
memcpy(rx_tx_addr,tx_map[rx_tx_addr[0]%3],3);
=======
rx_tx_addr[0]&=0xF8;
if (sub_protocol == E010)
{
@ -217,6 +293,7 @@ static void __attribute__((unused)) MJXQ_initialize_txid()
else
for(uint8_t i=0;i<3;i++)
rx_tx_addr[i]=pgm_read_byte_near( &MJXQ_map_txid[rx_tx_addr[4]%3][i] );
>>>>>>> refs/remotes/pascallanger/master
}
uint16_t MJXQ_callback()

View File

@ -12,7 +12,11 @@
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
<<<<<<< HEAD
// compatible with MT99xx, Eachine H7, Yi Zhan i6S
=======
// compatible with MT99xx, Eachine H7, Yi Zhan i6S and LS114/124
>>>>>>> refs/remotes/pascallanger/master
// Last sync with Goebish mt99xx_nrf24l01.c dated 2016-01-29
#if defined(MT99XX_NRF24L01_INO)
@ -37,6 +41,8 @@ enum{
FLAG_MT_FLIP = 0x80,
};
<<<<<<< HEAD
=======
enum{
// flags going to packet[6] (LS)
FLAG_LS_INVERT = 0x01,
@ -47,12 +53,43 @@ enum{
FLAG_LS_FLIP = 0x80,
};
>>>>>>> refs/remotes/pascallanger/master
enum {
MT99XX_INIT = 0,
MT99XX_BIND,
MT99XX_DATA
};
<<<<<<< HEAD
static void __attribute__((unused)) MT99XX_send_packet()
{
const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60};
const uint8_t mys_byte[] = {
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
};
static uint8_t yz_seq_num=0;
if(sub_protocol != YZ)
{ // MT99XX & H7
packet[0] = convert_channel_8b_scale(THROTTLE,0x00,0xE1); // throttle
packet[1] = convert_channel_8b_scale(RUDDER ,0x00,0xE1); // rudder
packet[2] = convert_channel_8b_scale(AILERON ,0x00,0xE1); // aileron
packet[3] = convert_channel_8b_scale(ELEVATOR,0x00,0xE1); // elevator
packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00)
packet[5] = 0x20; // roll trim (0x00-0x20-0x3f)
packet[6] = GET_FLAG( Servo_AUX1, FLAG_MT_FLIP )
| GET_FLAG( Servo_AUX3, FLAG_MT_SNAPSHOT )
| GET_FLAG( Servo_AUX4, FLAG_MT_VIDEO );
if(sub_protocol==MT99)
packet[6] |= 0x40 | FLAG_MT_RATE2;
else
packet[6] |= FLAG_MT_RATE1; // max rate on H7
// todo: mys_byte = next channel index ?
// low nibble: index in chan list ?
// high nibble: 0->start from start of list, 1->start from end of list ?
packet[7] = mys_byte[hopping_frequency_no];
=======
const uint8_t h7_mys_byte[] = {
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
@ -101,6 +138,7 @@ static void __attribute__((unused)) MT99XX_send_packet()
ls_counter=0;
}
>>>>>>> refs/remotes/pascallanger/master
uint8_t result=checksum_offset;
for(uint8_t i=0; i<8; i++)
result += packet[i];
@ -109,9 +147,15 @@ static void __attribute__((unused)) MT99XX_send_packet()
else
{ // YZ
packet[0] = convert_channel_8b_scale(THROTTLE,0x00,0x64); // throttle
<<<<<<< HEAD
packet[1] = convert_channel_8b_scale(RUDDER ,0x00,0x64); // rudder
packet[2] = convert_channel_8b_scale(ELEVATOR,0x00,0x64); // elevator
packet[3] = convert_channel_8b_scale(AILERON ,0x00,0x64); // aileron
=======
packet[1] = convert_channel_8b_scale(RUDDER ,0x64,0x00); // rudder
packet[2] = convert_channel_8b_scale(ELEVATOR,0x00,0x64); // elevator
packet[3] = convert_channel_8b_scale(AILERON ,0x64,0x00); // aileron
>>>>>>> refs/remotes/pascallanger/master
if(packet_count++ >= 23)
{
yz_seq_num ++;
@ -132,10 +176,14 @@ static void __attribute__((unused)) MT99XX_send_packet()
packet[8] = 0xff;
}
<<<<<<< HEAD
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no] + channel_offset);
=======
if(sub_protocol == LS)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x2D); // LS always transmits on the same channel
else
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no] + channel_offset);
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, MT99XX_PACKET_SIZE);
@ -153,11 +201,16 @@ static void __attribute__((unused)) MT99XX_send_packet()
static void __attribute__((unused)) MT99XX_init()
{
NRF24L01_Initialize();
<<<<<<< HEAD
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_FlushTx();
=======
if(sub_protocol == YZ)
XN297_SetScrambledMode(XN297_UNSCRAMBLED);
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_FlushTx();
XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5);
>>>>>>> refs/remotes/pascallanger/master
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
@ -169,18 +222,31 @@ static void __attribute__((unused)) MT99XX_init()
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
<<<<<<< HEAD
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP) | (sub_protocol == YZ ? BV(XN297_UNSCRAMBLED):0) );
XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5);
=======
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) );
>>>>>>> refs/remotes/pascallanger/master
}
static void __attribute__((unused)) MT99XX_initialize_txid()
{
<<<<<<< HEAD
=======
rx_tx_addr[3] = 0xCC;
rx_tx_addr[4] = 0xCC;
>>>>>>> refs/remotes/pascallanger/master
if(sub_protocol == YZ)
{
rx_tx_addr[0] = 0x53; // test (SB id)
rx_tx_addr[1] = 0x00;
<<<<<<< HEAD
}
checksum_offset = (rx_tx_addr[0] + rx_tx_addr[1]) & 0xff;
=======
rx_tx_addr[2] = 0x00;
}
else
@ -189,6 +255,7 @@ static void __attribute__((unused)) MT99XX_initialize_txid()
else //MT99 & H7
rx_tx_addr[2] = 0x00;
checksum_offset = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2];
>>>>>>> refs/remotes/pascallanger/master
channel_offset = (((checksum_offset & 0xf0)>>4) + (checksum_offset & 0x0f)) % 8;
}
@ -200,16 +267,26 @@ uint16_t MT99XX_callback()
{
if (bind_counter == 0)
{
<<<<<<< HEAD
rx_tx_addr[2] = 0x00;
rx_tx_addr[3] = 0xCC;
rx_tx_addr[4] = 0xCC;
=======
>>>>>>> refs/remotes/pascallanger/master
// set tx address for data packets
XN297_SetTXAddr(rx_tx_addr, 5);
BIND_DONE;
}
else
{
<<<<<<< HEAD
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
=======
if(sub_protocol == LS)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x2D); // LS always transmits on the same channel
else
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
>>>>>>> refs/remotes/pascallanger/master
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, MT99XX_PACKET_SIZE); // bind packet
@ -236,6 +313,25 @@ uint16_t initMT99XX(void)
MT99XX_init();
packet[0] = 0x20;
<<<<<<< HEAD
if(sub_protocol!=YZ)
{ // MT99 & H7
packet_period = MT99XX_PACKET_PERIOD_MT;
packet[1] = 0x14;
packet[2] = 0x03;
packet[3] = 0x25;
}
else
{ // YZ
packet_period = MT99XX_PACKET_PERIOD_YZ;
packet[1] = 0x15;
packet[2] = 0x05;
packet[3] = 0x06;
}
packet[4] = rx_tx_addr[0]; // 1st byte for data state tx address
packet[5] = rx_tx_addr[1]; // 2nd byte for data state tx address (always 0x00 on Yi Zhan ?)
packet[6] = 0x00; // 3rd byte for data state tx address (always 0x00 ?)
=======
packet_period = MT99XX_PACKET_PERIOD_MT;
switch(sub_protocol)
{ // MT99 & H7
@ -260,6 +356,7 @@ uint16_t initMT99XX(void)
packet[4] = rx_tx_addr[0];
packet[5] = rx_tx_addr[1];
packet[6] = rx_tx_addr[2];
>>>>>>> refs/remotes/pascallanger/master
packet[7] = checksum_offset; // checksum offset
packet[8] = 0xAA; // fixed
packet_count=0;

View File

@ -1,11 +1,17 @@
/*********************************************************
Multiprotocol Tx code
by Midelic and Pascal Langer(hpnuts)
http://www.rcgroups.com/forums/showthread.php?t=2165676
Multiprotocol Tx code
by Midelic and Pascal Langer(hpnuts)
fork by Tipouic
http://www.rcgroups.com/forums/showthread.php?t=2165676
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/edit/master/README.md
<<<<<<< HEAD
Thanks to PhracturedBlue, Hexfet, Goebish and all protocol developers
Ported from deviation firmware
=======
Thanks to PhracturedBlue, Hexfet, Goebish, Victzh and all protocol developers
Ported from deviation firmware
>>>>>>> refs/remotes/pascallanger/master
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
@ -22,12 +28,19 @@
*/
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
<<<<<<< HEAD
#include <util/delay.h>
=======
//#define DEBUG_TX
#include "Pins.h"
>>>>>>> refs/remotes/pascallanger/master
#include "Multiprotocol.h"
//Multiprotocol module configuration file
#include "_Config.h"
<<<<<<< HEAD
=======
#include "TX_Def.h"
#ifdef XMEGA
@ -40,6 +53,7 @@
#define DSM_TELEMETRY // Enable DSM telemetry
#endif
>>>>>>> refs/remotes/pascallanger/master
//Global constants/variables
uint32_t MProtocol_id;//tx id,
uint32_t MProtocol_id_master;
@ -53,16 +67,40 @@ uint8_t packet[40];
// Servo data
uint16_t Servo_data[NUM_CHN];
uint8_t Servo_AUX;
<<<<<<< HEAD
const uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
// Protocol variables
uint8_t rx_tx_addr[5];
uint8_t phase;
=======
uint16_t servo_max_100,servo_min_100,servo_max_125,servo_min_125;
// Protocol variables
uint8_t cyrfmfg_id[6];//for dsm2 and devo
uint8_t rx_tx_addr[5];
uint8_t phase;
>>>>>>> refs/remotes/pascallanger/master
uint16_t bind_counter;
uint8_t bind_phase;
uint8_t binding_idx;
uint16_t packet_period;
<<<<<<< HEAD
uint8_t packet_count;
uint8_t packet_sent;
uint8_t packet_length;
uint8_t hopping_frequency[23];
uint8_t *hopping_frequency_ptr;
uint8_t hopping_frequency_no=0;
uint8_t rf_ch_num;
uint8_t throttle, rudder, elevator, aileron;
uint8_t flags;
uint16_t crc;
//
uint32_t state;
uint8_t len;
uint8_t RX_num;
=======
uint8_t packet_count;
uint8_t packet_sent;
uint8_t packet_length;
@ -89,6 +127,7 @@ const uint8_t CH_AETR[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3,
const uint8_t CH_TAER[]={THROTTLE, AILERON, ELEVATOR, RUDDER, AUX1, AUX2, AUX3, AUX4, AUX5, AUX6, AUX7, AUX8};
const uint8_t CH_RETA[]={RUDDER, ELEVATOR, THROTTLE, AILERON, AUX1, AUX2, AUX3, AUX4, AUX5, AUX6, AUX7, AUX8};
const uint8_t CH_EATR[]={ELEVATOR, AILERON, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4, AUX5, AUX6, AUX7, AUX8};
>>>>>>> refs/remotes/pascallanger/master
// Mode_select variables
uint8_t mode_select;
@ -97,10 +136,20 @@ uint8_t protocol_flags=0,protocol_flags2=0;
// PPM variable
volatile uint16_t PPM_data[NUM_CHN];
<<<<<<< HEAD
// Serial variables
#define RXBUFFER_SIZE 25
#define TXBUFFER_SIZE 12
volatile uint8_t rx_buff[RXBUFFER_SIZE];
volatile uint8_t rx_ok_buff[RXBUFFER_SIZE];
volatile uint8_t tx_buff[TXBUFFER_SIZE];
volatile uint8_t idx = 0;
=======
#ifndef XMEGA
//Random variable
volatile uint32_t gWDT_entropy=0;
#endif
>>>>>>> refs/remotes/pascallanger/master
//Serial protocol
uint8_t sub_protocol;
@ -143,6 +192,11 @@ volatile uint8_t discard_frame = 0;
#define MAX_PKT 27
uint8_t pkt[MAX_PKT];//telemetry receiving packets
#if defined(TELEMETRY)
<<<<<<< HEAD
uint8_t pktt[MAX_PKT];//telemetry receiving packets
volatile uint8_t tx_head;
volatile uint8_t tx_tail;
=======
#ifdef INVERT_TELEMETRY
// enable bit bash for serial
#ifndef XMEGA
@ -158,6 +212,7 @@ uint8_t pkt[MAX_PKT];//telemetry receiving packets
volatile uint8_t tx_head=0;
volatile uint8_t tx_tail=0;
#endif // BASH_SERIAL
>>>>>>> refs/remotes/pascallanger/master
uint8_t v_lipo;
int16_t RSSI_dBm;
//const uint8_t RSSI_offset=72;//69 71.72 values db
@ -229,6 +284,19 @@ void setup()
#endif
// Set Chip selects
<<<<<<< HEAD
CS_on;
CC25_CSN_on;
NRF_CSN_on;
CYRF_CSN_on;
// Set SPI lines
SDI_on;
SCK_off;
// Timer1 config
TCCR1A = 0;
TCCR1B = (1 << CS11); //prescaler8, set timer1 to increment every 0.5us(16Mhz) and start timer
=======
#ifdef A7105_INSTALLED
A7105_CSN_on;
#endif
@ -244,10 +312,42 @@ void setup()
// Set SPI lines
SDI_on;
SCLK_off;
>>>>>>> refs/remotes/pascallanger/master
// Set servos positions
for(uint8_t i=0;i<NUM_CHN;i++)
Servo_data[i]=1500;
<<<<<<< HEAD
Servo_data[THROTTLE]=PPM_MIN_100;
memcpy((void *)PPM_data,Servo_data, sizeof(Servo_data));
//Wait for every component to start
delay(100);
// Read status of bind button
if( (PINB & _BV(5)) == 0x00 )
BIND_BUTTON_FLAG_on; // If bind button pressed save the status for protocol id reset under hubsan
// Read status of mode select binary switch
// after this mode_select will be one of {0000, 0001, ..., 1111}
mode_select=0x0F - ( ( (PINB>>2)&0x07 ) | ( (PINC<<3)&0x08) );//encoder dip switches 1,2,4,8=>B2,B3,B4,C0
//**********************************
//mode_select=1; // here to test PPM
//**********************************
// Update LED
LED_OFF;
LED_SET_OUTPUT;
// Read or create protocol id
MProtocol_id_master=random_id(10,false);
//Init RF modules
#ifdef CC2500_INSTALLED
CC2500_Reset();
#endif
=======
Servo_data[THROTTLE]=servo_min_100;
#ifdef ENABLE_PPM
memcpy((void *)PPM_data,Servo_data, sizeof(Servo_data));
@ -288,12 +388,17 @@ void setup()
MProtocol_id_master=random_id(10,false);
#ifdef ENABLE_PPM
>>>>>>> refs/remotes/pascallanger/master
//Protocol and interrupts initialization
if(mode_select != MODE_SERIAL)
{ // PPM
mode_select--;
<<<<<<< HEAD
cur_protocol[0] = PPM_prot[mode_select].protocol;
=======
protocol = PPM_prot[mode_select].protocol;
cur_protocol[1] = protocol;
>>>>>>> refs/remotes/pascallanger/master
sub_protocol = PPM_prot[mode_select].sub_proto;
RX_num = PPM_prot[mode_select].rx_num;
MProtocol_id = RX_num + MProtocol_id_master;
@ -301,6 +406,16 @@ void setup()
if(PPM_prot[mode_select].power) POWER_FLAG_on;
if(PPM_prot[mode_select].autobind) AUTOBIND_FLAG_on;
mode_select++;
<<<<<<< HEAD
protocol_init();
//Configure PPM interrupt
EICRA |=(1<<ISC11); // The rising edge of INT1 pin D3 generates an interrupt request
EIMSK |= (1<<INT1); // INT1 interrupt enable
#if defined(TELEMETRY)
PPM_Telemetry_serial_init(); // Configure serial for telemetry
=======
servo_max_100=PPM_MAX_100; servo_min_100=PPM_MIN_100;
servo_max_125=PPM_MAX_125; servo_min_125=PPM_MIN_125;
@ -319,6 +434,7 @@ void setup()
#if defined(TELEMETRY)
PPM_Telemetry_serial_init(); // Configure serial for telemetry
>>>>>>> refs/remotes/pascallanger/master
#endif
}
else
@ -338,6 +454,39 @@ void setup()
// Main
// Protocol scheduler
void loop()
<<<<<<< HEAD
{
if(mode_select==MODE_SERIAL && IS_RX_FLAG_on) // Serial mode and something has been received
{
update_serial_data(); // Update protocol and data
update_aux_flags();
if(IS_CHANGE_PROTOCOL_FLAG_on)
{ // Protocol needs to be changed
LED_OFF; //led off during protocol init
module_reset(); //reset previous module
protocol_init(); //init new protocol
CHANGE_PROTOCOL_FLAG_off; //done
}
}
if(mode_select!=MODE_SERIAL && IS_PPM_FLAG_on) // PPM mode and a full frame has been received
{
for(uint8_t i=0;i<NUM_CHN;i++)
{ // update servo data without interrupts to prevent bad read in protocols
cli(); // disable global int
Servo_data[i]=PPM_data[i];
sei(); // enable global int
}
update_aux_flags();
PPM_FLAG_off; // wait for next frame before update
}
update_led_status();
#if defined(TELEMETRY)
if( ((cur_protocol[0]&0x1F)==MODE_FRSKY) || ((cur_protocol[0]&0x1F)==MODE_HUBSAN) || ((cur_protocol[0]&0x1F)==MODE_FRSKYX) )
frskyUpdate();
#endif
if (remote_callback != 0)
CheckTimer(remote_callback);
=======
{
uint16_t next_callback,diff=0xFFFF;
@ -428,6 +577,7 @@ void Update_All()
if((protocol==MODE_FRSKYD) || (protocol==MODE_HUBSAN) || (protocol==MODE_FRSKYX) || (protocol==MODE_DSM) )
TelemetryUpdate();
#endif
>>>>>>> refs/remotes/pascallanger/master
}
// Update Servo_AUX flags based on servo AUX positions
@ -444,18 +594,64 @@ static void update_led_status(void)
{
if(blink<millis())
{
<<<<<<< HEAD
if(cur_protocol[0]==0) // No valid serial received at least once
blink+=BLINK_SERIAL_TIME; //blink slowly while waiting a valid serial input
=======
if(cur_protocol[1]==0) //No valid serial received at least once
blink+=BLINK_SERIAL_TIME; //blink slowly while waiting a valid serial input
>>>>>>> refs/remotes/pascallanger/master
else
if(remote_callback == 0)
{ // Invalid protocol
if(IS_LED_on) //flash to indicate invalid protocol
blink+=BLINK_BAD_PROTO_TIME_LOW;
else
blink+=BLINK_BAD_PROTO_TIME_HIGH;
}
if(IS_LED_on) //flash to indicate invalid protocol
blink+=BLINK_BAD_PROTO_TIME_LOW;
else
blink+=BLINK_BAD_PROTO_TIME_HIGH;
}
else
if(IS_BIND_DONE_on)
<<<<<<< HEAD
LED_OFF; //bind completed -> led on
else
blink+=BLINK_BIND_TIME; //blink fastly during binding
LED_TOGGLE;
}
}
// Protocol scheduler
static void CheckTimer(uint16_t (*cb)(void))
{
uint16_t next_callback;
uint32_t prev;
if( (TIFR1 & (1<<OCF1A)) != 0)
{
cli(); // disable global int
OCR1A=TCNT1; // Callback should already have been called... Use "now" as new sync point.
sei(); // enable global int
}
else
while((TIFR1 & (1<<OCF1A)) == 0); // wait before callback
prev=micros();
next_callback=cb();
if(prev+next_callback+50 > micros())
{ // Callback did not took more than requested time for next callback
if(next_callback>32000)
{ // next_callback should not be more than 32767 so we will wait here...
delayMicroseconds(next_callback-2000);
cli(); // disable global int
OCR1A=TCNT1+4000;
sei(); // enable global int
}
else
{
cli(); // disable global int
OCR1A+=next_callback*2; // set compare A for callback
sei(); // enable global int
}
TIFR1=(1<<OCF1A); // clear compare A=callback flag
}
=======
LED_off; //bind completed -> led on
else
blink+=BLINK_BIND_TIME; //blink fastly during binding
@ -494,6 +690,7 @@ inline void tx_resume()
#endif
}
#endif
>>>>>>> refs/remotes/pascallanger/master
}
// Protocol start
@ -517,10 +714,227 @@ static void protocol_init()
if(IS_BIND_BUTTON_FLAG_on)
AUTOBIND_FLAG_on;
if(IS_AUTOBIND_FLAG_on)
BIND_IN_PROGRESS; // Indicates bind in progress for blinking bind led
BIND_IN_PROGRESS; // Indicates bind in progress for blinking bind led
else
BIND_DONE;
<<<<<<< HEAD
CTRL1_on; //NRF24L01 antenna RF3 by default
CTRL2_off; //NRF24L01 antenna RF3 by default
switch(cur_protocol[0]&0x1F) // Init the requested protocol
{
#if defined(HM830_NRF24L01_INO)
case MODE_HM830:
next_callback=HM830_setup();
remote_callback = HM830_callback;
break;
#endif
#if defined(CFlie_NRF24L01_INO)
case MODE_CFLIE:
next_callback=Cflie_setup();
remote_callback = cflie_callback;
break;
#endif
#if defined(JOYSWAY_A7105_INO)
case MODE_JOYSWAY:
next_callback=JOYSWAY_Setup();
remote_callback = joysway_cb;
break;
#endif
#if defined(H377_NRF24L01_INO)
case MODE_H377:
next_callback=h377_setup();
remote_callback = h377_cb;
break;
#endif
#if defined(J6PRO_CYRF6936_INO)
case MODE_J6PRO:
next_callback=j6pro_setup();
remote_callback = j6pro_cb;
break;
#endif
#if defined(WK2x01_CYRF6936_INO)
case MODE_WK2x01:
next_callback=wk_setup();
remote_callback = wk_cb;
break;
#endif
#if defined(ESKY150_NRF24L01_INO)
case MODE_ESKY150:
next_callback=esky150_setup();
remote_callback = esky150_callback;
break;
#endif
#if defined(BlueFly_NRF24L01_INO)
case MODE_BlueFly:
next_callback=BlueFly_setup();
remote_callback = bluefly_cb;
break;
#endif
#if defined(HonTai_NRF24L01_INO)
case MODE_HonTai:
next_callback=ht_setup();
remote_callback = ht_callback;
break;
#endif
#if defined(UDI_NRF24L01_INO)
case MODE_UDI:
next_callback=UDI_setup();
remote_callback = UDI_callback;
break;
#endif
#if defined(NE260_NRF24L01_INO)
case MODE_NE260:
next_callback=NE260_setup();
remote_callback = ne260_cb;
break;
#endif
#if defined(SKYARTEC_CC2500_INO)
case MODE_SKYARTEC:
next_callback=skyartec_setup();
remote_callback = skyartec_cb;
break;
#endif
#if defined(FBL100_NRF24L01_INO)
case MODE_FBL100:
next_callback=fbl_setup();
remote_callback = ne260_cb;
break;
#endif
#if defined(FLYSKY_A7105_INO)
case MODE_FLYSKY:
CTRL1_off; //antenna RF1
next_callback = initFlySky();
remote_callback = ReadFlySky;
break;
#endif
#if defined(HUBSAN_A7105_INO)
case MODE_HUBSAN:
CTRL1_off; //antenna RF1
if(IS_BIND_BUTTON_FLAG_on) random_id(10,true); // Generate new ID if bind button is pressed.
next_callback = initHubsan();
remote_callback = ReadHubsan;
break;
#endif
#if defined(FRSKY_CC2500_INO)
case MODE_FRSKY:
CTRL1_off; //antenna RF2
CTRL2_on;
next_callback = initFrSky_2way();
remote_callback = ReadFrSky_2way;
break;
#endif
#if defined(FRSKYX_CC2500_INO)
case MODE_FRSKYX:
CTRL1_off; //antenna RF2
CTRL2_on;
next_callback = initFrSkyX();
remote_callback = ReadFrSkyX;
break;
#endif
#if defined(DSM2_CYRF6936_INO)
case MODE_DSM2:
CTRL2_on; //antenna RF4
next_callback = initDsm2();
//Servo_data[2]=1500;//before binding
remote_callback = ReadDsm2;
break;
#endif
#if defined(DEVO_CYRF6936_INO)
case MODE_DEVO:
CTRL2_on; //antenna RF4
next_callback = DevoInit();
remote_callback = devo_callback;
break;
#endif
#if defined(HISKY_NRF24L01_INO)
case MODE_HISKY:
next_callback=initHiSky();
remote_callback = hisky_cb;
break;
#endif
#if defined(V2X2_NRF24L01_INO)
case MODE_V2X2:
next_callback = initV2x2();
remote_callback = ReadV2x2;
break;
#endif
#if defined(YD717_NRF24L01_INO)
case MODE_YD717:
next_callback=initYD717();
remote_callback = yd717_callback;
break;
#endif
#if defined(KN_NRF24L01_INO)
case MODE_KN:
next_callback = initKN();
remote_callback = kn_callback;
break;
#endif
#if defined(SYMAX_NRF24L01_INO)
case MODE_SYMAX:
next_callback = initSymax();
remote_callback = symax_callback;
break;
#endif
#if defined(SLT_NRF24L01_INO)
case MODE_SLT:
next_callback=initSLT();
remote_callback = SLT_callback;
break;
#endif
#if defined(CX10_NRF24L01_INO)
case MODE_CX10:
next_callback=initCX10();
remote_callback = CX10_callback;
break;
#endif
#if defined(CG023_NRF24L01_INO)
case MODE_CG023:
next_callback=initCG023();
remote_callback = CG023_callback;
break;
#endif
#if defined(BAYANG_NRF24L01_INO)
case MODE_BAYANG:
next_callback=initBAYANG();
remote_callback = BAYANG_callback;
break;
#endif
#if defined(ESKY_NRF24L01_INO)
case MODE_ESKY:
next_callback=initESKY();
remote_callback = ESKY_callback;
break;
#endif
#if defined(MT99XX_NRF24L01_INO)
case MODE_MT99XX:
next_callback=initMT99XX();
remote_callback = MT99XX_callback;
break;
#endif
#if defined(MJXQ_NRF24L01_INO)
case MODE_MJXQ:
next_callback=initMJXQ();
remote_callback = MJXQ_callback;
break;
#endif
#if defined(SHENQI_NRF24L01_INO)
case MODE_SHENQI:
next_callback=initSHENQI();
remote_callback = SHENQI_callback;
break;
#endif
#if defined(FY326_NRF24L01_INO)
case MODE_FY326:
next_callback=initFY326();
remote_callback = FY326_callback;
break;
#endif
}
=======
PE1_on; //NRF24L01 antenna RF3 by default
PE2_off; //NRF24L01 antenna RF3 by default
@ -721,6 +1135,7 @@ static void protocol_init()
#endif
#endif
}
>>>>>>> refs/remotes/pascallanger/master
if(next_callback>32000)
{ // next_callback should not be more than 32767 so we will wait here...
@ -728,15 +1143,39 @@ static void protocol_init()
delayMilliseconds(temp);
next_callback-=temp<<10; // between 2-3ms left at this stage
}
<<<<<<< HEAD
cli(); // disable global int
OCR1A=TCNT1+next_callback*2; // set compare A for callback
sei(); // enable global int
TIFR1=(1<<OCF1A); // clear compare A flag
BIND_BUTTON_FLAG_off; // do not bind/reset id anymore even if protocol change
=======
cli(); // disable global int
OCR1A = TCNT1 + next_callback*2; // set compare A for callback
sei(); // enable global int
TIFR1 = OCF1A_bm ; // clear compare A flag
BIND_BUTTON_FLAG_off; // do not bind/reset id anymore even if protocol change
>>>>>>> refs/remotes/pascallanger/master
}
void update_serial_data()
{
<<<<<<< HEAD
if(rx_ok_buff[0]&0x20) //check range
RANGE_FLAG_on;
else
RANGE_FLAG_off;
if(rx_ok_buff[0]&0xC0) //check autobind(0x40) & bind(0x80) together
AUTOBIND_FLAG_on;
else
AUTOBIND_FLAG_off;
if(rx_ok_buff[1]&0x80) //if rx_ok_buff[1] ==1,power is low ,0-power high
POWER_FLAG_off; //power low
else
POWER_FLAG_on; //power high
option=rx_ok_buff[2];
=======
RX_DONOTUPDTAE_on;
RX_FLAG_off; //data is being processed
if(rx_ok_buff[1]&0x20) //check range
@ -751,11 +1190,29 @@ void update_serial_data()
POWER_FLAG_off; //power low
else
POWER_FLAG_on; //power high
>>>>>>> refs/remotes/pascallanger/master
option=rx_ok_buff[3];
if( (rx_ok_buff[0] != cur_protocol[0]) || ((rx_ok_buff[1]&0x5F) != (cur_protocol[1]&0x5F)) || ( (rx_ok_buff[2]&0x7F) != (cur_protocol[2]&0x7F) ) )
{ // New model has been selected
<<<<<<< HEAD
prev_protocol=cur_protocol[0]&0x1F; //store previous protocol so we can reset the module
cur_protocol[1] = rx_ok_buff[1]&0x7F; //store current protocol
CHANGE_PROTOCOL_FLAG_on; //change protocol
sub_protocol=(rx_ok_buff[1]>>4)& 0x07; //subprotocol no (0-7) bits 4-6
RX_num=rx_ok_buff[1]& 0x0F;
MProtocol_id=MProtocol_id_master+RX_num; //personalized RX bind + rx num // rx_num bits 0---3
}
else
if( ((rx_ok_buff[0]&0x80)!=0) && ((cur_protocol[0]&0x80)==0) ) // Bind flag has been set
CHANGE_PROTOCOL_FLAG_on; //restart protocol with bind
cur_protocol[0] = rx_ok_buff[0]; //store current protocol
// decode channel values
volatile uint8_t *p=rx_ok_buff+2;
uint8_t dec=-3;
=======
CHANGE_PROTOCOL_FLAG_on; //change protocol
protocol=(rx_ok_buff[0]==0x55?0:32) + (rx_ok_buff[1]&0x1F); //protocol no (0-63) bits 4-6 of buff[1] and bit 0 of buf[0]
sub_protocol=(rx_ok_buff[2]>>4)& 0x07; //subprotocol no (0-7) bits 4-6
@ -775,11 +1232,46 @@ void update_serial_data()
// decode channel values
volatile uint8_t *p=rx_ok_buff+3;
uint8_t dec=-3;
>>>>>>> refs/remotes/pascallanger/master
for(uint8_t i=0;i<NUM_CHN;i++)
{
dec+=3;
dec+=3;
if(dec>=8)
{
<<<<<<< HEAD
dec-=8;
p++;
}
p++;
Servo_data[i]=((((*((uint32_t *)p))>>dec)&0x7FF)*5)/8+860; //value range 860<->2140 -125%<->+125%
}
RX_FLAG_off; //data has been processed
}
static void module_reset()
{
if(remote_callback)
{ // previous protocol loaded
remote_callback = 0;
switch(prev_protocol)
{
case MODE_FLYSKY:
case MODE_HUBSAN:
A7105_Reset();
break;
case MODE_FRSKY:
case MODE_FRSKYX:
CC2500_Reset();
break;
case MODE_DSM2:
case MODE_DEVO:
CYRF_Reset();
break;
default: // MODE_HISKY, MODE_V2X2, MODE_YD717, MODE_KN, MODE_SYMAX, MODE_SLT, MODE_CX10, MODE_CG023, MODE_BAYANG, MODE_ESKY, MODE_MT99XX, MODE_MJXQ, MODE_SHENQI, MODE_FY326
NRF24L01_Reset();
break;
}
=======
dec-=8;
p++;
}
@ -796,6 +1288,7 @@ void update_serial_data()
{ memcpy((void*)rx_ok_buff,(const void*)rx_buff,RXBUFFER_SIZE);// Duplicate the buffer
RX_FLAG_on; // data to be processed next time...
RX_MISSED_BUFF_off;
>>>>>>> refs/remotes/pascallanger/master
}
#ifdef XMEGA
sei();
@ -862,15 +1355,35 @@ void Mprotocol_serial_init()
#if defined(TELEMETRY)
void PPM_Telemetry_serial_init()
{
<<<<<<< HEAD
if(Servo_data[ch]>PPM_MAX_100)
return PPM_MAX_100;
else
if (Servo_data[ch]<PPM_MIN_100)
return PPM_MIN_100;
return Servo_data[ch];
=======
if( (protocol==MODE_FRSKYD) || (protocol==MODE_HUBSAN))
initTXSerial( SPEED_9600 ) ;
if(protocol==MODE_FRSKYX)
initTXSerial( SPEED_57600 ) ;
if(protocol==MODE_DSM)
initTXSerial( SPEED_125K ) ;
>>>>>>> refs/remotes/pascallanger/master
}
#endif
<<<<<<< HEAD
#if defined(TELEMETRY)
void Serial_write(uint8_t data)
{
cli(); // disable global int
if(++tx_head>=TXBUFFER_SIZE)
tx_head=0;
tx_buff[tx_head]=data;
sei(); // enable global int
UCSR0B |= (1<<UDRIE0);//enable UDRE interrupt
=======
// Convert 32b id to rx_tx_addr
static void set_rx_tx_addr(uint32_t id)
{ // Used by almost all protocols
@ -889,15 +1402,53 @@ static void random_init(void)
WDTCSR |= _BV(WDCE); // WDT control register, This sets the Watchdog Change Enable (WDCE) flag, which is needed to set the prescaler
WDTCSR = _BV(WDIE); // Watchdog interrupt enable (WDIE)
sei(); // Turn interupts on
>>>>>>> refs/remotes/pascallanger/master
}
static uint32_t random_value(void)
{
<<<<<<< HEAD
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0A = 0 ; // Clear X2 bit
//Set frame format to 8 data bits, even parity, 2 stop bits
UCSR0C = (1<<UPM01)|(1<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);
while ( UCSR0A & (1 << RXC0) )//flush receive buffer
UDR0;
//enable reception and RC complete interrupt
UCSR0B = (1<<RXEN0)|(1<<RXCIE0);//rx enable and interrupt
UCSR0B |= (1<<TXEN0);//tx enable
=======
while (!gWDT_entropy);
return gWDT_entropy;
>>>>>>> refs/remotes/pascallanger/master
}
#endif
#if defined(TELEMETRY)
static void PPM_Telemetry_serial_init()
{
//9600 bauds
UBRR0H = 0x00;
UBRR0L = 0x67;
UCSR0A = 0 ; // Clear X2 bit
//Set frame format to 8 data bits, none, 1 stop bit
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
UCSR0B = (1<<TXEN0);//tx enable
}
#endif
// Convert 32b id to rx_tx_addr
static void set_rx_tx_addr(uint32_t id)
{ // Used by almost all protocols
rx_tx_addr[0] = (id >> 24) & 0xFF;
rx_tx_addr[1] = (id >> 16) & 0xFF;
rx_tx_addr[2] = (id >> 8) & 0xFF;
rx_tx_addr[3] = (id >> 0) & 0xFF;
rx_tx_addr[4] = 0xC1; // for YD717: always uses first data port
}
static uint32_t random_id(uint16_t adress, uint8_t create_new)
{
uint32_t id;
@ -928,6 +1479,13 @@ static uint32_t random_id(uint16_t adress, uint8_t create_new)
/**************************/
//PPM
<<<<<<< HEAD
ISR(INT1_vect)
{ // Interrupt on PPM pin
static int8_t chan=-1;
static uint16_t Prev_TCNT1=0;
uint16_t Cur_TCNT1;
=======
#ifdef ENABLE_PPM
#ifdef XMEGA
#if PPM_pin == 2
@ -946,6 +1504,7 @@ static uint32_t random_id(uint16_t adress, uint8_t create_new)
static int8_t chan=-1;
static uint16_t Prev_TCNT1=0;
uint16_t Cur_TCNT1;
>>>>>>> refs/remotes/pascallanger/master
Cur_TCNT1 = TCNT1 - Prev_TCNT1 ; // Capture current Timer1 value
if(Cur_TCNT1<1000)
@ -956,6 +1515,19 @@ static uint32_t random_id(uint16_t adress, uint8_t create_new)
chan=0; // start of frame
PPM_FLAG_on; // full frame present (even at startup since PPM_data has been initialized)
}
<<<<<<< HEAD
Prev_TCNT1+=Cur_TCNT1;
}
//Serial RX
ISR(USART_RX_vect)
{ // RX interrupt
if((UCSR0A&0x1C)==0) // Check frame error, data overrun and parity error
{ // received byte is ok to process
if(idx==0)
{ // Let's try to sync at this point
if(UDR0==0x55) // If 1st byte is 0x55 it looks ok
=======
else
if(chan!=-1) // need to wait for start of frame
{ //servo values between 500us and 2420us will end up here
@ -1000,6 +1572,7 @@ static uint32_t random_id(uint16_t adress, uint8_t create_new)
}
}
else
>>>>>>> refs/remotes/pascallanger/master
{
rx_buff[idx++]=UDR0; // Store received byte
if(idx>=RXBUFFER_SIZE)
@ -1070,4 +1643,30 @@ static uint32_t random_id(uint16_t adress, uint8_t create_new)
WDTCSR = 0; // Disable Watchdog interrupt
}
}
<<<<<<< HEAD
}
//Serial timer
ISR(TIMER1_COMPB_vect)
{ // Timer1 compare B interrupt
idx=0;
}
#if defined(TELEMETRY)
//Serial TX
ISR(USART_UDRE_vect)
{ // Transmit interrupt
uint8_t t = tx_tail;
if(tx_head!=t)
{
if(++t>=TXBUFFER_SIZE)//head
t=0;
UDR0=tx_buff[t];
tx_tail=t;
}
if (t == tx_head)
UCSR0B &= ~(1<<UDRIE0); // Check if all data is transmitted . if yes disable transmitter UDRE interrupt
}
=======
>>>>>>> refs/remotes/pascallanger/master
#endif

View File

@ -234,7 +234,11 @@ uint8_t NRF24L01_packet_ack()
///////////////
// XN297 emulation layer
<<<<<<< HEAD
uint8_t xn297_scramble_enabled;
=======
uint8_t xn297_scramble_enabled=XN297_SCRAMBLED; //enabled by default
>>>>>>> refs/remotes/pascallanger/master
uint8_t xn297_addr_len;
uint8_t xn297_tx_addr[5];
uint8_t xn297_rx_addr[5];
@ -247,6 +251,16 @@ static const uint8_t xn297_scramble[] = {
0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f,
0x8e, 0xc5, 0x2f};
<<<<<<< HEAD
const uint16_t PROGMEM 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};
=======
>>>>>>> refs/remotes/pascallanger/master
const uint16_t PROGMEM xn297_crc_xorout_scrambled[] = {
0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C,
0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x814B,
@ -320,8 +334,14 @@ void XN297_SetRXAddr(const uint8_t* addr, uint8_t len)
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, buf, 5);
}
void XN297_Configure(uint8_t flags)
void XN297_Configure(uint16_t flags)
{
<<<<<<< HEAD
xn297_scramble_enabled = !(flags & BV(XN297_UNSCRAMBLED));
xn297_crc = !!(flags & BV(NRF24L01_00_EN_CRC));
flags &= ~(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags & 0xFF);
=======
xn297_crc = !!(flags & _BV(NRF24L01_00_EN_CRC));
flags &= ~(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO));
NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags & 0xFF);
@ -330,6 +350,7 @@ void XN297_Configure(uint8_t flags)
void XN297_SetScrambledMode(const u8 mode)
{
xn297_scramble_enabled = mode;
>>>>>>> refs/remotes/pascallanger/master
}
void XN297_WritePayload(uint8_t* msg, uint8_t len)
@ -391,6 +412,32 @@ void XN297_ReadPayload(uint8_t* msg, uint8_t len)
// End of XN297 emulation
///////////////
<<<<<<< HEAD
// LT8910 emulation layer
uint8_t LT8910_buffer[64];
uint8_t LT8910_buffer_start;
uint16_t LT8910_buffer_overhead_bits;
uint8_t LT8910_addr[8];
uint8_t LT8910_addr_size;
uint8_t LT8910_Preamble_Len;
uint8_t LT8910_Tailer_Len;
uint8_t LT8910_CRC_Initial_Data;
uint8_t LT8910_Flags;
#define LT8910_CRC_ON 6
#define LT8910_SCRAMBLE_ON 5
#define LT8910_PACKET_LENGTH_EN 4
#define LT8910_DATA_PACKET_TYPE_1 3
#define LT8910_DATA_PACKET_TYPE_0 2
#define LT8910_FEC_TYPE_1 1
#define LT8910_FEC_TYPE_0 0
void LT8910_Config(uint8_t preamble_len, uint8_t trailer_len, uint8_t flags, uint8_t crc_init)
{
//Preamble 1 to 8 bytes
LT8910_Preamble_Len=preamble_len;
//Trailer 4 to 18 bits
LT8910_Tailer_Len=trailer_len;
=======
// LT8900 emulation layer
uint8_t LT8900_buffer[64];
uint8_t LT8900_buffer_start;
@ -415,23 +462,37 @@ void LT8900_Config(uint8_t preamble_len, uint8_t trailer_len, uint8_t flags, uin
LT8900_Preamble_Len=preamble_len;
//Trailer 4 to 18 bits
LT8900_Tailer_Len=trailer_len;
>>>>>>> refs/remotes/pascallanger/master
//Flags
// CRC_ON: 1 on, 0 off
// SCRAMBLE_ON: 1 on, 0 off
// PACKET_LENGTH_EN: 1 1st byte of payload is payload size
// DATA_PACKET_TYPE: 00 NRZ, 01 Manchester, 10 8bit/10bit line code, 11 interleave data type
// FEC_TYPE: 00 No FEC, 01 FEC13, 10 FEC23, 11 reserved
<<<<<<< HEAD
LT8910_Flags=flags;
//CRC init constant
LT8910_CRC_Initial_Data=crc_init;
}
void LT8910_SetChannel(uint8_t channel)
=======
LT8900_Flags=flags;
//CRC init constant
LT8900_CRC_Initial_Data=crc_init;
}
void LT8900_SetChannel(uint8_t channel)
>>>>>>> refs/remotes/pascallanger/master
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel +2); //NRF24L01 is 2400+channel but LT8900 is 2402+channel
}
<<<<<<< HEAD
void LT8910_SetTxRxMode(enum TXRX_State mode)
=======
void LT8900_SetTxRxMode(enum TXRX_State mode)
>>>>>>> refs/remotes/pascallanger/master
{
if(mode == TX_EN)
{
@ -457,12 +518,35 @@ void LT8900_SetTxRxMode(enum TXRX_State mode)
NRF24L01_SetTxRxMode(TXRX_OFF);
}
<<<<<<< HEAD
void LT8910_BuildOverhead()
=======
void LT8900_BuildOverhead()
>>>>>>> refs/remotes/pascallanger/master
{
uint8_t pos;
//Build overhead
//preamble
<<<<<<< HEAD
memset(LT8910_buffer,LT8910_addr[0]&0x01?0xAA:0x55,LT8910_Preamble_Len-1);
pos=LT8910_Preamble_Len-1;
//address
for(uint8_t i=0;i<LT8910_addr_size;i++)
{
LT8910_buffer[pos]=bit_reverse(LT8910_addr[i]);
pos++;
}
//trailer
memset(LT8910_buffer+pos,(LT8910_buffer[pos-1]&0x01)==0?0xAA:0x55,3);
LT8910_buffer_overhead_bits=pos*8+LT8910_Tailer_Len;
//nrf address length max is 5
pos+=LT8910_Tailer_Len/8;
LT8910_buffer_start=pos>5?5:pos;
}
void LT8910_SetAddress(uint8_t *address,uint8_t addr_size)
=======
memset(LT8900_buffer,LT8900_addr[0]&0x01?0xAA:0x55,LT8900_Preamble_Len-1);
pos=LT8900_Preamble_Len-1;
//address
@ -480,10 +564,33 @@ void LT8900_BuildOverhead()
}
void LT8900_SetAddress(uint8_t *address,uint8_t addr_size)
>>>>>>> refs/remotes/pascallanger/master
{
uint8_t addr[5];
//Address size (SyncWord) 2 to 8 bytes, 16/32/48/64 bits
<<<<<<< HEAD
LT8910_addr_size=addr_size;
memcpy(LT8910_addr,address,LT8910_addr_size);
//Build overhead
LT8910_BuildOverhead();
//Set NRF RX&TX address based on overhead content
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, LT8910_buffer_start-2);
for(uint8_t i=0;i<LT8910_buffer_start;i++) // reverse bytes order
addr[i]=LT8910_buffer[LT8910_buffer_start-i-1];
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, addr,LT8910_buffer_start);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr,LT8910_buffer_start);
}
uint8_t LT8910_ReadPayload(uint8_t* msg, uint8_t len)
{
uint8_t i,pos=0,shift,end,buffer[32];
unsigned int crc=LT8910_CRC_Initial_Data,a;
pos=LT8910_buffer_overhead_bits/8-LT8910_buffer_start;
end=pos+len+(LT8910_Flags&_BV(LT8910_PACKET_LENGTH_EN)?1:0)+(LT8910_Flags&_BV(LT8910_CRC_ON)?2:0);
=======
LT8900_addr_size=addr_size;
for (uint8_t i = 0; i < addr_size; i++)
LT8900_addr[i] = address[addr_size-1-i];
@ -505,14 +612,22 @@ uint8_t LT8900_ReadPayload(uint8_t* msg, uint8_t len)
unsigned int crc=LT8900_CRC_Initial_Data,a;
pos=LT8900_buffer_overhead_bits/8-LT8900_buffer_start;
end=pos+len+(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN)?1:0)+(LT8900_Flags&_BV(LT8900_CRC_ON)?2:0);
>>>>>>> refs/remotes/pascallanger/master
//Read payload
NRF24L01_ReadPayload(buffer,end+1);
//Check address + trail
for(i=0;i<pos;i++)
<<<<<<< HEAD
if(LT8910_buffer[LT8910_buffer_start+i]!=buffer[i])
return 0; // wrong address...
//Shift buffer to remove trail bits
shift=LT8910_buffer_overhead_bits&0x7;
=======
if(LT8900_buffer[LT8900_buffer_start+i]!=buffer[i])
return 0; // wrong address...
//Shift buffer to remove trail bits
shift=LT8900_buffer_overhead_bits&0x7;
>>>>>>> refs/remotes/pascallanger/master
for(i=pos;i<end;i++)
{
a=(buffer[i]<<8)+buffer[i+1];
@ -520,7 +635,11 @@ uint8_t LT8900_ReadPayload(uint8_t* msg, uint8_t len)
buffer[i]=(a>>8)&0xFF;
}
//Check len
<<<<<<< HEAD
if(LT8910_Flags&_BV(LT8910_PACKET_LENGTH_EN))
=======
if(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN))
>>>>>>> refs/remotes/pascallanger/master
{
crc=crc16_update(crc,buffer[pos]);
if(bit_reverse(len)!=buffer[pos++])
@ -533,7 +652,11 @@ uint8_t LT8900_ReadPayload(uint8_t* msg, uint8_t len)
msg[i]=bit_reverse(buffer[pos++]);
}
//Check CRC
<<<<<<< HEAD
if(LT8910_Flags&_BV(LT8910_CRC_ON))
=======
if(LT8900_Flags&_BV(LT8900_CRC_ON))
>>>>>>> refs/remotes/pascallanger/master
{
if(buffer[pos++]!=((crc>>8)&0xFF)) return 0; // wrong CRC...
if(buffer[pos]!=(crc&0xFF)) return 0; // wrong CRC...
@ -542,12 +665,21 @@ uint8_t LT8900_ReadPayload(uint8_t* msg, uint8_t len)
return 1;
}
<<<<<<< HEAD
void LT8910_WritePayload(uint8_t* msg, uint8_t len)
{
unsigned int crc=LT8910_CRC_Initial_Data,a,mask;
uint8_t i, pos=0,tmp, buffer[64], pos_final,shift;
//Add packet len
if(LT8910_Flags&_BV(LT8910_PACKET_LENGTH_EN))
=======
void LT8900_WritePayload(uint8_t* msg, uint8_t len)
{
unsigned int crc=LT8900_CRC_Initial_Data,a,mask;
uint8_t i, pos=0,tmp, buffer[64], pos_final,shift;
//Add packet len
if(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN))
>>>>>>> refs/remotes/pascallanger/master
{
tmp=bit_reverse(len);
buffer[pos++]=tmp;
@ -561,12 +693,27 @@ void LT8900_WritePayload(uint8_t* msg, uint8_t len)
crc=crc16_update(crc,tmp);
}
//Add CRC
<<<<<<< HEAD
if(LT8910_Flags&_BV(LT8910_CRC_ON))
=======
if(LT8900_Flags&_BV(LT8900_CRC_ON))
>>>>>>> refs/remotes/pascallanger/master
{
buffer[pos++]=crc>>8;
buffer[pos++]=crc;
}
//Shift everything to fit behind the trailer (4 to 18 bits)
<<<<<<< HEAD
shift=LT8910_buffer_overhead_bits&0x7;
pos_final=LT8910_buffer_overhead_bits/8;
mask=~(0xFF<<(8-shift));
LT8910_buffer[pos_final+pos]=0xFF;
for(i=pos-1;i!=0xFF;i--)
{
a=buffer[i]<<(8-shift);
LT8910_buffer[pos_final+i]=(LT8910_buffer[pos_final+i]&mask>>8)|a>>8;
LT8910_buffer[pos_final+i+1]=(LT8910_buffer[pos_final+i+1]&mask)|a;
=======
shift=LT8900_buffer_overhead_bits&0x7;
pos_final=LT8900_buffer_overhead_bits/8;
mask=~(0xFF<<(8-shift));
@ -576,10 +723,17 @@ void LT8900_WritePayload(uint8_t* msg, uint8_t len)
a=buffer[i]<<(8-shift);
LT8900_buffer[pos_final+i]=(LT8900_buffer[pos_final+i]&mask>>8)|a>>8;
LT8900_buffer[pos_final+i+1]=(LT8900_buffer[pos_final+i+1]&mask)|a;
>>>>>>> refs/remotes/pascallanger/master
}
if(shift)
pos++;
//Send everything
<<<<<<< HEAD
NRF24L01_WritePayload(LT8910_buffer+LT8910_buffer_start,pos_final+pos-LT8910_buffer_start);
}
// End of LT8910 emulation
=======
NRF24L01_WritePayload(LT8900_buffer+LT8900_buffer_start,pos_final+pos-LT8900_buffer_start);
}
// End of LT8900 emulation
>>>>>>> refs/remotes/pascallanger/master

View File

@ -0,0 +1,150 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(BlueFly_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_BlueFly_COUNT 800
#define TXID_BlueFly_SIZE 5
#define PAYLOAD_BlueFly_SIZE 12
// available frequency must be in between 2402 and 2477
static uint8_t hopping_frequency_start;
static uint8_t bluefly_binding_adr_rf[TXID_BlueFly_SIZE]={0x32,0xaa,0x45,0x45,0x78}; // fixed binding ids for all planes
static uint8_t bind_payload[PAYLOAD_BlueFly_SIZE];
static unsigned int ch_data_bluefly[8];
static void bluefly_binding_packet(void)
{
int i;
for (i = 0; i < TXID_BlueFly_SIZE; ++i)
bind_payload[i] = rx_tx_addr[i];
bind_payload[i++] = hopping_frequency_start;
for (; i < PAYLOAD_BlueFly_SIZE; ++i) bind_payload[i] = 0x55;
}
static void bluefly_init() {
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, PAYLOAD_BlueFly_SIZE); // payload size = 12
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81); // binding packet must be set in channel 81
// 2-bytes CRC, radio on
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2)
NRF24L01_SetBitrate(NRF24L01_BR_250K); // BlueFly - 250kbps
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
}
// HiSky channel sequence: AILE ELEV THRO RUDD GEAR PITCH, channel data value is from 0 to 1000
static void bluefly_ch_data() {
uint32_t temp;
int i;
for (i = 0; i< 8; ++i) {
temp = (uint32_t)Servo_data[ch[i]] * 300/PPM_MAX + 500; // 200-800 range
if (temp < 0)
ch_data_bluefly[i] = 0;
else if (temp > 1000)
ch_data_bluefly[i] = 1000;
else
ch_data_bluefly[i] = (unsigned int)temp;
packet[i] = (uint8_t)ch_data_bluefly[i];
}
packet[8] = (uint8_t)((ch_data_bluefly[0]>>8)&0x0003);
packet[8] |= (uint8_t)((ch_data_bluefly[1]>>6)&0x000c);
packet[8] |= (uint8_t)((ch_data_bluefly[2]>>4)&0x0030);
packet[8] |= (uint8_t)((ch_data_bluefly[3]>>2)&0x00c0);
packet[9] = (uint8_t)((ch_data_bluefly[4]>>8)&0x0003);
packet[9] |= (uint8_t)((ch_data_bluefly[5]>>6)&0x000c);
packet[9] |= (uint8_t)((ch_data_bluefly[6]>>4)&0x0030);
packet[9] |= (uint8_t)((ch_data_bluefly[7]>>2)&0x00c0);
unsigned char l, h, t;
l = h = 0xff;
for (int i=0; i<10; ++i) {
h ^= packet[i];
h ^= h >> 4;
t = h;
h = l;
l = t;
t = (l<<4) | (l>>4);
h ^= ((t<<2) | (t>>6)) & 0x1f;
h ^= t & 0xf0;
l ^= ((t<<1) | (t>>7)) & 0xe0;
}
// Checksum
packet[10] = h;
packet[11] = l;
}
static uint16_t bluefly_cb() {
switch(phase++) {
case 0:
bluefly_ch_data();
break;
case 1:
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency_start + hopping_frequency_no*2);
hopping_frequency_no++;
hopping_frequency_no %= 15;
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, PAYLOAD_BlueFly_SIZE);
break;
case 2:
break;
case 3:
if (bind_phase>0) {
bind_phase--;
if (! bind_phase) { BIND_DONE; }
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bluefly_binding_adr_rf, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81);
NRF24L01_FlushTx();
NRF24L01_WritePayload(bind_payload, PAYLOAD_BlueFly_SIZE);
}
break;
case 4:
break;
case 5:
NRF24L01_SetPower();
/* FALLTHROUGH */
default:
phase = 0;
break;
}
return 1000; // send 1 binding packet and 1 data packet per 9ms
}
static uint16_t BlueFly_setup() {
hopping_frequency_start = ((MProtocol_id >> 8) % 47) + 2;
bluefly_binding_packet();
bluefly_init();
if(IS_AUTOBIND_FLAG_on) { bind_phase = BIND_BlueFly_COUNT; } else { bind_phase = 0; }
return 1000;
}
#endif

View File

@ -0,0 +1,318 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/* NB: Not implemented
Uncomment define below to enable telemetry. Also add CFlie protocol to TELEMETRY_SetTypeByProtocol to set type to DSM.
#define CFLIE_TELEMETRY
*/
#if defined(CFlie_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_COUNT 60
// Address size
#define TX_ADDR_SIZE 5
// Timeout for callback in uSec, 10ms=10000us for Crazyflie
#define PACKET_PERIOD 10000
// For code readability
enum {
CHANNEL1 = 0,
CHANNEL2,
CHANNEL3,
CHANNEL4,
CHANNEL5,
CHANNEL6,
CHANNEL7,
CHANNEL8,
CHANNEL9,
CHANNEL10
};
#define PAYLOADSIZE 8 // receive data pipes set to this size, but unused
#define MAX_PACKET_SIZE 9 // YD717 packets have 8-byte payload, Syma X4 is 9
//static uint8_t packet[MAX_PACKET_SIZE];
static uint8_t data_rate, rf_channel;
enum {
CFLIE_INIT_SEARCH = 0,
CFLIE_INIT_DATA,
CFLIE_SEARCH,
CFLIE_DATA
};
#ifdef CFLIE_TELEMETRY
static const char * const cflie_opts[] = {
_tr_noop("Telemetry"), _tr_noop("Off"), _tr_noop("On"), NULL,
NULL
};
enum {
PROTOOPTS_TELEMETRY = 0,
LAST_PROTO_OPT,
};
ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts);
#define TELEM_OFF 0
#define TELEM_ON 1
#endif
#define PACKET_CHKTIME 500 // time to wait if packet not yet acknowledged or timed out
static uint16_t dbg_cnt = 0;
static uint8_t packet_ack() {
if (++dbg_cnt > 50) { dbg_cnt = 0; }
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))) {
case BV(NRF24L01_07_TX_DS):
return PKT_ACKED;
case BV(NRF24L01_07_MAX_RT):
return PKT_TIMEOUT;
}
return PKT_PENDING;
}
static void set_rate_channel(uint8_t rate, uint8_t channel) {
NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel); // Defined by model id
NRF24L01_SetBitrate(rate); // Defined by model id
}
static void send_search_packet() {
uint8_t buf[1];
buf[0] = 0xff;
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT)));
NRF24L01_FlushTx();
if (rf_channel++ > 125) {
rf_channel = 0;
switch(data_rate) {
case NRF24L01_BR_250K:
data_rate = NRF24L01_BR_1M;
break;
case NRF24L01_BR_1M:
data_rate = NRF24L01_BR_2M;
break;
case NRF24L01_BR_2M:
data_rate = NRF24L01_BR_250K;
break;
}
}
set_rate_channel(data_rate, rf_channel);
NRF24L01_WritePayload(buf, sizeof(buf));
++packet_counter;
}
// Frac 16.16
#define FRAC_MANTISSA 16
#define FRAC_SCALE (1 << FRAC_MANTISSA)
// Convert fractional 16.16 to float32
static void frac2float(uint32_t n, float* res) {
if (n == 0) {
*res = 0.0;
return;
}
uint32_t m = n < 0 ? -n : n;
int i;
for (i = (31-FRAC_MANTISSA); (m & 0x80000000) == 0; i--, m <<= 1) ;
m <<= 1; // Clear implicit leftmost 1
m >>= 9;
uint32_t e = 127 + i;
if (n < 0) m |= 0x80000000;
m |= e << 23;
*((uint32_t *) res) = m;
}
static void send_cmd_packet() {
// Commander packet, 15 bytes
uint8_t buf[15];
float x_roll, x_pitch, yaw;
// Channels in AETR order
// Roll, aka aileron, float +- 50.0 in degrees
// float roll = -(float) Servo_data[AILERON]*50.0/10000;
uint32_t f_roll = -Servo_data[AILERON] * FRAC_SCALE / (10000 / 50);
// Pitch, aka elevator, float +- 50.0 degrees
//float pitch = -(float) Servo_data[ELEVATOR]*50.0/10000;
uint32_t f_pitch = -Servo_data[ELEVATOR] * FRAC_SCALE / (10000 / 50);
// Thrust, aka throttle 0..65535, working range 5535..65535
// No space for overshoot here, hard limit Channel3 by -10000..10000
uint32_t ch = Servo_data[THROTTLE];
if (ch < PPM_MIN) {
ch = PPM_MIN;
} else if (ch > PPM_MAX) {
ch = PPM_MAX;
}
uint16_t thrust = ch*3L + 35535L;
// Yaw, aka rudder, float +- 400.0 deg/s
// float yaw = -(float) Servo_data[RUDDER]*400.0/10000;
uint32_t f_yaw = - Servo_data[RUDDER] * FRAC_SCALE / (10000 / 400);
frac2float(f_yaw, &yaw);
// Convert + to X. 181 / 256 = 0.70703125 ~= sqrt(2) / 2
uint32_t f_x_roll = (f_roll + f_pitch) * 181 / 256;
frac2float(f_x_roll, &x_roll);
uint32_t f_x_pitch = (f_pitch - f_roll) * 181 / 256;
frac2float(f_x_pitch, &x_pitch);
int bufptr = 0;
buf[bufptr++] = 0x30; // Commander packet to channel 0
memcpy(&buf[bufptr], (char*) &x_roll, 4); bufptr += 4;
memcpy(&buf[bufptr], (char*) &x_pitch, 4); bufptr += 4;
memcpy(&buf[bufptr], (char*) &yaw, 4); bufptr += 4;
memcpy(&buf[bufptr], (char*) &thrust, 2); bufptr += 2;
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT)));
NRF24L01_FlushTx();
NRF24L01_WritePayload(buf, sizeof(buf));
++packet_counter;
NRF24L01_SetPower();
}
static int cflie_init() {
NRF24L01_Initialize();
// CRC, radio on
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
// NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgement
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x01); // Auto Acknowledgement for data pipe 0
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, TX_ADDR_SIZE-2); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x13); // 3 retransmits, 500us delay
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_channel); // Defined by model id
NRF24L01_SetBitrate(data_rate); // Defined by model id
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
// this sequence necessary for module from stock tx
NRF24L01_ReadReg(NRF24L01_1D_FEATURE);
NRF24L01_Activate(0x73); // Activate feature register
NRF24L01_ReadReg(NRF24L01_1D_FEATURE);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x01); // Enable Dynamic Payload Length on pipe 0
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x06); // Enable Dynamic Payload Length, enable Payload with ACK
// NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, TX_ADDR_SIZE);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, TX_ADDR_SIZE);
NRF24L01_Activate(0x53); // switch bank back
// 50ms delay in callback
return 50000;
}
#ifdef CFLIE_TELEMETRY
static void update_telemetry() {
static uint8_t frameloss = 0;
frameloss += NRF24L01_ReadReg(NRF24L01_08_OBSERVE_TX) >> 4;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_channel); // reset packet loss counter
Telemetry.p.dsm.flog.frameloss = frameloss;
// Telemetry.p.dsm.flog.volt[0] = read battery voltage from ack payload
TELEMETRY_SetUpdated(TELEM_DSM_FLOG_FRAMELOSS);
}
#endif
static uint16_t cflie_callback() {
switch (phase) {
case CFLIE_INIT_SEARCH:
send_search_packet();
phase = CFLIE_SEARCH;
break;
case CFLIE_INIT_DATA:
send_cmd_packet();
phase = CFLIE_DATA;
break;
case CFLIE_SEARCH:
switch (packet_ack()) {
case PKT_PENDING:
return PACKET_CHKTIME; // packet send not yet complete
case PKT_ACKED:
phase = CFLIE_DATA;
BIND_DONE;
break;
case PKT_TIMEOUT:
send_search_packet();
counter = BIND_COUNT;
}
break;
case CFLIE_DATA:
#ifdef CFLIE_TELEMETRY
update_telemetry();
#endif
if (packet_ack() == PKT_PENDING)
return PACKET_CHKTIME; // packet send not yet complete
send_cmd_packet();
break;
}
return PACKET_PERIOD; // Packet at standard protocol interval
}
// Generate address to use from TX id and manufacturer id (STM32 unique id)
static uint8_t initialize_rx_tx_addr() {
rx_tx_addr[0] =
rx_tx_addr[1] =
rx_tx_addr[2] =
rx_tx_addr[3] =
rx_tx_addr[4] = 0xE7; // CFlie uses fixed address
data_rate = NRF24L01_BR_250K;
rf_channel = 0;
return CFLIE_INIT_SEARCH;
// return CFLIE_INIT_DATA;
}
static uint16_t Cflie_setup() {
phase = initialize_rx_tx_addr();
packet_counter = 0;
int delay = cflie_init();
#ifdef CFLIE_TELEMETRY
memset(&Telemetry, 0, sizeof(Telemetry));
TELEMETRY_SetType(TELEM_DSM);
#endif
if (phase == CFLIE_INIT_SEARCH) { BIND_IN_PROGRESS; }
return delay;
}
#endif

View File

@ -0,0 +1,172 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(ESKY150_NRF24L01_INO)
#include "iface_nrf24l01.h"
// Timeout for callback in uSec, 4.8ms=4800us for ESky150
#define ESKY150_PERIOD 4800
#define ESKY150_CHKTIME 100 // Time to wait for packet to be sent (no ACK, so very short)
#define esky150_PAYLOADSIZE 15
#define ADDR_esky150_SIZE 4
static uint32_t total_packets;
enum {
ESKY150_INIT2 = 0,
ESKY150_DATA
};
static uint8_t esky150_packet_ack() {
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))) {
case BV(NRF24L01_07_TX_DS): return PKT_ACKED;
case BV(NRF24L01_07_MAX_RT): return PKT_TIMEOUT;
}
return PKT_PENDING;
}
// 2-bytes CRC
#define CRC_CONFIG (BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO))
static uint16_t esky150_init() {
uint8_t rx_addr[ADDR_esky150_SIZE] = { 0x73, 0x73, 0x74, 0x63 };
uint8_t tx_addr[ADDR_esky150_SIZE] = { 0x71, 0x0A, 0x31, 0xF4 };
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG);
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, ADDR_esky150_SIZE-2); // 4-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0); // Disable retransmit
NRF24L01_SetPower();
NRF24L01_SetBitrate(NRF24L01_BR_2M);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_addr, ADDR_esky150_SIZE);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, tx_addr, ADDR_esky150_SIZE);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, esky150_PAYLOADSIZE); // bytes of data payload for pipe 0
NRF24L01_Activate(0x73);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
// Enable: Dynamic Payload Length, Payload with ACK , W_TX_PAYLOAD_NOACK
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, BV(NRF2401_1D_EN_DPL) | BV(NRF2401_1D_EN_ACK_PAY) | BV(NRF2401_1D_EN_DYN_ACK));
// Delay 50 ms
return 50000;
}
static uint16_t esky150_init2() {
NRF24L01_FlushTx();
NRF24L01_FlushRx();
packet_sent = 0;
packet_count = 0;
rf_ch_num = 0;
// Turn radio power on
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG | BV(NRF24L01_00_PWR_UP));
// delayMicroseconds(150);
return 150;
}
static void calc_fh_channels(uint32_t seed) {
// Use channels 2..79
uint8_t first = seed % 37 + 2;
uint8_t second = first + 40;
hopping_frequency[0] = first; // 0x22;
hopping_frequency[1] = second; // 0x4a;
}
static uint8_t convert_channel(uint8_t num) {
uint32_t ch = Servo_data[num];
if (ch < PPM_MIN) { ch = PPM_MIN; }
else if (ch > PPM_MAX) { ch = PPM_MAX; }
return (uint8_t) ((ch * 500 / PPM_MAX) + 1500);
}
static void read_controls(uint8_t* throttle, uint8_t* aileron, uint8_t* elevator, uint8_t* rudder) {
*throttle = convert_channel(THROTTLE);
*aileron = convert_channel(AILERON);
*elevator = convert_channel(ELEVATOR);
*rudder = convert_channel(RUDDER);
}
static void esky150_send_packet() {
uint8_t rf_ch = hopping_frequency[rf_ch_num];
rf_ch_num = 1 - rf_ch_num;
read_controls(&throttle, &aileron, &elevator, &rudder);
packet[0] = hopping_frequency[0];
packet[1] = hopping_frequency[1];
packet[2] = (throttle >> 8) & 0xFF;
packet[3] = throttle & 0xFF;
packet[4] = (aileron >> 8) & 0xFF;
packet[5] = aileron & 0xFF;
packet[6] = (elevator >> 8) & 0xFF;
packet[7] = elevator & 0xFF;
packet[8] = (rudder >> 8) & 0xFF;
packet[9] = rudder & 0xFF;
// Constant values 00 d8 18 f8
packet[10] = 0x00;
packet[11] = 0xd8;
packet[12] = 0x18;
packet[13] = 0xf8;
uint8_t sum = 0;
for (int i = 0; i < 14; ++i) sum += packet[i];
packet[14] = sum;
packet_sent = 0;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, sizeof(packet));
++total_packets;
packet_sent = 1;
}
static uint16_t esky150_callback() {
uint16_t timeout = ESKY150_PERIOD;
switch (phase) {
case ESKY150_INIT2:
timeout = esky150_init2();
phase = ESKY150_DATA;
break;
case ESKY150_DATA:
if (packet_count == 4)
packet_count = 0;
else {
if (packet_sent && esky150_packet_ack() != PKT_ACKED) {
return ESKY150_CHKTIME;
}
esky150_send_packet();
}
break;
}
return timeout;
}
static uint16_t esky150_setup() {
total_packets = 0;
uint16_t timeout = esky150_init();
return timeout;
}
#endif

View File

@ -0,0 +1,282 @@
/*
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 <http://www.gnu.org/licenses/>.
rewrite v977/v966 protocol to improve reliability
*/
#if defined(FBL100_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_FBL_COUNT 800
#define FBL_SIZE 5
#define FREQUENCE_FBL_NUM 20
static uint8_t binding_fbl_adr_rf[5]; // fixed binding ids for all planes
static uint8_t bind_fbl_buf_array[4][10];
static unsigned int fbl_data[8];
// HiSky protocol uses TX id as an address for nRF24L01, and uses frequency hopping sequence
// which does not depend on this id and is passed explicitly in binding sequence. So we are free
// to generate this sequence as we wish. It should be in the range [02..77]
static void calc_fbl_channels() {
int idx = 0;
uint32_t rnd = MProtocol_id;
while (idx < FREQUENCE_FBL_NUM) {
int i;
int count_2_26 = 0, count_27_50 = 0, count_51_74 = 0;
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
// Use least-significant byte. 73 is prime, so channels 76..77 are unused
uint8_t next_ch = ((rnd >> 8) % 73) + 2;
// Keep the distance 2 between the channels - either odd or even
if (((next_ch ^ MProtocol_id) & 0x01 )== 0) { continue; }
// Check that it's not duplicate and spread uniformly
for (i = 0; i < idx; i++) {
if(hopping_frequency[i] == next_ch) { break; }
if(hopping_frequency[i] <= 26) { count_2_26++; }
else if (hopping_frequency[i] <= 50) { count_27_50++; }
else { count_51_74++; }
}
if (i != idx) { continue; }
if ((next_ch <= 26 && count_2_26 < 8) ||(next_ch >= 27 && next_ch <= 50 && count_27_50 < 8) ||(next_ch >= 51 && count_51_74 < 8)) {
hopping_frequency[idx++] = next_ch;
}
}
}
static void fbl100_build_binding_packet(void) {
uint8_t i;
unsigned int sum;
uint8_t sum_l,sum_h;
sum = 0;
for(i=0;i<5;i++) { sum += rx_tx_addr[i]; }
sum_l = (uint8_t)sum;
sum >>= 8;
sum_h = (uint8_t)sum;
bind_fbl_buf_array[0][0] = 0xff;
bind_fbl_buf_array[0][1] = 0xaa;
bind_fbl_buf_array[0][2] = 0x55;
for(i=3;i<8;i++) { bind_fbl_buf_array[0][i] = rx_tx_addr[i-3]; }
for(i=1;i<4;i++) {
bind_fbl_buf_array[i][0] = sum_l;
bind_fbl_buf_array[i][1] = sum_h;
bind_fbl_buf_array[i][2] = i-1;
}
for(i=0;i<7;i++) { bind_fbl_buf_array[1][i+3] = hopping_frequency[i]; }
for(i=0;i<7;i++) { bind_fbl_buf_array[2][i+3] = hopping_frequency[i+7]; }
for(i=0;i<6;i++) { bind_fbl_buf_array[3][i+3] = hopping_frequency[i+14]; }
binding_idx = 0;
}
static void hp100_build_binding_packet(void) {
memcpy(packet, rx_tx_addr, 5);
packet[5] = hopping_frequency[0]; // start address
for (uint8_t i = 6; i < 12; i++) { packet[i] = 0x55; }
}
static void config_nrf24l01() {
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // 0:No Auto Acknoledgement; 1:Auto Acknoledgement
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, packet_length); // fbl100/v922's packet size = 10, hp100 = 12
// 2-bytes CRC, radio off
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2)
NRF24L01_SetBitrate(sub_protocol == HP100? NRF24L01_BR_250K:NRF24L01_BR_1M); //hp100:250kbps; fbl100: 1Mbps
NRF24L01_SetPower();
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
}
// FBL100 channel sequence: AILE ELEV THRO RUDD GEAR PITH, channel data value is from 0 to 1000
static void fbl100_build_ch_data() {
uint32_t temp;
uint8_t i;
for (i = 0; i< 8; i++) {
temp = (uint32_t)Servo_data[i] * 500/PPM_MAX + 500;
if (i == 2) { temp = 1000 -temp; } // It is clear that fbl100's thro stick is made reversely,so I adjust it here on purposely
if (temp < 0) { fbl_data[i] = 0; }
else if (temp > 1000) { fbl_data[i] = 1000; }
else { fbl_data[i] = (unsigned int)temp; }
packet[i] = (uint8_t)fbl_data[i];
}
packet[8] = (uint8_t)((fbl_data[0]>>8)&0x0003);
packet[8] |= (uint8_t)((fbl_data[1]>>6)&0x000c);
packet[8] |= (uint8_t)((fbl_data[2]>>4)&0x0030);
packet[8] |= (uint8_t)((fbl_data[3]>>2)&0x00c0);
packet[9] = (uint8_t)((fbl_data[4]>>8)&0x0003);
packet[9] |= (uint8_t)((fbl_data[5]>>6)&0x000c);
packet[9] |= (uint8_t)((fbl_data[6]>>4)&0x0030);
packet[9] |= (uint8_t)((fbl_data[7]>>2)&0x00c0);
}
static void hp100_build_ch_data() {
uint32_t temp;
uint8_t i;
for (i = 0; i< 8; i++) {
temp = (uint32_t)Servo_data[i] * 300/PPM_MAX + 500;
if (temp < 0) { temp = 0; }
else if (temp > 1000) { temp = 1000; }
if (i == 3 || i == 5) { temp = 1000 -temp; } // hp100's rudd and pit channel are made reversely,so I adjust them on purposely
fbl_data[i] = (unsigned int)temp;
packet[i] = (uint8_t)fbl_data[i];
}
packet[8] = (uint8_t)((fbl_data[0]>>8)&0x0003);
packet[8] |= (uint8_t)((fbl_data[1]>>6)&0x000c);
packet[8] |= (uint8_t)((fbl_data[2]>>4)&0x0030);
packet[8] |= (uint8_t)((fbl_data[3]>>2)&0x00c0);
packet[9] = (uint8_t)((fbl_data[4]>>8)&0x0003);
packet[9] |= (uint8_t)((fbl_data[5]>>6)&0x000c);
packet[9] |= (uint8_t)((fbl_data[6]>>4)&0x0030);
packet[9] |= (uint8_t)((fbl_data[7]>>2)&0x00c0);
unsigned char l, h, t;
l=h=0xff;
for(i=0; i<10; i++ ) {
h ^= packet[i];
h ^= h>>4;
t = h;
h = l;
l = t;
t = (l<<4) | (l>>4);
h^=((t<<2) | (t>>6)) & 0x1f;
h^=t&0xf0;
l^=((t<<1) | (t>>7)) & 0xe0;
}
packet[10] = h;
packet[11] = l;
}
static uint16_t fbl100_cb() {
switch(phase) {
case 0:
fbl100_build_ch_data();
break;
case 1:
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
hopping_frequency_no++;
if (hopping_frequency_no >= FREQUENCE_FBL_NUM) { hopping_frequency_no = 0; }
break;
case 2:
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, packet_length);
break;
case 3:
break;
case 4:
if (bind_phase>0) {
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, binding_fbl_adr_rf, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81);
}
break;
case 5:
if (bind_phase >0) {
bind_phase--;
if (! bind_phase) { BIND_DONE; } // binding finished, change tx add
NRF24L01_FlushTx(); // must be invoked before NRF24L01_WritePayload()
NRF24L01_WritePayload(bind_fbl_buf_array[binding_idx], packet_length);
binding_idx++;
if (binding_idx >= 4)
binding_idx = 0;
}
break;
case 6:
break;
case 7:
NRF24L01_SetPower();
break;
default:
break;
}
phase++;
if (phase >=9) { phase = 0; } // send 1 binding packet and 1 data packet per 9ms
return 1000;
}
static uint16_t hp100_cb() {
switch(phase) {
case 0:
hp100_build_ch_data();
break;
case 1:
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[0] + hopping_frequency_no*2);
hopping_frequency_no++;
hopping_frequency_no %= 15;
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, packet_length);
break;
case 2:
if(bind_phase>0) { hp100_build_binding_packet(); }
break;
case 3:
if (bind_phase>0) {
bind_phase--;
if (! bind_phase) { BIND_DONE; }
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, binding_fbl_adr_rf, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, packet_length);
}
break;
case 4:
break;
case 5:
NRF24L01_SetPower();
break;
default:
break;
}
phase++;
if (phase >= 6) { phase = 0; } // send 1 binding packet and 1 data packet per 10ms
return 1000;
}
static uint8_t fbl_setup() {
calc_fbl_channels();
printf("FH Seq: ");
for (int i = 0; i < FREQUENCE_FBL_NUM; ++i) { printf("%d, ", hopping_frequency[i]); }
printf("\r\n");
// debut init
if (sub_protocol == HP100) {
packet_length = 12;
binding_fbl_adr_rf[0] = 0x32; binding_fbl_adr_rf[1] = 0xaa; binding_fbl_adr_rf[2] = 0x45;
binding_fbl_adr_rf[3] = 0x45; binding_fbl_adr_rf[4] = 0x78;
} else {
packet_length = 10;
binding_fbl_adr_rf[0] = 0x12; binding_fbl_adr_rf[1] = 0x23; binding_fbl_adr_rf[2] = 0x23;
binding_fbl_adr_rf[3] = 0x45; binding_fbl_adr_rf[4] = 0x78;
fbl100_build_binding_packet();
}
config_nrf24l01();
if(IS_AUTOBIND_FLAG_on) { bind_phase = BIND_FBL_COUNT; }
else { bind_phase = 0; }
// CLOCK_StartTimer(1000, sub_protocol == HP100?hp100_cb:fbl100_cb);
}
#endif

View File

@ -0,0 +1,210 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined (H377_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_COUNT 800
#define TXID_H377_SIZE 5
#define FREQUENCE_NUM_H377 20
#define SET_NUM_H377 9
// available frequency must be in between 2402 and 2477
static uint8_t binding_ch=0x50;
static uint8_t hopping_frequency_data[SET_NUM_H377] = {0x1c,0x1b,0x1d,0x11,0x0e,0x0d,0x01,0x1d,0x15};
static const uint8_t binding_adr_rf[5]={0x32,0xaa,0x45,0x45,0x78};
static uint8_t rf_adr_buf[5];
static uint8_t rf_adr_buf_data[SET_NUM_H377][5] = {
{0xad,0x9a,0xa6,0x69,0xb2},//ansheng
{0x92,0x9a,0x9d,0x69,0x99},//dc59
{0x92,0xb2,0x9d,0x69,0x9a},//small two
{0xad,0x9a,0x5a,0x69,0x96},//james_1
{0x95,0x9a,0x52,0x69,0x99},//james_2
{0x52,0x52,0x52,0x69,0xb9},//james_3
{0x52,0x52,0x52,0x52,0x55},//small two_1
{0x92,0xB2,0x9D,0x69,0x9A},//small two_2
{0x96,0x9A,0x45,0x69,0xB2}//small two_3
};
static uint8_t bind_buf_array[10];
static uint8_t bind_buf_array_data[SET_NUM_H377][4] = {
{0xcf,0x1c,0x19,0x1a},
{0xff,0x48,0x19,0x19},
{0xf3,0x4d,0x19,0x19},
{0x9e,0x1f,0x19,0x19},
{0x8d,0x3d,0x19,0x19},
{0xbd,0x23,0x19,0x19},
{0xF3,0x28,0x19,0x19},
{0xF3,0x4D,0x19,0x19},
{0x82,0x8D,0x19,0x19}
};
static unsigned int ch_data[8];
static uint8_t payload[10];
static uint8_t counter1ms;
static int select_ch_id = 0;
static void h377_binding_packet(void) { //bind_buf_array
uint8_t i;
counter1ms = 0;
hopping_frequency_no = 0;
for(i=0;i<5;i++)
bind_buf_array[i] = rf_adr_buf[i];
bind_buf_array[5] = hopping_frequency[0];
for(i=0;i<4;i++)
bind_buf_array[i+6] = bind_buf_array_data[select_ch_id][i];
}
static void h377_init() {
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rf_adr_buf, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rf_adr_buf, 5);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 10); // payload size = 10
//NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81); // binding packet must be set in channel 81
NRF24L01_WriteReg(NRF24L01_05_RF_CH, binding_ch); // binding packet must be set in channel 81
// 2-bytes CRC, radio off
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2)
NRF24L01_SetBitrate(0); // 1Mbps
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
}
// H377 channel sequence: AILE ELEV THRO RUDD GEAR PITH, channel data value is from 0 to 1000
static void h377_ch_data() {
uint32_t temp;
uint8_t i;
for (i = 0; i< 8; i++) {
temp = (uint32_t)Servo_data[i] * 450/PPM_MAX + 500; // max/min servo range is +-125%
if (i == 2) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose
temp = 1000 -temp;
//if (i == 0) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose
// temp = 1000 -temp;
//if (i == 1) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose
// temp = 1000 -temp;
if (temp < 0)
ch_data[i] = 0;
else if (temp > 1000)
ch_data[i] = 1000;
else
ch_data[i] = (unsigned int)temp;
payload[i] = (uint8_t)ch_data[i];
}
payload[8] = (uint8_t)((ch_data[0]>>8)&0x0003);
payload[8] |= (uint8_t)((ch_data[1]>>6)&0x000c);
payload[8] |= (uint8_t)((ch_data[2]>>4)&0x0030);
payload[8] |= (uint8_t)((ch_data[3]>>2)&0x00c0);
payload[9] = (uint8_t)((ch_data[4]>>8)&0x0003);
payload[9] |= (uint8_t)((ch_data[5]>>6)&0x000c);
payload[9] |= (uint8_t)((ch_data[6]>>4)&0x0030);
payload[9] |= (uint8_t)((ch_data[7]>>2)&0x00c0);
}
static uint16_t h377_cb() {
counter1ms++;
if(counter1ms==1) { NRF24L01_FlushTx(); }
//-------------------------
else if(counter1ms==2) {
if (bind_phase>0) {
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)binding_adr_rf, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, binding_ch);
}
}
else if(counter1ms==3) {
if (bind_phase >0) {
bind_phase--;
if (! bind_phase) { BIND_DONE; } // binding finished, change tx add
NRF24L01_WritePayload(bind_buf_array,10);
}
}
else if (counter1ms==4) { if (bind_phase > 0) { NRF24L01_FlushTx(); }}
//-------------------------
else if(counter1ms==5) { NRF24L01_SetPower(); }
//-------------------------
else if (counter1ms == 6) {
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rf_adr_buf, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
hopping_frequency_no++;
if (hopping_frequency_no >= FREQUENCE_NUM_H377) { hopping_frequency_no = 0; }
}
else if (counter1ms == 7) { h377_ch_data(); }
else if(counter1ms>8) {
counter1ms = 0;
NRF24L01_WritePayload(payload,10);
}
return 1000; // send 1 binding packet and 1 data packet per 9ms
}
// Linear feedback shift register with 32-bit Xilinx polinomial x^32 + x^22 + x^2 + x + 1
static const uint32_t LFSR_FEEDBACK = 0x80200003ul;
static const uint32_t LFSR_INTAP = 32-1;
static void update_lfsr(uint32_t *lfsr, uint8_t b) {
for (int i = 0; i < 8; ++i) {
*lfsr = (*lfsr >> 1) ^ ((-(*lfsr & 1u) & LFSR_FEEDBACK) ^ ~((uint32_t)(b & 1) << LFSR_INTAP));
b >>= 1;
}
}
// Generate internal id from TX id and manufacturer id (STM32 unique id)
static void H377_tx_id() {
for(int i=0;i<5;i++)
rf_adr_buf[i] = rf_adr_buf_data[select_ch_id][i];
hopping_frequency[0] = hopping_frequency_data[select_ch_id];
for (int i = 1; i < FREQUENCE_NUM_H377; i++) {
hopping_frequency[i] = hopping_frequency[i-1] + 3;
}
}
static uint16_t h377_setup() {
select_ch_id = MProtocol_id_master%SET_NUM_H377;
H377_tx_id();//rf_adr_buf hopping_frequency
h377_binding_packet();//bind_buf_array (rf_adr_buf hopping_frequency)
h377_init();
if(IS_AUTOBIND_FLAG_on) {
bind_phase = BIND_COUNT;
BIND_IN_PROGRESS;
}
else { bind_phase = 0; }
return 1000;
}
#endif

View File

@ -0,0 +1,267 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/* This protocol is for the HM Hobby HM830 RC Paper Airplane
Protocol spec:
Channel data:
AA BB CC DD EE FF GG
AA : Throttle Min=0x00 max =0x64
BB :
bit 0,1,2: Left/Right magnitude, bit 5 Polarity (set = right)
bit 6: Accelerate
bit 7: Right button (also the ABC Button)
CC : bit 0 seems to be impacted by the Right button
DD
EE
FF : Trim (bit 0-5: Magnitude, bit 6 polarity (set = right)
GG : Checksum (CRC8 on bytes AA-FF), init = 0xa5, poly = 0x01
*/
#ifdef HM830_NRF24L01_INO
#include "iface_nrf24l01.h"
enum {
HM830_BIND1A = 0,
HM830_BIND2A,
HM830_BIND3A,
HM830_BIND4A,
HM830_BIND5A,
HM830_BIND6A,
HM830_BIND7A,
HM830_DATA1,
HM830_DATA2,
HM830_DATA3,
HM830_DATA4,
HM830_DATA5,
HM830_DATA6,
HM830_DATA7,
HM830_BIND1B = 0x80,
HM830_BIND2B,
HM830_BIND3B,
HM830_BIND4B,
HM830_BIND5B,
HM830_BIND6B,
HM830_BIND7B,
};
static uint8_t init_vals_hm830[][2] = {
{NRF24L01_17_FIFO_STATUS, 0x00},
{NRF24L01_16_RX_PW_P5, 0x07},
{NRF24L01_15_RX_PW_P4, 0x07},
{NRF24L01_14_RX_PW_P3, 0x07},
{NRF24L01_13_RX_PW_P2, 0x07},
{NRF24L01_12_RX_PW_P1, 0x07},
{NRF24L01_11_RX_PW_P0, 0x07},
{NRF24L01_0F_RX_ADDR_P5, 0xC6},
{NRF24L01_0E_RX_ADDR_P4, 0xC5},
{NRF24L01_0D_RX_ADDR_P3, 0xC4},
{NRF24L01_0C_RX_ADDR_P2, 0xC3},
{NRF24L01_09_CD, 0x00},
{NRF24L01_08_OBSERVE_TX, 0x00},
{NRF24L01_07_STATUS, 0x07},
// {NRF24L01_06_RF_SETUP, 0x07},
{NRF24L01_05_RF_CH, 0x18},
{NRF24L01_04_SETUP_RETR, 0x3F},
{NRF24L01_03_SETUP_AW, 0x03},
{NRF24L01_02_EN_RXADDR, 0x3F},
{NRF24L01_01_EN_AA, 0x3F},
{NRF24L01_00_CONFIG, 0x0E},
};
static uint8_t count;
static uint8_t rf_ch[] = {0x08, 0x35, 0x12, 0x3f, 0x1c, 0x49, 0x26};
static uint8_t bind_addr[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xc2};
static uint8_t crc8(uint32_t result, uint8_t *data, int len) {
int polynomial = 0x01;
for(int i = 0; i < len; i++) {
result = result ^ data[i];
for(int j = 0; j < 8; j++) {
if(result & 0x80) { result = (result << 1) ^ polynomial; }
else { result = result << 1; }
}
}
return result & 0xff;
}
static void HM830_init() {
NRF24L01_Initialize();
for (uint32_t i = 0; i < sizeof(init_vals_hm830) / sizeof(init_vals_hm830[0]); i++) { NRF24L01_WriteReg(init_vals_hm830[i][0], init_vals_hm830[i][1]); }
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_SetBitrate(0);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bind_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, bind_addr+1, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_addr, 5);
NRF24L01_Activate(0x73); //Enable FEATURE
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3F);
//NRF24L01_ReadReg(NRF24L01_07_STATUS) ==> 0x07
NRF24L01_Activate(0x53); // switch bank back
NRF24L01_FlushTx();
//NRF24L01_ReadReg(NRF24L01_07_STATUS) ==> 0x0e
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x0e);
//NRF24L01_ReadReg(NRF24L01_00_CONFIG); ==> 0x0e
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0e);
NRF24L01_ReadReg(NRF24L01_01_EN_AA); // No Auto Acknoledgement
}
static void build_bind_packet_hm830() {
for(int i = 0; i < 6; i++) { packet[i] = rx_tx_addr[i]; }
packet[6] = crc8(0xa5, packet, 6);
}
static void build_data_packet() {
uint8_t ail_sign = 0, trim_sign = 0;
throttle = (uint32_t)Servo_data[THROTTLE] * 50 / PPM_MAX + 50;
if (throttle < 0) { throttle = 0; }
aileron = (uint32_t)Servo_data[AILERON] * 8 / PPM_MAX;
if (aileron < 0) { aileron = -aileron; ail_sign = 1; }
if (aileron > 7) { aileron = 7; }
uint8_t turbo = (uint32_t)Servo_data[ELEVATOR] > 0 ? 1 : 0;
uint8_t trim = ((uint32_t)Servo_data[RUDDER] * 0x1f / PPM_MAX);
if (trim < 0) { trim = -trim; trim_sign = 1; }
if (trim > 0x1f) { trim = 0x1f; }
uint8_t rbutton = (uint32_t)Servo_data[4] > 0 ? 1 : 0;
packet[0] = throttle;
packet[1] = aileron;
if (ail_sign) { packet[1] |= 0x20; }
if (turbo) { packet[1] |= 0x40; }
if (rbutton) { packet[1] |= 0x80; }
packet[5] = trim;
if (trim_sign) { packet[5] |= 0x20;}
packet[6] = crc8(0xa5, packet, 6);
}
static void send_packet_hm830() {
NRF24L01_ReadReg(NRF24L01_17_FIFO_STATUS);
NRF24L01_WritePayload(packet, 7);
}
static uint16_t handle_binding() {
uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
if (status & 0x20) {
//Binding complete
phase = HM830_DATA1 + ((phase&0x7F)-HM830_BIND1A);
count = 0;
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, rx_tx_addr+1, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_FlushTx();
build_data_packet();
uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_07_STATUS, rb);
rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb);
send_packet_hm830();
return 14000;
}
switch (phase) {
case HM830_BIND1A:
//Look for a Rx that is already bound
NRF24L01_SetPower();
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, rx_tx_addr+1, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]);
build_bind_packet_hm830();
break;
case HM830_BIND1B:
//Look for a Rx that is not yet bound
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bind_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, bind_addr+1, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_addr, 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]);
break;
case HM830_BIND2A:
case HM830_BIND3A:
case HM830_BIND4A:
case HM830_BIND5A:
case HM830_BIND6A:
case HM830_BIND7A:
case HM830_BIND2B:
case HM830_BIND3B:
case HM830_BIND4B:
case HM830_BIND5B:
case HM830_BIND6B:
case HM830_BIND7B:
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[(phase&0x7F)-HM830_BIND1A]);
break;
}
NRF24L01_FlushTx();
uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_07_STATUS, rb);
rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb);
send_packet_hm830();
phase++;
if (phase == HM830_BIND7B+1) { phase = HM830_BIND1A; }
else if (phase == HM830_BIND7A+1) { phase = HM830_BIND1B; }
return 20000;
}
static uint16_t handle_data() {
uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
if (count <= 0 || !(status & 0x20)) {
if(count < 0 || ! (status & 0x20)) {
count = 0;
//We didn't get a response on this channel, try the next one
phase++;
if (phase-HM830_DATA1 > 6) { phase = HM830_DATA1; }
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]);
NRF24L01_FlushTx();
build_data_packet();
uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_07_STATUS, rb);
rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb);
send_packet_hm830();
return 14000;
}
}
build_data_packet();
count++;
if(count == 98) {
count = -1;
NRF24L01_SetPower();
}
uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_07_STATUS, rb);
rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E
NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb);
send_packet_hm830();
return 20000;
}
static uint16_t HM830_callback() {
if ((phase & 0x7F) < HM830_DATA1) { return handle_binding(); }
else { return handle_data(); }
}
static uint32_t HM830_setup(){
count = 0;
// initialize_tx_id
rx_tx_addr[4] = 0xee;
rx_tx_addr[5] = 0xc2;
HM830_init();
phase = HM830_BIND1A;
return 500;
}
#endif

View File

@ -0,0 +1,272 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(HonTai_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_HT_COUNT 80
#define PACKET_HT_PERIOD 13500 // 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
#define INITIAL_HT_WAIT 500
#define BIND_HT_PACKET_SIZE 10
#define PACKET_HT_SIZE 12
#define RF_BIND_HT_CHANNEL 0
enum {
FORMAT_HONTAI = 0,
FORMAT_JJRCX1,
};
#define CHANNEL_LED AUX1
#define CHANNEL_ARM AUX1 // for JJRC X1
#define CHANNEL_FLIP AUX2
#define CHANNEL_PICTURE AUX3
#define CHANNEL_VIDEO AUX4
#define CHANNEL_HEADLESS AUX5
#define CHANNEL_RTH AUX6
#define CHANNEL_CALIBRATE AUX7
enum {
HonTai_INIT1 = 0,
HonTai_BIND2,
HonTai_DATA
};
static uint8_t ht_txid[5];
static uint8_t rf_chan = 0;
static uint8_t rf_channels[][3] = {{0x05, 0x19, 0x28}, // Hontai
{0x0a, 0x1e, 0x2d}}; // JJRC X1
static uint8_t rx_tx_ht_addr[] = {0xd2, 0xb5, 0x99, 0xb3, 0x4a};
static uint8_t addr_vals[4][16] = {
{0x24, 0x26, 0x2a, 0x2c, 0x32, 0x34, 0x36, 0x4a, 0x4c, 0x4e, 0x54, 0x56, 0x5a, 0x64, 0x66, 0x6a},
{0x92, 0x94, 0x96, 0x9a, 0xa4, 0xa6, 0xac, 0xb2, 0xb4, 0xb6, 0xca, 0xcc, 0xd2, 0xd4, 0xd6, 0xda},
{0x93, 0x95, 0x99, 0x9b, 0xa5, 0xa9, 0xab, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xcd, 0xd3, 0xd5, 0xd9},
{0x25, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x49, 0x4b, 0x4d, 0x59, 0x5b, 0x65, 0x69, 0x6b, 0x6d, 0x6e}};
// proudly swiped from http://www.drdobbs.com/implementing-the-ccitt-cyclical-redundan/199904926
#define POLY 0x8408
static uint16_t crc16(uint8_t *data_p, uint32_t length)
{
uint8_t i;
uint32_t data;
uint32_t crc;
crc = 0xffff;
if (length == 0) return (~crc);
length -= 2;
do {
for (i = 0, data = (uint8_t)0xff & *data_p++;
i < 8;
i++, data >>= 1) {
if ((crc & 0x0001) ^ (data & 0x0001))
crc = (crc >> 1) ^ POLY;
else
crc >>= 1;
}
} while (--length);
crc = ~crc;
data = crc;
crc = (crc << 8) | (data >> 8 & 0xFF);
*data_p++ = crc >> 8;
*data_p = crc & 0xff;
return crc;
}
#define CHAN_RANGE (PPM_MAX - PPM_MIN)
static uint8_t scale_HT_channel(uint8_t ch, uint8_t start, uint8_t end)
{
uint32_t range = end - start;
uint32_t chanval = Servo_data[ch];
if (chanval < PPM_MIN) chanval = PPM_MIN;
else if (chanval > PPM_MAX) chanval = PPM_MAX;
uint32_t round = range < 0 ? 0 : CHAN_RANGE / range; // channels round up
if (start < 0) round = CHAN_RANGE / range / 2; // trims zero centered around zero
return (range * (chanval - PPM_MIN + round)) / CHAN_RANGE + start;
}
#define GET_FLAG(ch, mask) (Servo_data[ch] > 0 ? mask : 0)
static void send_HT_packet(uint8_t bind)
{
if (bind) {
memcpy(packet, ht_txid, 5);
memset(&packet[5], 0, 3);
} else {
if (sub_protocol == FORMAT_HONTAI) {
packet[0] = 0x0b;
} else {
packet[0] = GET_FLAG(CHANNEL_ARM, 0x02);
}
packet[1] = 0x00;
packet[2] = 0x00;
packet[3] = (scale_HT_channel(THROTTLE, 0, 127) << 1) // throttle
| GET_FLAG(CHANNEL_PICTURE, 0x01);
packet[4] = scale_HT_channel(AILERON, 63, 0); // aileron
if (sub_protocol == FORMAT_HONTAI) {
packet[4] |= GET_FLAG(CHANNEL_RTH, 0x80)
| GET_FLAG(CHANNEL_HEADLESS, 0x40);
} else {
packet[4] |= 0x80; // not sure what this bit does
}
packet[5] = scale_channel(CHANNEL2, 0, 63) // elevator
| GET_FLAG(CHANNEL_CALIBRATE, 0x80)
| GET_FLAG(CHANNEL_FLIP, 0x40);
packet[6] = scale_HT_channel(RUDDER, 0, 63) // rudder
| GET_FLAG(CHANNEL_VIDEO, 0x80);
packet[7] = scale_HT_channel(AILERON, -16, 16); // aileron trim
if (sub_protocol == FORMAT_HONTAI) {
packet[8] = scale_HT_channel(RUDDER, -16, 16); // rudder trim
} else {
packet[8] = 0xc0 // always in expert mode
| GET_FLAG(CHANNEL_RTH, 0x02)
| GET_FLAG(CHANNEL_HEADLESS, 0x01);
}
packet[9] = scale_HT_channel(ELEVATOR, -16, 16); // elevator trim
}
crc16(packet, bind ? BIND_HT_PACKET_SIZE : PACKET_HT_SIZE);
// Power on, TX mode, 2byte CRC
if (sub_protocol == FORMAT_HONTAI) {
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
} else {
NRF24L01_SetTxRxMode(TX_EN);
}
NRF24L01_WriteReg(NRF24L01_05_RF_CH, bind ? RF_BIND_HT_CHANNEL : rf_channels[sub_protocol][rf_chan++]);
rf_chan %= sizeof(rf_channels);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
if (sub_protocol == FORMAT_HONTAI) {
XN297_WritePayload(packet, bind ? BIND_HT_PACKET_SIZE : PACKET_HT_SIZE);
} else {
NRF24L01_WritePayload(packet, bind ? BIND_HT_PACKET_SIZE : PACKET_HT_SIZE);
}
NRF24L01_SetPower();
}
static void ht_init()
{
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
// SPI trace of stock TX has these writes to registers that don't appear in
// nRF24L01 or Beken 2421 datasheets. Uncomment if you have an XN297 chip?
// NRF24L01_WriteRegisterMulti(0x3f, "\x4c\x84\x67,\x9c,\x20", 5);
// NRF24L01_WriteRegisterMulti(0x3e, "\xc9\x9a\xb0,\x61,\xbb,\xab,\x9c", 7);
// NRF24L01_WriteRegisterMulti(0x39, "\x0b\xdf\xc4,\xa7,\x03,\xab,\x9c", 7);
if (sub_protocol == FORMAT_HONTAI) {
XN297_SetTXAddr(rx_tx_ht_addr, sizeof(rx_tx_ht_addr));
} else {
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_ht_addr, sizeof(rx_tx_ht_addr));
}
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_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
NRF24L01_Activate(0x73); // Activate feature register
if (sub_protocol == FORMAT_HONTAI) {
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x00);
NRF24L01_Activate(0x73); // Deactivate feature register
} else {
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xff); // JJRC uses dynamic payload length
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // match other stock settings even though AA disabled...
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
}
}
static void ht_init2()
{
uint8_t data_tx_addr[] = {0x2a, 0xda, 0xa5, 0x25, 0x24};
data_tx_addr[0] = addr_vals[0][ ht_txid[3] & 0x0f];
data_tx_addr[1] = addr_vals[1][(ht_txid[3] >> 4) & 0x0f];
data_tx_addr[2] = addr_vals[2][ ht_txid[4] & 0x0f];
data_tx_addr[3] = addr_vals[3][(ht_txid[4] >> 4) & 0x0f];
if (sub_protocol == FORMAT_HONTAI) {
XN297_SetTXAddr(data_tx_addr, sizeof(data_tx_addr));
} else {
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, data_tx_addr, sizeof(data_tx_addr));
}
}
static uint16_t ht_callback()
{
switch (phase) {
case HonTai_INIT1:
phase = HonTai_BIND2;
break;
case HonTai_BIND2:
if (counter == 0) {
ht_init2();
phase = HonTai_DATA;
BIND_DONE;
} else {
send_HT_packet(1);
counter -= 1;
}
break;
case HonTai_DATA:
send_HT_packet(0);
break;
}
return PACKET_HT_PERIOD;
}
static uint16_t ht_setup()
{
counter = BIND_HT_COUNT;
if (sub_protocol == FORMAT_HONTAI) {
ht_txid[0] = 0x4c; // first three bytes some kind of model id? - set same as stock tx
ht_txid[1] = 0x4b;
ht_txid[2] = 0x3a;
} else {
ht_txid[0] = 0x4b; // JJRC X1
ht_txid[1] = 0x59;
ht_txid[2] = 0x3a;
}
ht_txid[3] = (MProtocol_id >> 8 ) & 0xff;
ht_txid[4] = MProtocol_id & 0xff;
ht_init();
phase = HonTai_INIT1;
return INITIAL_HT_WAIT;
}
#endif

View File

@ -0,0 +1,272 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/* This code is based upon code from:
http://www.rcgroups.com/forums/showthread.php?t=1564343
Author : Ferenc Szili (kile at the rcgroups.net forum)
*/
#if defined(NE260_NRF24L01_INO)
#include "iface_nrf24l01.h"
////////////////////////////////////////////////////////////
///////////////////////
// register bits
///////////////////////
// CONFIG
#define MASK_RX_DR 6
#define MASK_TX_DS 5
#define MASK_MAX_RT 4
#define EN_CRC 3
#define CRCO 2
#define PWR_UP 1
#define PRIM_RX 0
// EN_AA
#define ENAA_P5 5
#define ENAA_P4 4
#define ENAA_P3 3
#define ENAA_P2 2
#define ENAA_P1 1
#define ENAA_P0 0
// EN_RXADDR
#define ERX_P5 5
#define ERX_P4 4
#define ERX_P3 3
#define ERX_P2 2
#define ERX_P1 1
#define ERX_P0 0
// RF_SETUP
#define CONT_WAVE 7
#define RF_DR_LOW 5
#define PLL_LOCK 4
#define RF_DR_HIGH 3
#define RF_PWR_HIGH 2
#define RF_PWR_LOW 1
#define LNA_HCURR 0 // obsolete in nRF24L01+
// STATUS
#define RX_DR 6
#define TX_DS 5
#define MAX_RT 4
#define TX_FULL 0
// FIFO_STATUS
#define TX_REUSE 6
#define FIFO_TX_FULL 5
#define TX_EMPTY 4
#define RX_FULL 1
#define RX_EMPTY 0
///////////////////////
// register bit values
///////////////////////
// CONFIG
#define vMASK_RX_DR (1<<(MASK_RX_DR))
#define vMASK_TX_DS (1<<(MASK_TX_DS))
#define vMASK_MAX_RT (1<<(MASK_MAX_RT))
#define vEN_CRC (1<<(EN_CRC))
#define vCRCO (1<<(CRCO))
#define vPWR_UP (1<<(PWR_UP))
#define vPRIM_RX (1<<(PRIM_RX))
// EN_AA
#define vENAA_P5 (1<<(ENAA_P5))
#define vENAA_P4 (1<<(ENAA_P4))
#define vENAA_P3 (1<<(ENAA_P3))
#define vENAA_P2 (1<<(ENAA_P2))
#define vENAA_P1 (1<<(ENAA_P1))
#define vENAA_P0 (1<<(ENAA_P0))
// EN_RXADDR
#define vERX_P5 (1<<(ERX_P5))
#define vERX_P4 (1<<(ERX_P4))
#define vERX_P3 (1<<(ERX_P3))
#define vERX_P2 (1<<(ERX_P2))
#define vERX_P1 (1<<(ERX_P1))
#define vERX_P0 (1<<(ERX_P0))
// SETUP_AW -- address widths in bytes
#define vAW_3 1
#define vAW_4 2
#define vAW_5 3
// RF_SETUP
#define vCONT_WAVE (1<<(CONT_WAVE))
#define vRF_DR_LOW (1<<(RF_DR_LOW))
#define vPLL_LOCK (1<<(PLL_LOCK))
#define vRF_DR_HIGH (1<<(RF_DR_HIGH))
#define vRF_PWR_HIGH (1<<(RF_PWR_HIGH))
#define vRF_PWR_LOW (1<<(RF_PWR_LOW))
#define vLNA_HCURR (1<<(LNA_HCURR)) // obsolete in nRF24L01+
#define vRF_DR_1MBPS 0
#define vRF_DR_2MBPS (1<<(RF_DR_HIGH))
#define vRF_DR_250KBPS (1<<(RF_DR_LOW))
#define vRF_PWR_M18DBM 0x00
#define vRF_PWR_M12DBM 0x02
#define vRF_PWR_M6DBM 0x04
#define vRF_PWR_0DBM 0x06
#define vARD_250us 0x00
#define vARD_500us 0x10
#define vARD_750us 0x20
#define vARD_1000us 0x30
#define vARD_1250us 0x40
#define vARD_1500us 0x50
#define vARD_1750us 0x60
#define vARD_2000us 0x70
#define vARD_2250us 0x80
#define vARD_2500us 0x90
#define vARD_2750us 0xA0
#define vARD_3000us 0xB0
#define vARD_3250us 0xC0
#define vARD_3500us 0xD0
#define vARD_3750us 0xE0
#define vARD_4000us 0xF0
// STATUS
#define vRX_DR (1<<(RX_DR))
#define vTX_DS (1<<(TX_DS))
#define vMAX_RT (1<<(MAX_RT))
#define vTX_FULL (1<<(TX_FULL))
#define RX_P_NO(stat) ((stat >> 1) & 7)
#define HAS_RX_PAYLOAD(stat) ((stat & 0b1110) < 0b1100)
// FIFO_STATUS
#define vTX_REUSE (1<<(TX_REUSE))
#define vTX_FULL (1<<(TX_FULL))
#define vTX_EMPTY (1<<(TX_EMPTY))
#define vRX_FULL (1<<(RX_FULL))
#define vRX_EMPTY (1<<(RX_EMPTY))
////////////////////////////////////////////////////////////
uint8_t neChannel = 10;
uint8_t neChannelOffset = 0;
#define PACKET_NE_LENGTH 7
static uint16_t model_id = 0xA04A;
uint8_t NE_ch[]={THROTTLE, RUDDER, ELEVATOR, AILERON, AUX1};
uint8_t NEAddr[] = {0x34, 0x43, 0x10, 0x10, 0x01};
enum {
NE260_BINDTX,
NE260_BINDRX,
NE260_DATA1,
NE260_DATA2,
NE260_DATA3,
};
static void ne260_init() {
NRF24L01_Initialize();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, NEAddr, 5); // write the address
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, NEAddr, 5);
NRF24L01_WriteReg(NRF24L01_01_EN_AA, vENAA_P0); // enable auto acknoledge
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, vARD_500us); // ARD=500us, ARC=disabled
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, vRF_DR_250KBPS | vLNA_HCURR | vRF_PWR_0DBM); // data rate, output power and noise cancel
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, PACKET_NE_LENGTH); // RX payload length
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, vERX_P0); // enable RX address
NRF24L01_WriteReg(NRF24L01_07_STATUS, vRX_DR | vTX_DS | vMAX_RT); // reset the IRQ flags
}
static void send_data_packet() {
for(int i = 0; i < 4; i++) {
uint32_t value = (uint32_t)Servo_data[NE_ch[i]] * 0x40 / PPM_MAX + 0x40;
if (value > 0x7f)
value = 0x7f;
else if(value < 0)
value = 0;
packet[i] = value;
}
packet[4] = 0x55;
packet[5] = model_id & 0xff;
packet[6] = (model_id >> 8) & 0xff;
NRF24L01_FlushTx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, vMAX_RT);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, neChannel + neChannelOffset);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, vEN_CRC | vCRCO | vPWR_UP);
// send a fresh packet to the nRF
NRF24L01_WritePayload((uint8_t*) packet, PACKET_NE_LENGTH);
}
static void send_bind_packet() {
packet[0] = 0xAA; //throttle
packet[1] = 0xAA; //rudder
packet[2] = 0xAA; //elevator
packet[3] = 0xAA; //aileron
packet[4] = 0xAA; //command
packet[5] = model_id & 0xff;
packet[6] = (model_id >> 8) & 0xff;
NRF24L01_WriteReg(NRF24L01_07_STATUS, vRX_DR | vTX_DS | vMAX_RT); // reset the status flags
NRF24L01_WriteReg(NRF24L01_05_RF_CH, neChannel + neChannelOffset);
NRF24L01_FlushTx();
NRF24L01_WritePayload((uint8_t*) &packet, PACKET_NE_LENGTH); // send the bind packet
}
static uint16_t ne260_cb() {
if (state == NE260_BINDTX) {
// do we have a packet?
if ((NRF24L01_ReadReg(NRF24L01_07_STATUS) & vRX_DR) != 0) {
// read the packet contents
NRF24L01_ReadPayload(packet, PACKET_NE_LENGTH);
// is this the bind response packet?
if (strncmp("\x55\x55\x55\x55\x55", (char*) (packet + 1), 5) == 0 && *((uint16_t*)(packet + 6)) == model_id) {
// exit the bind loop
state = NE260_DATA1;
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(TX_EN);
return 2000;
}
}
NRF24L01_SetTxRxMode(TX_EN);
send_bind_packet();
state = NE260_BINDRX;
return 500;
} else if (state == NE260_BINDRX) {
// switch to RX mode
while (!(NRF24L01_ReadReg(NRF24L01_07_STATUS) & (vMAX_RT | vTX_DS))) ;
NRF24L01_WriteReg(NRF24L01_07_STATUS, vTX_DS);
NRF24L01_SetTxRxMode(RX_EN);
NRF24L01_FlushRx();
state = NE260_BINDTX;
return 2000;
}
else if (state == NE260_DATA1) { neChannel = 10; state = NE260_DATA2; }
else if (state == NE260_DATA2) { neChannel = 30; state = NE260_DATA3; }
else if (state == NE260_DATA3) { neChannel = 50; state = NE260_DATA1; }
send_data_packet();
return 2500;
}
static uint16_t NE260_setup() {
ne260_init();
state = NE260_BINDTX;
return 10000;
}
#endif

View File

@ -0,0 +1,582 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// Known UDI 2.4GHz protocol variants, all using BK2421
// * UDI U819 coaxial 3ch helicoper
// * UDI U816/817/818 quadcopters
// - "V1" with orange LED on TX, U816 RX labeled '' , U817/U818 RX labeled 'UD-U817B'
// - "V2" with red LEDs on TX, U816 RX labeled '', U817/U818 RX labeled 'UD-U817OG'
// - "V3" with green LEDs on TX. Did not get my hands on yet.
// * U830 mini quadcopter with tilt steering ("Protocol 2014")
// * U839 nano quadcopter ("Protocol 2014")
#if defined(UDI_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_UDI_COUNT 1000
// Timeout for callback in uSec, 4ms=4000us for UDI
// ???
//#define PACKET_UDI_PERIOD 4000
#define BIND_PACKET_UDI_PERIOD 5000
#define PACKET_UDI_PERIOD 15000
#define BIND_PACKETS_UDI_PER_CHANNEL 11
#define PACKETS_UDI_PER_CHANNEL 11
#define NUM_UDI_RF_CHANNELS 16
#define INITIAL_UDI_WAIT 50000
#define PACKET_UDI_CHKTIME 100
// For readability
enum {
UDI_CAMERA = 1,
UDI_VIDEO = 2,
UDI_MODE2 = 4,
UDI_FLIP360 = 8,
UDI_FLIP =16,
UDI_LIGHTS =32
};
// This is maximum payload size used in UDI protocols
#define UDI_PAYLOADSIZE 16
static uint8_t payload_size; // Bytes in payload for selected variant
static uint8_t bind_channel;
static uint8_t packets_to_hop;
static uint8_t packets_to_check; // BIND_RX phase needs to receive/auto-ack more than one packet for RX to switch to next phase, it seems
static uint8_t packets_to_send; // Number of packets to send / check for in current bind phase
static uint8_t bind_step_success; // Indicates successfull transmission / receive of bind reply during current bind phase
static uint8_t tx_id[3];
static uint8_t rx_id[3];
static uint8_t randoms[3]; // 3 random bytes choosen by TX, sent in BIND packets. Lower nibble of first byte sets index in RF CH table to use for BIND2
//
enum {
UDI_INIT2 = 0,
UDI_INIT2_NO_BIND,
UDI_BIND1_TX,
UDI_BIND1_RX,
UDI_BIND2_TX,
UDI_BIND2_RX,
UDI_DATA
};
enum {
PROTOOPTS_FORMAT = 0,
PROTOOPTS_STARTBIND,
};
enum {
STARTBIND_NO = 0,
STARTBIND_YES = 1,
};
// This are frequency hopping tables for UDI protocols
// uint8_t16 V1 (Orange LED) Bind CH 0x07
// TX ID 0x57, 0x5A, 0x2D
static const uint8_t freq_hopping_uint8_t16_v1[NUM_UDI_RF_CHANNELS] = {
0x07, 0x21, 0x49, 0x0B, 0x39, 0x10, 0x25, 0x42,
0x1D, 0x31, 0x35, 0x14, 0x28, 0x3D, 0x18, 0x2D
};
// Protocol 2014 (uint8_t30,uint8_t39,...) BIND CH 0x23 (second entry)
// DATA: hops ~ every 0.361s (0.350 ... 0.372)
static const uint8_t freq_hopping_uint8_t39[NUM_UDI_RF_CHANNELS] = {
0x08, 0x23, 0x48, 0x0D, 0x3B, 0x12, 0x27, 0x44,
0x1F, 0x33, 0x37, 0x16, 0x2A, 0x3F, 0x1A, 0x2F
};
// Points to proper table
static const uint8_t * rf_udi_channels = NULL;
static uint8_t packet_udi_ack()
{
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))) {
case BV(NRF24L01_07_TX_DS): return PKT_ACKED;
case BV(NRF24L01_07_MAX_RT): return PKT_TIMEOUT;
}
return PKT_PENDING;
}
static void UDI_init()
{
NRF24L01_Initialize();
//NRF24L01_SetTxRxMode(TX_EN);
switch (sub_protocol) {
case U816_V1:
rf_udi_channels = freq_hopping_uint8_t16_v1;
payload_size = 8;
break;
case U816_V2:
rf_udi_channels = NULL; // NO HOPPING !
payload_size = 7;
break;
case U839_2014:
// UDI 2014 Protocol (uint8_t30, uint8_t39, all other new products ?)
rf_udi_channels = freq_hopping_uint8_t39;
payload_size = 8;
break;
}
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payload_size);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x07); // Clear status bits
if ((sub_protocol == U816_V1) || (sub_protocol == U816_V2)) {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, 0x27); //
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x3A); //
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // 3 byte address
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x3F); // Auto-acknowledge on all data pipers, same as YD
if (sub_protocol == U816_V1) {
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x7F); //
} else {
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x7A); //
}
} else
if (sub_protocol == U839_2014) {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, 0x0F); // 2Mbps air rate, 5dBm RF output power, high LNA gain
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x1A); // 500uS retransmit t/o, 10 tries (same as YD)
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // 3 byte address
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x3F); // Auto-acknowledge on all data pipers, same as YD
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F); // Enable CRC, 2 byte CRC, PWR UP, PRIMARY RX
}
NRF24L01_FlushTx();
NRF24L01_FlushRx();
uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_WriteReg(NRF24L01_07_STATUS, status);
status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_FlushTx();
status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_WriteReg(NRF24L01_07_STATUS, status);
// Implicit delay in callback
// delayMicroseconds(120)
}
static void UDI_init2()
{
NRF24L01_FlushTx();
bind_step_success = 0;
packet_sent = 0;
switch (sub_protocol) {
case U816_V1:
rf_ch_num = 0;
bind_channel = rf_udi_channels[rf_ch_num++];
break;
case U816_V2:
rf_ch_num = 0x07; // This is actual channel. No hopping here
bind_channel = 0;
break;
case U839_2014:
rf_ch_num = 1;
bind_channel = rf_udi_channels[rf_ch_num++];
break;
}
NRF24L01_WriteReg(NRF24L01_05_RF_CH, bind_channel);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *) "\xe7\x7e\xe7", 3);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *) "\xe7\x7e\xe7", 3);
// Turn radio power on
NRF24L01_SetTxRxMode(TX_EN);
uint8_t config = BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, config);
// Implicit delay in callback
// delayMicroseconds(150);
}
static void set_tx_id(uint32_t id)
{
tx_id[0] = (id >> 16) & 0xFF;
tx_id[1] = (id >> 8) & 0xFF;
tx_id[2] = (id >> 0) & 0xFF;
/*
uint32_t val = rand32(); randoms[0] = val & 0xff; randoms[1] = (val >> 8 ) & 0xff; randoms[2] = (val >> 16 ) & 0xff;
*/
// FIXME
// This one has been observed, leads to RF CH 0x1F (#08) used for BIND2
randoms[0] = 0x98; randoms[1] = 0x80; randoms[2] = 0x5B;
}
static void add_pkt_checksum()
{
// CHECKSUM was introduced with 2014 protocol
if (sub_protocol < U839_2014) return;
uint8_t sum = 0;
for (uint8_t i = 0; i < payload_size-1; ++i) sum += packet[i];
packet[payload_size-1] = sum & 0x3f; // *sick*
}
static uint8_t convert_channel(uint8_t num, uint8_t chn_max, uint8_t sign_ofs)
{
uint32_t ch = Servo_data[num];
if (ch < PPM_MIN) {
ch = PPM_MIN;
} else if (ch > PPM_MAX) {
ch = PPM_MAX;
}
uint32_t chn_val;
if (sign_ofs) chn_val = (((ch * chn_max / PPM_MAX) + sign_ofs) >> 1);
else chn_val = (ch * chn_max / PPM_MAX);
if (chn_val < 0) chn_val = 0;
else if (chn_val > chn_max) chn_val = chn_max;
return (uint8_t) chn_val;
}
static void read_controls(uint8_t* throttle, uint8_t* rudder, uint8_t* elevator, uint8_t* aileron,
uint8_t* flags)
{
// Protocol is registered AETRG, that is
// Aileron is channel 0, Elevator - 1, Throttle - 2, Rudder - 3
// Sometimes due to imperfect calibration or mixer settings
// throttle can be less than PPM_MIN or larger than
// PPM_MAX. As we have no space here, we hard-limit
// channels values by min..max range
// Channel 3: throttle is 0-100
*throttle = convert_channel(THROTTLE, 0x64, 0);
// Channel 4
*rudder = convert_channel(RUDDER, 0x3f, 0x20);
// Channel 2
*elevator = convert_channel(ELEVATOR, 0x3f, 0x20);
// Channel 1
*aileron = convert_channel(AILERON, 0x3f, 0x20);
// Channel 5
if (Servo_data[AUX1] <= 0) *flags &= ~UDI_FLIP360;
else *flags |= UDI_FLIP360;
// Channel 6
if (Servo_data[AUX2] <= 0) *flags &= ~UDI_FLIP;
else *flags |= UDI_FLIP;
// Channel 7
if (Servo_data[AUX3] <= 0) *flags &= ~UDI_CAMERA;
else *flags |= UDI_CAMERA;
// Channel 8
if (Servo_data[AUX4] <= 0) *flags &= ~UDI_VIDEO;
else *flags |= UDI_VIDEO;
// Channel 9
if (Servo_data[AUX5] <= 0) *flags &= ~UDI_LIGHTS;
else *flags |= UDI_LIGHTS;
// Channel 10
if (Servo_data[AUX6] <= 0) *flags &= ~UDI_MODE2;
else *flags |= UDI_MODE2;
}
static void send_udi_packet(uint8_t bind)
{
packet[7] = 0x4A;
if (bind == 1) {
// Bind phase 1
// MAGIC
packet[0] = 0x5A; // NOTE: Also 0xF3, when RX does not ACK packets (uint8_t39, only TX on) ...
// Current Address / TX ID
if (sub_protocol == U839_2014) {
// uint8_t39: Current RX/TX Addr
packet[1] = 0xE7;
packet[2] = 0x7E;
packet[3] = 0xE7;
} else {
// uint8_t16: ID Fixed per TX
packet[1] = tx_id[0];
packet[2] = tx_id[1];
packet[3] = tx_id[2];
}
// Pseudo random values (lower nibble of packet[4] determines index of RF CH used in BIND2)
packet[4] = randoms[0];
packet[5] = randoms[1];
packet[6] = randoms[2];
if (sub_protocol == U839_2014) {
packet[7] = (packet_counter < 4) ? 0x3f : 0x04; // first four packets use 0x3f here, then 0x04
}
} else if (bind == 2) {
// Bind phase 2
// MAGIC
packet[0] = 0xAA;
// Current Address (RX "ID", pseudorandom again)
packet[1] = rx_id[0];
packet[2] = rx_id[1];
packet[3] = rx_id[2];
// Pseudo random values
packet[4] = randoms[0];
packet[5] = randoms[1];
packet[6] = randoms[2];
if (sub_protocol == U839_2014) {
packet[7] = 0x04;
}
} else {
// regular packet
// Read channels (converts to required ranges)
read_controls(&throttle, &rudder, &elevator, &aileron, &flags);
// MAGIC
packet[0] = 0x55;
packet[1] = throttle; // throttle is 0-0x64
// 3 Channels packed into 2 bytes (5bit per channel)
uint16_t encoded = (rudder << 11) | (elevator << 6) | (aileron << 1);
packet[2] = (encoded >> 8) & 0xff;
packet[3] = encoded & 0xff;
// Trims and flags (0x20 = center)
packet[4] = 0x20; // rudder trim 6bit
packet[5] = 0x20; // elev trim 6bit
packet[6] = 0x20; // ail trim 6bit
if (flags & UDI_FLIP) packet[4] |= 0x80; // "Directional" flip
if (flags & UDI_LIGHTS) packet[4] |= 0x40; // Light on/off
if (flags & UDI_MODE2) packet[5] |= 0x80; // High rate ("Mode2")
if (flags & UDI_FLIP360) packet[5] |= 0x40; // 360 degree flip
if (flags & UDI_VIDEO) packet[6] |= 0x80; // Video recording on/off
if (flags & UDI_CAMERA) packet[6] |= 0x40; // Take picture
// NOTE: Only newer protocols have this (handled by routine)
add_pkt_checksum();
}
uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_WriteReg(NRF24L01_07_STATUS,status);
if (packet_sent && bind && (status & BV(NRF24L01_07_TX_DS))) {
bind_step_success = 1;
}
packet_sent = 0;
// Check if its time to change channel
// This seems to be done by measuring time,
// not by counting packets, on UDI transmitters
// NOTE: Seems even in bind phase channels are changed
// NOTE: Only hop in TX mode ???
if (rf_udi_channels && (bind == 0) && (packets_to_hop-- == 0)) {
uint8_t rf_ch = rf_udi_channels[rf_ch_num];
rf_ch_num++;
rf_ch_num %= NUM_UDI_RF_CHANNELS;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
packets_to_hop = bind ? BIND_PACKETS_UDI_PER_CHANNEL : PACKETS_UDI_PER_CHANNEL;
}
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, payload_size);
++packet_counter;
packet_sent = 1;
}
static uint16_t UDI_callback() {
switch (phase) {
case UDI_INIT2:
UDI_init2();
phase = UDI_BIND1_TX;
return 120;
break;
case UDI_INIT2_NO_BIND:
// Do nothing (stay forever)
// Cannot re-bind on UDI protocol since IDs are random
return 10000; // 10ms
break;
case UDI_BIND1_TX:
if (packet_sent && packet_udi_ack() == PKT_ACKED) { bind_step_success = 1; }
if (bind_step_success) {
// All fine, wait for reply of receiver
phase = UDI_BIND1_RX;
NRF24L01_SetTxRxMode(RX_EN);
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F);
bind_step_success = 0;
//packets_to_check = 12; // according to SPI traces on uint8_t17B RX it receives 12 packets (and answers with 5)
packets_to_check = 3;
} else {
send_udi_packet(1);
}
return BIND_PACKET_UDI_PERIOD;
break;
case UDI_BIND1_RX:
// Check if data has been received
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR) ) {
uint8_t data[UDI_PAYLOADSIZE];
NRF24L01_ReadPayload(data, payload_size);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x4E); // On original TX this is done on LAST packet check only !
NRF24L01_FlushRx();
// Verify MAGIC and Random ID
// (may be reply to bind packet from other TX)
if ((data[0] == 0xA5) &&
(data[4] == randoms[0]) &&
(data[5] == randoms[1]) &&
(data[6] == randoms[2]) &&
(data[7] == randoms[2])) {
rx_id[0] = data[1];
rx_id[1] = data[2];
rx_id[2] = data[3];
if (sub_protocol != U816_V2) {
rf_ch_num = randoms[0] & 0x0f;
}
bind_step_success = 1;
}
}
// RX seems to need more than one ACK
if (packets_to_check) packets_to_check--;
//NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F);
if (bind_step_success && !packets_to_check) {
// All fine, switch address and RF channel,
// send bind packets with channel hopping now
phase = UDI_BIND2_TX;
packet_sent = 0;
packets_to_send = 4;
bind_step_success = 0;
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_id, 3);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_id, 3);
if (sub_protocol != U816_V2) {
// Switch RF channel
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_udi_channels[rf_ch_num++]);
rf_ch_num %= NUM_UDI_RF_CHANNELS;
}
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x7E);
NRF24L01_SetTxRxMode(TX_EN);
//NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0E)
return 10; // 10 µs (start sending immediately)
}
return BIND_PACKET_UDI_PERIOD;
break;
case UDI_BIND2_TX:
if (packet_sent && packet_udi_ack() == PKT_ACKED) {
bind_step_success = 1;
}
send_udi_packet(2);
if (packets_to_send) --packets_to_send;
if (bind_step_success || !packets_to_send) {
// Seems the original TX ignores AACK, too !
// U816 V1: 3 packets send, U839: 4 packets send
// All fine, wait for reply of receiver
phase = UDI_BIND2_RX;
NRF24L01_SetTxRxMode(RX_EN);
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F);
bind_step_success = 0;
packets_to_check = 14; // ???
}
return bind_step_success ? 4000 : 12000; // 4ms if no packed acked yet, 12ms afterwards
// return 120; // FIXME: Varies for first three packets !!!
break;
case UDI_BIND2_RX:
// Check if data has been received
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR) ) {
uint8_t data[UDI_PAYLOADSIZE];
NRF24L01_ReadPayload(data, payload_size);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x4E);
NRF24L01_FlushRx();
// Verify MAGIC, RX Addr, Random ID
// (may be reply to bind packet from other TX)
if ((data[0] == 0xDD) &&
(data[1] == rx_id[0]) &&
(data[2] == rx_id[1]) &&
(data[3] == rx_id[2]) &&
(data[4] == randoms[0]) &&
(data[5] == randoms[1]) &&
(data[6] == randoms[2]) &&
(data[7] == randoms[2])) {
bind_step_success = 1;
}
}
// RX seems to need more than one ACK
if (packets_to_check) packets_to_check--;
//NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F);
if (bind_step_success && !packets_to_check) {
phase = UDI_DATA;
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x7E);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0E);
NRF24L01_FlushTx();
// Switch RF channel
if (sub_protocol == U816_V2) {
// FIXED RF Channel
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num);
} else {
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_udi_channels[rf_ch_num++]);
rf_ch_num %= NUM_UDI_RF_CHANNELS;
}
flags = 0;
BIND_DONE;
}
return BIND_PACKET_UDI_PERIOD;
break;
case UDI_DATA:
if (packet_sent && packet_udi_ack() != PKT_ACKED) {
return PACKET_UDI_CHKTIME;
}
send_udi_packet(0);
break;
}
// Packet every 15ms
return PACKET_UDI_PERIOD;
}
static uint16_t UDI_setup()
{
packet_counter = 0;
UDI_init();
phase = UDI_INIT2;
// observed on U839 TX
set_tx_id(0x457C27);
return INITIAL_UDI_WAIT;
}
#endif

View File

@ -1,3 +1,5 @@
<<<<<<< HEAD
=======
/*
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
@ -13,6 +15,7 @@
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
>>>>>>> refs/remotes/pascallanger/master
#if defined(SHENQI_NRF24L01_INO)
#include "iface_nrf24l01.h"
@ -39,10 +42,17 @@ void SHENQI_init()
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5 bytes rx/tx address
<<<<<<< HEAD
LT8910_Config(4, 8, _BV(LT8910_CRC_ON)|_BV(LT8910_PACKET_LENGTH_EN), 0xAA);
LT8910_SetChannel(2);
LT8910_SetAddress((uint8_t *)"\x9A\x9A\x9A\x9A",4);
LT8910_SetTxRxMode(RX_EN);
=======
LT8900_Config(4, 8, _BV(LT8900_CRC_ON)|_BV(LT8900_PACKET_LENGTH_EN), 0xAA);
LT8900_SetChannel(2);
LT8900_SetAddress((uint8_t *)"\x9A\x9A\x9A\x9A",4);
LT8900_SetTxRxMode(RX_EN);
>>>>>>> refs/remotes/pascallanger/master
}
void SHENQI_send_packet()
@ -51,6 +61,16 @@ void SHENQI_send_packet()
if(packet_count==0)
{
uint8_t bind_addr[4];
<<<<<<< HEAD
bind_addr[0]=0x9A;
bind_addr[1]=0x9A;
bind_addr[2]=rx_tx_addr[2];
bind_addr[3]=rx_tx_addr[3];
LT8910_SetAddress(bind_addr,4);
LT8910_SetChannel(2);
packet[1]=rx_tx_addr[1];
packet[2]=rx_tx_addr[0];
=======
bind_addr[0]=rx_tx_addr[0];
bind_addr[1]=rx_tx_addr[1];
bind_addr[2]=0x9A;
@ -59,24 +79,39 @@ void SHENQI_send_packet()
LT8900_SetChannel(2);
packet[1]=rx_tx_addr[2];
packet[2]=rx_tx_addr[3];
>>>>>>> refs/remotes/pascallanger/master
packet_period=2508;
}
else
{
<<<<<<< HEAD
LT8910_SetAddress(rx_tx_addr,4);
packet[1]=255-convert_channel_8b(RUDDER);
packet[2]=255-convert_channel_8b_scale(THROTTLE,0x60,0xA0);
uint8_t freq=pgm_read_byte_near(&SHENQI_Freq[hopping_frequency_no])+(rx_tx_addr[1]&0x0F);
LT8910_SetChannel(freq);
=======
LT8900_SetAddress(rx_tx_addr,4);
packet[1]=255-convert_channel_8b(RUDDER);
packet[2]=255-convert_channel_8b_scale(THROTTLE,0x60,0xA0);
uint8_t freq=pgm_read_byte_near(&SHENQI_Freq[hopping_frequency_no])+(rx_tx_addr[2]&0x0F);
LT8900_SetChannel(freq);
>>>>>>> refs/remotes/pascallanger/master
hopping_frequency_no++;
if(hopping_frequency_no==60)
hopping_frequency_no=0;
packet_period=1750;
}
// Send packet + 1 retransmit - not sure why but needed (not present on original TX...)
<<<<<<< HEAD
LT8910_WritePayload(packet,3);
while(NRF24L01_packet_ack()!=PKT_ACKED);
LT8910_WritePayload(packet,3);
=======
LT8900_WritePayload(packet,3);
while(NRF24L01_packet_ack()!=PKT_ACKED);
LT8900_WritePayload(packet,3);
>>>>>>> refs/remotes/pascallanger/master
packet_count++;
if(packet_count==7)
@ -94,6 +129,16 @@ uint16_t SHENQI_callback()
SHENQI_send_packet();
else
{
<<<<<<< HEAD
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR))
{
if(LT8910_ReadPayload(packet, 3))
{
BIND_DONE;
rx_tx_addr[3]=packet[1];
rx_tx_addr[2]=packet[2];
LT8910_SetTxRxMode(TX_EN);
=======
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
{
if(LT8900_ReadPayload(packet, 3))
@ -102,6 +147,7 @@ uint16_t SHENQI_callback()
rx_tx_addr[0]=packet[1];
rx_tx_addr[1]=packet[2];
LT8900_SetTxRxMode(TX_EN);
>>>>>>> refs/remotes/pascallanger/master
packet_period=14000;
}
NRF24L01_FlushRx();
@ -116,7 +162,11 @@ uint16_t initSHENQI()
SHENQI_init();
hopping_frequency_no = 0;
packet_count=0;
<<<<<<< HEAD
packet_period=100;
=======
packet_period=500;
>>>>>>> refs/remotes/pascallanger/master
return 1000;
}

View File

@ -1,3 +1,178 @@
<<<<<<< HEAD
//*************************************
// FrSky Telemetry serial code *
// By Midelic on RCGroups *
//*************************************
#if defined TELEMETRY
#if defined FRSKYX_CC2500_INO
#define SPORT_TELEMETRY
#endif
#if defined FRSKY_CC2500_INO
#define HUB_TELEMETRY
#endif
#if defined SPORT_TELEMETRY
#define SPORT_TELEMETRY
#define SPORT_TIME 12000
uint32_t last=0;
uint8_t sport_counter=0;
uint8_t RxBt=0;
uint8_t rssi;
uint8_t ADC2;
#endif
#if defined HUB_TELEMETRY
#define MAX_PKTX 10
uint8_t pktx[MAX_PKTX];
uint8_t index;
uint8_t prev_index;
uint8_t pass = 0;
#endif
#define USER_MAX_BYTES 6
uint8_t frame[18];
void frskySendStuffed()
{
Serial_write(0x7E);
for (uint8_t i = 0; i < 9; i++)
{
if ((frame[i] == 0x7e) || (frame[i] == 0x7d))
{
Serial_write(0x7D);
frame[i] ^= 0x20;
}
Serial_write(frame[i]);
}
Serial_write(0x7E);
}
void compute_RSSIdbm(){
RSSI_dBm = (((uint16_t)(pktt[len-2])*18)>>5);
if(pktt[len-2] >=128)
RSSI_dBm -= 82;
else
RSSI_dBm += 65;
}
void frsky_check_telemetry(uint8_t *pkt,uint8_t len)
{
if(pkt[1] != rx_tx_addr[3] || pkt[2] != rx_tx_addr[2] || len != pkt[0] + 3)
{//only packets with the required id and packet length
for(uint8_t i=3;i<6;i++)
pktt[i]=0;
return;
}
else
{
for (uint8_t i=3;i<len;i++)
pktt[i]=pkt[i];
telemetry_link=1;
if(pktt[6]>0)
telemetry_counter=(telemetry_counter+1)%32;
}
}
void frsky_link_frame()
{
frame[0] = 0xFE;
if ((cur_protocol[0]&0x1F)==MODE_FRSKY)
{
compute_RSSIdbm();
frame[1] = pktt[3];
frame[2] = pktt[4];
frame[3] = (uint8_t)RSSI_dBm;
frame[4] = pktt[5]*2;
}
else
if ((cur_protocol[0]&0x1F)==MODE_HUBSAN)
{
frame[1] = v_lipo*2; //v_lipo; common 0x2A=42/10=4.2V
frame[2] = frame[1];
frame[3] = 0x00;
frame[4] = (uint8_t)RSSI_dBm;
}
frame[5] = frame[6] = frame[7] = frame[8] = 0;
frskySendStuffed();
}
#if defined HUB_TELEMETRY
void frsky_user_frame()
{
uint8_t indexx = 0, c=0, j=8, n=0, i;
if(pktt[6]>0 && pktt[6]<=MAX_PKTX)
{//only valid hub frames
frame[0] = 0xFD;
frame[1] = 0;
frame[2] = pktt[7];
switch(pass)
{
case 0:
indexx=pktt[6];
for(i=0;i<indexx;i++)
{
if(pktt[j]==0x5E)
{
if(c++)
{
c=0;
n++;
j++;
}
}
pktx[i]=pktt[j++];
}
indexx = indexx-n;
pass=1;
case 1:
index=indexx;
prev_index = indexx;
if(index<USER_MAX_BYTES)
{
for(i=0;i<index;i++)
frame[i+3]=pktx[i];
pktt[6]=0;
pass=0;
}
else
{
index = USER_MAX_BYTES;
for(i=0;i<index;i++)
frame[i+3]=pktx[i];
pass=2;
}
break;
case 2:
index = prev_index - index;
prev_index=0;
if(index<MAX_PKTX-USER_MAX_BYTES) //10-6=4
for(i=0;i<index;i++)
frame[i+3]=pktx[USER_MAX_BYTES+i];
pass=0;
pktt[6]=0;
break;
default:
break;
}
if(!index)
return;
frame[1] = index;
frskySendStuffed();
}
else
pass=0;
}
#endif
#if defined SPORT_TELEMETRY
/* SPORT details serial
100K 8E2 normal-multiprotocol
-every 12ms-
1 2 3 4 5 6 7 8 9 CRC DESCR
=======
/*
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
@ -230,6 +405,7 @@ pkt[6]|(counter++)|00 01 02 03 04 05 06 07 08 09
100K 8E2 normal-multiprotocol
-every 12ms-or multiple of 12; %36
1 2 3 4 5 6 7 8 9 CRC DESCR
>>>>>>> refs/remotes/pascallanger/master
7E 98 10 05 F1 20 23 0F 00 A6 SWR_ID
7E 98 10 01 F1 33 00 00 00 C9 RSSI_ID
7E 98 10 04 F1 58 00 00 00 A1 BATT_ID
@ -241,15 +417,24 @@ pkt[6]|(counter++)|00 01 02 03 04 05 06 07 08 09
7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID
<<<<<<< HEAD
Telemetry frames(RF) SPORT info 15 bytes
SPORT frame 6+3 bytes
=======
Telemetry frames(RF) SPORT info
15 bytes payload
SPORT frame valid 6+3 bytes
>>>>>>> refs/remotes/pascallanger/master
[00] PKLEN 0E 0E 0E 0E
[01] TXID1 DD DD DD DD
[02] TXID2 6D 6D 6D 6D
[03] CONST 02 02 02 02
[04] RS/RB 2C D0 2C CE //D0;CE=2*RSSI;....2C = RX battery voltage(5V from Bec)
<<<<<<< HEAD
[05] ????? 03 10 21 32 //TX/RX telemetry hand-shake bytes
=======
[05] HD-SK 03 10 21 32 //TX/RX telemetry hand-shake bytes
>>>>>>> refs/remotes/pascallanger/master
[06] NO.BT 00 00 06 03 //No.of valid SPORT frame bytes in the frame
[07] STRM1 00 00 7E 00
[08] STRM2 00 00 1A 00
@ -257,6 +442,146 @@ pkt[6]|(counter++)|00 01 02 03 04 05 06 07 08 09
[10] STRM4 03 03 03 03
[11] STRM5 F1 F1 F1 F1
[12] STRM6 D1 D1 D0 D0
<<<<<<< HEAD
[13] CHKSUM1
[14] CHKSUM2
*/
void sportSend(uint8_t *p)
{
uint16_t crc_s = 0;
Serial_write(0x7e);//+9
for (uint8_t i = 0; i < 9; i++)
{
if (i == 8)
p[i] = 0xff - crc_s;
if ((p[i] == 0x7e) || (p[i] == 0x7d))
{
Serial_write(0x7d);
Serial_write(0x20 ^ p[i]);
}
else
Serial_write(p[i]);
if (i>0)
{
crc_s += p[i]; //0-1FF
crc_s += crc_s >> 8; //0-100
crc_s &= 0x00ff;
}
}
}
void sportIdle()
{
Serial_write(0x7e);
}
void sportSendFrame()
{
//at the moment only SWR RSSI,RxBt and A2.
sport_counter = (sport_counter + 1) %9;
for (uint8_t i=5;i<8;i++)
frame[i]=0;
switch (sport_counter)
{
case 0: // SWR
frame[0] = 0x98;
frame[1] = 0x10;
frame[2] = 0x05;
frame[3] = 0xf1;
frame[4] = 0x20;//dummy values if swr 20230f00
frame[5] = 0x23;
frame[6] = 0x0F;
frame[7] = 0x00;
break;
case 1: // RSSI
frame[0] = 0x98;
frame[1] = 0x10;
frame[2] = 0x01;
frame[3] = 0xf1;
frame[4] = rssi;
break;
case 2: //BATT
frame[0] = 0x98;
frame[1] = 0x10;
frame[2] = 0x04;
frame[3] = 0xf1;
frame[4] = RxBt;//a1;
break;
case 3: //ADC2(A2)
frame[0] = 0x1A;
frame[1] = 0x10;
frame[2] = 0x03;
frame[3] = 0xf1;
frame[4] = ADC2;//a2;;
break;
default:
sportIdle();
return;
}
sportSend(frame);
}
void process_sport_data()//only for ADC2
{
uint8_t j=7;
if(pktt[6]>0 && pktt[6]<=USER_MAX_BYTES)
{
for(uint8_t i=0;i<6;i++)
if(pktt[j++]==0x03)
if(pktt[j]==0xF1)
{
ADC2=pktt[j+1];
break;
}
pktt[6]=0;//new frame
}
}
#endif
void frskyUpdate()
{
if(telemetry_link && (cur_protocol[0]&0x1F) != MODE_FRSKYX )
{
frsky_link_frame();
telemetry_link=0;
return;
}
#if defined HUB_TELEMETRY
if(!telemetry_link && (cur_protocol[0]&0x1F) != MODE_HUBSAN && (cur_protocol[0]&0x1F) != MODE_FRSKYX)
{
frsky_user_frame();
return;
}
#endif
#if defined SPORT_TELEMETRY
if ((cur_protocol[0]&0x1F)==MODE_FRSKYX)
{
if(telemetry_link)
{
process_sport_data();
if(pktt[4]>0x36)
rssi=pktt[4]/2;
else
RxBt=pktt[4];
telemetry_link=0;
}
uint32_t now = micros();
if ((now - last) > SPORT_TIME)
{
sportSendFrame();
last = now;
}
}
#endif
}
#endif
=======
[13] CHKSUM1 --|2 CRC bytes sent by RX (calculated on RX side crc16/table)
[14] CHKSUM2 --|
+2 appended bytes automatically RSSI and LQI/CRC bytes(len=0x0E+3);
@ -824,3 +1149,4 @@ ISR(TIMER0_OVF_vect)
#endif // BASH_SERIAL
#endif // TELEMETRY
>>>>>>> refs/remotes/pascallanger/master

View File

@ -34,6 +34,16 @@
#define YD717_PAYLOADSIZE 8 // receive data pipes set to this size, but unused
<<<<<<< HEAD
enum {
YD717_INIT1 = 0,
YD717_BIND2,
YD717_BIND3,
YD717_DATA
};
=======
>>>>>>> refs/remotes/pascallanger/master
static void __attribute__((unused)) yd717_send_packet(uint8_t bind)
{
uint8_t rudder_trim, elevator_trim, aileron_trim;
@ -139,6 +149,11 @@ static void __attribute__((unused)) yd717_init()
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); // Set feature bits on
NRF24L01_Activate(0x73);
<<<<<<< HEAD
static void __attribute__((unused)) YD717_init1()
{
=======
>>>>>>> refs/remotes/pascallanger/master
// for bind packets set address to prearranged value known to receiver
uint8_t bind_rx_tx_addr[] = {0x65, 0x65, 0x65, 0x65, 0x65};
@ -153,6 +168,16 @@ static void __attribute__((unused)) yd717_init()
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_rx_tx_addr, 5);
}
<<<<<<< HEAD
static void __attribute__((unused)) YD717_init2()
{
// set rx/tx address for data phase
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
}
=======
>>>>>>> refs/remotes/pascallanger/master
uint16_t yd717_callback()
{
if(IS_BIND_DONE_on)

View File

@ -13,6 +13,85 @@
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
<<<<<<< HEAD
/** Multiprotocol module configuration file ***/
//Uncomment your TX type
#define TARANIS //TARANIS TAER (1100<->1900µs)
//#define TX_ER9X //ER9X AETR (988<->2012µs)
//#define TX_DEVO7 //DEVO7 EATR (1120<->1920µs)
//#define TX_SPEKTRUM //Spektrum TAER (1100<->1900µs)
//#define TX_HISKY //HISKY AETR (1100<->1900µs)
//Uncomment to enable telemetry
#define TELEMETRY
//Comment if a module is not installed
#define A7105_INSTALLED
#define CYRF6936_INSTALLED
//#define CC2500_INSTALLED
#define NFR24L01_INSTALLED
//Comment a protocol to exclude it from compilation
#ifdef A7105_INSTALLED
#define JOYSWAY_A7105_INO
#define FLYSKY_A7105_INO
#define HUBSAN_A7105_INO
#endif
#ifdef CYRF6936_INSTALLED
#define J6PRO_CYRF6936_INO
#define WK2x01_CYRF6936_INO
#define DEVO_CYRF6936_INO
#define DSM2_CYRF6936_INO
#endif
#ifdef CC2500_INSTALLED
#define SKYARTEC_CC2500_INO
#define FRSKY_CC2500_INO
#define FRSKYX_CC2500_INO
#endif
#ifdef NFR24L01_INSTALLED
#define HM830_NRF24L01_INO
#define CFlie_NRF24L01_INO
#define H377_NRF24L01_INO
#define ESKY150_NRF24L01_INO
#define HonTai_NRF24L01_INO
#define UDI_NRF24L01_INO
#define NE260_NRF24L01_INO
#define BlueFly_NRF24L01_INO //probleme gene id
#define FBL100_NRF24L01_INO // finir id
#define BAYANG_NRF24L01_INO
#define CG023_NRF24L01_INO
#define CX10_NRF24L01_INO
#define ESKY_NRF24L01_INO
#define HISKY_NRF24L01_INO
#define KN_NRF24L01_INO
#define SLT_NRF24L01_INO
#define SYMAX_NRF24L01_INO
#define V2X2_NRF24L01_INO
#define YD717_NRF24L01_INO
#define MT99XX_NRF24L01_INO
#define MJXQ_NRF24L01_INO
#define SHENQI_NRF24L01_INO
#define FY326_NRF24L01_INO
#endif
//Update this table to set which protocol and all associated settings are called for the corresponding dial number
const PPM_Parameters PPM_prot[15]= {
// Dial Protocol Sub protocol RX_Num Power Auto Bind Option
/* 1 */ {MODE_FLYSKY, Flysky , 0 , P_HIGH , AUTOBIND , 0 },
/* 2 */ {MODE_HUBSAN, 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 3 */ {MODE_FRSKY , 0 , 0 , P_HIGH , NO_AUTOBIND , 0xD7 }, // D7 fine tuning
/* 4 */ {MODE_HISKY , Hisky , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 5 */ {MODE_V2X2 , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 6 */ {MODE_DSM2 , DSM2 , 0 , P_HIGH , NO_AUTOBIND , 6 }, // 6 channels @ 11ms
/* 7 */ {MODE_DEVO , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 8 */ {MODE_YD717 , YD717 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 9 */ {MODE_KN , FEILUN , 0 , P_HIGH , AUTOBIND , 0 },
=======
/**********************************************/
/** Multiprotocol module configuration file ***/
/**********************************************/
@ -155,6 +234,7 @@ const PPM_Parameters PPM_prot[15]= {
/* 7 */ {MODE_DEVO , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 8 */ {MODE_YD717 , YD717 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 9 */ {MODE_KN , WLTOYS , 0 , P_HIGH , NO_AUTOBIND , 0 },
>>>>>>> refs/remotes/pascallanger/master
/* 10 */ {MODE_SYMAX , SYMAX , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 11 */ {MODE_SLT , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 12 */ {MODE_CX10 , CX10_BLUE , 0 , P_HIGH , NO_AUTOBIND , 0 },
@ -162,7 +242,11 @@ const PPM_Parameters PPM_prot[15]= {
/* 14 */ {MODE_BAYANG, 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 15 */ {MODE_SYMAX , SYMAX5C , 0 , P_HIGH , NO_AUTOBIND , 0 }
};
<<<<<<< HEAD
/* Available protocols and associated sub protocols:
=======
/* Available protocols and associated sub protocols to pick and choose from
>>>>>>> refs/remotes/pascallanger/master
MODE_FLYSKY
Flysky
V9X9
@ -170,18 +254,28 @@ const PPM_Parameters PPM_prot[15]= {
V912
MODE_HUBSAN
NONE
<<<<<<< HEAD
MODE_FRSKY
=======
MODE_FRSKYD
>>>>>>> refs/remotes/pascallanger/master
NONE
MODE_HISKY
Hisky
HK310
MODE_V2X2
NONE
<<<<<<< HEAD
MODE_DSM2
DSM2
DSMX
=======
MODE_DSM
DSM2_22
DSM2_11
DSMX_22
DSMX_11
>>>>>>> refs/remotes/pascallanger/master
MODE_DEVO
NONE
MODE_YD717
@ -214,20 +308,137 @@ const PPM_Parameters PPM_prot[15]= {
MODE_BAYANG
NONE
MODE_FRSKYX
<<<<<<< HEAD
NONE
=======
CH_16
CH_8
>>>>>>> refs/remotes/pascallanger/master
MODE_ESKY
NONE
MODE_MT99XX
MT99
H7
YZ
<<<<<<< HEAD
=======
LS
>>>>>>> refs/remotes/pascallanger/master
MODE_MJXQ
WLH08
X600
X800
H26D
<<<<<<< HEAD
MODE_SHENQI
NONE
MODE_FY326
FY326
FY319
RX_Num value between 0 and 15
Power P_HIGH or P_LOW
Auto Bind AUTOBIND or NO_AUTOBIND
Option value between 0 and 255. 0xD7 or 0x00 for Frsky fine tuning.
*/
//******************
//TX definitions with timing endpoints and channels order
// Turnigy PPM and channels
#if defined(TX_ER9X)
#define PPM_MAX 2140
#define PPM_MIN 860
#define PPM_MAX_100 2012
#define PPM_MIN_100 988
#define AETR
#endif
// Devo PPM and channels
#if defined(TX_DEVO7)
#define PPM_MAX 2100
#define PPM_MIN 900
#define PPM_MAX_100 1920
#define PPM_MIN_100 1120
#define EATR
#endif
// SPEKTRUM PPM and channels
#if defined(TX_SPEKTRUM)
#define PPM_MAX 2000
#define PPM_MIN 1000
#define PPM_MAX_100 1900
#define PPM_MIN_100 1100
#define TAER
#endif
// TARANIS PPM and channels
#if defined(TARANIS)
#define PPM_MAX 2000
#define PPM_MIN 1000
#define PPM_MAX_100 1900
#define PPM_MIN_100 1100
#define EATR
#endif
// HISKY
#if defined(TX_HISKY)
#define PPM_MAX 2000
#define PPM_MIN 1000
#define PPM_MAX_100 1900
#define PPM_MIN_100 1100
#define AETR
#endif
#if defined(EATR)
enum chan_order{
ELEVATOR=0,
AILERON,
THROTTLE,
RUDDER,
};
#endif
#if defined(TAER)
enum chan_order{
THROTTLE=0,
AILERON,
ELEVATOR,
RUDDER,
};
#endif
#if defined(AETR)
enum chan_order{
AILERON =0,
ELEVATOR,
THROTTLE,
RUDDER,
};
#endif
enum chan_orders{
AUX1 =4,
AUX2,
AUX3,
AUX4,
AUX5,
AUX6,
AUX7,
AUX8,
AUX9
};
#define PPM_MIN_COMMAND 1250
#define PPM_SWITCH 1550
#define PPM_MAX_COMMAND 1750
//Uncoment the desired serial speed
#define BAUD 100000
//#define BAUD 125000
=======
E010
MODE_SHENQI
NONE
@ -260,4 +471,5 @@ const PPM_Parameters PPM_prot[15]= {
// As an exxample, it's usefull for the WLTOYS F929/F939/F949/F959 (all using the Flysky protocol) which requires a bind at each power up.
// Option: the value is between -127 and +127.
// The option value is only valid for some protocols, read this page for more information: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md
// The option value is only valid for some protocols, read this page for more information: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md
>>>>>>> refs/remotes/pascallanger/master

View File

@ -103,8 +103,13 @@ enum {
//#define NOP 0xFF
// XN297 emulation layer
<<<<<<< HEAD
#define XN297_UNSCRAMBLED 8
=======
enum {
XN297_UNSCRAMBLED = 0,
XN297_SCRAMBLED
};
>>>>>>> refs/remotes/pascallanger/master
#endif

94
Multiprotocol/multi.lua Normal file
View File

@ -0,0 +1,94 @@
-- Multiprotocole Midelic et Pascallanger
local debut = 0
local tps = 0
local tpsact = 1024
local mix, mixe
local channel
local inp = {
{ "Protocole", VALUE, 1, 26, 2 },
{ "Switch", SOURCE }
}
-- 6 7 8 15 16 17 24 25 26
-- 4 5 12 13 14 21 22 23
-- 1 2 3 9 10 11 18 19 20
local out = { "Bind", "Gaz", "Aile", "Prof", "Dir" }
local function run_func(proto, sw)
-- test mixage lua
if debut == 0 then
-- passage en lua
for channel = 0, 3, 1 do
local mix = model.getMix(channel, 0)
mix_source = mix["source"]
if mix_source < 33 or 1 then
model.deleteMix(channel, 0)
mix["source"] = channel + 34
mix["name"] = "Lua "
model.insertMix(channel, 0, mix)
end
end
end
-- inter install
channel = 4
mix = { name="Raz Bind", source=33, weight=100, switch=0, multiplex=REPLACE }
count = model.getMixesCount(channel + 0)
if count == 0 and inter == 1 then
model.insertMix(channel + 0, 0, mix)
elseif count == 1 and inter == 0 then
mixe = model.getMix(channel, 0)
if mixe["name"] == mix["name"] then
model.deleteMix(channel, 0)
end
end
-- delais init
if proto ~= debut then
tps = getTime() + 250 -- delai pour mini 12 cycle PPM
tpsact = 1024
debut = proto
end
local gaz = 1024
local ail = 0
local dir = 0
local pro = 0
if tpsact == 0 and sw < 100 then
-- reprise valeur input
pro = getValue(1)
ail = getValue(2)
gaz = getValue(3)
dir = getValue(4)
elseif tpsact ~= 0 then
-- decallage pour position memo (centre)
if proto > 4 then proto = proto + 1 end
-- calcul position
-- decallage pour > 18
if proto > 18 then
ail = 1024
proto = proto - 18
end
-- decallage pour > 9
if proto > 9 then
ail = -1024
proto = proto - 9
end
if proto < 4 then pro = -1024 end
if proto > 6 then pro = 1024 end
if proto % 3 == 1 then dir = -1024 end
if proto % 3 == 0 then dir = 1024 end
if tps < getTime() then
tpsact = tpsact - 512
if tpsact>-20 then tps = getTime() + 250 end
end
sw = tpsact
end
return sw, gaz, ail, pro, dir
end
return { run=run_func, input=inp, output=out}

View File

@ -0,0 +1,485 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Check selected board type
#ifndef XMEGA
#if not defined(ARDUINO_AVR_PRO) && not defined(ARDUINO_AVR_MINI) && not defined(ARDUINO_AVR_NANO)
#error You must select the board type "Arduino Pro or Pro Mini" or "Arduino Mini"
#endif
#if F_CPU != 16000000L || not defined(__AVR_ATmega328P__)
#error You must select the processor type "ATmega328(5V, 16MHz)"
#endif
#endif
//******************
// Protocols
//******************
enum PROTOCOLS
{
MODE_SERIAL = 0, // Serial commands
MODE_FLYSKY = 1, // =>A7105
MODE_HUBSAN = 2, // =>A7105
MODE_FRSKYD = 3, // =>CC2500
MODE_HISKY = 4, // =>NRF24L01
MODE_V2X2 = 5, // =>NRF24L01
MODE_DSM = 6, // =>CYRF6936
MODE_DEVO = 7, // =>CYRF6936
MODE_YD717 = 8, // =>NRF24L01
MODE_KN = 9, // =>NRF24L01
MODE_SYMAX = 10, // =>NRF24L01
MODE_SLT = 11, // =>NRF24L01
MODE_CX10 = 12, // =>NRF24L01
MODE_CG023 = 13, // =>NRF24L01
MODE_BAYANG = 14, // =>NRF24L01
MODE_FRSKYX = 15, // =>CC2500
MODE_ESKY = 16, // =>NRF24L01
MODE_MT99XX = 17, // =>NRF24L01
MODE_MJXQ = 18, // =>NRF24L01
MODE_SHENQI = 19, // =>NRF24L01
MODE_FY326 = 20, // =>NRF24L01
MODE_SFHSS = 21, // =>CC2500
MODE_J6PRO = 22, // =>CYRF6936
MODE_FQ777 = 23, // =>NRF24L01
MODE_ASSAN = 24, // =>NRF24L01
MODE_FRSKYV = 25, // =>CC2500
MODE_HONTAI = 26, // =>NRF24L01
MODE_OPENLRS = 27, // =>OpenLRS hardware
};
enum Flysky
{
Flysky = 0,
V9X9 = 1,
V6X6 = 2,
V912 = 3
};
enum Hisky
{
Hisky = 0,
HK310 = 1
};
enum DSM
{
DSM2_22 = 0,
DSM2_11 = 1,
DSMX_22 = 2,
DSMX_11 = 3,
DSM_AUTO = 4
};
enum YD717
{
YD717 = 0,
SKYWLKR = 1,
SYMAX4 = 2,
XINXUN = 3,
NIHUI = 4
};
enum KN
{
WLTOYS = 0,
FEILUN = 1
};
enum SYMAX
{
SYMAX = 0,
SYMAX5C = 1
};
enum CX10
{
CX10_GREEN = 0,
CX10_BLUE = 1, // also compatible with CX10-A, CX12
DM007 = 2,
Q282 = 3,
JC3015_1 = 4,
JC3015_2 = 5,
MK33041 = 6,
Q242 = 7
};
enum CG023
{
CG023 = 0,
YD829 = 1,
H8_3D = 2
};
enum MT99XX
{
MT99 = 0,
H7 = 1,
YZ = 2,
LS = 3
};
enum MJXQ
{
WLH08 = 0,
X600 = 1,
X800 = 2,
H26D = 3,
E010 = 4
};
enum FRSKYX
{
CH_16 = 0,
CH_8 = 1,
};
enum HONTAI
{
FORMAT_HONTAI = 0,
FORMAT_JJRCX1 = 1,
FORMAT_X5C1 = 2
};
#define NONE 0
#define P_HIGH 1
#define P_LOW 0
#define AUTOBIND 1
#define NO_AUTOBIND 0
struct PPM_Parameters
{
uint8_t protocol : 6;
uint8_t sub_proto : 3;
uint8_t rx_num : 4;
uint8_t power : 1;
uint8_t autobind : 1;
uint8_t option;
};
// Macros
#define NOP() __asm__ __volatile__("nop")
//*******************
//*** Timer ***
//*******************
#ifdef XMEGA
#define TIFR1 TCC1.INTFLAGS
#define OCF1A_bm TC1_CCAIF_bm
#define OCR1A TCC1.CCA
#define TCNT1 TCC1.CNT
#define UDR0 USARTC0.DATA
#define OCF1B_bm TC1_CCBIF_bm
#define OCR1B TCC1.CCB
#define TIMSK1 TCC1.INTCTRLB
#define SET_TIMSK1_OCIE1B TIMSK1 = (TIMSK1 & 0xF3) | 0x04
#define CLR_TIMSK1_OCIE1B TIMSK1 &= 0xF3
#else
#define OCF1A_bm _BV(OCF1A)
#define OCF1B_bm _BV(OCF1B)
#define SET_TIMSK1_OCIE1B TIMSK1 |= _BV(OCIE1B)
#define CLR_TIMSK1_OCIE1B TIMSK1 &=~_BV(OCIE1B)
#endif
//***************
//*** Flags ***
//***************
#define RX_FLAG_on protocol_flags |= _BV(0)
#define RX_FLAG_off protocol_flags &= ~_BV(0)
#define IS_RX_FLAG_on ( ( protocol_flags & _BV(0) ) !=0 )
//
#define CHANGE_PROTOCOL_FLAG_on protocol_flags |= _BV(1)
#define CHANGE_PROTOCOL_FLAG_off protocol_flags &= ~_BV(1)
#define IS_CHANGE_PROTOCOL_FLAG_on ( ( protocol_flags & _BV(1) ) !=0 )
//
#define POWER_FLAG_on protocol_flags |= _BV(2)
#define POWER_FLAG_off protocol_flags &= ~_BV(2)
#define IS_POWER_FLAG_on ( ( protocol_flags & _BV(2) ) !=0 )
//
#define RANGE_FLAG_on protocol_flags |= _BV(3)
#define RANGE_FLAG_off protocol_flags &= ~_BV(3)
#define IS_RANGE_FLAG_on ( ( protocol_flags & _BV(3) ) !=0 )
//
#define AUTOBIND_FLAG_on protocol_flags |= _BV(4)
#define AUTOBIND_FLAG_off protocol_flags &= ~_BV(4)
#define IS_AUTOBIND_FLAG_on ( ( protocol_flags & _BV(4) ) !=0 )
//
#define BIND_BUTTON_FLAG_on protocol_flags |= _BV(5)
#define BIND_BUTTON_FLAG_off protocol_flags &= ~_BV(5)
#define IS_BIND_BUTTON_FLAG_on ( ( protocol_flags & _BV(5) ) !=0 )
//PPM RX OK
#define PPM_FLAG_off protocol_flags &= ~_BV(6)
#define PPM_FLAG_on protocol_flags |= _BV(6)
#define IS_PPM_FLAG_on ( ( protocol_flags & _BV(6) ) !=0 )
//Bind flag
#define BIND_IN_PROGRESS protocol_flags &= ~_BV(7)
#define BIND_DONE protocol_flags |= _BV(7)
#define IS_BIND_DONE_on ( ( protocol_flags & _BV(7) ) !=0 )
//
#define BAD_PROTO_off protocol_flags2 &= ~_BV(0)
#define BAD_PROTO_on protocol_flags2 |= _BV(0)
#define IS_BAD_PROTO_on ( ( protocol_flags2 & _BV(0) ) !=0 )
//
#define RX_DONOTUPDTAE_off protocol_flags2 &= ~_BV(1)
#define RX_DONOTUPDTAE_on protocol_flags2 |= _BV(1)
#define IS_RX_DONOTUPDTAE_on ( ( protocol_flags2 & _BV(1) ) !=0 )
//
#define RX_MISSED_BUFF_off protocol_flags2 &= ~_BV(2)
#define RX_MISSED_BUFF_on protocol_flags2 |= _BV(2)
#define IS_RX_MISSED_BUFF_on ( ( protocol_flags2 & _BV(2) ) !=0 )
//TX Pause
#define TX_MAIN_PAUSE_off protocol_flags2 &= ~_BV(3)
#define TX_MAIN_PAUSE_on protocol_flags2 |= _BV(3)
#define IS_TX_MAIN_PAUSE_on ( ( protocol_flags2 & _BV(3) ) !=0 )
#define TX_RX_PAUSE_off protocol_flags2 &= ~_BV(4)
#define TX_RX_PAUSE_on protocol_flags2 |= _BV(4)
#define IS_TX_RX_PAUSE_on ( ( protocol_flags2 & _BV(4) ) !=0 )
#define IS_TX_PAUSE_on ( ( protocol_flags2 & (_BV(4)|_BV(3)) ) !=0 )
//********************
//*** Blink timing ***
//********************
#define BLINK_BIND_TIME 100
#define BLINK_SERIAL_TIME 500
#define BLINK_BAD_PROTO_TIME_LOW 1000
#define BLINK_BAD_PROTO_TIME_HIGH 50
//*******************
//*** AUX flags ***
//*******************
#define GET_FLAG(ch, mask) ( ch ? mask : 0)
#define Servo_AUX1 Servo_AUX & _BV(0)
#define Servo_AUX2 Servo_AUX & _BV(1)
#define Servo_AUX3 Servo_AUX & _BV(2)
#define Servo_AUX4 Servo_AUX & _BV(3)
#define Servo_AUX5 Servo_AUX & _BV(4)
#define Servo_AUX6 Servo_AUX & _BV(5)
#define Servo_AUX7 Servo_AUX & _BV(6)
#define Servo_AUX8 Servo_AUX & _BV(7)
//************************
//*** Power settings ***
//************************
enum {
TXPOWER_100uW,
TXPOWER_300uW,
TXPOWER_1mW,
TXPOWER_3mW,
TXPOWER_10mW,
TXPOWER_30mW,
TXPOWER_100mW,
TXPOWER_150mW
};
// A7105 power
// Power amp is ~+16dBm so:
enum A7105_POWER
{
A7105_POWER_0 = 0x00<<3 | 0x00, // TXPOWER_100uW = -23dBm == PAC=0 TBG=0
A7105_POWER_1 = 0x00<<3 | 0x01, // TXPOWER_300uW = -20dBm == PAC=0 TBG=1
A7105_POWER_2 = 0x00<<3 | 0x02, // TXPOWER_1mW = -16dBm == PAC=0 TBG=2
A7105_POWER_3 = 0x00<<3 | 0x04, // TXPOWER_3mW = -11dBm == PAC=0 TBG=4
A7105_POWER_4 = 0x01<<3 | 0x05, // TXPOWER_10mW = -6dBm == PAC=1 TBG=5
A7105_POWER_5 = 0x02<<3 | 0x07, // TXPOWER_30mW = 0dBm == PAC=2 TBG=7
A7105_POWER_6 = 0x03<<3 | 0x07, // TXPOWER_100mW = 1dBm == PAC=3 TBG=7
A7105_POWER_7 = 0x03<<3 | 0x07 // TXPOWER_150mW = 1dBm == PAC=3 TBG=7
};
#define A7105_HIGH_POWER A7105_POWER_7
#define A7105_LOW_POWER A7105_POWER_3
#define A7105_RANGE_POWER A7105_POWER_0
#define A7105_BIND_POWER A7105_POWER_0
// NRF Power
// Power setting is 0..3 for nRF24L01
// Claimed power amp for nRF24L01 from eBay is 20dBm.
enum NRF_POWER
{ // Raw w 20dBm PA
NRF_POWER_0 = 0x00, // 0 : -18dBm (16uW) 2dBm (1.6mW)
NRF_POWER_1 = 0x01, // 1 : -12dBm (60uW) 8dBm (6mW)
NRF_POWER_2 = 0x02, // 2 : -6dBm (250uW) 14dBm (25mW)
NRF_POWER_3 = 0x03 // 3 : 0dBm (1mW) 20dBm (100mW)
};
#define NRF_HIGH_POWER NRF_POWER_2
#define NRF_LOW_POWER NRF_POWER_1
#define NRF_RANGE_POWER NRF_POWER_0
#define NRF_BIND_POWER NRF_POWER_0
// CC2500 power output from the chip itself
// The numbers do not take into account any outside amplifier
enum CC2500_POWER
{
CC2500_POWER_0 = 0x00, // 55dbm or less
CC2500_POWER_1 = 0x50, // -30dbm
CC2500_POWER_2 = 0x44, // 28dbm
CC2500_POWER_3 = 0xC0, // 26dbm
CC2500_POWER_4 = 0x84, // 24dbm
CC2500_POWER_5 = 0x81, // 22dbm
CC2500_POWER_6 = 0x46, // 20dbm
CC2500_POWER_7 = 0x93, // 18dbm
CC2500_POWER_8 = 0x55, // 16dbm
CC2500_POWER_9 = 0x8D, // 14dbm
CC2500_POWER_10 = 0xC6, // -12dbm
CC2500_POWER_11 = 0x97, // -10dbm
CC2500_POWER_12 = 0x6E, // -8dbm
CC2500_POWER_13 = 0x7F, // -6dbm
CC2500_POWER_14 = 0xA9, // -4dbm
CC2500_POWER_15 = 0xBB, // -2dbm
CC2500_POWER_16 = 0xFE, // 0dbm
CC2500_POWER_17 = 0xFF // +1dbm
};
#define CC2500_HIGH_POWER CC2500_POWER_16
#define CC2500_LOW_POWER CC2500_POWER_13
#define CC2500_RANGE_POWER CC2500_POWER_1
#define CC2500_BIND_POWER CC2500_POWER_1
// CYRF power
enum CYRF_POWER
{
CYRF_POWER_0 = 0x00, // -35dbm
CYRF_POWER_1 = 0x01, // -30dbm
CYRF_POWER_2 = 0x02, // -24dbm
CYRF_POWER_3 = 0x03, // -18dbm
CYRF_POWER_4 = 0x04, // -13dbm
CYRF_POWER_5 = 0x05, // -5dbm
CYRF_POWER_6 = 0x06, // 0dbm
CYRF_POWER_7 = 0x07 // +4dbm
};
#define CYRF_HIGH_POWER CYRF_POWER_7
#define CYRF_LOW_POWER CYRF_POWER_3
#define CYRF_RANGE_POWER CYRF_POWER_1 // 1/30 of the full power distance
#define CYRF_BIND_POWER CYRF_POWER_0
enum TXRX_State {
TXRX_OFF,
TX_EN,
RX_EN
};
// Packet ack status values
enum {
PKT_PENDING = 0,
PKT_ACKED,
PKT_TIMEOUT
};
// baudrate defines for serial
#define SPEED_100K 0
#define SPEED_9600 1
#define SPEED_57600 2
#define SPEED_125K 3
//****************************************
//*** MULTI protocol serial definition ***
//****************************************
/*
**************************
16 channels serial protocol
**************************
Serial: 100000 Baud 8e2 _ xxxx xxxx p --
Total of 26 bytes
Stream[0] = 0x55 sub_protocol values are 0..31
Stream[0] = 0x54 sub_protocol values are 32..63
header
Stream[1] = sub_protocol|BindBit|RangeCheckBit|AutoBindBit;
sub_protocol is 0..31 (bits 0..4), value should be added with 32 if Stream[0] = 0x54
=> Reserved 0
Flysky 1
Hubsan 2
FrskyD 3
Hisky 4
V2x2 5
DSM 6
Devo 7
YD717 8
KN 9
SymaX 10
SLT 11
CX10 12
CG023 13
Bayang 14
FrskyX 15
ESky 16
MT99XX 17
MJXQ 18
SHENQI 19
FY326 20
SFHSS 21
J6PRO 22
FQ777 23
ASSAN 24
FrskyV 25
HONTAI 26
OpenLRS 27
BindBit=> 0x80 1=Bind/0=No
AutoBindBit=> 0x40 1=Yes /0=No
RangeCheck=> 0x20 1=Yes /0=No
Stream[2] = RxNum | Power | Type;
RxNum value is 0..15 (bits 0..3)
Type is 0..7 <<4 (bit 4..6)
sub_protocol==Flysky
Flysky 0
V9x9 1
V6x6 2
V912 3
sub_protocol==Hisky
Hisky 0
HK310 1
sub_protocol==DSM
DSM2_22 0
DSM2_11 1
DSMX_22 2
DSMX_11 3
sub_protocol==YD717
YD717 0
SKYWLKR 1
SYMAX4 2
XINXUN 3
NIHUI 4
sub_protocol==KN
WLTOYS 0
FEILUN 1
sub_protocol==SYMAX
SYMAX 0
SYMAX5C 1
sub_protocol==CX10
CX10_GREEN 0
CX10_BLUE 1 // also compatible with CX10-A, CX12
DM007 2
Q282 3
JC3015_1 4
JC3015_2 5
MK33041 6
Q242 7
sub_protocol==CG023
CG023 0
YD829 1
H8_3D 2
sub_protocol==MT99XX
MT99 0
H7 1
YZ 2
LS 3
sub_protocol==MJXQ
WLH08 0
X600 1
X800 2
H26D 3
E010 4
sub_protocol==FRSKYX
CH_16 0
CH_8 1
sub_protocol==HONTAI
FORMAT_HONTAI 0
FORMAT_JJRCX1 1
FORMAT_X5C1 2
Power value => 0x80 0=High/1=Low
Stream[3] = option_protocol;
option_protocol value is -127..127
Stream[4] to [25] = Channels
16 Channels on 11 bits (0..2047)
0 -125%
204 -100%
1024 0%
1843 +100%
2047 +125%
Channels bits are concatenated to fit in 22 bytes like in SBUS protocol
*/

Binary file not shown.

BIN
Multiprotocol/opentx.bin Normal file

Binary file not shown.

192
README.md
View File

@ -1,8 +1,12 @@
<<<<<<< HEAD
# DIY-Multiprotocol-TX-Module
=======
# Overview of the MPTM
The **Multiprotocol Tx Module** (or **MPTM**) is a 2.4GHz transmitter module which enables almost any TX to control lot of different models available on the market.
The source code is partly based on the [Deviation TX project](http://www.deviationtx.com), thanks to all the developers for their great job on protocols.
>>>>>>> refs/remotes/pascallanger/master
## Quicklinks
* [Download latest releases of the firmware](https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/releases)
@ -11,6 +15,193 @@ The source code is partly based on the [Deviation TX project](http://www.deviati
* [The old documentation](docs/README-old.md)
* [Documentation to-do list](docs/Documentation_To_Do_List.md)
<<<<<<< HEAD
Fork du projet https://github.com/pascallanger/DIY-Multiprotocol-TX-Module
Afin d'ajouter :
- Une sélection du protocole via les manches de la radio
- Un rebind hardware en PPM
- La radio TARANIS (TAERB, B = rebind ;-) ) et redéclaration des radios
- Un script "LUA" afin de faciliter la position des manches
Programme des évolutions :
- Ajout du de la télémetrie TARANIS à l'aide du projet https://github.com/shadow974/TxAdapter
(Attention, il faut rajouter un transistor afin d'inverser et amplifier le signal)
#Schematic
![Screenshot](http://static.rcgroups.net/forums/attachments/4/0/8/5/8/3/a8443844-119-multiprotocol_diagram_rotary_serial_2.jpg)
Notes:
- Attention: All modules are 3.3V only, never power them with 5V.
- For serial, the dial switch is not needed and the bind button optionnal
- Ajout d'un switch + transistor sur le TX
![Alt text](telemetryFRSKY.jpg)
#Protocoles ajoutés mais non testés (Issue de Deviation)
##CYRF6936 RF Module
###J6PRO
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12
---|---|---|---|---|---|---|---|---|---|---|---
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12
###WK2x01
Autobind
####Sub_protocol WK2401
CH1|CH2|CH3|CH4
---|---|---|---
CH1|CH2|CH3|CH4
####Sub_protocol WK2601
Option:
0 = 5+1
2 = 6+1
..1 = Hélicoptère (. = autres options pour ce mode)
.01 = Hélicoptère normal
.11 = Hélicoptère avec pit inversé
0.1 = Pitch curve -100
1.1 = Pitch curve 100
CH1|CH2|CH3|CH4|CH5|CH6|CH7
---|---|---|---|---|---|---
CH1|CH2|CH3|CH4|???|CONF|Gyro & Rudder mix
CONF: Option 1 = Rate Throtle
Option 2 = Pitch
####Sub_protocol WK2801
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
---|---|---|---|---|---|---|---
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
##A7105 RF Module
###Joysway
CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
##CC2500 RF Module
###SKYARTEC
CH1|CH2|CH3|CH4|CH5|CH6|CH7
---|---|---|---|---|---|---
? | ? | ? | ? | ? | ? | ?
##NRF24L01 RF Module
###BLUEFLY
Autobind
CH1|CH2|CH3|CH4|CH5|CH6
---|---|---|---|---|---
A|E|T|R|GEAR|PITCH
###CFLIE
Modele: CrazyFlie Nano quad
Autobind
CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
###ESKY150
Autobind
CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
###FBL100
Autobind
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
---|---|---|---|---|---|---|---
? | ? | ? | ? | ? | ? | ? | ?
####Sub_protocol HP100
Same channels assignement as above.
###Fy326
Autobind
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9
---|---|---|---|---|---|---|---|---
A|E|T|R|FLIP|HEADLESS|RTH|Calibrate|Expert
####Sub_protocol FY319
Same channels assignement as above.
###H377
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
---|---|---|---|---|---|---|---
A|E|T|R|CH5|CH6|CH7|CH8
###HM830
Modele: HM Hobby HM830 RC Paper Airplane
Autobind
CH1|CH2|CH3|CH4|CH5
---|---|---|---
A|Turbo|T|Trim|Bouton
###HONTAI
Autobind
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11
---|---|---|---|---|---|---|---|---|---|---
A|E|T|R|LED|FLIP|PICTURE|VIDEO|HEADLESS|RTH|Calibrate
####Sub_protocol JJRCX1
Modele: JJRC X1
CH5|CH6|CH7|CH8|CH9|CH10|CH11
---|---|---|---|---|---|---|---|---|---|---
ARM|FLIP|PICTURE|VIDEO|HEADLESS|RTH|Calibrate
###NE260
Modele: Nine Eagles SoloPro
Autobind
CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
###UDI
Modele: Known UDI 2.4GHz protocol variants, all using BK2421
* UDI U819 coaxial 3ch helicoper
* UDI U816/817/818 quadcopters
- "V1" with orange LED on TX, U816 RX labeled '' , U817/U818 RX labeled 'UD-U817B'
- "V2" with red LEDs on TX, U816 RX labeled '', U817/U818 RX labeled 'UD-U817OG'
- "V3" with green LEDs on TX. Did not get my hands on yet.
* U830 mini quadcopter with tilt steering ("Protocol 2014")
* U839 nano quadcopter ("Protocol 2014")
Autobind
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10
---|---|---|---|---|---|---|---|---|---
A|E|T|R|FLIP 360|FLIP|VIDEO|LED|MODE 2
####Sub_protocol U816_V1 (orange)
####Sub_protocol U816_V2 (red)
####Sub_protocol U839_2014
Same channels assignement as above.
###D'autres à venir
=======
## Outline of the documentation
1. Introduction (this page)
1. [Available protocols](docs/Protocol_Details.md)
@ -119,3 +310,4 @@ A very big thanks to all the people who have shared their time so graciously to
* hexfet from Deviation-tx
Your help would be greatly appreciated. If protocol reverse-engineering and dev is not your thing then any help with testing and contributing to the documentation would be amazing. Given the number of different Tx/module hardware/RF module/protocol/model combinations the process of testing and documenting is a major bottleneck for the developers. Anything you can do to help will free them up to do even greater things.
>>>>>>> refs/remotes/pascallanger/master

BIN
sync.ffs_db Normal file

Binary file not shown.

BIN
taranis_switches.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
telemetryFRSKY.fzz Normal file

Binary file not shown.

BIN
telemetryFRSKY.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB