Ajout de 5 protocoles

This commit is contained in:
tipouic 2016-03-01 22:12:10 +01:00
parent bdb8eec3ea
commit f6b4db1618
19 changed files with 2321 additions and 467 deletions

View File

@ -191,10 +191,16 @@ const uint8_t PROGMEM HUBSAN_A7105_regs[] = {
0xFF, 0xFF, 0xFF
};
const uint8_t PROGMEM FLYSKY_A7105_regs[] = {
/*
0xff, 0x42, 0x00, 0x14, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50,
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f,
0x13, 0xc3, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00,
0x01, 0x0f, 0xff
*/
-1, 0x42, 0x00, 0x14, 0x00, -1 , -1 , 0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50,
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f,
0x13, 0xc3, 0x00, -1, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00,
0x01, 0x0f, -1
};
#define ID_NORMAL 0x55201041
#define ID_PLUS 0xAA201041

View File

@ -114,14 +114,6 @@ uint8_t data_col;
uint16_t cyrf_state;
uint8_t crcidx;
uint8_t binding;
/*
#ifdef USE_FIXED_MFGID
const uint8_t cyrfmfg_id[6] = {0x5e, 0x28, 0xa3, 0x1b, 0x00, 0x00}; //dx8
const uint8_t cyrfmfg_id[6] = {0xd4, 0x62, 0xd6, 0xad, 0xd3, 0xff}; //dx6i
#else
//uint8_t cyrfmfg_id[6];
#endif
*/
static void __attribute__((unused)) build_bind_packet()
{

View File

@ -117,7 +117,6 @@ static void __attribute__((unused)) ESKY_send_packet(uint8_t bind)
// For arithmetic simplicity, channels are repeated in rf_channels array
if (hopping_frequency_no == 0)
{
const uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2};
for (uint8_t i = 0; i < 6; i++)
{
packet[i*2] = Servo_data[ch[i]]>>8; //high byte of servo timing(1000-2000us)

View File

@ -45,7 +45,7 @@ enum {
FLAG_V9X9_VIDEO = 0x40,
FLAG_V9X9_CAMERA= 0x80,
// flags going to byte 12
FLAG_V9X9_UNK = 0x10, // undocumented ?
FLAG_V9X9_FLIP = 0x10,
FLAG_V9X9_LED = 0x20,
};
@ -82,7 +82,7 @@ static void __attribute__((unused)) flysky_apply_extension_flags()
{
case V9X9:
if(Servo_AUX1)
packet[12] |= FLAG_V9X9_UNK;
packet[12] |= FLAG_V9X9_FLIP;
if(Servo_AUX2)
packet[12] |= FLAG_V9X9_LED;
if(Servo_AUX3)
@ -210,3 +210,4 @@ uint16_t initFlySky() {
}
#endif

View File

@ -43,39 +43,179 @@
0x34, 0x1B, 0x00, 0x1D, 0x03
};
uint8_t hop(uint8_t byte)
static uint8_t __attribute__((unused)) hop(uint8_t byte)
{
return pgm_read_byte_near(&hop_data[byte]);
}
uint16_t initFrSkyX()
static void __attribute__((unused)) set_start(uint8_t ch )
{
while(!chanskip)
{
randomSeed((uint32_t)analogRead(A6) << 10 | analogRead(A7));
chanskip=random(0xfefefefe)%47;
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]));
}
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)
static void __attribute__((unused)) frskyX_init()
{
state = FRSKY_BIND;
initialize_data(1);
}
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
{
state = FRSKY_DATA1;
initialize_data(0);
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);
}
return 10000;
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);
}
uint16_t ReadFrSkyX()
@ -137,179 +277,33 @@
return 1;
}
void set_start(uint8_t ch )
uint16_t initFrSkyX()
{
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]));
}
void frskyX_init()
while(!chanskip)
{
CC2500_Reset();
cc2500_writeReg(CC2500_02_IOCFG0, 0x06);
cc2500_writeReg(CC2500_00_IOCFG2, 0x06);
cc2500_writeReg(CC2500_17_MCSM1, 0x0C);
cc2500_writeReg(CC2500_18_MCSM0, 0x18);
cc2500_writeReg(CC2500_06_PKTLEN, 0x1E);
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x04);
cc2500_writeReg(CC2500_08_PKTCTRL0, 0x01);
cc2500_writeReg(CC2500_3E_PATABLE, 0xff);
cc2500_writeReg(CC2500_0B_FSCTRL1, 0x0A);
cc2500_writeReg(CC2500_0C_FSCTRL0, 0x00);
cc2500_writeReg(CC2500_0D_FREQ2, 0x5c);
cc2500_writeReg(CC2500_0E_FREQ1, 0x76);
cc2500_writeReg(CC2500_0F_FREQ0, 0x27);
cc2500_writeReg(CC2500_10_MDMCFG4, 0x7B);
cc2500_writeReg(CC2500_11_MDMCFG3, 0x61);
cc2500_writeReg(CC2500_12_MDMCFG2, 0x13);
cc2500_writeReg(CC2500_13_MDMCFG1, 0x23);
cc2500_writeReg(CC2500_14_MDMCFG0, 0x7a);
cc2500_writeReg(CC2500_15_DEVIATN, 0x51);
cc2500_writeReg(CC2500_19_FOCCFG, 0x16);
cc2500_writeReg(CC2500_1A_BSCFG, 0x6c);
cc2500_writeReg(CC2500_1B_AGCCTRL2,0x43);
cc2500_writeReg(CC2500_1C_AGCCTRL1,0x40);
cc2500_writeReg(CC2500_1D_AGCCTRL0,0x91);
cc2500_writeReg(CC2500_21_FREND1, 0x56);
cc2500_writeReg(CC2500_22_FREND0, 0x10);
cc2500_writeReg(CC2500_23_FSCAL3, 0xa9);
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_03_FIFOTHR, 0x07);
cc2500_writeReg(CC2500_09_ADDR, 0x00);
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########
randomSeed((uint32_t)analogRead(A6) << 10 | analogRead(A7));
chanskip=random(0xfefefefe)%47;
}
while((chanskip-ctr)%4)
ctr=(ctr+1)%4;
void initialize_data(uint8_t adr)
counter_rst=(chanskip-ctr)>>2;
//for test***************
//rx_tx_addr[3]=0xB3;
//rx_tx_addr[2]=0xFD;
//************************
frskyX_init();
//
if(IS_AUTOBIND_FLAG_on)
{
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);
state = FRSKY_BIND;
initialize_data(1);
}
void frskyX_build_bind_packet()
else
{
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);
//
state = FRSKY_DATA1;
initialize_data(0);
}
void 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);
}
uint16_t 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;
}
uint8_t crc_Byte( uint8_t byte )
{
crc = (crc<<8) ^ pgm_read_word(&CRCTable[((uint8_t)(crc>>8) ^ byte) & 0xFF]);
return byte;
return 10000;
}
#endif

