First commit

This commit is contained in:
pascallanger 2015-12-30 01:41:12 +01:00
parent e99246dae7
commit e4ab6fd1b0
26 changed files with 6880 additions and 0 deletions

254
Multiprotocol/A7105_SPI.ino Normal file
View File

@ -0,0 +1,254 @@
/*
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/>.
*/
//-------------------------------
//-------------------------------
//A7105 SPI routines
//-------------------------------
//-------------------------------
#include "iface_a7105.h"
void A7105_WriteData(uint8_t len, uint8_t channel)
{
uint8_t i;
CS_off;
A7105_Write(A7105_RST_WRPTR);
A7105_Write(0x05);
for (i = 0; i < len; i++)
A7105_Write(packet[i]);
CS_on;
A7105_WriteReg(0x0F, channel);
A7105_Strobe(A7105_TX);
}
void A7105_ReadData() {
uint8_t i;
A7105_Strobe(0xF0); //A7105_RST_RDPTR
CS_off;
A7105_Write(0x45);
for (i=0;i<16;i++)
packet[i]=A7105_Read();
CS_on;
}
void A7105_WriteReg(uint8_t address, uint8_t data) {
CS_off;
A7105_Write(address);
NOP();
A7105_Write(data);
CS_on;
}
uint8_t A7105_ReadReg(uint8_t address) {
uint8_t result;
CS_off;
A7105_Write(address |=0x40); //bit 6 =1 for reading
result = A7105_Read();
CS_on;
return(result);
}
void A7105_Write(uint8_t command) {
uint8_t n=8;
SCK_off;//SCK start low
SDI_off;
while(n--) {
if(command&0x80)
SDI_on;
else
SDI_off;
SCK_on;
NOP();
SCK_off;
command = command << 1;
}
SDI_on;
}
uint8_t A7105_Read(void) {
uint8_t result=0;
uint8_t i;
SDI_SET_INPUT;
for(i=0;i<8;i++) {
if(SDI_1) ///if SDIO =1
result=(result<<1)|0x01;
else
result=result<<1;
SCK_on;
NOP();
SCK_off;
NOP();
}
SDI_SET_OUTPUT;
return result;
}
//------------------------
void A7105_SetTxRxMode(uint8_t mode)
{
if(mode == TX_EN) {
A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x33);
A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x31);
} else if (mode == RX_EN) {
A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x31);
A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x33);
} else {
//The A7105 seems to some with a cross-wired power-amp (A7700)
//On the XL7105-D03, TX_EN -> RXSW and RX_EN -> TXSW
//This means that sleep mode is wired as RX_EN = 1 and TX_EN = 1
//If there are other amps in use, we'll need to fix this
A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x33);
A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x33);
}
}
//------------------------
uint8_t A7105_Reset()
{
uint8_t result;
delay(10); //wait 10ms for A7105 wakeup
A7105_WriteReg(0x00, 0x00);
delay(1000);
A7105_SetTxRxMode(TXRX_OFF); //Set both GPIO as output and low
result=A7105_ReadReg(0x10) == 0x9E; //check if is reset.
A7105_Strobe(A7105_STANDBY);
return result;
}
void A7105_WriteID(uint32_t ida) {
CS_off;
A7105_Write(0x06);//ex id=0x5475c52a ;txid3txid2txid1txid0
A7105_Write((ida>>24)&0xff);//53
A7105_Write((ida>>16)&0xff);//75
A7105_Write((ida>>8)&0xff);//c5
A7105_Write((ida>>0)&0xff);//2a
CS_on;
}
void A7105_SetPower_Value(int power)
{
/*
Power amp is ~+16dBm so:
TXPOWER_100uW = -23dBm == PAC=0 TBG=0
TXPOWER_300uW = -20dBm == PAC=0 TBG=1
TXPOWER_1mW = -16dBm == PAC=0 TBG=2
TXPOWER_3mW = -11dBm == PAC=0 TBG=4
TXPOWER_10mW = -6dBm == PAC=1 TBG=5
TXPOWER_30mW = 0dBm == PAC=2 TBG=7
TXPOWER_100mW = 1dBm == PAC=3 TBG=7
TXPOWER_150mW = 1dBm == PAC=3 TBG=7
*/
uint8_t pac, tbg;
switch(power) {
case 0: pac = 0; tbg = 0; break;
case 1: pac = 0; tbg = 1; break;
case 2: pac = 0; tbg = 2; break;
case 3: pac = 0; tbg = 4; break;
case 4: pac = 1; tbg = 5; break;
case 5: pac = 2; tbg = 7; break;
case 6: pac = 3; tbg = 7; break;
case 7: pac = 3; tbg = 7; break;
default: pac = 0; tbg = 0; break;
};
A7105_WriteReg(0x28, (pac << 3) | tbg);
}
void A7105_SetPower()
{
uint8_t power=A7105_BIND_POWER;
if(IS_BIND_DONE_on)
power=IS_POWER_FLAG_on?A7105_HIGH_POWER:A7105_LOW_POWER;
else
if(IS_RANGE_FLAG_on)
power=A7105_POWER_0;
A7105_WriteReg(0x28, power);
}
void A7105_Strobe(uint8_t address) {
CS_off;
A7105_Write(address);
CS_on;
}
const uint8_t PROGMEM HUBSAN_A7105_regs[] = {
0xFF, 0x63, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x04, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0xFF, 0xFF, 0x62, 0x80, 0xFF, 0xFF, 0x0A, 0xFF, 0xFF, 0x07,
0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x47, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
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
};
void A7105_Init(uint8_t protocol)
{
void *A7105_Regs;
if(protocol==INIT_FLYSKY)
{
A7105_WriteID(0x5475c52A);//0x2Ac57554
A7105_Regs=(void *)FLYSKY_A7105_regs;
}
else
{
A7105_WriteID(0x55201041);
A7105_Regs=(void *)HUBSAN_A7105_regs;
}
for (uint8_t i = 0; i < 0x33; i++){
if( pgm_read_byte_near((uint16_t)(A7105_Regs)+i) != 0xFF)
A7105_WriteReg(i, pgm_read_byte_near((uint16_t)(A7105_Regs)+i));
}
A7105_Strobe(A7105_STANDBY);
//IF Filter Bank Calibration
A7105_WriteReg(A7105_02_CALC,1);
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
// A7105_ReadReg(A7105_22_IF_CALIB_I);
// A7105_ReadReg(A7105_24_VCO_CURCAL);
if(protocol==INIT_FLYSKY)
{
//VCO Current Calibration
A7105_WriteReg(A7105_24_VCO_CURCAL,0x13); //Recommended calibration from A7105 Datasheet
//VCO Bank Calibration
A7105_WriteReg(A7105_26_VCO_SBCAL_II,0x3b); //Recommended calibration from A7105 Datasheet
}
//VCO Bank Calibrate channel 0
A7105_WriteReg(A7105_0F_CHANNEL, 0);
A7105_WriteReg(A7105_02_CALC,2);
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
// A7105_ReadReg(A7105_25_VCO_SBCAL_I);
//VCO Bank Calibrate channel A0
A7105_WriteReg(A7105_0F_CHANNEL, 0xa0);
A7105_WriteReg(A7105_02_CALC, 2);
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
// A7105_ReadReg(A7105_25_VCO_SBCAL_I);
//Reset VCO Band calibration
if(protocol==INIT_FLYSKY)
A7105_WriteReg(A7105_25_VCO_SBCAL_I,0x08);
A7105_SetTxRxMode(TX_EN);
A7105_SetPower();
A7105_Strobe(A7105_STANDBY);
}

View File

@ -0,0 +1,177 @@
/*
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/>.
*/
// compatible with EAchine H8 mini, H10, BayangToys X6/X7/X9, JJRC JJ850 ...
#if defined(BAYANG_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BAYANG_BIND_COUNT 1000
#define BAYANG_PACKET_PERIOD 2000
#define BAYANG_INITIAL_WAIT 500
#define BAYANG_PACKET_SIZE 15
#define BAYANG_RF_NUM_CHANNELS 4
#define BAYANG_RF_BIND_CHANNEL 0
#define BAYANG_ADDRESS_LENGTH 5
enum BAYANG_FLAGS {
// flags going to packet[2]
BAYANG_FLAG_RTH = 0x01,
BAYANG_FLAG_HEADLESS = 0x02,
BAYANG_FLAG_FLIP = 0x08
};
enum BAYANG_PHASES {
BAYANG_BIND = 0,
BAYANG_DATA
};
void BAYANG_send_packet(uint8_t bind)
{
uint8_t i;
if (bind)
{
packet[0]= 0xA4;
for(i=0;i<5;i++)
packet[i+1]=rx_tx_addr[i];
for(i=0;i<4;i++)
packet[i+6]=hopping_frequency[i];
packet[10] = rx_tx_addr[0];
packet[11] = rx_tx_addr[1];
}
else
{
uint16_t val;
packet[0] = 0xA5;
packet[1] = 0xFA; // normal mode is 0xf7, expert 0xfa
//Flags
packet[2] =0x00;
if(Servo_data[AUX1] > PPM_SWITCH)
packet[2] |= BAYANG_FLAG_FLIP;
if(Servo_data[AUX2] > PPM_SWITCH)
packet[2] |= BAYANG_FLAG_HEADLESS;
if(Servo_data[AUX3] > PPM_SWITCH)
packet[2] |= BAYANG_FLAG_RTH;
packet[3] = 0x00;
//Aileron
val = convert_channel_10b(AILERON);
packet[4] = (val>>8) + ((val>>2) & 0xFC);
packet[5] = val & 0xFF;
//Elevator
val = convert_channel_10b(ELEVATOR);
packet[6] = (val>>8) + ((val>>2) & 0xFC);
packet[7] = val & 0xFF;
//Throttle
val = convert_channel_10b(THROTTLE);
packet[8] = (val>>8) + 0x7C;
packet[9] = val & 0xFF;
//Rudder
val = convert_channel_10b(RUDDER);
packet[10] = (val>>8) + (val>>2 & 0xFC);
packet[11] = val & 0xFF;
}
packet[12] = rx_tx_addr[2];
packet[13] = 0x0A;
packet[14] = 0;
for (uint8_t i=0; i < BAYANG_PACKET_SIZE-1; i++)
packet[14] += packet[i];
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
if (bind)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, BAYANG_RF_BIND_CHANNEL);
else
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no%=BAYANG_RF_NUM_CHANNELS;
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, BAYANG_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
void BAYANG_init()
{
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
XN297_SetTXAddr((uint8_t *)"\x00\x00\x00\x00\x00", BAYANG_ADDRESS_LENGTH);
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03);
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
NRF24L01_Activate(0x73); // Activate feature register
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x01);
NRF24L01_Activate(0x73);
}
uint16_t BAYANG_callback()
{
switch (phase)
{
case BAYANG_BIND:
if (bind_counter == 0)
{
XN297_SetTXAddr(rx_tx_addr, BAYANG_ADDRESS_LENGTH);
phase = BAYANG_DATA;
BIND_DONE;
}
else
{
BAYANG_send_packet(1);
bind_counter--;
}
break;
case BAYANG_DATA:
BAYANG_send_packet(0);
break;
}
return BAYANG_PACKET_PERIOD;
}
void BAYANG_initialize_txid()
{
// Strange txid, rx_tx_addr and rf_channels could be anything so I will use on rx_tx_addr for all of them...
// Strange also that there is no check of duplicated rf channels... I think we need to implement that later...
for(uint8_t i=0; i<BAYANG_RF_NUM_CHANNELS; i++)
hopping_frequency[i]=rx_tx_addr[i]%42;
hopping_frequency_no=0;
}
uint16_t initBAYANG(void)
{
BIND_IN_PROGRESS; // autobind protocol
bind_counter = BAYANG_BIND_COUNT;
BAYANG_initialize_txid();
phase=BAYANG_BIND;
BAYANG_init();
return BAYANG_INITIAL_WAIT+BAYANG_PACKET_PERIOD;
}
#endif

View File

@ -0,0 +1,189 @@
/*
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/>.
*/
//-------------------------------
//-------------------------------
//CC2500 SPI routines
//-------------------------------
//-------------------------------
#include "iface_cc2500.h"
void cc2500_readFifo(uint8_t *dpbuffer, uint8_t len)
{
ReadRegisterMulti(CC2500_3F_RXFIFO | CC2500_READ_BURST, dpbuffer, len);
}
//----------------------
static void ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
{
CC25_CSN_off;
cc2500_spi_write(address);
for(uint8_t i = 0; i < length; i++)
data[i] = cc2500_spi_read();
CC25_CSN_on;
}
//*********************************************
void CC2500_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
{
CC25_CSN_off;
cc2500_spi_write(CC2500_WRITE_BURST | address);
for(uint8_t i = 0; i < length; i++)
cc2500_spi_write(data[i]);
CC25_CSN_on;
}
void cc2500_writeFifo(uint8_t *dpbuffer, uint8_t len)
{
cc2500_strobe(CC2500_SFTX);//0x3B
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, dpbuffer, len);
cc2500_strobe(CC2500_STX);//0x35
}
//--------------------------------------
void cc2500_spi_write(uint8_t command) {
uint8_t n=8;
SCK_off;//SCK start low
SDI_off;
while(n--)
{
if(command&0x80)
SDI_on;
else
SDI_off;
SCK_on;
NOP();
SCK_off;
command = command << 1;
}
SDI_on;
}
//----------------------------
void cc2500_writeReg(uint8_t address, uint8_t data) {//same as 7105
CC25_CSN_off;
cc2500_spi_write(address);
NOP();
cc2500_spi_write(data);
CC25_CSN_on;
}
uint8_t cc2500_spi_read(void)
{
uint8_t result;
uint8_t i;
result=0;
for(i=0;i<8;i++)
{
if(SDO_1) ///
result=(result<<1)|0x01;
else
result=result<<1;
SCK_on;
NOP();
SCK_off;
NOP();
}
return result;
}
//--------------------------------------------
uint8_t cc2500_readReg(uint8_t address)
{
uint8_t result;
CC25_CSN_off;
address |=0x80; //bit 7 =1 for reading
cc2500_spi_write(address);
result = cc2500_spi_read();
CC25_CSN_on;
return(result);
}
//------------------------
void cc2500_strobe(uint8_t address)
{
CC25_CSN_off;
cc2500_spi_write(address);
CC25_CSN_on;
}
//------------------------
void cc2500_resetChip(void)
{
// Toggle chip select signal
CC25_CSN_on;
_delay_us(30);
CC25_CSN_off;
_delay_us(30);
CC25_CSN_on;
_delay_us(45);
cc2500_strobe(CC2500_SRES);
_delay_ms(100);
}
uint8_t CC2500_Reset()
{
cc2500_strobe(CC2500_SRES);
_delay_us(1000);
CC2500_SetTxRxMode(TXRX_OFF);
return cc2500_readReg(CC2500_0E_FREQ1) == 0xC4;//check if reset
}
void CC2500_SetPower_Value(uint8_t power)
{
const unsigned char patable[8]= {
0xC5, // -12dbm
0x97, // -10dbm
0x6E, // -8dbm
0x7F, // -6dbm
0xA9, // -4dbm
0xBB, // -2dbm
0xFE, // 0dbm
0xFF // 1.5dbm
};
if (power > 7)
power = 7;
cc2500_writeReg(CC2500_3E_PATABLE, patable[power]);
}
void CC2500_SetPower()
{
uint8_t power=CC2500_BIND_POWER;
if(IS_BIND_DONE_on)
power=IS_POWER_FLAG_on?CC2500_HIGH_POWER:CC2500_LOW_POWER;
else
if(IS_RANGE_FLAG_on)
power=CC2500_POWER_0;
cc2500_writeReg(CC2500_3E_PATABLE, power);
}
void CC2500_SetTxRxMode(uint8_t mode)
{
if(mode == TX_EN)
{//from deviation firmware
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F | 0x40);
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F);
}
else
if (mode == RX_EN)
{
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F);
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F | 0x40);
}
else
{
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F);
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F);
}
}

View File

@ -0,0 +1,194 @@
/*
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/>.
*/
// compatible with EAchine 3D X4, CG023/CG031, Attop YD-822/YD-829/YD-829C
#if defined(CG023_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define CG023_PACKET_PERIOD 8200 // Timeout for callback in uSec
#define CG023_INITIAL_WAIT 500
#define CG023_PACKET_SIZE 15 // packets have 15-byte payload
#define CG023_RF_BIND_CHANNEL 0x2D
#define CG023_BIND_COUNT 800 // 6 seconds
#define YD829_PACKET_PERIOD 4100 // Timeout for callback in uSec
enum CG023_FLAGS {
// flags going to packet[13]
CG023_FLAG_FLIP = 0x01,
CG023_FLAG_EASY = 0x02,
CG023_FLAG_VIDEO = 0x04,
CG023_FLAG_STILL = 0x08,
CG023_FLAG_LED_OFF = 0x10,
CG023_FLAG_RATE_LOW = 0x00,
CG023_FLAG_RATE_MID = 0x20,
CG023_FLAG_RATE_HIGH= 0x40,
};
enum YD829_FLAGS {
// flags going to packet[13] (YD-829)
YD829_FLAG_FLIP = 0x01,
YD829_MASK_RATE = 0x0C,
YD829_FLAG_RATE_MID = 0x04,
YD829_FLAG_RATE_HIGH= 0x08,
YD829_FLAG_HEADLESS = 0x20,
YD829_FLAG_VIDEO = 0x40,
YD829_FLAG_STILL = 0x80,
};
enum CG023_PHASES {
CG023_BIND = 0,
CG023_DATA
};
void CG023_send_packet(uint8_t bind)
{
if (bind)
packet[0]= 0xaa;
else
packet[0]= 0x55;
// transmitter id
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
// unknown
packet[3] = 0x00;
packet[4] = 0x00;
// throttle : 0x00 - 0xFF
packet[5] = convert_channel_8b(THROTTLE);
// rudder
packet[6] = convert_channel_8b_scale(RUDDER,0x44,0xBC); // yaw right : 0x80 (neutral) - 0xBC (right)
if (packet[6]<=0x80)
packet[6]=0x80-packet[6]; // yaw left : 0x00 (neutral) - 0x3C (left)
// elevator : 0xBB - 0x7F - 0x43
packet[7] = convert_channel_8b_scale(ELEVATOR, 0x43, 0xBB);
// aileron : 0x43 - 0x7F - 0xBB
packet[8] = convert_channel_8b_scale(AILERON, 0x43, 0xBB);
// throttle trim : 0x30 - 0x20 - 0x10
packet[9] = 0x20; // neutral
// neutral trims
packet[10] = 0x20;
packet[11] = 0x40;
packet[12] = 0x40;
if(sub_protocol==CG023)
{
// rate
packet[13] = CG023_FLAG_RATE_HIGH;
// flags
if(Servo_data[AUX1] > PPM_SWITCH)
packet[13] |= CG023_FLAG_FLIP;
if(Servo_data[AUX2] > PPM_SWITCH)
packet[13] |= CG023_FLAG_LED_OFF;
if(Servo_data[AUX3] > PPM_SWITCH)
packet[13] |= CG023_FLAG_STILL;
if(Servo_data[AUX4] > PPM_SWITCH)
packet[13] |= CG023_FLAG_VIDEO;
if(Servo_data[AUX5] > PPM_SWITCH)
packet[13] |= CG023_FLAG_EASY;
}
else
{// YD829
// rate
packet[13] = YD829_FLAG_RATE_HIGH;
// flags
if(Servo_data[AUX1] > PPM_SWITCH)
packet[13] |= YD829_FLAG_FLIP;
if(Servo_data[AUX3] > PPM_SWITCH)
packet[13] |= YD829_FLAG_STILL;
if(Servo_data[AUX4] > PPM_SWITCH)
packet[13] |= YD829_FLAG_VIDEO;
if(Servo_data[AUX5] > PPM_SWITCH)
packet[13] |= YD829_FLAG_HEADLESS;
}
packet[14] = 0;
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
if (bind)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CG023_RF_BIND_CHANNEL);
else
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency_no);
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, CG023_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
void CG023_init()
{
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
}
uint16_t CG023_callback()
{
switch (phase)
{
case CG023_BIND:
if (bind_counter == 0)
{
phase = CG023_DATA;
BIND_DONE;
}
else
{
CG023_send_packet(1);
bind_counter--;
}
break;
case CG023_DATA:
CG023_send_packet(0);
break;
}
if(sub_protocol==CG023)
return CG023_PACKET_PERIOD;
else
return YD829_PACKET_PERIOD;
}
void CG023_initialize_txid()
{
rx_tx_addr[0]= 0x80 | (rx_tx_addr[0] % 0x40);
if( rx_tx_addr[0] == 0xAA) // avoid using same freq for bind and data channel
rx_tx_addr[0] ++;
hopping_frequency_no = rx_tx_addr[0] - 0x7D; // rf channel for data packets
}
uint16_t initCG023(void)
{
BIND_IN_PROGRESS; // autobind protocol
bind_counter = CG023_BIND_COUNT;
CG023_initialize_txid();
CG023_init();
phase=CG023_BIND;
if(sub_protocol==CG023)
return CG023_INITIAL_WAIT+CG023_PACKET_PERIOD;
else
return CG023_INITIAL_WAIT+YD829_PACKET_PERIOD;
}
#endif