View File

@ -38,45 +38,19 @@ static void __attribute__((unused)) frsky2way_init(uint8_t bind)
// Configure cc2500 for tx mode
CC2500_Reset();
//
cc2500_writeReg(CC2500_02_IOCFG0, 0x06);
cc2500_writeReg(CC2500_00_IOCFG2, 0x06);
cc2500_writeReg(CC2500_17_MCSM1, 0x0c);
cc2500_writeReg(CC2500_18_MCSM0, 0x18);
cc2500_writeReg(CC2500_06_PKTLEN, 0x19);
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x04);
cc2500_writeReg(CC2500_08_PKTCTRL0, 0x05);
cc2500_writeReg(CC2500_3E_PATABLE, 0xff);
cc2500_writeReg(CC2500_0B_FSCTRL1, 0x08);
cc2500_writeReg(CC2500_0C_FSCTRL0, option);
//base freq FREQ = 0x5C7627 (F = 2404MHz)
cc2500_writeReg(CC2500_0D_FREQ2, 0x5c);
cc2500_writeReg(CC2500_0E_FREQ1, 0x76);
cc2500_writeReg(CC2500_0F_FREQ0, 0x27);
//
cc2500_writeReg(CC2500_10_MDMCFG4, 0xAA);
cc2500_writeReg(CC2500_11_MDMCFG3, 0x39);
cc2500_writeReg(CC2500_12_MDMCFG2, 0x11);
cc2500_writeReg(CC2500_13_MDMCFG1, 0x23);
cc2500_writeReg(CC2500_14_MDMCFG0, 0x7a);
cc2500_writeReg(CC2500_15_DEVIATN, 0x42);
cc2500_writeReg(CC2500_19_FOCCFG, 0x16);
cc2500_writeReg(CC2500_1A_BSCFG, 0x6c);
cc2500_writeReg(CC2500_1B_AGCCTRL2, bind ? 0x43 : 0x03);
cc2500_writeReg(CC2500_1C_AGCCTRL1,0x40);
cc2500_writeReg(CC2500_1D_AGCCTRL0,0x91);
cc2500_writeReg(CC2500_21_FREND1, 0x56);
cc2500_writeReg(CC2500_22_FREND0, 0x10);
cc2500_writeReg(CC2500_23_FSCAL3, 0xa9);
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_03_FIFOTHR, 0x07);
cc2500_writeReg(CC2500_09_ADDR, 0x00);
//
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();

View File

@ -45,8 +45,7 @@ uint8_t packet[40];
// Servo data
uint16_t Servo_data[NUM_CHN];
uint8_t Servo_AUX;
// PPM variable
volatile uint16_t PPM_data[NUM_CHN];
const uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
// Protocol variables
uint8_t rx_tx_addr[5];
@ -73,9 +72,11 @@ uint8_t RX_num;
// Mode_select variables
uint8_t mode_select;
uint8_t ppm_select=0, flag_decal=0;
uint8_t protocol_flags=0,protocol_flags2=0;
// PPM variable
volatile uint16_t PPM_data[NUM_CHN];
// Serial variables
#define RXBUFFER_SIZE 25
#define TXBUFFER_SIZE 12
@ -158,25 +159,24 @@ void setup()
MProtocol_id_master=random_id(10,false);
//Init RF modules
#ifdef CC2500_INSTALLED
CC2500_Reset();
#endif
//Protocol and interrupts initialization
#if !defined(POTAR_SELECT)
if(mode_select == MODE_SERIAL)
{ // Serial
cur_protocol[0]=0;
cur_protocol[1]=0;
prev_protocol=0;
Mprotocol_serial_init(); // Configure serial and enable RX interrupt
}
else
#else
if(mode_select==MODE_SERIAL) { flag_decal=1; } else { flag_decal=0;}
#endif
if(mode_select != MODE_SERIAL)
{ // PPM
prev_protocol=0;
CHANGE_PROTOCOL_FLAG_on;
update_ppm_data();
mode_select--;
cur_protocol[0] = PPM_prot[mode_select].protocol;
sub_protocol = PPM_prot[mode_select].sub_proto;
RX_num = PPM_prot[mode_select].rx_num;
MProtocol_id = RX_num + MProtocol_id_master;
option = PPM_prot[mode_select].option;
if(PPM_prot[mode_select].power) POWER_FLAG_on;
if(PPM_prot[mode_select].autobind) AUTOBIND_FLAG_on;
mode_select++;
protocol_init();
//Configure PPM interrupt
EICRA |=(1<<ISC11); // The rising edge of INT1 pin D3 generates an interrupt request
@ -185,32 +185,41 @@ void setup()
PPM_Telemetry_serial_init(); // Configure serial for telemetry
#endif
}
else
{ // Serial
cur_protocol[0]=0;
cur_protocol[1]=0;
prev_protocol=0;
Mprotocol_serial_init(); // Configure serial and enable RX interrupt
}
}
// Main
void loop()
{
#if !defined(POTAR_SELECT)
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(mode_select!=MODE_SERIAL && IS_PPM_FLAG_on) // PPM mode and a full frame has been received
#else
if(IS_PPM_FLAG_on) // PPM mode and a full frame has been received
#endif
{
update_PPM_servo();
update_aux_flags();
update_ppm_data();
}
if(IS_CHANGE_PROTOCOL_FLAG_on) { // Protocol needs to be changed
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) )
@ -220,20 +229,12 @@ void loop()
CheckTimer(remote_callback);
}
static void update_PPM_servo(void) {
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
}
PPM_FLAG_off; // wait for next frame before update
}
// Update Servo_AUX flags based on servo AUX positions
static void update_aux_flags(void)
{
Servo_AUX=0;
for(uint8_t i=0;i<8;i++)
if(Servo_data[AUX1+i+flag_decal]>PPM_SWITCH)
if(Servo_data[AUX1+i]>PPM_SWITCH)
Servo_AUX|=1<<i;
}
@ -357,6 +358,36 @@ static void protocol_init()
remote_callback = fy326_callback;
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_BlueFly:
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(FLYSKY_A7105_INO)
case MODE_FLYSKY:
@ -475,6 +506,12 @@ static void protocol_init()
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
}
@ -490,59 +527,6 @@ static void protocol_init()
BIND_BUTTON_FLAG_off; // do not bind/reset id anymore even if protocol change
}
static void update_ppm_data() {
#if defined(POTAR_SELECT)
if(Servo_data[AUX1]>PPM_SWITCH) {
CHANGE_PROTOCOL_FLAG_on;
tone(BUZZER, BUZZER_HTZ);
delay(BUZZER_TPS);
noTone(BUZZER);
}
#endif
if(IS_CHANGE_PROTOCOL_FLAG_on) {
ppm_select = 10;
if(mode_select == 0) {
while(ppm_select) {
while(!IS_PPM_FLAG_on) {} // wait
update_PPM_servo();
if(Servo_data[AUX1] < PPM_MAX_100) { ppm_select--; }
} // attente de la d<>activation du rebind
}
prev_protocol = ppm_select;
// protocol selection
ppm_select = 0;
if(Servo_data[POTAR_SELECT_M] > PPM_MAX_COMMAND) { ppm_select += 18; } // THROTTLE up
else if(Servo_data[POTAR_SELECT_M] < PPM_MIN_COMMAND) { ppm_select += 9; } // THROTTLE down
else { ppm_select += 0; } // THROTTLE middle
if(Servo_data[POTAR_SELECT_V] > PPM_MAX_COMMAND) { ppm_select += 7; } // Elevator up
else if(Servo_data[POTAR_SELECT_V] < PPM_MIN_COMMAND) { ppm_select += 1; } // Elevator down
else { ppm_select += 4; } // Elevator middle
if(Servo_data[POTAR_SELECT_H] > PPM_MAX_COMMAND) { ppm_select += 2; } // Aileron right
else if(Servo_data[POTAR_SELECT_H] < PPM_MIN_COMMAND) { ppm_select += 0; } // Aileron left
else { ppm_select += 1; } // Aileron middle
// if(ppm_select == 5) { ppm_select = eeprom_read_byte(30); } else { eeprom_update_byte(30, ppm_select); }
if(ppm_select != 5) {
if(ppm_select > 5) { ppm_select--; }
ppm_select--;
cur_protocol[0] = PPM_prot[ppm_select].protocol;
sub_protocol = PPM_prot[ppm_select].sub_proto;
MProtocol_id = PPM_prot[ppm_select].rx_num + MProtocol_id_master;
option = PPM_prot[ppm_select].option;
if(PPM_prot[ppm_select].power) POWER_FLAG_on;
if(PPM_prot[ppm_select].autobind) AUTOBIND_FLAG_on;
ppm_select++;
}
while(Servo_data[THROTTLE] > PPM_MIN_100) { delay(100); update_PPM_servo(); } // attente de la remise des gaz <20> z<>ro (pouss<73> <20> fond avec le script lua)
}
}
static void update_serial_data()
{
if(rx_ok_buff[0]&0x20) //check range
@ -610,7 +594,7 @@ static void module_reset()
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
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
NRF24L01_Reset();
break;
}
@ -682,7 +666,6 @@ void Serial_write(uint8_t data)
static void Mprotocol_serial_init()
{
#define BAUD 100000
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
@ -778,7 +761,6 @@ ISR(INT1_vect)
}
//Serial RX
#if !defined(POTAR_SELECT)
ISR(USART_RX_vect)
{ // RX interrupt
if((UCSR0A&0x1C)==0) // Check frame error, data overrun and parity error
@ -815,7 +797,6 @@ ISR(USART_RX_vect)
idx=0; // Error encountered discard full frame...
}
}
#endif
//Serial timer
ISR(TIMER1_COMPB_vect)