View File

@ -0,0 +1,214 @@
/*
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/>.
*/
// compatible with Cheerson CX-10 blue & newer red pcb, CX-10A, CX11, CX-10 green pcb, DM007, Floureon FX-10, CX-Stars
#if defined(CX10_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define CX10_BIND_COUNT 4360 // 6 seconds
#define CX10_PACKET_SIZE 15
#define CX10A_PACKET_SIZE 19 // CX10 blue board packets have 19-byte payload
#define CX10_PACKET_PERIOD 1316 // Timeout for callback in uSec
#define CX10A_PACKET_PERIOD 6000
#define INITIAL_WAIT 500
// flags
#define CX10_FLAG_FLIP 0x10 // goes to rudder channel
#define CX10_FLAG_MODE_MASK 0x03
#define CX10_FLAG_HEADLESS 0x04
// flags2
#define CX10_FLAG_VIDEO 0x02
#define CX10_FLAG_SNAPSHOT 0x04
// frequency channel management
#define RF_BIND_CHANNEL 0x02
#define NUM_RF_CHANNELS 4
enum {
CX10_INIT1 = 0,
CX10_BIND1,
CX10_BIND2,
CX10_DATA
};
void CX10_Write_Packet(uint8_t bind)
{
uint8_t offset = 0;
if(sub_protocol == CX10_BLUE)
offset = 4;
packet[0] = bind ? 0xAA : 0x55;
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
packet[3] = rx_tx_addr[2];
packet[4] = rx_tx_addr[3];
// packet[5] to [8] (aircraft id) is filled during bind for blue board
packet[5+offset] = lowByte(Servo_data[AILERON]);
packet[6+offset]= highByte(Servo_data[AILERON]);
packet[7+offset]= lowByte(Servo_data[ELEVATOR]);
packet[8+offset]= highByte(Servo_data[ELEVATOR]);
packet[9+offset]= lowByte(Servo_data[THROTTLE]);
packet[10+offset]= highByte(Servo_data[THROTTLE]);
packet[11+offset]= lowByte(Servo_data[RUDDER]);
packet[12+offset]= highByte(Servo_data[RUDDER]);
// Channel 5 - flip flag
if(Servo_data[AUX1] > PPM_SWITCH)
packet[12+offset] |= CX10_FLAG_FLIP; // flip flag
// Channel 6 - mode
if(Servo_data[AUX2] > PPM_MAX_COMMAND) // mode 3 / headless on CX-10A
packet[13+offset] = 0x02;
else
if(Servo_data[AUX2] < PPM_MIN_COMMAND)
packet[13+offset] = 0x00; // mode 1
else
packet[13+offset] = 0x01; // mode 2
flags=0;
if(sub_protocol == DM007)
{
// Channel 7 - snapshot
if(Servo_data[AUX3] > PPM_SWITCH)
flags |= CX10_FLAG_SNAPSHOT;
// Channel 8 - video
if(Servo_data[AUX4] > PPM_SWITCH)
flags |= CX10_FLAG_VIDEO;
// Channel 9 - headless
if(Servo_data[AUX5] > PPM_SWITCH)
packet[13+offset] |= CX10_FLAG_HEADLESS;
}
packet[14+offset] = flags;
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
if (bind)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
else
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no %= NUM_RF_CHANNELS;
}
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, packet_length);
NRF24L01_SetPower();
}
void CX10_init()
{
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TX_EN);
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
XN297_SetRXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
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 Acknowledgment on all data pipes
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, packet_length); // rx pipe 0 (used only for blue board)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
}
uint16_t CX10_callback() {
switch (phase) {
case CX10_INIT1:
phase = bind_phase;
break;
case CX10_BIND1:
if (bind_counter == 0)
{
phase = CX10_DATA;
BIND_DONE;
}
else
{
CX10_Write_Packet(1);
bind_counter--;
}
break;
case CX10_BIND2:
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR))
{ // RX fifo data ready
XN297_ReadPayload(packet, packet_length);
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
if(packet[9] == 1)
phase = CX10_BIND1;
}
else
{
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
CX10_Write_Packet(1);
delay(1); // used to be 300µs in deviation but not working so 1ms now
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP) | BV(NRF24L01_00_PRIM_RX));
}
break;
case CX10_DATA:
CX10_Write_Packet(0);
break;
}
return packet_period;
}
void initialize_txid()
{
rx_tx_addr[1]%= 0x30;
hopping_frequency[0] = 0x03 + (rx_tx_addr[0] & 0x0F);
hopping_frequency[1] = 0x16 + (rx_tx_addr[0] >> 4);
hopping_frequency[2] = 0x2D + (rx_tx_addr[1] & 0x0F);
hopping_frequency[3] = 0x40 + (rx_tx_addr[1] >> 4);
}
uint16_t initCX10(void)
{
switch(sub_protocol)
{
case CX10_GREEN:
case DM007:
packet_length = CX10_PACKET_SIZE;
packet_period = CX10_PACKET_PERIOD;
bind_phase = CX10_BIND1;
bind_counter = CX10_BIND_COUNT;
break;
case CX10_BLUE:
packet_length = CX10A_PACKET_SIZE;
packet_period = CX10A_PACKET_PERIOD;
bind_phase = CX10_BIND2;
bind_counter=0;
for(uint8_t i=0; i<4; i++)
packet[5+i] = 0xff; // clear aircraft id
packet[9] = 0;
break;
}
initialize_txid();
CX10_init();
phase = CX10_INIT1;
BIND_IN_PROGRESS; // autobind protocol
return INITIAL_WAIT;
}
#endif

View File

@ -0,0 +1,284 @@
/*
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/>.
*/
#include "iface_cyrf6936.h"
void cyrf_spi_write(uint8_t command)
{
uint8_t n=8;
SCK_off;//SCK start low
SDI_off;
while(n--) {
if(command&0x80)
SDI_on;
else
SDI_off;
SCK_on;
NOP();
SCK_off;
command = command << 1;
}
SDI_on;
}
uint8_t cyrf_spi_read()
{
uint8_t result;
uint8_t i;
result=0;
for(i=0;i<8;i++)
{
if(SDO_1) ///
result=(result<<1)|0x01;
else
result=result<<1;
SCK_on;
NOP();
SCK_off;
NOP();
}
return result;
}
void CYRF_WriteRegister(uint8_t address, uint8_t data)
{
CYRF_CSN_off;
cyrf_spi_write(0x80 | address);
cyrf_spi_write(data);
CYRF_CSN_on;
}
void CYRF_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
{
uint8_t i;
CYRF_CSN_off;
cyrf_spi_write(0x80 | address);
for(i = 0; i < length; i++)
cyrf_spi_write(data[i]);
CYRF_CSN_on;
}
void CYRF_ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
{
uint8_t i;
CYRF_CSN_off;
cyrf_spi_write(address);
for(i = 0; i < length; i++)
data[i] = cyrf_spi_read();
CYRF_CSN_on;
}
uint8_t CYRF_ReadRegister(uint8_t address)
{
uint8_t data;
CYRF_CSN_off;
cyrf_spi_write(address);
data = cyrf_spi_read();
CYRF_CSN_on;
return data;
}
//
uint8_t CYRF_Reset()
{
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x01);//software reset
_delay_us(200);//
// RS_HI;
// _delay_us(100);
// RS_LO;
// _delay_us(100);
CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0); //Enable XOUT as GPIO
CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04); //Enable PACTL as GPIO
CYRF_SetTxRxMode(TXRX_OFF);
//Verify the CYRD chip is responding
return (CYRF_ReadRegister(CYRF_10_FRAMING_CFG) == 0xa5);//return if reset
}
uint8_t CYRF_MaxPower()
{
return (*((uint8_t*)0x08001007) == 0) ? CYRF_PWR_100MW : CYRF_PWR_10MW;
}
/*
*
*/
void CYRF_GetMfgData(uint8_t data[])
{
/* Fuses power on */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0xFF);
CYRF_ReadRegisterMulti(CYRF_25_MFG_ID, data, 6);
/* Fuses power off */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0x00);
}
/*
* 1 - Tx else Rx
*/
void CYRF_SetTxRxMode(uint8_t mode)
{
//Set the post tx/rx state
CYRF_WriteRegister(CYRF_0F_XACT_CFG, mode == TX_EN ? 0x28 : 0x2C); //was 0x2C:0x28 but reversed in last deviation
if(mode == TX_EN)
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x80);
else
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x20);
}
/*
*
*/
void CYRF_ConfigRFChannel(uint8_t ch)
{
CYRF_WriteRegister(CYRF_00_CHANNEL,ch);
}
void CYRF_SetPower_Value(uint8_t power)
{
uint8_t val = CYRF_ReadRegister(CYRF_03_TX_CFG) & 0xF8;
CYRF_WriteRegister(CYRF_03_TX_CFG, val | (power & 0x07));
}
void CYRF_SetPower(uint8_t val)
{
uint8_t power=CYRF_BIND_POWER;
if(IS_BIND_DONE_on)
power=IS_POWER_FLAG_on?CYRF_HIGH_POWER:CYRF_LOW_POWER;
else
if(IS_RANGE_FLAG_on)
power=CYRF_POWER_0;
CYRF_WriteRegister(CYRF_03_TX_CFG, val | power);
}
/*
*
*/
void CYRF_ConfigCRCSeed(uint16_t crc)
{
CYRF_WriteRegister(CYRF_15_CRC_SEED_LSB,crc & 0xff);
CYRF_WriteRegister(CYRF_16_CRC_SEED_MSB,crc >> 8);
}
/*
* these are the recommended sop codes from Cyrpress
* See "WirelessUSB LP/LPstar and PRoC LP/LPstar Technical Reference Manual"
*/
void CYRF_ConfigSOPCode(const uint8_t *sopcodes)
{
//NOTE: This can also be implemented as:
//for(i = 0; i < 8; i++) WriteRegister)0x23, sopcodes[i];
CYRF_WriteRegisterMulti(CYRF_22_SOP_CODE, sopcodes, 8);
}
void CYRF_ConfigDataCode(const uint8_t *datacodes, uint8_t len)
{
//NOTE: This can also be implemented as:
//for(i = 0; i < len; i++) WriteRegister)0x23, datacodes[i];
CYRF_WriteRegisterMulti(CYRF_23_DATA_CODE, datacodes, len);
}
void CYRF_WritePreamble(uint32_t preamble)
{
CYRF_CSN_off;
cyrf_spi_write(0x80 | 0x24);
cyrf_spi_write(preamble & 0xff);
cyrf_spi_write((preamble >> 8) & 0xff);
cyrf_spi_write((preamble >> 16) & 0xff);
CYRF_CSN_on;
}
/*
*
*/
void CYRF_StartReceive()
{
CYRF_WriteRegister(CYRF_05_RX_CTRL,0x87);
}
void CYRF_ReadDataPacket(uint8_t dpbuffer[])
{
CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, 0x10);
}
void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length)
{
ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, length);
}
void CYRF_WriteDataPacketLen(const uint8_t dpbuffer[], uint8_t len)
{
CYRF_WriteRegister(CYRF_01_TX_LENGTH, len);
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40);
CYRF_WriteRegisterMulti(CYRF_20_TX_BUFFER, dpbuffer, len);
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0xBF);
}
void CYRF_WriteDataPacket(const uint8_t dpbuffer[])
{
CYRF_WriteDataPacketLen(dpbuffer, 16);
}
uint8_t CYRF_ReadRSSI(uint8_t dodummyread)
{
uint8_t result;
if(dodummyread)
CYRF_ReadRegister(CYRF_13_RSSI);
result = CYRF_ReadRegister(CYRF_13_RSSI);
if(result & 0x80)
result = CYRF_ReadRegister(CYRF_13_RSSI);
return (result & 0x0F);
}
//NOTE: This routine will reset the CRC Seed
void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uint8_t min, uint8_t max)
{
#define NUM_FREQ 80
#define FREQ_OFFSET 4
uint8_t rssi[NUM_FREQ];
if (min < FREQ_OFFSET)
min = FREQ_OFFSET;
if (max > NUM_FREQ)
max = NUM_FREQ;
uint8_t i;
int8_t j;
memset(channels, 0, sizeof(uint8_t) * len);
CYRF_ConfigCRCSeed(0x0000);
CYRF_SetTxRxMode(RX_EN);
//Wait for pre-amp to switch from send to receive
_delay_us(1000);
for(i = 0; i < NUM_FREQ; i++)
{
CYRF_ConfigRFChannel(i);
CYRF_ReadRegister(CYRF_13_RSSI);
CYRF_StartReceive();
_delay_us(10);
rssi[i] = CYRF_ReadRegister(CYRF_13_RSSI);
}
for (i = 0; i < len; i++)
{
channels[i] = min;
for (j = min; j < max; j++)
if (rssi[j] < rssi[channels[i]])
channels[i] = j;
for (j = channels[i] - minspace; j < channels[i] + minspace; j++) {
//Ensure we don't reuse any channels within minspace of the selected channel again
if (j < 0 || j >= NUM_FREQ)
continue;
rssi[j] = 0xff;
}
}
CYRF_SetTxRxMode(TX_EN);
}

View File

@ -0,0 +1,535 @@
/*
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 DSM2_NUM_CHANNELS 7
#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 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}
},
};
//
uint8_t chidx;
uint8_t sop_col;
uint8_t data_col;
uint16_t cyrf_state;
uint8_t crcidx;
uint8_t binding;
uint16_t crc;
uint8_t model;
/*
#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
*/
void 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]) + model;
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] = DSM2_NUM_CHANNELS;
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
#if DSM2_NUM_CHANNELS < 8
packet[12] = 0x01;
#else
packet[12] = 0x02;
#endif
packet[13] = 0x00; //???
for(i = 8; i < 14; i++)
sum += packet[i];
packet[14] = sum >> 8;
packet[15] = sum & 0xff;
}
void build_data_packet(uint8_t upper)//
{
#if DSM2_NUM_CHANNELS==4
const uint8_t ch_map[] = {0, 1, 2, 3, 0xff, 0xff, 0xff}; //Guess
#elif DSM2_NUM_CHANNELS==5
const uint8_t ch_map[] = {0, 1, 2, 3, 4, 0xff, 0xff}; //Guess
#elif DSM2_NUM_CHANNELS==6
const uint8_t ch_map[] = {1, 5, 2, 3, 0, 4, 0xff}; //HP6DSM
#elif DSM2_NUM_CHANNELS==7
const uint8_t ch_map[] = {1, 5, 2, 4, 3, 6, 0}; //DX6i
#elif DSM2_NUM_CHANNELS==8
const uint8_t ch_map[] = {1, 5, 2, 3, 6, 0xff, 0xff, 4, 0, 7, 0xff, 0xff, 0xff, 0xff}; //DX8
#elif DSM2_NUM_CHANNELS==9
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 0xff, 0xff, 0xff, 0xff, 0xff}; //DM9
#elif DSM2_NUM_CHANNELS==10
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff};
#elif DSM2_NUM_CHANNELS==11
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 10, 0xff, 0xff, 0xff};
#elif DSM2_NUM_CHANNELS==12
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 10, 11, 0xff, 0xff};
#endif
uint8_t i;
uint8_t bits;
//
if( binding && PROTOCOL_SticksMoved(0) )
{
//BIND_DONE;
binding = 0;
}
if (sub_protocol==DSMX)
{
packet[0] = cyrfmfg_id[2];
packet[1] = cyrfmfg_id[3] + model;
bits=11;
}
else
{
packet[0] = (0xff ^ cyrfmfg_id[2]);
packet[1] = (0xff ^ cyrfmfg_id[3]) + model;
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;
}
}
uint8_t 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);//
}
uint8_t 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},
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER},
{CYRF_12_DATA64_THOLD, 0x0a},
{CYRF_0F_XACT_CFG, 0x04},
{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, SDE mode, was max-power but replaced by low power
{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
};
void 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);
}
void initialize_bind_state()
{
const uint8_t pn_bind[] = { 0xc6,0x94,0x22,0xfe,0x48,0xe6,0x57,0x4e };
uint8_t data_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);
CYRF_ConfigSOPCode(pncodes[pn_row][sop_col]);
memcpy(data_code, pncodes[pn_row][data_col], 16);
memcpy(data_code + 16, pncodes[0][8], 8);
memcpy(data_code + 24, pn_bind, 8);
CYRF_ConfigDataCode(data_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 | 7},
{CYRF_10_FRAMING_CFG, 0xea},
{CYRF_1F_TX_OVERRIDE, 0x00},
{CYRF_1E_RX_OVERRIDE, 0x00},
{CYRF_03_TX_CFG, 0x28 | 7},
{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},
};
void cyrf_configdata()
{
for(uint8_t i = 0; i < sizeof(data_vals) / 2; i++)
CYRF_WriteRegister(data_vals[i][0], data_vals[i][1]);
}
void set_sop_data_crc()
{
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);
CYRF_ConfigSOPCode(pncodes[pn_row][sop_col]);
CYRF_ConfigDataCode(pncodes[pn_row][data_col], 16);
if(sub_protocol == DSMX)
chidx = (chidx + 1) % 23;
else
chidx = (chidx + 1) % 2;
crcidx = !crcidx;
}
void 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 DSM2_NUM_CHANNELS < 8
cyrf_state = DSM2_CH1_WRITE_A; // change from CH2_CHECK_A to CH1_WRITE_A (ie no upper)
return 11000 - CH1_CH2_DELAY - WRITE_DELAY ; // Original is 22000 from deviation but it works better this way
#else
cyrf_state = DSM2_CH1_WRITE_B; // change from CH2_CHECK_A to CH1_WRITE_A (to transmit upper)
#endif
}
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
model=MProtocol_id-MProtocol_id_master; // RxNum for serial or 0 for ppm
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

@ -0,0 +1,391 @@
/*
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(DEVO_CYRF6936_INO)
#include "iface_cyrf6936.h"
#define DEVO_NUM_CHANNELS 8
//For Debug
//#define NO_SCRAMBLE
#define PKTS_PER_CHANNEL 4
#define DEVO_BIND_COUNT 0x1388
//#define TELEMETRY_ENABLE 0x30
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
//
//#define TELEM_ON 0
//#define TELEM_OFF 1
enum Devo_PhaseState
{
DEVO_BIND,
DEVO_BIND_SENDCH,
DEVO_BOUND,
DEVO_BOUND_1,
DEVO_BOUND_2,
DEVO_BOUND_3,
DEVO_BOUND_4,
DEVO_BOUND_5,
DEVO_BOUND_6,
DEVO_BOUND_7,
DEVO_BOUND_8,
DEVO_BOUND_9,
DEVO_BOUND_10,
};
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;
void scramble_pkt()
{
#ifdef NO_SCRAMBLE
return;
#else
uint8_t i;
for(i = 0; i < 15; i++)
packet[i + 1] ^= cyrfmfg_id[i % 4];
#endif
}
void add_pkt_suffix()
{
uint8_t bind_state;
if (use_fixed_id)
{
if (bind_counter > 0)
bind_state = 0xc0;
else
bind_state = 0x80;
}
else
bind_state = 0x00;
packet[10] = bind_state | (PKTS_PER_CHANNEL - pkt_num - 1);
packet[11] = *(hopping_frequency_ptr + 1);
packet[12] = *(hopping_frequency_ptr + 2);
packet[13] = fixed_id & 0xff;
packet[14] = (fixed_id >> 8) & 0xff;
packet[15] = (fixed_id >> 16) & 0xff;
}
void build_beacon_pkt(uint8_t upper)
{
packet[0] = ((DEVO_NUM_CHANNELS << 4) | 0x07);
// uint8_t enable = 0;
uint8_t max = 8;
// int offset = 0;
if (upper)
{
packet[0] += 1;
max = 4;
// offset = 8;
}
for(uint8_t i = 0; i < max; i++)
packet[i+1] = 0;
// packet[9] = enable;
packet[9] = 0;
add_pkt_suffix();
}
void build_bind_pkt()
{
packet[0] = (DEVO_NUM_CHANNELS << 4) | 0x0a;
packet[1] = bind_counter & 0xff;
packet[2] = (bind_counter >> 8);
packet[3] = *hopping_frequency_ptr;
packet[4] = *(hopping_frequency_ptr + 1);
packet[5] = *(hopping_frequency_ptr + 2);
packet[6] = cyrfmfg_id[0];
packet[7] = cyrfmfg_id[1];
packet[8] = cyrfmfg_id[2];
packet[9] = cyrfmfg_id[3];
add_pkt_suffix();
//The fixed-id portion is scrambled in the bind packet
//I assume it is ignored
packet[13] ^= cyrfmfg_id[0];
packet[14] ^= cyrfmfg_id[1];
packet[15] ^= cyrfmfg_id[2];
}
void build_data_pkt()
{
uint8_t i;
packet[0] = (DEVO_NUM_CHANNELS << 4) | (0x0b + ch_idx);
uint8_t sign = 0x0b;
for (i = 0; i < 4; i++)
{
//
int16_t value= map(Servo_data[ch_idx * 4 + i],PPM_MIN,PPM_MAX,-1600,1600);//range -1600...+1600
//s32 value = (s32)Channels[ch_idx * 4 + i] * 0x640 / CHAN_MAX_VALUE;//10000
if(value < 0)
{
value = -value;
sign |= 1 << (7 - i);
}
packet[2 * i + 1] = value & 0xff;
packet[2 * i + 2] = (value >> 8) & 0xff;
}
packet[9] = sign;
ch_idx = ch_idx + 1;
if (ch_idx * 4 >= DEVO_NUM_CHANNELS)
ch_idx = 0;
add_pkt_suffix();
}
void cyrf_set_bound_sop_code()
{
/* 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]);
if(! crc)
crc = 1;
uint8_t sopidx = (0xff &((cyrfmfg_id[0] << 2) + cyrfmfg_id[1] + cyrfmfg_id[2])) % 10;
CYRF_SetTxRxMode(TX_EN);
CYRF_ConfigCRCSeed((crc << 8) + crc);
CYRF_ConfigSOPCode(sopcodes[sopidx]);
CYRF_SetPower(0x08);
}
void cyrf_init()
{
/* Initialise CYRF chip */
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x39);
CYRF_SetPower(0x08);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4A);
CYRF_WriteRegister(CYRF_0B_PWR_CTRL, 0x00);
CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04);
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL, 0x20);
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xA4);
CYRF_WriteRegister(CYRF_11_DATA32_THOLD, 0x05);
CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0E);
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C);
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
CYRF_WriteRegister(CYRF_39_ANALOG_CTRL, 0x01);
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x10);
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
CYRF_WriteRegister(CYRF_01_TX_LENGTH, 0x10);
CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x10);
CYRF_WriteRegister(CYRF_27_CLK_OVERRIDE, 0x02);
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x28);
}
void set_radio_channels()
{
//int i;
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80);
//printf("Radio Channels:");
// for (i = 0; i < 3; i++) {
// printf(" %02x", radio_ch[i]);
//Serial.print(radio_ch[i]);
// }
// printf("\n");
//Makes code a little easier to duplicate these here
hopping_frequency[3] = hopping_frequency[0];
hopping_frequency[4] = hopping_frequency[1];
}
void DEVO_BuildPacket()
{
switch(phase)
{
case DEVO_BIND:
if(bind_counter>0)
bind_counter--;
build_bind_pkt();
phase = DEVO_BIND_SENDCH;
break;
case DEVO_BIND_SENDCH:
if(bind_counter>0)
bind_counter--;
build_data_pkt();
scramble_pkt();
if (bind_counter == 0)
{
phase = DEVO_BOUND;
BIND_DONE;
}
else
phase = DEVO_BIND;
break;
case DEVO_BOUND:
case DEVO_BOUND_1:
case DEVO_BOUND_2:
case DEVO_BOUND_3:
case DEVO_BOUND_4:
case DEVO_BOUND_5:
case DEVO_BOUND_6:
case DEVO_BOUND_7:
case DEVO_BOUND_8:
case DEVO_BOUND_9:
build_data_pkt();
scramble_pkt();
phase++;
if (bind_counter > 0)
{
bind_counter--;
if (bind_counter == 0)
BIND_DONE;
}
break;
case DEVO_BOUND_10:
build_beacon_pkt(DEVO_NUM_CHANNELS > 8 ? failsafe_pkt : 0);
failsafe_pkt = failsafe_pkt ? 0 : 1;
scramble_pkt();
phase = DEVO_BOUND_1;
break;
}
pkt_num++;
if(pkt_num == PKTS_PER_CHANNEL)
pkt_num = 0;
}
uint16_t devo_callback()
{
if (txState == 0)
{
txState = 1;
DEVO_BuildPacket();
CYRF_WriteDataPacket(packet);
return 1200;
}
txState = 0;
uint8_t i = 0;
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
if(++i > NUM_WAIT_LOOPS)
return 1200;
if (phase == DEVO_BOUND)
{
/* exit binding state */
phase = DEVO_BOUND_3;
cyrf_set_bound_sop_code();
}
if(pkt_num == 0)
{
//Keep tx power updated
CYRF_SetPower(0x08);
hopping_frequency_ptr = hopping_frequency_ptr == &hopping_frequency[2] ? hopping_frequency : hopping_frequency_ptr + 1;
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
}
return 1200;
}
void devo_bind()
{
fixed_id = Model_fixed_id;
bind_counter = DEVO_BIND_COUNT;
use_fixed_id = 1;
//PROTOCOL_SetBindState(0x1388 * 2400 / 1000); //msecs 12000ms
}
/*
void 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();
}
}
*/
uint16_t DevoInit()
{
CYRF_Reset();
cyrf_init();
CYRF_GetMfgData(cyrfmfg_id);
CYRF_SetTxRxMode(TX_EN);
CYRF_ConfigCRCSeed(0x0000);
CYRF_ConfigSOPCode(sopcodes[0]);
set_radio_channels();
use_fixed_id = 0;
failsafe_pkt = 0;
hopping_frequency_ptr = hopping_frequency;
//
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
//FIXME: Properly setnumber of channels;
pkt_num = 0;
ch_idx = 0;
txState = 0;
//uint8_t txid[4];
//
/*
if(BIND_0){
Model_fixed_id=0;
eeprom_write_block((const void*)0,(void*)40,4);
while(1){
LED_ON;
delay(100);
LED_OFF;
delay(100);
}
}
else{
eeprom_read_block((void*)txid,(const void*)40,3);
Model_fixed_id=(txid[0] | ((uint32_t)txid[1]<<8) | ((uint32_t)txid[2]<<16));
}
*/
if(! Model_fixed_id)
{//model fixed ID =0
fixed_id = ((uint32_t)(hopping_frequency[0] ^ cyrfmfg_id[0] ^ cyrfmfg_id[3]) << 16)
| ((uint32_t)(hopping_frequency[1] ^ cyrfmfg_id[1] ^ cyrfmfg_id[4]) << 8)
| ((uint32_t)(hopping_frequency[2] ^ cyrfmfg_id[2] ^ cyrfmfg_id[5]) << 0);
fixed_id = fixed_id % 1000000;
bind_counter = DEVO_BIND_COUNT;
phase = DEVO_BIND;
//PROTOCOL_SetBindState(0x1388 * 2400 / 1000); //msecs
}
else
{
fixed_id = Model_fixed_id;
use_fixed_id = 1;
phase = DEVO_BOUND_1;
bind_counter = 0;
cyrf_set_bound_sop_code();
}
return 2400;
}
#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.
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(FLYSKY_A7105_INO)
#include "iface_a7105.h"
//FlySky constants & variables
#define FLYSKY_BIND_COUNT 2500
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_UNK = 0x10, // undocumented ?
FLAG_V9X9_LED = 0x20,
};
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,
};
enum {
// flags going to byte 14
FLAG_V912_TOPBTN= 0x40,
FLAG_V912_BTMBTN= 0x80,
};
uint8_t chanrow;
uint8_t chancol;
uint8_t chanoffset;
void flysky_apply_extension_flags()
{
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_data[AUX1] > PPM_SWITCH)
packet[12] |= FLAG_V9X9_UNK;
if(Servo_data[AUX2] > PPM_SWITCH)
packet[12] |= FLAG_V9X9_LED;
if(Servo_data[AUX3] > PPM_SWITCH)
packet[10] |= FLAG_V9X9_CAMERA;
if(Servo_data[AUX4] > PPM_SWITCH)
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_data[AUX1] > PPM_SWITCH)
packet[14] |= FLAG_V6X6_FLIP;
if(Servo_data[AUX2] > PPM_SWITCH)
packet[14] |= FLAG_V6X6_LED;
if(Servo_data[AUX3] > PPM_SWITCH)
packet[14] |= FLAG_V6X6_CAMERA;
if(Servo_data[AUX4] > PPM_SWITCH)
packet[14] |= FLAG_V6X6_VIDEO;
if(Servo_data[AUX5] > PPM_SWITCH)
{
packet[13] |= FLAG_V6X6_HLESS1;
packet[14] |= FLAG_V6X6_HLESS2;
}
if(Servo_data[AUX6] > PPM_SWITCH) //use option to manipulate these bytes
packet[14] |= FLAG_V6X6_RTH;
if(Servo_data[AUX7] > PPM_SWITCH)
packet[14] |= FLAG_V6X6_XCAL;
if(Servo_data[AUX8] > PPM_SWITCH)
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_data[AUX1] > PPM_SWITCH)
packet[14] |= FLAG_V912_BTMBTN;
if(Servo_data[AUX2] > PPM_SWITCH)
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;
}
}
void 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
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];
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)
}
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;
}
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;
}
#endif

View File

@ -0,0 +1,21 @@
/*
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(FRSKYX_CC2500_INO)
#include "iface_cc2500.h"
#endif

View File

@ -0,0 +1,269 @@
/*
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
};
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)//20 bytes
{
cc2500_readFifo(pkt, len); //received telemetry packets
#if defined(TELEMETRY)
//parse telemetry packet here
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;
}
#if defined(TELEMETRY)
void 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;
}
}
void compute_RSSIdbm(){
if(pktt[len-2] >=128){
RSSI_dBm =(((uint16_t)(pktt[len-2])*18)>>5)- 82;
}
else{
RSSI_dBm = (((uint16_t)(pktt[len-2])*18)>>5)+65;
}
}
#endif
void 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, fine);
//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);
//
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########
}
uint8_t 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;
}
void 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;
}
uint8_t telemetry_counter=0;
void 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;//
packet[4] = pkt[6]?(telemetry_counter++)%32:0;
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));
}
}
}
#endif

View File

@ -0,0 +1,242 @@
/*
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(HISKY_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define BIND_COUNT 1000
#define TXID_SIZE 5
#define FREQUENCE_NUM 20
//
uint8_t bind_buf_arry[4][10];
// 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]
void calc_fh_channels(uint32_t seed)
{
uint8_t idx = 0;
uint32_t rnd = seed;
while (idx < FREQUENCE_NUM)
{
uint8_t i;
uint8_t 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 ^ (uint8_t)seed) & 0x01 )== 0)
continue;
// Check that it's not duplicated 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;//find hopping frequency
}
}
void build_binding_packet(void)
{
uint8_t i;
uint16_t sum=0;
uint8_t sum_l,sum_h;
for(i=0;i<5;i++)
sum += rx_tx_addr[i];
sum_l = (uint8_t)sum;//low byte
sum >>= 8;
sum_h = (uint8_t)sum;//high bye
bind_buf_arry[0][0] = 0xff;
bind_buf_arry[0][1] = 0xaa;
bind_buf_arry[0][2] = 0x55;
for(i=3;i<8;i++)
bind_buf_arry[0][i] = rx_tx_addr[i-3];
for(i=1;i<4;i++)
{
bind_buf_arry[i][0] = sum_l;
bind_buf_arry[i][1] = sum_h;
bind_buf_arry[i][2] = i-1;
}
for(i=0;i<7;i++)
{ bind_buf_arry[1][i+3] = hopping_frequency[i];
bind_buf_arry[2][i+3] = hopping_frequency[i+7];
bind_buf_arry[3][i+3] = hopping_frequency[i+14];
}
}
void hisky_init()
{
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgement
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81); // binding packet must be set in channel 81
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 10); // payload size = 10
if(sub_protocol==HK310)
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps
else
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower(); // Set power
NRF24L01_SetTxRxMode(TX_EN); // TX mode, 2-bytes CRC, radio on
}
// HiSky channel sequence: AILE ELEV THRO RUDD GEAR PITCH, channel data value is from 0 to 1000
// Channel 7 - Gyro mode, 0 - 6 axis, 3 - 3 axis
void build_ch_data()
{
uint16_t temp;
uint8_t i,j;
uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
for (i = 0; i< 8; i++) {
j=ch[i];
temp=map(limit_channel_100(j),PPM_MIN_100,PPM_MAX_100,0,1000);
if (j == THROTTLE) // It is clear that hisky's throttle stick is made reversely, so I adjust it here on purpose
temp = 1000 -temp;
if (j == AUX3)
temp = temp < 400 ? 0 : 3; // Gyro mode, 0 - 6 axis, 3 - 3 axis
packet[i] = (uint8_t)(temp&0xFF);
packet[i<4?8:9]>>=2;
packet[i<4?8:9]|=(temp>>2)&0xc0;
}
}
uint16_t hisky_cb()
{
phase++;
if(sub_protocol==HK310)
switch(phase)
{
case 1:
NRF24L01_SetPower();
phase=2;
break;
case 4:
phase=6;
break;
case 7: // build packet and send failsafe every 100ms
convert_channel_HK310(hopping_frequency_no!=0?RUDDER:AUX2,&packet[0],&packet[1]);
convert_channel_HK310(hopping_frequency_no!=0?THROTTLE:AUX3,&packet[2],&packet[3]);
convert_channel_HK310(hopping_frequency_no!=0?AUX1:AUX4,&packet[4],&packet[5]);
packet[7]=hopping_frequency_no!=0?0x55:0xAA;
packet[8]=hopping_frequency_no!=0?0x67:0x5A;
phase=8;
break;
}
switch(phase)
{
case 1:
NRF24L01_FlushTx();
break;
case 2:
if (bind_counter != 0)
{
//Set TX id and channel for bind packet
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x12\x23\x23\x45\x78", 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81);
}
break;
case 3:
if (bind_counter != 0)
{
bind_counter--;//
if (! bind_counter) //Binding complete
BIND_DONE;//
//Send bind packet
NRF24L01_WritePayload(bind_buf_arry[binding_idx],10);
binding_idx++;
if (binding_idx >= 4)
binding_idx = 0;
}
break;
case 4:
if (bind_counter != 0)
NRF24L01_FlushTx();
break;
case 5:
//Set TX power
NRF24L01_SetPower();
break;
case 6:
//Set TX id and channel for normal packet
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_NUM)
hopping_frequency_no = 0;
break;
case 7:
//Build normal packet
build_ch_data();
break;
case 8:
break;
default:
//Send normal packet
phase = 0;
NRF24L01_WritePayload(packet,10);
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)
void initialize_tx_id()
{
//Generate frequency hopping table
if(sub_protocol==HK310)
for(uint8_t i=0;i<FREQUENCE_NUM;i++)
hopping_frequency[i]=i; // Sequential order hop channels...
else
calc_fh_channels(MProtocol_id);
}
uint16_t initHiSky()
{
initialize_tx_id();
build_binding_packet();
hisky_init();
phase = 0;
hopping_frequency_no = 0;
binding_idx = 0;
if(IS_AUTOBIND_FLAG_on)
bind_counter = BIND_COUNT;
else
bind_counter = 0;
return 1000;
}
#endif

View File

@ -0,0 +1,262 @@
/*
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(HUBSAN_A7105_INO)
#include "iface_a7105.h"
enum{
HUBSAN_FLAG_VIDEO = 0x01, // record video
HUBSAN_FLAG_FLIP = 0x08,
HUBSAN_FLAG_LIGHT = 0x04
};
uint32_t sessionid;
const uint32_t txid = 0xdb042679;
enum {
BIND_1,
BIND_2,
BIND_3,
BIND_4,
BIND_5,
BIND_6,
BIND_7,
BIND_8,
DATA_1,
DATA_2,
DATA_3,
DATA_4,
DATA_5,
};
#define WAIT_WRITE 0x80
void update_crc()
{
uint8_t sum = 0;
for(uint8_t i = 0; i < 15; i++)
sum += packet[i];
packet[15] = (256 - (sum % 256)) & 0xFF;
}
void hubsan_build_bind_packet(uint8_t state)
{
packet[0] = state;
packet[1] = channel;
packet[2] = (sessionid >> 24) & 0xFF;
packet[3] = (sessionid >> 16) & 0xFF;
packet[4] = (sessionid >> 8) & 0xFF;
packet[5] = (sessionid >> 0) & 0xFF;
packet[6] = 0x08;
packet[7] = 0xe4;
packet[8] = 0xea;
packet[9] = 0x9e;
packet[10] = 0x50;
packet[11] = (txid >> 24) & 0xFF;
packet[12] = (txid >> 16) & 0xFF;
packet[13] = (txid >> 8) & 0xFF;
packet[14] = (txid >> 0) & 0xFF;
update_crc();
}
//cc : throttle observed range: 0x00 - 0xFF (smaller is down)
//ee : rudder observed range: 0x34 - 0xcc (smaller is right)52-204-60%
//gg : elevator observed range: 0x3e - 0xbc (smaller is up)62-188 -50%
//ii : aileron observed range: 0x45 - 0xc3 (smaller is right)69-195-50%
void hubsan_build_packet()
{
static uint8_t vtx_freq = 0;
memset(packet, 0, 16);
if(vtx_freq != option || packet_count==100) // set vTX frequency (H107D)
{
vtx_freq = option;
packet[0] = 0x40;
packet[1] = (option>0xF2)?0x17:0x16;
packet[2] = option+0x0D; // 5645 - 5900 MHz
packet[3] = 0x82;
packet_count++;
}
else //20 00 00 00 80 00 7d 00 84 02 64 db 04 26 79 7b
{
packet[0] = 0x20;
packet[2] = convert_channel_8b(THROTTLE);//throtle
}
packet[4] = 0xFF - convert_channel_8b(RUDDER);//Rudder is reversed
packet[6] = 0xFF - convert_channel_8b(ELEVATOR); //Elevator is reversed
packet[8] = convert_channel_8b(AILERON);//aileron
if( packet_count < 100) {
packet[9] = 0x02 | HUBSAN_FLAG_LIGHT | HUBSAN_FLAG_FLIP;
packet_count++;
}
else
{
packet[9] = 0x02;
// Channel 5
if( Servo_data[AUX1] >= PPM_SWITCH)
packet[9] |= HUBSAN_FLAG_FLIP;
// Channel 6
if( Servo_data[AUX2] >= PPM_SWITCH)
packet[9] |= HUBSAN_FLAG_LIGHT;
// Channel 8
if( Servo_data[AUX4] > PPM_SWITCH)
packet[9] |= HUBSAN_FLAG_VIDEO;
}
packet[10] = 0x64;
packet[11] = (txid >> 24) & 0xFF;
packet[12] = (txid >> 16) & 0xFF;
packet[13] = (txid >> 8) & 0xFF;
packet[14] = (txid >> 0) & 0xFF;
update_crc();
}
uint8_t hubsan_check_integrity()
{
uint8_t sum = 0;
for(int i = 0; i < 15; i++)
sum += packet[i];
return packet[15] == ((256 - (sum % 256)) & 0xFF);
}
uint16_t ReadHubsan()
{
static uint8_t txState=0;
static uint8_t rfMode=0;
uint16_t delay;
uint8_t i;
switch(phase) {
case BIND_1:
case BIND_3:
case BIND_5:
case BIND_7:
hubsan_build_bind_packet(phase == BIND_7 ? 9 : (phase == BIND_5 ? 1 : phase + 1 - BIND_1));
A7105_Strobe(A7105_STANDBY);
A7105_WriteData(16, channel);
phase |= WAIT_WRITE;
return 3000;
case BIND_1 | WAIT_WRITE:
case BIND_3 | WAIT_WRITE:
case BIND_5 | WAIT_WRITE:
case BIND_7 | WAIT_WRITE:
//wait for completion
for(i = 0; i< 20; i++) {
if(! (A7105_ReadReg(A7105_00_MODE) & 0x01))
break;
}
A7105_SetTxRxMode(RX_EN);
A7105_Strobe(A7105_RX);
phase &= ~WAIT_WRITE;
phase++;
return 4500; //7.5msec elapsed since last write
case BIND_2:
case BIND_4:
case BIND_6:
A7105_SetTxRxMode(TX_EN);
if(A7105_ReadReg(A7105_00_MODE) & 0x01) {
phase = BIND_1;
return 4500; //No signal, restart binding procedure. 12msec elapsed since last write
}
A7105_ReadData();
phase++;
if (phase == BIND_5)
A7105_WriteID(((uint32_t)packet[2] << 24) | ((uint32_t)packet[3] << 16) | ((uint32_t)packet[4] << 8) | packet[5]);
return 500; //8msec elapsed time since last write;
case BIND_8:
A7105_SetTxRxMode(TX_EN);
if(A7105_ReadReg(A7105_00_MODE) & 0x01) {
phase = BIND_7;
return 15000; //22.5msec elapsed since last write
}
A7105_ReadData();
if(packet[1] == 9) {
phase = DATA_1;
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
BIND_DONE;
return 28000; //35.5msec elapsed since last write
} else {
phase = BIND_7;
return 15000; //22.5 msec elapsed since last write
}
case DATA_1:
case DATA_2:
case DATA_3:
case DATA_4:
case DATA_5:
if( txState == 0) { // send packet
rfMode = A7105_TX;
if( phase == DATA_1)
A7105_SetPower(); //Keep transmit power in sync
hubsan_build_packet();
A7105_Strobe(A7105_STANDBY);
A7105_WriteData(16, phase == DATA_5 ? channel + 0x23 : channel);
if (phase == DATA_5)
phase = DATA_1;
else
phase++;
delay=3000;
}
else {
#if defined(TELEMETRY)
if( rfMode == A7105_TX) {// switch to rx mode 3ms after packet sent
for( i=0; i<10; i++)
{
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)) {// wait for tx completion
A7105_SetTxRxMode(RX_EN);
A7105_Strobe(A7105_RX);
rfMode = A7105_RX;
break;
}
}
}
if( rfMode == A7105_RX) { // check for telemetry frame
for( i=0; i<10; i++) {
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)) { // data received
A7105_ReadData();
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)){ // data received
v_lipo=packet[13];// hubsan lipo voltage 8bits the real value is h_lipo/10(0x2A=42-4.2V)
telemetry_link=1;
}
A7105_Strobe(A7105_RX);
break;
}
}
}
#endif
delay=1000;
}
if (++txState == 8) { // 3ms + 7*1ms
A7105_SetTxRxMode(TX_EN);
txState = 0;
}
return delay;
}
return 0;
}
uint16_t initHubsan() {
const uint8_t allowed_ch[] = {0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64, 0x6e, 0x78, 0x82};
A7105_Init(INIT_HUBSAN); //hubsan_init();
randomSeed((uint32_t)analogRead(A0) << 10 | analogRead(A4));
sessionid = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
channel = allowed_ch[random(0xfefefefe) % sizeof(allowed_ch)];
BIND_IN_PROGRESS; // autobind protocol
phase = BIND_1;
packet_count=0;
return 10000;
}
#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.
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(KN_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define KN_BIND_COUNT 1000 // for KN 2sec every 2ms - 1000 packets
// Timeout for callback in uSec, 2ms=2000us for KN
#define KN_PACKET_PERIOD 2000
#define KN_PACKET_CHKTIME 100 // Time to wait for packet to be sent (no ACK, so very short)
//#define PAYLOADSIZE 16
#define NFREQCHANNELS 4
#define KN_TXID_SIZE 4
enum {
KN_FLAG_DR = 0x01, // Dual Rate
KN_FLAG_TH = 0x02, // Throttle Hold
KN_FLAG_IDLEUP = 0x04, // Idle up
KN_FLAG_RES1 = 0x08,
KN_FLAG_RES2 = 0x10,
KN_FLAG_RES3 = 0x20,
KN_FLAG_GYRO3 = 0x40, // 00 - 6G mode, 01 - 3G mode
KN_FLAG_GYROR = 0x80 // Always 0 so far
};
//
enum {
KN_INIT2 = 0,
KN_INIT2_NO_BIND,
KN_BIND,
KN_DATA
};
/*enum {
USE1MBPS_NO = 0,
USE1MBPS_YES = 1,
};*/
// 2-bytes CRC
#define CRC_CONFIG (BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO))
void kn_init()
{
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, 0x03); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0); // Disable retransmit
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 0x20); // bytes of data payload for pipe 0
NRF24L01_Activate(0x73); // Activate feature register
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));
NRF24L01_Activate(0x73);
}
uint16_t kn_init2()
{
NRF24L01_FlushTx();
NRF24L01_FlushRx();
packet_sent = 0;
packet_count = 0;
hopping_frequency_no = 0;
// Turn radio power on
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG | BV(NRF24L01_00_PWR_UP));
return 150;
}
void set_tx_for_bind()
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 83);
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps for binding
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *) "KNDZK", 5);
}
void set_tx_for_data()
{
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
}
void kn_calc_fh_channels(uint32_t seed)
{
uint8_t idx = 0;
uint32_t rnd = seed;
while (idx < NFREQCHANNELS) {
uint8_t i;
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 ^ seed) & 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 (i != idx)
continue;
hopping_frequency[idx++] = next_ch;
}
}
void kn_initialize_tx_id()
{
rx_tx_addr[4] = 'K';
kn_calc_fh_channels(MProtocol_id);
}
#define PACKET_COUNT_SHIFT 5
#define RF_CHANNEL_SHIFT 2
void kn_send_packet(uint8_t bind)
{
uint8_t rf_ch;
if (bind) {
rf_ch = 83;
packet[0] = 'K';
packet[1] = 'N';
packet[2] = 'D';
packet[3] = 'Z';
packet[4] = rx_tx_addr[0];
packet[5] = rx_tx_addr[1];
packet[6] = rx_tx_addr[2];
packet[7] = rx_tx_addr[3];
packet[8] = hopping_frequency[0];
packet[9] = hopping_frequency[1];
packet[10] = hopping_frequency[2];
packet[11] = hopping_frequency[3];
packet[12] = 0x00;
packet[13] = 0x00;
packet[14] = 0x00;
packet[15] = 0x01; //mode_bitrate == USE1MBPS_YES ? 0x01 : 0x00;
} else {
rf_ch = hopping_frequency[hopping_frequency_no];
// Each packet is repeated 4 times on the same channel
// We're not strictly repeating them, rather we
// send new packet on the same frequency, so the
// receiver gets the freshest command. As receiver
// hops to a new frequency as soon as valid packet
// received it does not matter that the packet is
// not the same one repeated twice - nobody checks this
// NB! packet_count overflow is handled and used in
// callback.
if (++packet_count == 4)
hopping_frequency_no = (hopping_frequency_no + 1) & 0x03;
uint16_t kn_throttle, kn_rudder, kn_elevator, kn_aileron;
kn_throttle = convert_channel_10b(THROTTLE);
kn_aileron = convert_channel_10b(AILERON);
kn_elevator = convert_channel_10b(ELEVATOR);
kn_rudder = convert_channel_10b(RUDDER);
packet[0] = (kn_throttle >> 8) & 0xFF;
packet[1] = kn_throttle & 0xFF;
packet[2] = (kn_aileron >> 8) & 0xFF;
packet[3] = kn_aileron & 0xFF;
packet[4] = (kn_elevator >> 8) & 0xFF;
packet[5] = kn_elevator & 0xFF;
packet[6] = (kn_rudder >> 8) & 0xFF;
packet[7] = kn_rudder & 0xFF;
// Trims, middle is 0x64 (100) 0-200
packet[8] = 0x64; // T
packet[9] = 0x64; // A
packet[10] = 0x64; // E
packet[11] = 0x64; // R
if (Servo_data[AUX1] > PPM_SWITCH)
flags |= KN_FLAG_DR;
else
flags=0;
if (Servo_data[AUX2] > PPM_SWITCH)
flags |= KN_FLAG_TH;
if (Servo_data[AUX3] > PPM_SWITCH)
flags |= KN_FLAG_IDLEUP;
if (Servo_data[AUX4] > PPM_SWITCH)
flags |= KN_FLAG_GYRO3;
packet[12] = flags;
packet[13] = (packet_count << PACKET_COUNT_SHIFT) | (hopping_frequency_no << RF_CHANNEL_SHIFT);
packet[14] = 0x00;
packet[15] = 0x00;
}
packet_sent = 0;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, 16);
//++total_packets;
packet_sent = 1;
if (! hopping_frequency_no) {
//Keep transmit power updated
NRF24L01_SetPower();
}
}
uint16_t kn_callback()
{
uint16_t timeout = KN_PACKET_PERIOD;
switch (phase)
{
case KN_INIT2:
bind_counter = KN_BIND_COUNT;
timeout = kn_init2();
phase = KN_BIND;
set_tx_for_bind();
break;
case KN_INIT2_NO_BIND:
timeout = kn_init2();
phase = KN_DATA;
set_tx_for_data();
break;
case KN_BIND:
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED)
return KN_PACKET_CHKTIME;
kn_send_packet(1);
if (--bind_counter == 0) {
phase = KN_DATA;
set_tx_for_data();
BIND_DONE;
}
break;
case KN_DATA:
if (packet_count == 4)
packet_count = 0;
else {
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED)
return KN_PACKET_CHKTIME;
kn_send_packet(0);
}
break;
}
return timeout;
}
uint16_t initKN(){
//total_packets = 0;
//mode_bitrate=USE1MBPS_YES;
kn_init();
phase = IS_AUTOBIND_FLAG_on ? KN_INIT2 : KN_INIT2_NO_BIND;
kn_initialize_tx_id();
// Call callback in 50ms
return 50000;
}
#endif