View File

@ -154,7 +154,7 @@ void NRF24L01_SetBitrate(uint8_t bitrate)
// Note that bitrate 250kbps (and bit RF_DR_LOW) is valid only
// for nRF24L01+. There is no way to programmatically tell it from
// older version, nRF24L01, but the older is practically phased out
// by Nordic, so we assume that we deal with with modern version.
// by Nordic, so we assume that we deal with modern version.
// Bit 0 goes to RF_DR_HIGH, bit 1 - to RF_DR_LOW
rf_setup = (rf_setup & 0xD7) | ((bitrate & 0x02) << 4) | ((bitrate & 0x01) << 3);
@ -409,3 +409,196 @@ void XN297_ReadPayload(uint8_t* msg, uint8_t len)
}
// End of XN297 emulation
///////////////
// 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;
//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
LT8910_Flags=flags;
//CRC init constant
LT8910_CRC_Initial_Data=crc_init;
}
void LT8910_SetChannel(uint8_t channel)
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel +2); //NRF24L01 is 2400+channel but LT8900 is 2402+channel
}
void LT8910_SetTxRxMode(enum TXRX_State mode)
{
if(mode == TX_EN)
{
//Switch to TX
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
//Disable CRC
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_PWR_UP));
}
else
if (mode == RX_EN)
{
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 32);
//Switch to RX
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
// Disable CRC
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_PWR_UP) | (1 << NRF24L01_00_PRIM_RX) );
}
else
NRF24L01_SetTxRxMode(TXRX_OFF);
}
void LT8910_BuildOverhead()
{
uint8_t pos;
//Build overhead
//preamble
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)
{
uint8_t addr[5];
//Address size (SyncWord) 2 to 8 bytes, 16/32/48/64 bits
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);
//Read payload
NRF24L01_ReadPayload(buffer,end+1);
//Check address + trail
for(i=0;i<pos;i++)
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;
for(i=pos;i<end;i++)
{
a=(buffer[i]<<8)+buffer[i+1];
a<<=shift;
buffer[i]=(a>>8)&0xFF;
}
//Check len
if(LT8910_Flags&_BV(LT8910_PACKET_LENGTH_EN))
{
crc=crc16_update(crc,buffer[pos]);
if(bit_reverse(len)!=buffer[pos++])
return 0; // wrong len...
}
//Decode message
for(i=0;i<len;i++)
{
crc=crc16_update(crc,buffer[pos]);
msg[i]=bit_reverse(buffer[pos++]);
}
//Check CRC
if(LT8910_Flags&_BV(LT8910_CRC_ON))
{
if(buffer[pos++]!=((crc>>8)&0xFF)) return 0; // wrong CRC...
if(buffer[pos]!=(crc&0xFF)) return 0; // wrong CRC...
}
//Everything ok
return 1;
}
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))
{
tmp=bit_reverse(len);
buffer[pos++]=tmp;
crc=crc16_update(crc,tmp);
}
//Add payload
for(i=0;i<len;i++)
{
tmp=bit_reverse(msg[i]);
buffer[pos++]=tmp;
crc=crc16_update(crc,tmp);
}
//Add CRC
if(LT8910_Flags&_BV(LT8910_CRC_ON))
{
buffer[pos++]=crc>>8;
buffer[pos++]=crc;
}
//Shift everything to fit behind the trailer (4 to 18 bits)
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;
}
if(shift)
pos++;
//Send everything
NRF24L01_WritePayload(LT8910_buffer+LT8910_buffer_start,pos_final+pos-LT8910_buffer_start);
}
// End of LT8910 emulation

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(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
// bluefly_rf_adr_buf can be used for fixed id
static uint8_t bluefly_rf_adr_buf[TXID_BlueFly_SIZE]; // ={0x13,0x88,0x46,0x57,0x76};
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] = bluefly_rf_adr_buf[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, bluefly_rf_adr_buf, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bluefly_rf_adr_buf, 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, bluefly_rf_adr_buf, 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 (counter>0) {
counter--;
if (! counter) { 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
}
// Generate internal id from TX id and manufacturer id (STM32 unique id)
static void initialize_tx_id() {
uint32_t lfsr = 0x7649eca9ul;
if (Model.fixed_id) {
for (uint8_t i = 0, j = 0; i < sizeof(Model.fixed_id); ++i, j += 8)
rand32_r(&lfsr, (Model.fixed_id >> j) & 0xff);
}
// Pump zero bytes for LFSR to diverge more
for (int i = 0; i < TXID_BlueFly_SIZE; ++i) rand32_r(&lfsr, 0);
for (uint8_t i = 0; i < TXID_BlueFly_SIZE; ++i) {
bluefly_rf_adr_buf[i] = lfsr & 0xff;
rand32_r(&lfsr, i);
}
printf("Effective id: %02X%02X%02X%02X%02X\r\n", bluefly_rf_adr_buf[0], bluefly_rf_adr_buf[1], bluefly_rf_adr_buf[2], bluefly_rf_adr_buf[3], bluefly_rf_adr_buf[4]);
// Use LFSR to seed frequency hopping sequence after another
// divergence round
for (uint8_t i = 0; i < sizeof(lfsr); ++i) rand32_r(&lfsr, 0);
hopping_frequency_start = ((lfsr >> 8) % 47) + 2;
bluefly_binding_packet();
}
static uint16_t BlueFly_setup() {
initialize_tx_id();
bluefly_init();
if(IS_AUTOBIND_FLAG_on) {
counter = BIND_BlueFly_COUNT;
// PROTOCOL_SetBindState(BIND_BlueFly_COUNT * 10); //8 seconds binding time
} else {
counter = 0;
}
return 1000;
}
#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

@ -75,14 +75,14 @@ static void send_packet(uint8_t bind)
| GET_FLAG(CHANNEL_CALIBRATE, 0x01)
| GET_FLAG(CHANNEL_EXPERT, 4);
}
packet[2] = 200 - scale_channel(AILERON, 0, 200); // aileron
packet[3] = scale_channel(ELEVATOR, 0, 200); // elevator
packet[4] = 200 - scale_channel(RUDDER, 0, 200); // rudder
packet[5] = scale_channel(THROTTLE, 0, 200); // throttle
packet[2] = 200 - scale_channel(AILERON, 0, 200); // aileron 1
packet[3] = scale_channel(ELEVATOR, 0, 200); // elevator 2
packet[4] = 200 - scale_channel(RUDDER, 0, 200); // rudder 4
packet[5] = scale_channel(THROTTLE, 0, 200); // throttle 3
if(sub_protocol == FY319) {
packet[6] = 255 - scale_channel(CHANNEL1, 0, 255);
packet[7] = scale_channel(CHANNEL2, 0, 255);
packet[8] = 255 - scale_channel(CHANNEL4, 0, 255);
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] = txid[0];
@ -148,7 +148,7 @@ static uint16_t fy326_callback()
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
phase = FY319_BIND1;
BIND_IN_PROGRESS;
return PACKET_CHKTIME;
return FY326_CHKTIME;
break;
case FY319_BIND1:
@ -158,7 +158,7 @@ static uint16_t fy326_callback()
packet[0] = txid[3];
packet[1] = 0x80;
packet[14]= txid[4];
bind_counter = BIND_COUNT;
bind_counter = FY326_BIND_COUNT;
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
@ -168,7 +168,7 @@ static uint16_t fy326_callback()
packet[i] = rf_chans[0];
phase = FY319_BIND2;
}
return PACKET_CHKTIME;
return FY326_CHKTIME;
break;
case FY319_BIND2:
@ -264,3 +264,4 @@ static uint16_t FY326_setup()
return INITIAL_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/>.
*/
#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,274 @@
/*
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 u32 bind_count;
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();
bind_count = 10000;
state = NE260_BINDTX;
return 10000;
}
#endif

View File

@ -0,0 +1,583 @@
/*
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;
counter = BIND_UDI_COUNT;
// observed on U839 TX
set_tx_id(0x457C27);
return INITIAL_UDI_WAIT;
}
#endif

View File

@ -0,0 +1,108 @@
#if defined(SHENQI_NRF24L01_INO)
#include "iface_nrf24l01.h"
const uint8_t PROGMEM SHENQI_Freq[] = {
50,50,20,60,30,40,
10,30,40,20,60,10,
50,20,50,40,10,60,
30,30,60,10,40,50,
20,10,60,20,50,30,
40,40,30,50,20,60,
10,10,20,30,40,50,
60,60,50,40,30,20,
10,60,10,50,30,40,
20,10,40,30,60,20 };
void SHENQI_init()
{
NRF24L01_Initialize();
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_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5 bytes rx/tx address
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);
}
void SHENQI_send_packet()
{
packet[0]=0x00;
if(packet_count==0)
{
uint8_t bind_addr[4];
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];
packet_period=2508;
}
else
{
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);
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...)
LT8910_WritePayload(packet,3);
while(NRF24L01_packet_ack()!=PKT_ACKED);
LT8910_WritePayload(packet,3);
packet_count++;
if(packet_count==7)
{
packet_count=0;
packet_period=3000;
}
// Set power
NRF24L01_SetPower();
}
uint16_t SHENQI_callback()
{
if(IS_BIND_DONE_on)
SHENQI_send_packet();
else
{
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);
packet_period=14000;
}
NRF24L01_FlushRx();
}
}
return packet_period;
}
uint16_t initSHENQI()
{
BIND_IN_PROGRESS; // autobind protocol
SHENQI_init();
hopping_frequency_no = 0;
packet_count=0;
packet_period=100;
return 1000;
}
#endif

View File

@ -25,14 +25,6 @@
//Uncomment to enable telemetry
#define TELEMETRY
//Uncomment to enable potar select
#define POTAR_SELECT
#define POTAR_SELECT_V ELEVATOR
#define POTAR_SELECT_H RUDDER
#define POTAR_SELECT_M AILERON
//Comment if a module is not installed
#define A7105_INSTALLED
#define CYRF6936_INSTALLED
@ -62,6 +54,11 @@
#define CFlie_NRF24L01_INO
#define H377_NRF24L01_INO
#define FY326_NRF24L01_INO
#define ESKY150_NRF24L01_INO
// #define BlueFly_NRF24L01_INO //probleme gene id
#define HonTai_NRF24L01_INO
#define UDI_NRF24L01_INO
#define NE260_NRF24L01_INO
#define BAYANG_NRF24L01_INO
#define CG023_NRF24L01_INO
@ -75,27 +72,27 @@
#define YD717_NRF24L01_INO
#define MT99XX_NRF24L01_INO
#define MJXQ_NRF24L01_INO
// #define SHENQI_NRF24L01_INO
#endif
//Update this table to set which protocol and all associated settings are called for the corresponding dial number
static const PPM_Parameters PPM_prot[15]=
{
// Protocol Sub protocol RX_Num Power Auto Bind Option
{MODE_FLYSKY, Flysky , 0 , P_HIGH , AUTOBIND , 0 }, //Dial=1
{MODE_HUBSAN, 0 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=2
{MODE_FRSKY , 0 , 0 , P_HIGH , NO_AUTOBIND , 0xD7 }, //Dial=3
{MODE_HISKY , Hisky , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=4
{MODE_V2X2 , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=5
{MODE_DSM2 , DSM2 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=6
{MODE_DEVO , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=7
{MODE_YD717 , YD717 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=8
{MODE_KN , WLTOYS , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=9
{MODE_SYMAX , SYMAX , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=10
{MODE_SLT , 0 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=11
{MODE_CX10 , CX10_BLUE , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=12
{MODE_CG023 , CG023 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=13
{MODE_BAYANG, 0 , 0 , P_HIGH , NO_AUTOBIND , 0 }, //Dial=14
{MODE_SYMAX , SYMAX5C , 0 , P_HIGH , NO_AUTOBIND , 0 } //Dial=15
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 },
/* 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 , 0 },
/* 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 },
/* 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 },
/* 13 */ {MODE_CG023 , CG023 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 14 */ {MODE_BAYANG, 0 , 0 , P_HIGH , NO_AUTOBIND , 0 },
/* 15 */ {MODE_SYMAX , SYMAX5C , 0 , P_HIGH , NO_AUTOBIND , 0 }
};
/* Available protocols and associated sub protocols:
MODE_FLYSKY
@ -159,6 +156,8 @@ static const PPM_Parameters PPM_prot[15]=
X600
X800
H26D
MODE_SHENQI
NONE
RX_Num value between 0 and 15
@ -191,7 +190,7 @@ Option value between 0 and 255. 0xD7 or 0x00 for Frsky fine tuning.
#endif
// SPEKTRUM PPM and channels
#if defined(TX_SPEKTRUM) | defined(TARANIS)
#if defined(TX_SPEKTRUM)
#define PPM_MAX 2000
#define PPM_MIN 1000
#define PPM_MAX_100 1900
@ -199,6 +198,15 @@ Option value between 0 and 255. 0xD7 or 0x00 for Frsky fine tuning.
#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
@ -249,3 +257,7 @@ enum chan_orders{
#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

View File

@ -156,4 +156,43 @@ enum {
//void CC2500_WriteData(u8 *packet, u8 length);
//void CC2500_ReadData(u8 *dpbuffer, int len);
//void CC2500_SetTxRxMode(enum TXRX_State);
const PROGMEM uint8_t cc2500_conf[][2]={
{ CC2500_02_IOCFG0, 0x06 },
{ CC2500_00_IOCFG2, 0x06 },
{ CC2500_17_MCSM1, 0x0c },
{ CC2500_18_MCSM0, 0x18 },
{ CC2500_06_PKTLEN, 0x19 },
{ CC2500_07_PKTCTRL1, 0x04 },
{ CC2500_08_PKTCTRL0, 0x05 },
{ CC2500_3E_PATABLE, 0xff },
{ CC2500_0B_FSCTRL1, 0x08 },
{ CC2500_0C_FSCTRL0, 0x00 }, // option
{ CC2500_0D_FREQ2, 0x5c },
{ CC2500_0E_FREQ1, 0x76 },
{ CC2500_0F_FREQ0, 0x27 },
{ CC2500_10_MDMCFG4, 0xAA },
{ CC2500_11_MDMCFG3, 0x39 },
{ CC2500_12_MDMCFG2, 0x11 },
{ CC2500_13_MDMCFG1, 0x23 },
{ CC2500_14_MDMCFG0, 0x7a },
{ CC2500_15_DEVIATN, 0x42 },
{ CC2500_19_FOCCFG, 0x16 },
{ CC2500_1A_BSCFG, 0x6c },
{ CC2500_1B_AGCCTRL2, 0x43 }, // bind ? 0x43 : 0x03
{ CC2500_1C_AGCCTRL1,0x40 },
{ CC2500_1D_AGCCTRL0,0x91 },
{ CC2500_21_FREND1, 0x56 },
{ CC2500_22_FREND0, 0x10 },
{ CC2500_23_FSCAL3, 0xa9 },
{ CC2500_24_FSCAL2, 0x0A },
{ CC2500_25_FSCAL1, 0x00 },
{ CC2500_26_FSCAL0, 0x11 },
{ CC2500_29_FSTEST, 0x59 },
{ CC2500_2C_TEST2, 0x88 },
{ CC2500_2D_TEST1, 0x31 },
{ CC2500_2E_TEST0, 0x0B },
{ CC2500_03_FIFOTHR, 0x07 },
{ CC2500_09_ADDR, 0x00 }
};
#endif

View File

@ -30,6 +30,11 @@ enum PROTOCOLS
MODE_H377=44, // =>NRF24L01
MODE_WK2x01 = 45, // =>CYRF6936
MODE_FY326=46, // =>NRF24L01
MODE_ESKY150=47, // =>NRF24L01
MODE_BlueFly=48, // =>NRF24L01
MODE_HonTai=49, // =>NRF24L01
MODE_UDI=50, // =>NRF24L01
MODE_NE260=51, // =>NRF24L01
MODE_SERIAL = 0, // Serial commands
MODE_FLYSKY = 1, // =>A7105
@ -49,7 +54,8 @@ enum PROTOCOLS
MODE_FRSKYX = 15, // =>CC2500
MODE_ESKY = 16, // =>NRF24L01
MODE_MT99XX=17, // =>NRF24L01
MODE_MJXQ=18 // =>NRF24L01
MODE_MJXQ=18, // =>NRF24L01
MODE_SHENQI=19 // =>NRF24L01
};
enum Flysky
{
@ -127,6 +133,12 @@ enum FY326
FY326 = 0,
FY319 = 1
};
enum UDI
{
U816_V1 = 0,
U816_V2,
U839_2014
};
#define NONE 0
#define P_HIGH 1
@ -147,10 +159,6 @@ struct PPM_Parameters
//*******************
//*** Pinouts ***
//*******************
#define BUZZER 19 //A5
#define BUZZER_HTZ 440 // La clé de sol
#define BUZZER_TPS 300
//#define BIND_pin 13
#define LED_pin 13 //Promini original led on B5
//
@ -440,6 +448,7 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
ESky 16
MT99XX 17
MJXQ 18
SHENQI 19
BindBit=> 0x80 1=Bind/0=No
AutoBindBit=> 0x40 1=Yes /0=No
RangeCheck=> 0x20 1=Yes /0=No

View File

@ -60,6 +60,7 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7
CH1|CH2|CH3|CH4|???|CONF|Gyro & Rudder mix
CONF: Option 1 = Rate Throtle
Option 2 = Pitch
@ -76,6 +77,13 @@ CH1|CH2|CH3|CH4
A|E|T|R
##NRF24L01 RF Module
###BLUEFLY
Autobind
CH1|CH2|CH3|CH4|CH5|CH6
---|---|---|---|---|---
A|E|T|R|GEAR|PITCH
###CFLIE
Modele: CrazyFlie Nano quad
@ -85,6 +93,14 @@ CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
###ESKY150
Autobind
CH1|CH2|CH3|CH4
---|---|---|---
A|E|T|R
###Fy326
Autobind
@ -107,6 +123,51 @@ Autobind
CH1|CH2|CH3|CH4|CH5
---|---|---|---
A|Turbo|T|Trim|Bouton ???
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 U816 (2014)
Same channels assignement as above.
###D'autres à venir