View File

@ -0,0 +1,842 @@
/*********************************************************
Multiprotocol Tx code
by Midelic and Pascal Langer(hpnuts)
http://www.rcgroups.com/forums/showthread.php?t=2165676
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/edit/master/README.md
Thanks to PhracturedBlue
Ported from deviation firmware
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/>.
*/
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "multiprotocol.h"
//******************************************************
// Multiprotocol module configuration starts here
//Uncomment the TX type
#define ER9X
//#define DEVO7
//Uncomment to enable 8 channels serial protocol, 16 otherwise
//#define NUM_SERIAL_CH_8
//Uncomment to enable telemetry
#define TELEMETRY
//Protocols to include in compilation, comment to exclude
#define BAYANG_NRF24L01_INO
#define CG023_NRF24L01_INO
#define CX10_NRF24L01_INO
#define DEVO_CYRF6936_INO
#define DSM2_CYRF6936_INO
#define FLYSKY_A7105_INO
#define FRSKY_CC2500_INO
#define HISKY_NRF24L01_INO
#define HUBSAN_A7105_INO
#define KN_NRF24L01_INO
#define SLT_NRF24L01_INO
#define SYMAX_NRF24L01_INO
#define V2X2_NRF24L01_INO
#define YD717_NRF24L01_INO
//#define FRSKYX_CC2500_INO
//Update this table to set which protocol/sub_protocol is called for the corresponding dial number
static const uint8_t PPM_prot[15][2]= { {MODE_FLYSKY , Flysky }, //Dial=1
{MODE_HUBSAN , 0 }, //Dial=2
{MODE_FRSKY , 0 }, //Dial=3
{MODE_HISKY , Hisky }, //Dial=4
{MODE_V2X2 , 0 }, //Dial=5
{MODE_DSM2 , DSM2 }, //Dial=6
{MODE_DEVO , 0 }, //Dial=7
{MODE_YD717 , YD717 }, //Dial=8
{MODE_KN , 0 }, //Dial=9
{MODE_SYMAX , SYMAX }, //Dial=10
{MODE_SLT , 0 }, //Dial=11
{MODE_CX10 , CX10_BLUE }, //Dial=12
{MODE_CG023 , CG023 }, //Dial=13
{MODE_BAYANG , 0 }, //Dial=14
{MODE_SYMAX , SYMAX5C } //Dial=15
};
//
//TX definitions with timing endpoints and channels order
//
// Turnigy PPM and channels
#if defined(ER9X)
#define PPM_MAX 2140
#define PPM_MIN 860
#define PPM_MAX_100 2012
#define PPM_MIN_100 988
enum chan_order{
AILERON =0,
ELEVATOR,
THROTTLE,
RUDDER,
AUX1,
AUX2,
AUX3,
AUX4,
AUX5,
AUX6,
AUX7,
AUX8
};
#endif
// Devo PPM and channels
#if defined(DEVO7)
#define PPM_MAX 2100
#define PPM_MIN 900
#define PPM_MAX_100 1920
#define PPM_MIN_100 1120
enum chan_order{
ELEVATOR=0,
AILERON,
THROTTLE,
RUDDER,
AUX1,
AUX2,
AUX3,
AUX4,
AUX5,
AUX6,
AUX7,
AUX8
};
#endif
//CC2500 RF module frequency adjustment, use in case you cannot bind with Frsky RX
//Note: this is set via Option when serial protocol is used
//values from 0-127 offset increase frequency, values from 255 to 127 decrease base frequency
//uint8_t fine = 0x00;
uint8_t fine = 0xd7; //* 215=-41 *
// Multiprotocol module configuration ends here
//******************************************************
//Global constants/variables
uint32_t MProtocol_id;//tx id,
uint32_t MProtocol_id_master;
uint32_t Model_fixed_id=0;
uint32_t fixed_id;
uint8_t cyrfmfg_id[6];//for dsm2 and devo
uint32_t blink=0;
//
uint16_t counter;
uint8_t channel;
uint8_t packet[40];
#define NUM_CHN 16
// Servo data
uint16_t Servo_data[NUM_CHN];
// PPM variable
volatile uint16_t PPM_data[NUM_CHN];
// NRF variables
uint8_t rx_tx_addr[5];
uint8_t phase;
uint16_t bind_counter;
uint8_t bind_phase;
uint8_t binding_idx;
uint32_t packet_counter;
uint16_t packet_period;
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;
// Mode_select variables
uint8_t mode_select;
uint8_t protocol_flags;
// Serial variables
#if defined(NUM_SERIAL_CH_8) //8 channels serial protocol
#define RXBUFFER_SIZE 14
#else //16 channels serial protocol
#define RXBUFFER_SIZE 25
#endif
#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;
//Serial protocol
uint8_t sub_protocol;
uint8_t option;
uint8_t cur_protocol[2];
uint8_t prev_protocol=0;
// Telemetry
#if defined(TELEMETRY)
uint8_t pkt[27];//telemetry receiving packets
uint8_t pktt[27];//telemetry receiving packets
volatile uint8_t tx_head;
volatile uint8_t tx_tail;
uint8_t v_lipo;
int16_t RSSI_dBm;
//const uint8_t RSSI_offset=72;//69 71.72 values db
uint8_t telemetry_link=0;
#include "telemetry.h"
#endif
// Callback
typedef uint16_t (*void_function_t) (void);//pointer to a function with no parameters which return an uint16_t integer
void_function_t remote_callback = 0;
void CheckTimer(uint16_t (*cb)(void));
// Init
void setup()
{
// General pinout
DDRD = (1<<CS_pin)|(1<<SDI_pin)|(1<<SCLK_pin)|(1<<CS_pin)|(1<< CC25_CSN_pin);
DDRC = (1<<CTRL1)|(1<<CTRL2); //output
//DDRC |= (1<<5);//RST pin A5(C5) CYRF output
DDRB = _BV(0)|_BV(1);
PORTB = _BV(2)|_BV(3)|_BV(4)|_BV(5);//pullup 10,11,12 and bind button
PORTC = _BV(0);//A0 high pullup
// Set Chip selects
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
// Set servos positions
for(uint8_t i=0;i<NUM_CHN;i++)
Servo_data[i]=1500;
Servo_data[THROTTLE]=PPM_MIN_100;
memcpy((void *)PPM_data,Servo_data, sizeof(Servo_data));
// 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=14; // here to test PPM
//**********************************
// Update LED
LED_OFF;
LED_SET_OUTPUT;
// Read or create protocol id
MProtocol_id=random_id(10,false);
MProtocol_id_master=MProtocol_id;
//Set power transmission flags
POWER_FLAG_on; //By default high power for everything
//Protocol and interrupts initialization
if(mode_select != MODE_SERIAL)
{ // PPM
cur_protocol[0]= PPM_prot[mode_select-1][0];
sub_protocol = PPM_prot[mode_select-1][1];
protocol_init(cur_protocol[0]);
//Configure PPM interrupt
EICRA |=(1<<ISC11); // The rising edge of INT1 pin D3 generates an interrupt request
EIMSK |= (1<<INT1); // INT1 interrupt enable
}
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(mode_select==MODE_SERIAL && IS_RX_FLAG_on) // Serial mode and something has been received
{
update_serial_data(); // Update protocol and data
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(cur_protocol[0]&0x1F); //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
}
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))
frskyUpdate();
#endif
if (remote_callback != 0)
CheckTimer(remote_callback);
}
// Update led status based on binding and serial
void update_led_status(void)
{
if(cur_protocol[0]==0)
{ // serial without valid protocol
if(blink<millis())
{
LED_TOGGLE;
blink+=BLINK_SERIAL_TIME; //blink slowly while waiting a valid serial input
}
}
else
if(remote_callback == 0)
LED_OFF;
else
if(IS_BIND_DONE_on)
LED_ON; //bind completed -> led on
else
if(blink<millis())
{
LED_TOGGLE;
blink+=BLINK_BIND_TIME; //blink fastly during binding
}
}
// Protocol scheduler
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
}
}
void protocol_init(uint8_t protocol)
{
uint16_t next_callback=100; // Default is immediate call back
remote_callback = 0;
set_rx_tx_addr(MProtocol_id);
blink=millis()+BLINK_BIND_TIME;
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
else
BIND_DONE;
CTRL1_on; //antenna RF3 by default
CTRL2_off; //antenna RF3 by default
switch(protocol) // Init the requested protocol
{
#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(next_callback>32000)
{ // next_callback should not be more than 32767 so we will wait here...
delayMicroseconds(next_callback-2000);
next_callback=2000;
}
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
}
void update_serial_data()
{
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];
fine=option; // Update FrSky fine tuning
if( ((rx_ok_buff[0]&0x5F) != (cur_protocol[0]&0x5F)) || ( (rx_ok_buff[1]&0x7F) != cur_protocol[1] ) )
{ // New model has been selected
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
MProtocol_id=MProtocol_id_master+(rx_ok_buff[1]& 0x0F); //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
#if defined(NUM_SERIAL_CH_8) //8 channels serial protocol
Servo_data[0]=rx_ok_buff[3]+((rx_ok_buff[11]&0xC0)<<2);
Servo_data[1]=rx_ok_buff[4]+((rx_ok_buff[11]&0x30)<<4);
Servo_data[2]=rx_ok_buff[5]+((rx_ok_buff[11]&0x0C)<<6);
Servo_data[3]=rx_ok_buff[6]+((rx_ok_buff[11]&0x03)<<8);
Servo_data[4]=rx_ok_buff[7]+((rx_ok_buff[12]&0xC0)<<2);
Servo_data[5]=rx_ok_buff[8]+((rx_ok_buff[12]&0x30)<<4);
Servo_data[6]=rx_ok_buff[9]+((rx_ok_buff[12]&0x0C)<<6);
Servo_data[7]=rx_ok_buff[10]+((rx_ok_buff[12]&0x03)<<8);
for(uint8_t i=0;i<8;i++)
Servo_data[i]=((Servo_data[i]*5)>>2)+860; //range 860-2140;
#else //16 channels serial protocol
volatile uint8_t *p=rx_ok_buff+2;
uint8_t dec=-3;
for(uint8_t i=0;i<NUM_CHN;i++)
{
dec+=3;
if(dec>=8)
{
dec-=8;
p++;
}
p++;
Servo_data[i]=((((*((uint32_t *)p))>>dec)&0x7FF)*5)/8+860; //value range 860<->2140 -125%<->+125%
}
#endif
RX_FLAG_off; //data has been processed
}
void module_reset()
{
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_HISKY:
case MODE_V2X2:
case MODE_YD717:
case MODE_KN:
case MODE_SYMAX:
case MODE_SLT:
case MODE_CX10:
NRF24L01_Reset();
break;
case MODE_DSM2:
case MODE_DEVO:
CYRF_Reset();
break;
}
}
// Channel value is converted to 8bit values full scale
uint8_t convert_channel_8b(uint8_t num)
{
return (uint8_t) (map(limit_channel_100(num),PPM_MIN_100,PPM_MAX_100,0,255));
}
// Channel value is converted to 8bit values to provided values scale
uint8_t convert_channel_8b_scale(uint8_t num,uint8_t min,uint8_t max)
{
return (uint8_t) (map(limit_channel_100(num),PPM_MIN_100,PPM_MAX_100,min,max));
}
// Channel value is converted sign + magnitude 8bit values
uint8_t convert_channel_s8b(uint8_t num)
{
uint8_t ch;
ch = convert_channel_8b(num);
return (ch < 128 ? 127-ch : ch);
}
// Channel value is converted to 10bit values
uint16_t convert_channel_10b(uint8_t num)
{
return (uint16_t) (map(limit_channel_100(num),PPM_MIN_100,PPM_MAX_100,1,1023));
}
// Channel value is multiplied by 1.5
uint16_t convert_channel_frsky(uint8_t num)
{
return Servo_data[num] + Servo_data[num]/2;
}
// Channel value is converted for HK310
void convert_channel_HK310(uint8_t num, uint8_t *low, uint8_t *high)
{
uint16_t temp=0xFFFF-(4*Servo_data[num])/3;
*low=(uint8_t)(temp&0xFF);
*high=(uint8_t)(temp>>8);
}
// Channel value is limited to PPM_100
uint16_t limit_channel_100(uint8_t ch)
{
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];
}
// Convert 32b id to rx_tx_addr
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
}
#if defined(TELEMETRY)
void Serial_write(uint8_t data)
{
uint8_t t=tx_head;
if(++t>=TXBUFFER_SIZE)
t=0;
tx_buff[t]=data;
tx_head=t;
UCSR0B |= (1<<UDRIE0);//enable UDRE interrupt
}
#endif
void Mprotocol_serial_init()
{
#if defined(NUM_SERIAL_CH_8) //8 channels serial protocol
#define BAUD 125000
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Set frame format to 8 data bits, no parity, 1 stop bit
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
#else //16 channels serial protocol
#define BAUD 100000
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Set frame format to 8 data bits, even parity, 2 stop bits
UCSR0C |= (1<<UPM01)|(1<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);
#endif
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
}
static uint32_t random_id(uint16_t adress, uint8_t create_new)
{
uint32_t id;
uint8_t txid[4];
if (eeprom_read_byte((uint8_t*)(adress+10))==0xf0 && !create_new)
{ // TXID exists in EEPROM
eeprom_read_block((void*)txid,(const void*)adress,4);
id=(txid[0] | ((uint32_t)txid[1]<<8) | ((uint32_t)txid[2]<<16) | ((uint32_t)txid[3]<<24));
}
else
{ // if not generate a random ID
randomSeed((uint32_t)analogRead(A6)<<10|analogRead(A7));//seed
//
id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
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*)adress,4);
eeprom_write_byte((uint8_t*)(adress+10),0xf0);//write bind flag in eeprom.
}
return id;
}
/**************************/
/**************************/
/** Interrupt routines **/
/**************************/
/**************************/
ISR(INT1_vect)
{ // Interrupt on PPM pin
static int8_t chan=-1;
static uint16_t Prev_TCNT1=0;
uint16_t Cur_TCNT1;
Cur_TCNT1=TCNT1-Prev_TCNT1; // Capture current Timer1 value
if(Cur_TCNT1<1000)
chan=-1; // bad frame
else
if(Cur_TCNT1>4840)
{
chan=0; // start of frame
PPM_FLAG_on; // full frame present (even at startup since PPM_data has been initialized)
}
else
if(chan!=-1) // need to wait for start of frame
{ //servo values between 500us and 2420us will end up here
PPM_data[chan] = Cur_TCNT1/2;
if(PPM_data[chan]<PPM_MIN) PPM_data[chan]=PPM_MIN;
else if(PPM_data[chan]>PPM_MAX) PPM_data[chan]=PPM_MAX;
if(chan++>=NUM_CHN)
chan=-1; // don't accept any new channels
}
Prev_TCNT1+=Cur_TCNT1;
}
#if defined(TELEMETRY)
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
}
#endif
#if defined(NUM_SERIAL_CH_8) //8 channels serial protocol
ISR(USART_RX_vect)
{ // RX interrupt
static uint16_t crc = 0;
if(idx==0)
{ // Let's try to sync at this point
OCR1B=TCNT1+5000L; // timer for 2500us
TIFR1=(1<<OCF1B); // clear OCR1B match flag
TIMSK1 |=(1<<OCIE1B); // enable interrupt on compare B match
crc=0;
}
if(idx<RXBUFFER_SIZE-1)
{ // Store bytes in buffer and calculate crc as we go
rx_buff[idx]=UDR0;
crc = (crc<<8) ^ pgm_read_word(&CRCTable[((uint8_t)(crc>>8) ^ rx_buff[idx++]) & 0xFF]);
}
else
{ // A frame has been received and needs to be checked before giving it to main
TIMSK1 &=~(1<<OCIE1B); // disable interrupt on compare B match
if(UDR0==(uint8_t)(crc & 0xFF) && !IS_RX_FLAG_on)
{ //Good frame received and main has finished with previous buffer
for(idx=0;idx<RXBUFFER_SIZE;idx++)
rx_ok_buff[idx]=rx_buff[idx]; // Duplicate the buffer
RX_FLAG_on; //flag for main to process servo data
LED_ON;
}
idx=0;
}
}
#else //16 channels serial protocol
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
{
idx++;
OCR1B=TCNT1+6500L; // Full message should be received within timer of 3250us
TIFR1=(1<<OCF1B); // clear OCR1B match flag
TIMSK1 |=(1<<OCIE1B); // enable interrupt on compare B match
}
}
else
{
rx_buff[(idx++)-1]=UDR0; // Store received byte
if(idx>RXBUFFER_SIZE)
{ // A full frame has been received
TIMSK1 &=~(1<<OCIE1B); // disable interrupt on compare B match
if(!IS_RX_FLAG_on)
{ //Good frame received and main has finished with previous buffer
for(idx=0;idx<RXBUFFER_SIZE;idx++)
rx_ok_buff[idx]=rx_buff[idx]; // Duplicate the buffer
RX_FLAG_on; //flag for main to process servo data
}
idx=0; // start again
}
}
}
else
{
idx=UDR0; // Dummy read
idx=0; // Error encountered discard full frame...
}
}
#endif
ISR(TIMER1_COMPB_vect)
{ // Timer1 compare B interrupt
idx=0;
}

View File

@ -0,0 +1,418 @@
/*
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/>.
*/
//---------------------------
// AVR nrf chip bitbang SPI functions
//---------------------------
#include "iface_nrf24l01.h"
void nrf_spi_write(uint8_t command)
{
uint8_t n=8;
SCK_off;//SCK start low
SDI_off;
while(n--) {
if(command&0x80)
SDI_on;
else
SDI_off;
SCK_on;
NOP();
SCK_off;
command = command << 1;
}
SDI_on;
}
//VARIANT 2
uint8_t nrf_spi_read(void)
{
uint8_t result;
uint8_t i;
result=0;
for(i=0;i<8;i++) {
result<<=1;
if(SDO_1) ///
result|=0x01;
SCK_on;
NOP();
SCK_off;
NOP();
}
return result;
}
//--------------------------------------------
//---------------------------
// NRF24L01+ SPI Specific Functions
//---------------------------
uint8_t rf_setup;
void NRF24L01_Initialize()
{
rf_setup = 0x09;
}
void NRF24L01_WriteReg(uint8_t reg, uint8_t data)
{
NRF_CSN_off;
nrf_spi_write(W_REGISTER | (REGISTER_MASK & reg));
nrf_spi_write(data);
NRF_CSN_on;
}
void NRF24L01_WriteRegisterMulti(uint8_t reg, uint8_t * data, uint8_t length)
{
NRF_CSN_off;
nrf_spi_write(W_REGISTER | ( REGISTER_MASK & reg));
for (uint8_t i = 0; i < length; i++)
nrf_spi_write(data[i]);
NRF_CSN_on;
}
void NRF24L01_WritePayload(uint8_t * data, uint8_t length)
{
NRF_CSN_off;
nrf_spi_write(W_TX_PAYLOAD);
for (uint8_t i = 0; i < length; i++)
nrf_spi_write(data[i]);
NRF_CSN_on;
}
uint8_t NRF24L01_ReadReg(uint8_t reg)
{
NRF_CSN_off;
nrf_spi_write(R_REGISTER | (REGISTER_MASK & reg));
uint8_t data = nrf_spi_read();
NRF_CSN_on;
return data;
}
void NRF24L01_ReadRegisterMulti(uint8_t reg, uint8_t * data, uint8_t length)
{
NRF_CSN_off;
nrf_spi_write(R_REGISTER | (REGISTER_MASK & reg));
for(uint8_t i = 0; i < length; i++)
data[i] = nrf_spi_read();
NRF_CSN_on;
}
void NRF24L01_ReadPayload(uint8_t * data, uint8_t length)
{
NRF_CSN_off;
nrf_spi_write(R_RX_PAYLOAD);
for(uint8_t i = 0; i < length; i++)
data[i] = nrf_spi_read();
NRF_CSN_on;
}
void NRF24L01_Strobe(uint8_t state)
{
NRF_CSN_off;
nrf_spi_write(state);
NRF_CSN_on;
}
void NRF24L01_FlushTx()
{
NRF24L01_Strobe(FLUSH_TX);
}
void NRF24L01_FlushRx()
{
NRF24L01_Strobe(FLUSH_RX);
}
void NRF24L01_Activate(uint8_t code)
{
NRF_CSN_off;
nrf_spi_write(ACTIVATE);
nrf_spi_write(code);
NRF_CSN_on;
}
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.
// Bit 0 goes to RF_DR_HIGH, bit 1 - to RF_DR_LOW
rf_setup = (rf_setup & 0xD7) | ((bitrate & 0x02) << 4) | ((bitrate & 0x01) << 3);
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
}
void NRF24L01_SetPower_Value(uint8_t power)
{
uint8_t nrf_power = 0;
switch(power) {
case TXPOWER_100uW: nrf_power = 0; break;
case TXPOWER_300uW: nrf_power = 0; break;
case TXPOWER_1mW: nrf_power = 0; break;
case TXPOWER_3mW: nrf_power = 1; break;
case TXPOWER_10mW: nrf_power = 1; break;
case TXPOWER_30mW: nrf_power = 2; break;
case TXPOWER_100mW: nrf_power = 3; break;
case TXPOWER_150mW: nrf_power = 3; break;
default: nrf_power = 0; break;
};
// Power is in range 0..3 for nRF24L01
rf_setup = (rf_setup & 0xF9) | ((nrf_power & 0x03) << 1);
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
}
void NRF24L01_SetPower()
{
uint8_t power=NRF_BIND_POWER;
if(IS_BIND_DONE_on)
power=IS_POWER_FLAG_on?NRF_HIGH_POWER:NRF_LOW_POWER;
else
if(IS_RANGE_FLAG_on)
power=NRF_POWER_0;
rf_setup = (rf_setup & 0xF9) | (power << 1);
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
}
void NRF24L01_SetTxRxMode(enum TXRX_State mode)
{
if(mode == TX_EN) {
NRF_CSN_off;
NRF24L01_WriteReg(NRF24L01_07_STATUS, (1 << NRF24L01_07_RX_DR) //reset the flag(s)
| (1 << NRF24L01_07_TX_DS)
| (1 << NRF24L01_07_MAX_RT));
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_EN_CRC) // switch to TX mode
| (1 << NRF24L01_00_CRCO)
| (1 << NRF24L01_00_PWR_UP));
_delay_us(130);
NRF_CSN_on;
}
else
if (mode == RX_EN) {
NRF_CSN_off;
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // reset the flag(s)
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F); // switch to RX mode
NRF24L01_WriteReg(NRF24L01_07_STATUS, (1 << NRF24L01_07_RX_DR) //reset the flag(s)
| (1 << NRF24L01_07_TX_DS)
| (1 << NRF24L01_07_MAX_RT));
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_EN_CRC) // switch to RX mode
| (1 << NRF24L01_00_CRCO)
| (1 << NRF24L01_00_PWR_UP)
| (1 << NRF24L01_00_PRIM_RX));
_delay_us(130);
NRF_CSN_on;
}
else
{
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_EN_CRC)); //PowerDown
NRF_CSN_off;
}
}
void NRF24L01_Reset()
{
//** not in deviation but needed to hot switch between models
NRF24L01_Activate(0x73); // Activate feature register
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x00); // Set feature bits off
NRF24L01_Activate(0x73);
//**
NRF24L01_FlushTx();
NRF24L01_FlushRx();
NRF24L01_Strobe(0xff); // NOP
NRF24L01_ReadReg(0x07);
NRF24L01_SetTxRxMode(TXRX_OFF);
_delay_us(100);
}
uint8_t NRF24L01_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;
}
//---------------------------
/*
void NRF24L01_spi_test(void)
{
unsigned long errors = 0;
unsigned long test = 0;
unsigned long time;
uint8_t test_data_r[5];
uint8_t test_data_w[5] = {0x01,0x02,0x03,0x04,0x05};
time = micros();
Serial.println("Testing SPI");
for(test=0; test < 2775600 ; test++) // should run for X mins.
{
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, test_data_w, 5);
NRF24L01_ReadRegisterMulti(NRF24L01_0B_RX_ADDR_P1, test_data_r, 5);
if(0 != memcmp(test_data_r, test_data_w, sizeof(test_data_r))) errors++;
test_data_w[0] ++;
test_data_w[1] ++;
test_data_w[2] ++;
test_data_w[3] ++;
test_data_w[4] ++;
}
Serial.print("test "); Serial.print(test, HEX); Serial.print("\n");
Serial.print("errors "); Serial.print(errors, HEX); Serial.print("\n");
Serial.print("time "); Serial.print(micros()- time, DEC); Serial.print("\n");
// 124211960
// 90899216
}
*/
//---------------------------
///////////////
// XN297 emulation layer
uint8_t xn297_addr_len;
uint8_t xn297_tx_addr[5];
uint8_t xn297_rx_addr[5];
uint8_t xn297_crc = 0;
static const uint8_t xn297_scramble[] = {
0xe3, 0xb1, 0x4b, 0xea, 0x85, 0xbc, 0xe5, 0x66,
0x0d, 0xae, 0x8c, 0x88, 0x12, 0x69, 0xee, 0x1f,
0xc7, 0x62, 0x97, 0xd5, 0x0b, 0x79, 0xca, 0xcc,
0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f,
0x8e, 0xc5, 0x2f};
static const uint16_t xn297_crc_xorout[] = {
0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, // 1st entry is missing, probably never needed
0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x8148, // it's used for 3-byte address w/ 0 byte payload only
0xD461, 0xF494, 0x2503, 0x691D, 0xFE8B, 0x9BA7,
0x8B17, 0x2920, 0x8B5F, 0x61B1, 0xD391, 0x7401,
0x2138, 0x129F, 0xB3A0, 0x2988};
uint8_t bit_reverse(uint8_t b_in)
{
uint8_t b_out = 0;
for (uint8_t i = 0; i < 8; ++i)
{
b_out = (b_out << 1) | (b_in & 1);
b_in >>= 1;
}
return b_out;
}
uint16_t crc16_update(uint16_t crc, uint8_t a)
{
static const uint16_t polynomial = 0x1021;
crc ^= a << 8;
for (uint8_t i = 0; i < 8; ++i)
if (crc & 0x8000)
crc = (crc << 1) ^ polynomial;
else
crc = crc << 1;
return crc;
}
void XN297_SetTXAddr(const uint8_t* addr, uint8_t len)
{
if (len > 5) len = 5;
if (len < 3) len = 3;
uint8_t buf[] = { 0x55, 0x0F, 0x71, 0x0C, 0x00 }; // bytes for XN297 preamble 0xC710F55 (28 bit)
xn297_addr_len = len;
if (xn297_addr_len < 4)
for (uint8_t i = 0; i < 4; ++i)
buf[i] = buf[i+1];
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, buf, 5);
// Receive address is complicated. We need to use scrambled actual address as a receive address
// but the TX code now assumes fixed 4-byte transmit address for preamble. We need to adjust it
// first. Also, if the scrambled address begins with 1 nRF24 will look for preamble byte 0xAA
// instead of 0x55 to ensure enough 0-1 transitions to tune the receiver. Still need to experiment
// with receiving signals.
memcpy(xn297_tx_addr, addr, len);
}
void XN297_SetRXAddr(const uint8_t* addr, uint8_t len)
{
if (len > 5) len = 5;
if (len < 3) len = 3;
uint8_t buf[] = { 0, 0, 0, 0, 0 };
memcpy(buf, addr, len);
memcpy(xn297_rx_addr, addr, len);
for (uint8_t i = 0; i < xn297_addr_len; ++i)
buf[i] = xn297_rx_addr[i] ^ xn297_scramble[xn297_addr_len-i-1];
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, buf, 5);
}
void XN297_Configure(uint8_t flags)
{
xn297_crc = !!(flags & BV(NRF24L01_00_EN_CRC));
flags &= ~(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags);
}
void XN297_WritePayload(uint8_t* msg, uint8_t len)
{
uint8_t buf[32];
uint8_t last = 0;
if (xn297_addr_len < 4)
{
// If address length (which is defined by receive address length)
// is less than 4 the TX address can't fit the preamble, so the last
// byte goes here
buf[last++] = 0x55;
}
for (uint8_t i = 0; i < xn297_addr_len; ++i)
buf[last++] = xn297_tx_addr[xn297_addr_len-i-1] ^ xn297_scramble[i];
for (uint8_t i = 0; i < len; ++i) {
// bit-reverse bytes in packet
uint8_t b_out = bit_reverse(msg[i]);
buf[last++] = b_out ^ xn297_scramble[xn297_addr_len+i];
}
if (xn297_crc)
{
uint8_t offset = xn297_addr_len < 4 ? 1 : 0;
uint16_t crc = 0xb5d2;
for (uint8_t i = offset; i < last; ++i)
crc = crc16_update(crc, buf[i]);
crc ^= xn297_crc_xorout[xn297_addr_len - 3 + len];
buf[last++] = crc >> 8;
buf[last++] = crc & 0xff;
}
NRF24L01_WritePayload(buf, last);
}
void XN297_ReadPayload(uint8_t* msg, uint8_t len)
{
NRF24L01_ReadPayload(msg, len);
for(uint8_t i=0; i<len; i++)
msg[i] = bit_reverse(msg[i]) ^ bit_reverse(xn297_scramble[i+xn297_addr_len]);
}
// End of XN297 emulation

View File

@ -0,0 +1,203 @@
/*
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(SLT_NRF24L01_INO)
#include "iface_nrf24l01.h"
// For code readability
#define SLT_PAYLOADSIZE 7
#define SLT_NFREQCHANNELS 15
#define SLT_TXID_SIZE 4
enum {
SLT_INIT2 = 0,
SLT_BIND,
SLT_DATA1,
SLT_DATA2,
SLT_DATA3
};
void SLT_init()
{
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO)); // 2-bytes CRC, radio off
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, 0x02); // 4-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // Disable auto retransmit
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 4); // bytes of data payload for pipe 1
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 256kbps
NRF24L01_SetPower();
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"\xC3\xC3\xAA\x55", 4);
NRF24L01_FlushRx();
}
static void SLT_init2()
{
NRF24L01_FlushTx();
packet_sent = 0;
hopping_frequency_no = 0;
// Turn radio power on
NRF24L01_SetTxRxMode(TX_EN);
}
void SLT_set_tx_id(void)
{
// Frequency hopping sequence generation
for (uint8_t i = 0; i < 4; ++i)
{
uint8_t next_i = (i+1) % 4; // is & 3 better than % 4 ?
uint8_t base = i < 2 ? 0x03 : 0x10;
hopping_frequency[i*4 + 0] = (rx_tx_addr[i] & 0x3f) + base;
hopping_frequency[i*4 + 1] = (rx_tx_addr[i] >> 2) + base;
hopping_frequency[i*4 + 2] = (rx_tx_addr[i] >> 4) + (rx_tx_addr[next_i] & 0x03)*0x10 + base;
if (i*4 + 3 < SLT_NFREQCHANNELS) // guard for 16 channel
hopping_frequency[i*4 + 3] = (rx_tx_addr[i] >> 6) + (rx_tx_addr[next_i] & 0x0f)*0x04 + base;
}
// unique
uint8_t done = 0;
for (uint8_t i = 0; i < SLT_NFREQCHANNELS; ++i)
while (!done)
{
done = 1;
for (uint8_t j = 0; j < i; ++j)
if (hopping_frequency[i] == hopping_frequency[j])
{
done = 0;
hopping_frequency[i] += 7;
if (hopping_frequency[i] >= 0x50)
hopping_frequency[i] = hopping_frequency[i] - 0x50 + 0x03;
}
}
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 4);
}
void wait_radio()
{
if (packet_sent)
while (!(NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_TX_DS))) ;
packet_sent = 0;
}
void send_data(uint8_t *data, uint8_t len)
{
wait_radio();
NRF24L01_FlushTx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_RX_DR) | BV(NRF24L01_07_MAX_RT));
NRF24L01_WritePayload(data, len);
//NRF24L01_PulseCE();
packet_sent = 1;
}
void SLT_build_packet()
{
// aileron, elevator, throttle, rudder, gear, pitch
uint8_t e = 0; // byte where extension 2 bits for every 10-bit channel are packed
uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER};
for (uint8_t i = 0; i < 4; ++i) {
uint16_t v = convert_channel_10b(ch[i]);
packet[i] = v;
e = (e >> 2) | (uint8_t) ((v >> 2) & 0xC0);
}
// Extra bits for AETR
packet[4] = e;
// 8-bit channels
packet[5] = convert_channel_8b(AUX1);
packet[6] = convert_channel_8b(AUX2);
// Set radio channel - once per packet batch
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
if (++hopping_frequency_no >= SLT_NFREQCHANNELS)
hopping_frequency_no = 0;
}
static void send_bind_packet()
{
wait_radio();
NRF24L01_SetPower();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x7E\xB8\x63\xA9", 4);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x50);
send_data(rx_tx_addr, 4);
// NB: we should wait until the packet's sent before changing TX address!
wait_radio();
NRF24L01_SetPower();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 4);
}
uint16_t SLT_callback()
{
uint16_t delay_us = 20000; // 3 packets with 1ms intervals every 22ms
switch (phase)
{
case SLT_INIT2:
SLT_init2();
phase = SLT_BIND;
delay_us = 150;
break;
case SLT_BIND:
send_bind_packet();
phase = SLT_DATA1;
delay_us = 19000;
BIND_DONE;
break;
case SLT_DATA1:
SLT_build_packet();
send_data(packet, 7);
phase = SLT_DATA2;
delay_us = 1000;
break;
case SLT_DATA2:
send_data(packet, 7);
phase = SLT_DATA3;
delay_us = 1000;
break;
case SLT_DATA3:
send_data(packet, 7);
if (++counter >= 100)
{
counter = 0;
phase = SLT_BIND;
delay_us = 1000;
}
else
{
NRF24L01_SetPower(); // Set tx_power
phase = SLT_DATA1;
}
break;
}
return delay_us;
}
uint16_t initSLT()
{
counter = 0;
SLT_init();
phase = SLT_INIT2;
SLT_set_tx_id();
BIND_IN_PROGRESS; // autobind protocol
return 50000;
}
#endif

View File

@ -0,0 +1,378 @@
/*
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(SYMAX_NRF24L01_INO)
#include "iface_nrf24l01.h"
/***
Main protocol compatible with Syma X5C-1, X11, X11C, X12.
SymaX5C protocol option compatible with Syma X5C (original) and X2.
***/
#define SYMAX_BIND_COUNT 345 // 1.5 seconds
#define SYMAX_FIRST_PACKET_DELAY 12000
#define SYMAX_PACKET_PERIOD 4000 // Timeout for callback in uSec
#define SYMAX_INITIAL_WAIT 500
#define SYMAX_MAX_RF_CHANNELS 17
#define SYMAX_FLAG_FLIP 0x01
#define SYMAX_FLAG_VIDEO 0x02
#define SYMAX_FLAG_PICTURE 0x04
#define SYMAX_FLAG_HEADLESS 0x08
#define SYMAX_PAYLOADSIZE 10 // receive data pipes set to this size, but unused
#define SYMAX_MAX_PACKET_LENGTH 16 // X11,X12,X5C-1 10-byte, X5C 16-byte
enum {
SYMAX_INIT1 = 0,
SYMAX_BIND2,
SYMAX_BIND3,
SYMAX_DATA
};
/*
http://www.deviationtx.com/forum/protocol-development/3768-syma-x5c-1-x11-x12?start=140
TX address Channel Sequence
S1 3B B6 00 00 A2 15 35 1D 3D
D1 9A E9 02 00 A2 14 34 1C 3C
D2 46 18 00 00 A2 11 21 31 41
*/
uint8_t SYMAX_checksum(uint8_t *data)
{
uint8_t sum = data[0];
for (uint8_t i=1; i < packet_length-1; i++)
if ( sub_protocol==SYMAX5C )
sum += data[i];
else
sum ^= data[i];
return sum + ( sub_protocol==SYMAX5C ? 0 : 0x55 );
}
void SYMAX_read_controls()
{
// Protocol is registered AETRF, that is
// Aileron is channel 1, Elevator - 2, Throttle - 3, Rudder - 4, Flip control - 5
aileron = convert_channel_s8b(AILERON);
elevator = convert_channel_s8b(ELEVATOR);
throttle = convert_channel_8b(THROTTLE);
rudder = convert_channel_s8b(RUDDER);
// Channel 5
if (Servo_data[AUX1] > PPM_SWITCH)
flags = SYMAX_FLAG_FLIP;
else
flags=0;
// Channel 7
if (Servo_data[AUX3] > PPM_SWITCH)
flags |= SYMAX_FLAG_PICTURE;
// Channel 8
if (Servo_data[AUX4] > PPM_SWITCH)
flags |= SYMAX_FLAG_VIDEO;
// Channel 9
if (Servo_data[AUX5] > PPM_SWITCH)
flags |= SYMAX_FLAG_HEADLESS;
}
#define X5C_CHAN2TRIM(X) ((((X) & 0x80 ? 0xff - (X) : 0x80 + (X)) >> 2) + 0x20)
void SYMAX_build_packet_x5c(uint8_t bind)
{
if (bind)
{
memset(packet, 0, packet_length);
packet[7] = 0xae;
packet[8] = 0xa9;
packet[14] = 0xc0;
packet[15] = 0x17;
}
else
{
SYMAX_read_controls();
packet[0] = throttle;
packet[1] = rudder;
packet[2] = elevator ^ 0x80; // reversed from default
packet[3] = aileron;
packet[4] = X5C_CHAN2TRIM(rudder ^ 0x80);// drive trims for extra control range
packet[5] = X5C_CHAN2TRIM(elevator);
packet[6] = X5C_CHAN2TRIM(aileron ^ 0x80);
packet[7] = 0xae;
packet[8] = 0xa9;
packet[9] = 0x00;
packet[10] = 0x00;
packet[11] = 0x00;
packet[12] = 0x00;
packet[13] = 0x00;
packet[14] = (flags & SYMAX_FLAG_VIDEO ? 0x10 : 0x00)
| (flags & SYMAX_FLAG_PICTURE ? 0x08 : 0x00)
| (flags & SYMAX_FLAG_FLIP ? 0x01 : 0x00)
| 0x04;// (flags & SYMAX_FLAG_RATES ? 0x04 : 0x00);
packet[15] = SYMAX_checksum(packet);
}
}
void SYMAX_build_packet(uint8_t bind)
{
if (bind)
{
packet[0] = rx_tx_addr[4];
packet[1] = rx_tx_addr[3];
packet[2] = rx_tx_addr[2];
packet[3] = rx_tx_addr[1];
packet[4] = rx_tx_addr[0];
packet[5] = 0xaa;
packet[6] = 0xaa;
packet[7] = 0xaa;
packet[8] = 0x00;
}
else
{
SYMAX_read_controls();
packet[0] = throttle;
packet[1] = elevator;
packet[2] = rudder;
packet[3] = aileron;
packet[4] = (flags & SYMAX_FLAG_VIDEO ? 0x80 : 0x00) | (flags & SYMAX_FLAG_PICTURE ? 0x40 : 0x00);
packet[5] = (elevator >> 2) | 0xc0; //always high rates (bit 7 is rate control) (flags & SYMAX_FLAG_RATES ? 0x80 : 0x00) | 0x40; // use trims to extend controls
packet[6] = (rudder >> 2) | (flags & SYMAX_FLAG_FLIP ? 0x40 : 0x00);
packet[7] = (aileron >> 2) | (flags & SYMAX_FLAG_HEADLESS ? 0x80 : 0x00);
packet[8] = 0x00;
}
packet[9] = SYMAX_checksum(packet);
}
void SYMAX_send_packet(uint8_t bind)
{
if (sub_protocol==SYMAX5C)
SYMAX_build_packet_x5c(bind);
else
SYMAX_build_packet(bind);
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x2e);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, packet_length);
if (packet_counter++ % 2) // use each channel twice
hopping_frequency_no = (hopping_frequency_no + 1) % rf_ch_num;
NRF24L01_SetPower(); // Set tx_power
}
static void symax_init()
{
NRF24L01_Initialize();
//
NRF24L01_SetTxRxMode(TX_EN);
//
NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3F); // Enable all data pipes (even though not used?)
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xff); // 4mS retransmit t/o, 15 tries (retries w/o AA?)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x08);
if (sub_protocol==SYMAX5C)
{
NRF24L01_SetBitrate(NRF24L01_BR_1M);
packet_length = 16;
}
else
{
NRF24L01_SetBitrate(NRF24L01_BR_250K);
packet_length = 10;
}
//
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_09_CD, 0x00);
NRF24L01_WriteReg(NRF24L01_0C_RX_ADDR_P2, 0xC3); // LSB byte of pipe 2 receive address
NRF24L01_WriteReg(NRF24L01_0D_RX_ADDR_P3, 0xC4);
NRF24L01_WriteReg(NRF24L01_0E_RX_ADDR_P4, 0xC5);
NRF24L01_WriteReg(NRF24L01_0F_RX_ADDR_P5, 0xC6);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, SYMAX_PAYLOADSIZE); // bytes of data payload for pipe 1
NRF24L01_WriteReg(NRF24L01_12_RX_PW_P1, SYMAX_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_13_RX_PW_P2, SYMAX_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_14_RX_PW_P3, SYMAX_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_15_RX_PW_P4, SYMAX_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_16_RX_PW_P5, SYMAX_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR , sub_protocol==SYMAX5C ? (uint8_t *)"\x6D\x6A\x73\x73\x73" : (uint8_t *)"\xAB\xAC\xAD\xAE\xAF" ,5);
NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_FlushTx();
NRF24L01_ReadReg(NRF24L01_07_STATUS);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x0e);
NRF24L01_ReadReg(NRF24L01_00_CONFIG);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0c);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0e); // power on
}
void symax_init1()
{
// duplicate stock tx sending strange packet (effect unknown)
uint8_t first_packet[] = {0xf9, 0x96, 0x82, 0x1b, 0x20, 0x08, 0x08, 0xf2, 0x7d, 0xef, 0xff, 0x00, 0x00, 0x00, 0x00};
uint8_t chans_bind[] = {0x4b, 0x30, 0x40, 0x20};
uint8_t chans_bind_x5c[] = {0x27, 0x1b, 0x39, 0x28, 0x24, 0x22, 0x2e, 0x36,
0x19, 0x21, 0x29, 0x14, 0x1e, 0x12, 0x2d, 0x18};
//uint8_t data_rx_tx_addr[] = {0x3b,0xb6,0x00,0x00,0xa2};
//uint8_t data_rx_tx_addr[] = {0x9A,0xe9,0x03,0x00,0xa2};//<<---- is ok
//uint8_t data_rx_tx_addr[] = {0x3b,0xb6,0x00,0x00,0xa2};//<<--- is ok
//uint8_t data_rx_tx_addr[] = {0x9A,0xe9,0x00,0x00,0xa2};
//uint8_t data_rx_tx_addr[] = {0x9A,0xe9,0x03,0x00,0xa2};//<<---- is ok
//uint8_t data_rx_tx_addr[] = {0x46,0x18,0x00,0x00,0xa2};
NRF24L01_FlushTx();
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x08);
NRF24L01_WritePayload(first_packet, 15);
if (sub_protocol==SYMAX5C)
{
rf_ch_num = sizeof(chans_bind_x5c);
memcpy(hopping_frequency, chans_bind_x5c, rf_ch_num);
}
else
{
rx_tx_addr[4] = 0xa2; // this is constant in ID
rf_ch_num = sizeof(chans_bind);
memcpy(hopping_frequency, chans_bind, rf_ch_num);
}
hopping_frequency_no = 0;
packet_counter = 0;
}
// channels determined by last byte of tx address
void symax_set_channels(uint8_t address)
{
static const uint8_t start_chans_1[] = {0x0a, 0x1a, 0x2a, 0x3a};
static const uint8_t start_chans_2[] = {0x2a, 0x0a, 0x42, 0x22};
static const uint8_t start_chans_3[] = {0x1a, 0x3a, 0x12, 0x32};
//static const uint8_t start_chans_4[] = {0x15, 0x35, 0x1d, 0x3d};
//static const uint8_t start_chans_5[] = {0x14, 0x34, 0x1c, 0x3c};
//static const uint8_t start_chans_6[] = {0x11, 0x21, 0x31, 0x41};
uint8_t laddress = address & 0x1f;
uint8_t i;
uint32_t *pchans = (uint32_t *)hopping_frequency; // avoid compiler warning
rf_ch_num = 4;
if (laddress < 0x10)
{
if (laddress == 6)
laddress = 7;
for(i=0; i < rf_ch_num; i++)
hopping_frequency[i] = start_chans_1[i] + laddress;
}
else
if (laddress < 0x18)
{
for(i=0; i < rf_ch_num; i++)
hopping_frequency[i] = start_chans_2[i] + (laddress & 0x07);
if (laddress == 0x16)
{
hopping_frequency[0]++;
hopping_frequency[1]++;
}
}
else
if (laddress < 0x1e)
{
for(i=0; i < rf_ch_num; i++)
hopping_frequency[i] = start_chans_3[i] + (laddress & 0x07);
}
else
if (laddress == 0x1e)
*pchans = 0x38184121;
else
*pchans = 0x39194121;
}
void symax_init2()
{
uint8_t chans_data_x5c[] = {0x1d, 0x2f, 0x26, 0x3d, 0x15, 0x2b, 0x25, 0x24,
0x27, 0x2c, 0x1c, 0x3e, 0x39, 0x2d, 0x22};
if (sub_protocol==SYMAX5C)
{
rf_ch_num = sizeof(chans_data_x5c);
memcpy(hopping_frequency, chans_data_x5c, rf_ch_num);
}
else
{
symax_set_channels(rx_tx_addr[0]);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
}
hopping_frequency_no = 0;
packet_counter = 0;
}
uint16_t symax_callback()
{
switch (phase)
{
case SYMAX_INIT1:
symax_init1();
phase = SYMAX_BIND2;
return SYMAX_FIRST_PACKET_DELAY;
break;
case SYMAX_BIND2:
counter = SYMAX_BIND_COUNT;
phase = SYMAX_BIND3;
SYMAX_send_packet(1);
break;
case SYMAX_BIND3:
if (counter == 0)
{
symax_init2();
phase = SYMAX_DATA;
BIND_DONE;
}
else
{
SYMAX_send_packet(1);
counter--;
}
break;
case SYMAX_DATA:
SYMAX_send_packet(0);
break;
}
return SYMAX_PACKET_PERIOD;
}
uint16_t initSymax()
{
packet_counter = 0;
flags = 0;
BIND_IN_PROGRESS; // autobind protocol
symax_init();
phase = SYMAX_INIT1;
return SYMAX_INITIAL_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.
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(V2X2_NRF24L01_INO)
// compatible with WLToys V2x2, JXD JD38x, JD39x, JJRC H6C, Yizhan Tarantula X6 ...
#include "iface_nrf24l01.h"
#define BIND_COUNT 1000
// Timeout for callback in uSec, 4ms=4000us for V202
#define PACKET_PERIOD 4000
//
// Time to wait for packet to be sent (no ACK, so very short)
#define PACKET_CHKTIME 100
//
enum {
V2X2_FLAG_CAMERA = 0x01, // also automatic Missile Launcher and Hoist in one direction
V2X2_FLAG_VIDEO = 0x02, // also Sprayer, Bubbler, Missile Launcher(1), and Hoist in the other dir.
V2X2_FLAG_FLIP = 0x04,
V2X2_FLAG_UNK9 = 0x08,
V2X2_FLAG_LIGHT = 0x10,
V2X2_FLAG_UNK10 = 0x20,
V2X2_FLAG_BIND = 0xC0,
// flags going to byte 10
V2X2_FLAG_HEADLESS = 0x02,
V2X2_FLAG_MAG_CAL_X = 0x08,
V2X2_FLAG_MAG_CAL_Y = 0x20
};
//
#define V2X2_PAYLOADSIZE 16
enum {
V202_INIT2 = 0,
V202_INIT2_NO_BIND,//1
V202_BIND1,//2
V202_BIND2,//3
V202_DATA//4
};
// static u32 bind_count;
// This is frequency hopping table for V202 protocol
// The table is the first 4 rows of 32 frequency hopping
// patterns, all other rows are derived from the first 4.
// For some reason the protocol avoids channels, dividing
// by 16 and replaces them by subtracting 3 from the channel
// number in this case.
// The pattern is defined by 5 least significant bits of
// sum of 3 bytes comprising TX id
static const uint8_t freq_hopping[][16] = {
{ 0x27, 0x1B, 0x39, 0x28, 0x24, 0x22, 0x2E, 0x36,
0x19, 0x21, 0x29, 0x14, 0x1E, 0x12, 0x2D, 0x18 }, // 00
{ 0x2E, 0x33, 0x25, 0x38, 0x19, 0x12, 0x18, 0x16,
0x2A, 0x1C, 0x1F, 0x37, 0x2F, 0x23, 0x34, 0x10 }, // 01
{ 0x11, 0x1A, 0x35, 0x24, 0x28, 0x18, 0x25, 0x2A,
0x32, 0x2C, 0x14, 0x27, 0x36, 0x34, 0x1C, 0x17 }, // 02
{ 0x22, 0x27, 0x17, 0x39, 0x34, 0x28, 0x2B, 0x1D,
0x18, 0x2A, 0x21, 0x38, 0x10, 0x26, 0x20, 0x1F } // 03
};
//static uint8_t hopping_frequency[16];
void v202_init()
{
NRF24L01_Initialize();
// 2-bytes CRC, radio off
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3F); // Enable all data pipes
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xFF); // 4ms retransmit t/o, 15 tries
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x08); // Channel 8
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
// NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00); // no write bits in this field
// NRF24L01_WriteReg(NRF24L01_00_CD, 0x00); // same
NRF24L01_WriteReg(NRF24L01_0C_RX_ADDR_P2, 0xC3); // LSB byte of pipe 2 receive address
NRF24L01_WriteReg(NRF24L01_0D_RX_ADDR_P3, 0xC4);
NRF24L01_WriteReg(NRF24L01_0E_RX_ADDR_P4, 0xC5);
NRF24L01_WriteReg(NRF24L01_0F_RX_ADDR_P5, 0xC6);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, V2X2_PAYLOADSIZE); // bytes of data payload for pipe 1
NRF24L01_WriteReg(NRF24L01_12_RX_PW_P1, V2X2_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_13_RX_PW_P2, V2X2_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_14_RX_PW_P3, V2X2_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_15_RX_PW_P4, V2X2_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_16_RX_PW_P5, V2X2_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
uint8_t v2x2_rx_tx_addr[] = {0x66, 0x88, 0x68, 0x68, 0x68};
uint8_t rx_p1_addr[] = {0x88, 0x66, 0x86, 0x86, 0x86};
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, v2x2_rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, rx_p1_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, v2x2_rx_tx_addr, 5);
}
void V202_init2()
{
NRF24L01_FlushTx();
packet_sent = 0;
hopping_frequency_no = 0;
// Turn radio power on
NRF24L01_SetTxRxMode(TX_EN);
//Done by TX_EN??? => NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
}
void set_tx_id(void)
{
uint8_t sum;
sum = rx_tx_addr[1] + rx_tx_addr[2] + rx_tx_addr[3];
// Base row is defined by lowest 2 bits
const uint8_t *fh_row = freq_hopping[sum & 0x03];
// Higher 3 bits define increment to corresponding row
uint8_t increment = (sum & 0x1e) >> 2;
for (uint8_t i = 0; i < 16; ++i) {
uint8_t val = fh_row[i] + increment;
// Strange avoidance of channels divisible by 16
hopping_frequency[i] = (val & 0x0f) ? val : val - 3;
}
}
void add_pkt_checksum()
{
uint8_t sum = 0;
for (uint8_t i = 0; i < 15; ++i)
sum += packet[i];
packet[15] = sum;
}
void send_packet(uint8_t bind)
{
uint8_t flags2=0;
if (bind)
{
flags = V2X2_FLAG_BIND;
packet[0] = 0;
packet[1] = 0;
packet[2] = 0;
packet[3] = 0;
packet[4] = 0;
packet[5] = 0;
packet[6] = 0;
}
else
{
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_s8b(RUDDER);
packet[2] = convert_channel_s8b(ELEVATOR);
packet[3] = convert_channel_s8b(AILERON);
// Trims, middle is 0x40
packet[4] = 0x40; // yaw
packet[5] = 0x40; // pitch
packet[6] = 0x40; // roll
//Flags
// Channel 5
if (Servo_data[AUX1] > PPM_SWITCH)
flags |= V2X2_FLAG_FLIP;
// Channel 6
if (Servo_data[AUX2] > PPM_SWITCH)
flags |= V2X2_FLAG_LIGHT;
// Channel 7
if (Servo_data[AUX3] > PPM_SWITCH)
flags |= V2X2_FLAG_CAMERA;
// Channel 8
if (Servo_data[AUX4] > PPM_SWITCH)
flags |= V2X2_FLAG_VIDEO;
//Flags2
// Channel 9
if (Servo_data[AUX5] > PPM_SWITCH)
flags2 = V2X2_FLAG_HEADLESS;
// Channel 10
if (Servo_data[AUX6] > PPM_SWITCH)
flags2 |= V2X2_FLAG_MAG_CAL_X;
// Channel 11
if (Servo_data[AUX7] > PPM_SWITCH)
flags2 |= V2X2_FLAG_MAG_CAL_Y;
}
// TX id
packet[7] = rx_tx_addr[1];
packet[8] = rx_tx_addr[2];
packet[9] = rx_tx_addr[3];
// empty
packet[10] = flags2;
packet[11] = 0x00;
packet[12] = 0x00;
packet[13] = 0x00;
//
packet[14] = flags;
add_pkt_checksum();
packet_sent = 0;
uint8_t rf_ch = hopping_frequency[hopping_frequency_no >> 1];
hopping_frequency_no = (hopping_frequency_no + 1) & 0x1F;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet, V2X2_PAYLOADSIZE);
++packet_counter;
packet_sent = 1;
if (! hopping_frequency_no)
NRF24L01_SetPower();
}
uint16_t ReadV2x2()
{
switch (phase) {
case V202_INIT2:
V202_init2();
phase = V202_BIND2;
return 150;
break;
case V202_INIT2_NO_BIND:
V202_init2();
phase = V202_DATA;
return 150;
break;
case V202_BIND2:
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED) {
return PACKET_CHKTIME;
}
send_packet(1);
if (--counter == 0) {
phase = V202_DATA;
BIND_DONE;
}
break;
case V202_DATA:
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED) {
return PACKET_CHKTIME;
}
send_packet(0);
break;
}
// Packet every 4ms
return PACKET_PERIOD;
}
uint16_t initV2x2()
{
flags=0;
packet_counter = 0;
v202_init();
//
if (IS_AUTOBIND_FLAG_on)
{
counter = BIND_COUNT;
phase = V202_INIT2;
}
else
phase = V202_INIT2_NO_BIND;
set_tx_id();
return 50000;
}
#endif

View File

@ -0,0 +1,256 @@
/*
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(YD717_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define YD717_BIND_COUNT 60
#define YD717_PACKET_PERIOD 8000 // Timeout for callback in uSec, 8ms=8000us for YD717
#define YD717_INITIAL_WAIT 50000 // Initial wait before starting callbacks
#define YD717_PACKET_CHKTIME 500 // Time to wait if packet not yet acknowledged or timed out
// Stock tx fixed frequency is 0x3C. Receiver only binds on this freq.
#define RF_CHANNEL 0x3C
#define YD717_FLAG_FLIP 0x0F
#define YD717_FLAG_LIGHT 0x80
#define YD717_FLAG_PICTURE 0x40
#define YD717_FLAG_VIDEO 0x20
#define YD717_FLAG_HEADLESS 0x10
#define YD717_PAYLOADSIZE 8 // receive data pipes set to this size, but unused
//#define YD717_MAX_PACKET_SIZE 9 // YD717 packets have 8-byte payload, Syma X4 is 9
enum {
YD717_INIT1 = 0,
YD717_BIND2,
YD717_BIND3,
YD717_DATA
};
void yd717_send_packet(uint8_t bind)
{
uint8_t rudder_trim, elevator_trim, aileron_trim;
if (bind)
{
packet[0]= rx_tx_addr[0]; // send data phase address in first 4 bytes
packet[1]= rx_tx_addr[1];
packet[2]= rx_tx_addr[2];
packet[3]= rx_tx_addr[3];
packet[4] = 0x56;
packet[5] = 0xAA;
packet[6] = (sub_protocol == NIHUI) ? 0x00 : 0x32;
packet[7] = 0x00;
}
else
{
// Throttle
packet[0] = convert_channel_8b(THROTTLE);
// Rudder
if( sub_protocol==XINXUN )
{
rudder = convert_channel_8b(RUDDER);
rudder_trim = (0xff - rudder) >> 1;
}
else
{
rudder = 0xff - convert_channel_8b(RUDDER);
rudder_trim = rudder >> 1;
}
packet[1] = rudder;
// Elevator
elevator = convert_channel_8b(ELEVATOR);
elevator_trim = elevator >> 1;
packet[3] = elevator;
// Aileron
aileron = 0xff - convert_channel_8b(AILERON);
aileron_trim = aileron >> 1;
packet[4] = aileron;
// Trims
if( sub_protocol == YD717 )
{
packet[2] = elevator_trim;
packet[5] = aileron_trim;
packet[6] = rudder_trim;
}
else
{
packet[2] = rudder_trim;
packet[5] = elevator_trim;
packet[6] = aileron_trim;
}
// Flags
// Channel 5
if (Servo_data[AUX1] > PPM_SWITCH)
flags = YD717_FLAG_FLIP;
else
flags=0;
// Channel 6
if (Servo_data[AUX2] > PPM_SWITCH)
flags |= YD717_FLAG_LIGHT;
// Channel 7
if (Servo_data[AUX3] > PPM_SWITCH)
flags |= YD717_FLAG_PICTURE;
// Channel 8
if (Servo_data[AUX4] > PPM_SWITCH)
flags |= YD717_FLAG_VIDEO;
// Channel 9
if (Servo_data[AUX5] > PPM_SWITCH)
flags |= YD717_FLAG_HEADLESS;
packet[7] = flags;
}
// 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( sub_protocol == YD717 )
NRF24L01_WritePayload(packet, 8);
else
{
packet[8] = packet[0]; // checksum
for(uint8_t i=1; i < 8; i++)
packet[8] += packet[i];
packet[8] = ~packet[8];
NRF24L01_WritePayload(packet, 9);
}
NRF24L01_SetPower(); // Set tx_power
}
void yd717_init()
{
NRF24L01_Initialize();
// CRC, radio on
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x3F); // Auto Acknoledgement on all data pipes
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3F); // Enable all data pipes
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x1A); // 500uS retransmit t/o, 10 tries
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_CHANNEL); // Channel 3C
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
NRF24L01_SetPower();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_0C_RX_ADDR_P2, 0xC3); // LSB byte of pipe 2 receive address
NRF24L01_WriteReg(NRF24L01_0D_RX_ADDR_P3, 0xC4);
NRF24L01_WriteReg(NRF24L01_0E_RX_ADDR_P4, 0xC5);
NRF24L01_WriteReg(NRF24L01_0F_RX_ADDR_P5, 0xC6);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, YD717_PAYLOADSIZE); // bytes of data payload for pipe 1
NRF24L01_WriteReg(NRF24L01_12_RX_PW_P1, YD717_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_13_RX_PW_P2, YD717_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_14_RX_PW_P3, YD717_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_15_RX_PW_P4, YD717_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_16_RX_PW_P5, YD717_PAYLOADSIZE);
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
NRF24L01_Activate(0x73); // Activate feature register
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3F); // Enable dynamic payload length on all pipes
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); // Set feature bits on
NRF24L01_Activate(0x73);
// set tx id
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
}
void YD717_init1()
{
// for bind packets set address to prearranged value known to receiver
uint8_t bind_rx_tx_addr[] = {0x65, 0x65, 0x65, 0x65, 0x65};
if( sub_protocol==SYMAX2 )
for(uint8_t i=0; i < 5; i++)
bind_rx_tx_addr[i] = 0x60;
else
if( sub_protocol==NIHUI )
for(uint8_t i=0; i < 5; i++)
bind_rx_tx_addr[i] = 0x64;
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bind_rx_tx_addr, 5);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_rx_tx_addr, 5);
}
void 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);
}
uint16_t yd717_callback()
{
switch (phase)
{
case YD717_INIT1:
yd717_send_packet(0); // receiver doesn't re-enter bind mode if connection lost...check if already bound
phase = YD717_BIND3;
break;
case YD717_BIND2:
if (counter == 0)
{
if (NRF24L01_packet_ack() == PKT_PENDING)
return YD717_PACKET_CHKTIME; // packet send not yet complete
YD717_init2(); // change to data phase rx/tx address
yd717_send_packet(0);
phase = YD717_BIND3;
}
else
{
if (NRF24L01_packet_ack() == PKT_PENDING)
return YD717_PACKET_CHKTIME; // packet send not yet complete;
yd717_send_packet(1);
counter--;
}
break;
case YD717_BIND3:
switch (NRF24L01_packet_ack())
{
case PKT_PENDING:
return YD717_PACKET_CHKTIME; // packet send not yet complete
case PKT_ACKED:
phase = YD717_DATA;
BIND_DONE; // bind complete
break;
case PKT_TIMEOUT:
YD717_init1(); // change to bind rx/tx address
counter = YD717_BIND_COUNT;
phase = YD717_BIND2;
yd717_send_packet(1);
}
break;
case YD717_DATA:
if (NRF24L01_packet_ack() == PKT_PENDING)
return YD717_PACKET_CHKTIME; // packet send not yet complete
yd717_send_packet(0);
break;
}
return YD717_PACKET_PERIOD; // Packet every 8ms
}
uint16_t initYD717()
{
rx_tx_addr[4] = 0xC1; // always uses first data port
flags = 0;
yd717_init();
phase = YD717_INIT1;
BIND_IN_PROGRESS; // autobind protocol
// Call callback in 50ms
return YD717_INITIAL_WAIT;
}
#endif

View File

@ -0,0 +1,95 @@
/*
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/>.
*/
#ifndef _IFACE_A7105_H_
#define _IFACE_A7105_H_
enum A7105_State {
A7105_SLEEP = 0x80,
A7105_IDLE = 0x90,
A7105_STANDBY = 0xA0,
A7105_PLL = 0xB0,
A7105_RX = 0xC0,
A7105_TX = 0xD0,
A7105_RST_WRPTR = 0xE0,
A7105_RST_RDPTR = 0xF0,
};
enum {
A7105_00_MODE = 0x00,
A7105_01_MODE_CONTROL = 0x01,
A7105_02_CALC = 0x02,
A7105_03_FIFOI = 0x03,
A7105_04_FIFOII = 0x04,
A7105_05_FIFO_DATA = 0x05,
A7105_06_ID_DATA = 0x06,
A7105_07_RC_OSC_I = 0x07,
A7105_08_RC_OSC_II = 0x08,
A7105_09_RC_OSC_III = 0x09,
A7105_0A_CK0_PIN = 0x0A,
A7105_0B_GPIO1_PIN1 = 0x0B,
A7105_0C_GPIO2_PIN_II = 0x0C,
A7105_0D_CLOCK = 0x0D,
A7105_0E_DATA_RATE = 0x0E,
A7105_0F_PLL_I = 0x0F,
A7105_10_PLL_II = 0x10,
A7105_11_PLL_III = 0x11,
A7105_12_PLL_IV = 0x12,
A7105_13_PLL_V = 0x13,
A7105_14_TX_I = 0x14,
A7105_15_TX_II = 0x15,
A7105_16_DELAY_I = 0x16,
A7105_17_DELAY_II = 0x17,
A7105_18_RX = 0x18,
A7105_19_RX_GAIN_I = 0x19,
A7105_1A_RX_GAIN_II = 0x1A,
A7105_1B_RX_GAIN_III = 0x1B,
A7105_1C_RX_GAIN_IV = 0x1C,
A7105_1D_RSSI_THOLD = 0x1D,
A7105_1E_ADC = 0x1E,
A7105_1F_CODE_I = 0x1F,
A7105_20_CODE_II = 0x20,
A7105_21_CODE_III = 0x21,
A7105_22_IF_CALIB_I = 0x22,
A7105_23_IF_CALIB_II = 0x23,
A7105_24_VCO_CURCAL = 0x24,
A7105_25_VCO_SBCAL_I = 0x25,
A7105_26_VCO_SBCAL_II = 0x26,
A7105_27_BATTERY_DET = 0x27,
A7105_28_TX_TEST = 0x28,
A7105_29_RX_DEM_TEST_I = 0x29,
A7105_2A_RX_DEM_TEST_II = 0x2A,
A7105_2B_CPC = 0x2B,
A7105_2C_XTAL_TEST = 0x2C,
A7105_2D_PLL_TEST = 0x2D,
A7105_2E_VCO_TEST_I = 0x2E,
A7105_2F_VCO_TEST_II = 0x2F,
A7105_30_IFAT = 0x30,
A7105_31_RSCALE = 0x31,
A7105_32_FILTER_TEST = 0x32,
};
#define A7105_0F_CHANNEL A7105_0F_PLL_I
enum A7105_MASK {
A7105_MASK_FBCF = 1 << 4,
A7105_MASK_VBCF = 1 << 3,
};
enum {
INIT_FLYSKY,
INIT_HUBSAN
};
#endif

View File

@ -0,0 +1,149 @@
/*
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/>.
*/
#ifndef _IFACE_CC2500_H_
#define _IFACE_CC2500_H_
enum {
CC2500_00_IOCFG2 = 0x00, // GDO2 output pin configuration
CC2500_01_IOCFG1 = 0x01, // GDO1 output pin configuration
CC2500_02_IOCFG0 = 0x02, // GDO0 output pin configuration
CC2500_03_FIFOTHR = 0x03, // RX FIFO and TX FIFO thresholds
CC2500_04_SYNC1 = 0x04, // Sync word, high byte
CC2500_05_SYNC0 = 0x05, // Sync word, low byte
CC2500_06_PKTLEN = 0x06, // Packet length
CC2500_07_PKTCTRL1 = 0x07, // Packet automation control
CC2500_08_PKTCTRL0 = 0x08, // Packet automation control
CC2500_09_ADDR = 0x09, // Device address
CC2500_0A_CHANNR = 0x0A, // Channel number
CC2500_0B_FSCTRL1 = 0x0B, // Frequency synthesizer control
CC2500_0C_FSCTRL0 = 0x0C, // Frequency synthesizer control
CC2500_0D_FREQ2 = 0x0D, // Frequency control word, high byte
CC2500_0E_FREQ1 = 0x0E, // Frequency control word, middle byte
CC2500_0F_FREQ0 = 0x0F, // Frequency control word, low byte
CC2500_10_MDMCFG4 = 0x10, // Modem configuration
CC2500_11_MDMCFG3 = 0x11, // Modem configuration
CC2500_12_MDMCFG2 = 0x12, // Modem configuration
CC2500_13_MDMCFG1 = 0x13, // Modem configuration
CC2500_14_MDMCFG0 = 0x14, // Modem configuration
CC2500_15_DEVIATN = 0x15, // Modem deviation setting
CC2500_16_MCSM2 = 0x16, // Main Radio Cntrl State Machine config
CC2500_17_MCSM1 = 0x17, // Main Radio Cntrl State Machine config
CC2500_18_MCSM0 = 0x18, // Main Radio Cntrl State Machine config
CC2500_19_FOCCFG = 0x19, // Frequency Offset Compensation config
CC2500_1A_BSCFG = 0x1A, // Bit Synchronization configuration
CC2500_1B_AGCCTRL2 = 0x1B, // AGC control
CC2500_1C_AGCCTRL1 = 0x1C, // AGC control
CC2500_1D_AGCCTRL0 = 0x1D, // AGC control
CC2500_1E_WOREVT1 = 0x1E, // High byte Event 0 timeout
CC2500_1F_WOREVT0 = 0x1F, // Low byte Event 0 timeout
CC2500_20_WORCTRL = 0x20, // Wake On Radio control
CC2500_21_FREND1 = 0x21, // Front end RX configuration
CC2500_22_FREND0 = 0x22, // Front end TX configuration
CC2500_23_FSCAL3 = 0x23, // Frequency synthesizer calibration
CC2500_24_FSCAL2 = 0x24, // Frequency synthesizer calibration
CC2500_25_FSCAL1 = 0x25, // Frequency synthesizer calibration
CC2500_26_FSCAL0 = 0x26, // Frequency synthesizer calibration
CC2500_27_RCCTRL1 = 0x27, // RC oscillator configuration
CC2500_28_RCCTRL0 = 0x28, // RC oscillator configuration
CC2500_29_FSTEST = 0x29, // Frequency synthesizer cal control
CC2500_2A_PTEST = 0x2A, // Production test
CC2500_2B_AGCTEST = 0x2B, // AGC test
CC2500_2C_TEST2 = 0x2C, // Various test settings
CC2500_2D_TEST1 = 0x2D, // Various test settings
CC2500_2E_TEST0 = 0x2E, // Various test settings
// Status registers
CC2500_30_PARTNUM = 0x30, // Part number
CC2500_31_VERSION = 0x31, // Current version number
CC2500_32_FREQEST = 0x32, // Frequency offset estimate
CC2500_33_LQI = 0x33, // Demodulator estimate for link quality
CC2500_34_RSSI = 0x34, // Received signal strength indication
CC2500_35_MARCSTATE = 0x35, // Control state machine state
CC2500_36_WORTIME1 = 0x36, // High byte of WOR timer
CC2500_37_WORTIME0 = 0x37, // Low byte of WOR timer
CC2500_38_PKTSTATUS = 0x38, // Current GDOx status and packet status
CC2500_39_VCO_VC_DAC = 0x39, // Current setting from PLL cal module
CC2500_3A_TXBYTES = 0x3A, // Underflow and # of bytes in TXFIFO
CC2500_3B_RXBYTES = 0x3B, // Overflow and # of bytes in RXFIFO
// Multi byte memory locations
CC2500_3E_PATABLE = 0x3E,
CC2500_3F_TXFIFO = 0x3F,
CC2500_3F_RXFIFO = 0x3F,
};
// Definitions for burst/single access to registers
#define CC2500_WRITE_SINGLE 0x00
#define CC2500_WRITE_BURST 0x40
#define CC2500_READ_SINGLE 0x80
#define CC2500_READ_BURST 0xC0
// Strobe commands
#define CC2500_SRES 0x30 // Reset chip.
#define CC2500_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
// If in RX/TX: Go to a wait state where only the synthesizer is
// running (for quick RX / TX turnaround).
#define CC2500_SXOFF 0x32 // Turn off crystal oscillator.
#define CC2500_SCAL 0x33 // Calibrate frequency synthesizer and turn it off
// (enables quick start).
#define CC2500_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and
// MCSM0.FS_AUTOCAL=1.
#define CC2500_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if
// MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled:
// Only go to TX if channel is clear.
#define CC2500_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit
// Wake-On-Radio mode if applicable.
#define CC2500_SAFC 0x37 // Perform AFC adjustment of the frequency synthesizer
#define CC2500_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio)
#define CC2500_SPWD 0x39 // Enter power down mode when CSn goes high.
#define CC2500_SFRX 0x3A // Flush the RX FIFO buffer.
#define CC2500_SFTX 0x3B // Flush the TX FIFO buffer.
#define CC2500_SWORRST 0x3C // Reset real time clock.
#define CC2500_SNOP 0x3D // No operation. May be used to pad strobe commands to two
// bytes for simpler software.
//----------------------------------------------------------------------------------
// Chip Status Byte
//----------------------------------------------------------------------------------
// Bit fields in the chip status byte
#define CC2500_STATUS_CHIP_RDYn_BM 0x80
#define CC2500_STATUS_STATE_BM 0x70
#define CC2500_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F
// Chip states
#define CC2500_STATE_IDLE 0x00
#define CC2500_STATE_RX 0x10
#define CC2500_STATE_TX 0x20
#define CC2500_STATE_FSTXON 0x30
#define CC2500_STATE_CALIBRATE 0x40
#define CC2500_STATE_SETTLING 0x50
#define CC2500_STATE_RX_OVERFLOW 0x60
#define CC2500_STATE_TX_UNDERFLOW 0x70
//----------------------------------------------------------------------------------
// Other register bit fields
//----------------------------------------------------------------------------------
#define CC2500_LQI_CRC_OK_BM 0x80
#define CC2500_LQI_EST_BM 0x7F
//void CC2500_WriteReg(u8 addr, u8 data);
//u8 CC2500_ReadReg(u8 addr);
//void CC2500_Reset();
//void CC2500_Strobe(u8 cmd);
//void CC2500_WriteData(u8 *packet, u8 length);
//void CC2500_ReadData(u8 *dpbuffer, int len);
//void CC2500_SetTxRxMode(enum TXRX_State);
#endif

View File

@ -0,0 +1,99 @@
/*
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/>.
*/
#ifndef _IFACE_CYRF6936_H_
#define _IFACE_CYRF6936_H_
enum {
CYRF_00_CHANNEL = 0x00,
CYRF_01_TX_LENGTH = 0x01,
CYRF_02_TX_CTRL = 0x02,
CYRF_03_TX_CFG = 0x03,
CYRF_04_TX_IRQ_STATUS = 0x04,
CYRF_05_RX_CTRL = 0x05,
CYRF_06_RX_CFG = 0x06,
CYRF_07_RX_IRQ_STATUS = 0x07,
CYRF_08_RX_STATUS = 0x08,
CYRF_09_RX_COUNT = 0x09,
CYRF_0A_RX_LENGTH = 0x0A,
CYRF_0B_PWR_CTRL = 0x0B,
CYRF_0C_XTAL_CTRL = 0x0C,
CYRF_0D_IO_CFG = 0x0D,
CYRF_0E_GPIO_CTRL = 0x0E,
CYRF_0F_XACT_CFG = 0x0F,
CYRF_10_FRAMING_CFG = 0x10,
CYRF_11_DATA32_THOLD = 0x11,
CYRF_12_DATA64_THOLD = 0x12,
CYRF_13_RSSI = 0x13,
CYRF_14_EOP_CTRL = 0x14,
CYRF_15_CRC_SEED_LSB = 0x15,
CYRF_16_CRC_SEED_MSB = 0x16,
CYRF_17_TX_CRC_LSB = 0x17,
CYRF_18_TX_CRC_MSB = 0x18,
CYRF_19_RX_CRC_LSB = 0x19,
CYRF_1A_RX_CRC_MSB = 0x1A,
CYRF_1B_TX_OFFSET_LSB = 0x1B,
CYRF_1C_TX_OFFSET_MSB = 0x1C,
CYRF_1D_MODE_OVERRIDE = 0x1D,
CYRF_1E_RX_OVERRIDE = 0x1E,
CYRF_1F_TX_OVERRIDE = 0x1F,
/*Register Files */
CYRF_20_TX_BUFFER = 0x20,
CYRF_21_RX_BUFFER = 0x21,
CYRF_22_SOP_CODE = 0x22,
CYRF_23_DATA_CODE = 0x23,
CYRF_24_PREAMBLE = 0x24,
CYRF_25_MFG_ID = 0x25,
/*****************/
CYRF_26_XTAL_CFG = 0x26,
CYRF_27_CLK_OVERRIDE = 0x27,
CYRF_28_CLK_EN = 0x28,
CYRF_29_RX_ABORT = 0x29,
CYRF_32_AUTO_CAL_TIME = 0x32,
CYRF_35_AUTOCAL_OFFSET = 0x35,
CYRF_39_ANALOG_CTRL = 0x39,
};
enum CYRF_PWR {
CYRF_PWR_100MW,
CYRF_PWR_10MW,
CYRF_PWR_DEFAULT,
};
/* SPI CYRF6936 */
/*
void CYRF_Initialize();
int CYRF_Reset();
void CYRF_GetMfgData(u8 data[]);
void CYRF_SetTxRxMode(enum TXRX_State);
void CYRF_ConfigRFChannel(u8 ch);
void CYRF_SetPower(u8 power);
void CYRF_ConfigCRCSeed(u16 crc);
void CYRF_StartReceive();
void CYRF_ConfigSOPCode(const u8 *sopcodes);
void CYRF_ConfigDataCode(const u8 *datacodes, u8 len);
u8 CYRF_ReadRSSI(u32 dodummyread);
void CYRF_ReadDataPacket(u8 dpbuffer[]);
void CYRF_WriteDataPacket(const u8 dpbuffer[]);
void CYRF_WriteDataPacketLen(const u8 dpbuffer[], u8 len);
void CYRF_WriteRegister(u8 address, u8 data);
u8 CYRF_ReadRegister(u8 address);
void CYRF_WritePreamble(u32 preamble);
u8 CYRF_MaxPower();
void CYRF_FindBestChannels(u8 *channels, u8 len, u8 minspace, u8 minchan, u8 maxchan);
*/
#endif

View File

@ -0,0 +1,119 @@
/*
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/>.
*/
#ifndef _IFACE_NRF24L01_H_
#define _IFACE_NRF24L01_H_
// Register map
enum {
NRF24L01_00_CONFIG = 0x00,
NRF24L01_01_EN_AA = 0x01,
NRF24L01_02_EN_RXADDR = 0x02,
NRF24L01_03_SETUP_AW = 0x03,
NRF24L01_04_SETUP_RETR = 0x04,
NRF24L01_05_RF_CH = 0x05,
NRF24L01_06_RF_SETUP = 0x06,
NRF24L01_07_STATUS = 0x07,
NRF24L01_08_OBSERVE_TX = 0x08,
NRF24L01_09_CD = 0x09,
NRF24L01_0A_RX_ADDR_P0 = 0x0A,
NRF24L01_0B_RX_ADDR_P1 = 0x0B,
NRF24L01_0C_RX_ADDR_P2 = 0x0C,
NRF24L01_0D_RX_ADDR_P3 = 0x0D,
NRF24L01_0E_RX_ADDR_P4 = 0x0E,
NRF24L01_0F_RX_ADDR_P5 = 0x0F,
NRF24L01_10_TX_ADDR = 0x10,
NRF24L01_11_RX_PW_P0 = 0x11,
NRF24L01_12_RX_PW_P1 = 0x12,
NRF24L01_13_RX_PW_P2 = 0x13,
NRF24L01_14_RX_PW_P3 = 0x14,
NRF24L01_15_RX_PW_P4 = 0x15,
NRF24L01_16_RX_PW_P5 = 0x16,
NRF24L01_17_FIFO_STATUS = 0x17,
NRF24L01_1C_DYNPD = 0x1C,
NRF24L01_1D_FEATURE = 0x1D,
//Instructions
NRF24L01_61_RX_PAYLOAD = 0x61,
NRF24L01_A0_TX_PAYLOAD = 0xA0,
NRF24L01_E1_FLUSH_TX = 0xE1,
NRF24L01_E2_FLUSH_RX = 0xE2,
NRF24L01_E3_REUSE_TX_PL = 0xE3,
NRF24L01_50_ACTIVATE = 0x50,
NRF24L01_60_R_RX_PL_WID = 0x60,
NRF24L01_B0_TX_PYLD_NOACK = 0xB0,
NRF24L01_FF_NOP = 0xFF,
NRF24L01_A8_W_ACK_PAYLOAD0 = 0xA8,
NRF24L01_A8_W_ACK_PAYLOAD1 = 0xA9,
NRF24L01_A8_W_ACK_PAYLOAD2 = 0xAA,
NRF24L01_A8_W_ACK_PAYLOAD3 = 0xAB,
NRF24L01_A8_W_ACK_PAYLOAD4 = 0xAC,
NRF24L01_A8_W_ACK_PAYLOAD5 = 0xAD,
};
// Bit mnemonics
enum {
NRF24L01_00_MASK_RX_DR = 6,
NRF24L01_00_MASK_TX_DS = 5,
NRF24L01_00_MASK_MAX_RT = 4,
NRF24L01_00_EN_CRC = 3,
NRF24L01_00_CRCO = 2,
NRF24L01_00_PWR_UP = 1,
NRF24L01_00_PRIM_RX = 0,
NRF24L01_07_RX_DR = 6,
NRF24L01_07_TX_DS = 5,
NRF24L01_07_MAX_RT = 4,
NRF2401_1D_EN_DYN_ACK = 0,
NRF2401_1D_EN_ACK_PAY = 1,
NRF2401_1D_EN_DPL = 2,
};
// Bitrates
enum {
NRF24L01_BR_1M = 0,
NRF24L01_BR_2M,
NRF24L01_BR_250K,
NRF24L01_BR_RSVD
};
/* Instruction Mnemonics */
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define REGISTER_MASK 0x1F
#define ACTIVATE 0x50
#define R_RX_PL_WID 0x60
#define R_RX_PAYLOAD 0x61
#define W_TX_PAYLOAD 0xA0
#define W_ACK_PAYLOAD 0xA8
#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2
#define REUSE_TX_PL 0xE3
//#define NOP 0xFF
/*
void NRF24L01_Initialize();
byte NRF24L01_WriteReg(byte reg, byte data);
byte NRF24L01_WriteRegisterMulti(byte reg, byte data[], byte length);
byte NRF24L01_WritePayload(byte *data, byte len);
byte NRF24L01_ReadReg(byte reg);
byte NRF24L01_ReadRegisterMulti(byte reg, byte data[], byte length);
byte NRF24L01_ReadPayload(byte *data, byte len);
byte NRF24L01_FlushTx();
byte NRF24L01_FlushRx();
byte NRF24L01_Activate(byte code);
*/
#endif

View File

@ -0,0 +1,462 @@
/*
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/>.
*/
//******************
enum PROTOCOLS
{
MODE_SERIAL = 0, // Serial commands
MODE_FLYSKY = 1, // =>A7105 / FLYSKY protocol
MODE_HUBSAN = 2, // =>A7105 / HUBSAN protocol
MODE_FRSKY = 3, // =>CC2500 / FRSKY protocol
MODE_HISKY = 4, // =>NRF24L01 / HISKY protocol
MODE_V2X2 = 5, // =>NRF24L01 / V2x2 protocol
MODE_DSM2 = 6, // =>CYRF6936 / DSM2 protocol
MODE_DEVO =7, // =>CYRF6936 / DEVO protocol
MODE_YD717 = 8, // =>NRF24L01 / YD717 protocol (CX10 red pcb)
MODE_KN = 9, // =>NRF24L01 / KN protocol
MODE_SYMAX = 10, // =>NRF24L01 / SYMAX protocol (SYMAX4 working)
MODE_SLT = 11, // =>NRF24L01 / SLT protocol
MODE_CX10 = 12, // =>NRF24L01 / CX-10 protocol
MODE_CG023 = 13, // =>NRF24L01 / CG023 protocol
MODE_BAYANG = 14, // =>NRF24L01 / BAYANG protocol
MODE_FRSKYX = 15, // =>CC2500 / FRSKYX protocol
};
enum Flysky
{
Flysky=0,
V9X9=1,
V6X6=2,
V912=3
};
enum Hisky
{
Hisky=0,
HK310=1
};
enum DSM2{
DSM2=0,
DSMX=1
};
enum YD717
{
YD717=0,
SKYWLKR=1,
SYMAX2=2,
XINXUN=3,
NIHUI=4
};
enum SYMAX
{
SYMAX=0,
SYMAX5C=1,
};
enum CX10 {
CX10_GREEN = 0,
CX10_BLUE, // also compatible with CX10-A, CX12
DM007
};
enum CG023 {
CG023 = 0,
YD829 = 1
};
#define PPM_MIN_COMMAND 1250
#define PPM_SWITCH 1550
#define PPM_MAX_COMMAND 1750
enum TXRX_State {
TXRX_OFF,
TX_EN,
RX_EN
};
// Packet ack status values
enum {
PKT_PENDING = 0,
PKT_ACKED,
PKT_TIMEOUT
};
//*******************
//*** Pinouts ***
//*******************
//#define BIND_pin 13
#define LED_pin 13 //Promini original led on B5
//
#define PPM_pin 3 //PPM -D3
#define SDI_pin 5 //SDIO-D5
#define SCLK_pin 4 //SCK-D4
#define CS_pin 2 //CS-D2
#define SDO_pin 6 //D6
//
#define CTRL1 1 //C1 (A1)
#define CTRL2 2 //C2 (A2)
//
#define CTRL1_on PORTC |= _BV(1)
#define CTRL1_off PORTC &= ~_BV(1)
//
#define CTRL2_on PORTC |= _BV(2)
#define CTRL2_off PORTC &= ~_BV(2)
//
#define CS_on PORTD |= _BV(2) //D2
#define CS_off PORTD &= ~_BV(2) //D2
//
#define SCK_on PORTD |= _BV(4) //D4
#define SCK_off PORTD &= ~_BV(4) //D4
//
#define SDI_on PORTD |= _BV(5) //D5
#define SDI_off PORTD &= ~_BV(5) //D5
#define SDI_1 (PIND & (1<<SDI_pin)) == (1<<SDI_pin) //D5
#define SDI_0 (PIND & (1<<SDI_pin)) == 0x00 //D5
//
#define SDI_SET_INPUT DDRD &= ~_BV(5) //D5
#define SDI_SET_OUTPUT DDRD |= _BV(5) //D5
//Hisky /CC2500/CYRF aditional pinout
#define CC25_CSN_pin 7
#define NRF_CSN_pin 8
#define CYRF_CSN_pin 9
//
#define CYRF_RST_pin A5 //reset pin
//
#define CC25_CSN_on PORTD |= _BV(7) //D7
#define CC25_CSN_off PORTD &= ~_BV(7) //D7
//
#define NRF_CSN_on PORTB |= _BV(0) //D8
#define NRF_CSN_off PORTB &= ~_BV(0) //D8
//
#define CYRF_CSN_on PORTB |= _BV(1) //D9
#define CYRF_CSN_off PORTB &= ~_BV(1) //D9
//
#define SDO_1 (PIND & (1<<SDO_pin)) == (1<<SDO_pin) //D6
#define SDO_0 (PIND & (1<<SDO_pin)) == 0x00 //D6
//
#define RS_HI PORTC|=_BV(5) //reset pin cyrf
#define RX_LO PORTB &= ~_BV(5)//
//
//
// LED
#define LED_ON PORTB |= _BV(5)
#define LED_OFF PORTB &= ~_BV(5)
#define LED_TOGGLE PORTB ^= _BV(5)
#define LED_SET_OUTPUT DDRB |= _BV(5)
// Macros
#define NOP() __asm__ __volatile__("nop")
#define BV(bit) (1 << bit)
//Serial flags definition
#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 for blinking
#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 BLINK_BIND_TIME 100
#define BLINK_SERIAL_TIME 500
//************************
//*** 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_5
#define A7105_LOW_POWER A7105_POWER_3
#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_BIND_POWER NRF_POWER_0
// CC2500 power
enum CC2500_POWER
{
CC2500_POWER_0 = 0xC5, // -12dbm
CC2500_POWER_1 = 0x97, // -10dbm
CC2500_POWER_2 = 0x6E, // -8dbm
CC2500_POWER_3 = 0x7F, // -6dbm
CC2500_POWER_4 = 0xA9, // -4dbm
CC2500_POWER_5 = 0xBB, // -2dbm
CC2500_POWER_6 = 0xFE, // 0dbm
CC2500_POWER_7 = 0xFF // 1.5dbm
};
#define CC2500_HIGH_POWER CC2500_POWER_6
#define CC2500_LOW_POWER CC2500_POWER_3
#define CC2500_BIND_POWER CC2500_POWER_0
// CYRF power
enum CYRF_POWER
{
CYRF_POWER_0 = 0x00, //
CYRF_POWER_1 = 0x01, //
CYRF_POWER_2 = 0x02, //
CYRF_POWER_3 = 0x03, //
CYRF_POWER_4 = 0x04, //
CYRF_POWER_5 = 0x05, //
CYRF_POWER_6 = 0x06, //
CYRF_POWER_7 = 0x07 //
};
#define CYRF_HIGH_POWER 7
#define CYRF_LOW_POWER 3
#define CYRF_BIND_POWER 0
//*******************
//*** CRC Table ***
//*******************
const uint16_t PROGMEM CRCTable[]=
{
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78
};
//****************************************
//*** MULTI protocol serial definition ***
//****************************************
/*
**************************
16 channels serial protocol
**************************
Serial: 100000 Baud 8e2 _ xxxx xxxx p --
Total of 26 bytes
Stream[0] = 0x55
header
Stream[1] = sub_protocol|BindBit|RangeCheckBit|AutoBindBit;
sub_protocol is 0..31 (bits 0..4)
=> Reserved 0
Flysky 1
Hubsan 2
Frsky 3
Hisky 4
V2x2 5
DSM2 6
Devo 7
YD717 8
KN 9
SymaX 10
SLT 11
CX10 12
CG023 13
Bayang 14
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==DSM2
DSM2 0
DSMX 1
sub_protocol==YD717
YD717 0
SKYWLKR 1
SYMAX2 2
XINXUN 3
NIHUI 4
sub_protocol==SYMAX
SYMAX 0
SYMAX5C 1
sub_protocol==CX10
CX10_GREEN 0
CX10_BLUE 1 // also compatible with CX10-A, CX12
DM007 2
sub_protocol==CG023
CG023 0
YD829 1
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
**************************
8 channels serial protocol
**************************
Serial: 125000 Baud 8n1 _ xxxx xxxx - ---
Channels:
Nbr=8
10bits=0..1023
0 -125%
96 -100%
512 0%
928 +100%
1023 +125%
Stream[0] = sub_protocol|BindBit|RangeCheckBit|AutoBindBit;
sub_protocol is 0..31 (bits 0..4)
=> Reserved 0
Flysky 1
Hubsan 2
Frsky 3
Hisky 4
V2x2 5
DSM2 6
Devo 7
YD717 8
KN 9
SymaX 10
SLT 11
CX10 12
CG023 13
Bayang 14
BindBit=> 0x80 1=Bind/0=No
AutoBindBit=> 0x40 1=Yes /0=No
RangeCheck=> 0x20 1=Yes /0=No
Stream[1] = 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==DSM2
DSM2 0
DSMX 1
sub_protocol==YD717
YD717 0
SKYWLKR 1
SYMAX2 2
XINXUN 3
NIHUI 4
sub_protocol==SYMAX
SYMAX 0
SYMAX5C 1
sub_protocol==CX10
CX10_GREEN 0
CX10_BLUE 1 // also compatible with CX10-A, CX12
DM007 2
sub_protocol==CG023
CG023 0
YD829 1
Power value => 0x80 0=High/1=Low
Stream[2] = option_protocol;
option_protocol value is -127..127
Stream[i+3] = lowByte(channel[i]) // with i[0..7]
Stream[11] = highByte(channel[0])<<6 | highByte(channel[1])<<4 | highByte(channel[2])<<2 | highByte(channel[3])
Stream[12] = highByte(channel[4])<<6 | highByte(channel[5])<<4 | highByte(channel[6])<<2 | highByte(channel[7])
Stream[13] = lowByte(CRC16(Stream[0..12])
*/

69
Multiprotocol/telemetry.h Normal file
View File

@ -0,0 +1,69 @@
/*
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/>.
*/
void frskySendStuffed(uint8_t frame[])
{
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 frskySendFrame()
{
uint8_t frame[9];
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;//txrssi
frame[5] = frame[6] = frame[7] = frame[8] = 0;
}
else
if ((cur_protocol[0]&0x1F)==MODE_HUBSAN)
{
frame[1] = v_lipo*2;
frame[2] = 0;
frame[3] = 0x5A;//dummy value
frame[4] = 2 * 0x5A;//dummy value
frame[5] = frame[6] = frame[7] = frame[8] = 0;
}
frskySendStuffed(frame);
}
void frskyUpdate()
{
if(telemetry_link){
frskySendFrame();
telemetry_link=0;
}
}