mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-04 20:58:10 +00:00
984aa3f413
- Change how PPM is handled with a resolution of 2048 and scaled to match serial input range. PPM is now fully scaled for all protocols which was not the case before. If you are using PPM, you might have to adjust the end points depending on the protocols. - Change all range conversions to use 2048 where possible - Updated all protocols with new range functions - Protocols which are taking advantage of 2048 are Assan, FrSky V/D/X, DSM, Devo, WK2x01 - Renamed AUX xto CHx for code readbility
410 lines
10 KiB
C++
410 lines
10 KiB
C++
/*
|
|
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(Q303_NRF24L01_INO)
|
|
|
|
#include "iface_nrf24l01.h"
|
|
|
|
#define Q303_BIND_COUNT 1500
|
|
#define Q303_INITIAL_WAIT 500
|
|
#define Q303_RF_BIND_CHANNEL 0x02
|
|
|
|
#define Q303_BTN_TAKEOFF 1
|
|
#define Q303_BTN_DESCEND 2
|
|
#define Q303_BTN_SNAPSHOT 4
|
|
#define Q303_BTN_VIDEO 8
|
|
#define Q303_BTN_RTH 16
|
|
#define Q303_BTN_VTX 32
|
|
|
|
static uint8_t __attribute__((unused)) cx10wd_getButtons()
|
|
{
|
|
#define CX10WD_FLAG_LAND 0x20
|
|
#define CX10D_FLAG_LAND 0x80
|
|
#define CX10WD_FLAG_TAKEOFF 0x40
|
|
|
|
static uint8_t BTN_state;
|
|
static uint8_t command;
|
|
|
|
// startup
|
|
if(packet_count < 50)
|
|
{
|
|
BTN_state = 0;
|
|
command = 0;
|
|
packet_count++;
|
|
}
|
|
// auto land
|
|
else if((Channel_data[CH5]<CHANNEL_MIN_COMMAND) && !(BTN_state & Q303_BTN_DESCEND))
|
|
{
|
|
BTN_state |= Q303_BTN_DESCEND;
|
|
BTN_state &= ~Q303_BTN_TAKEOFF;
|
|
switch(sub_protocol)
|
|
{
|
|
case CX10WD:
|
|
command ^= CX10WD_FLAG_LAND;
|
|
break;
|
|
case CX10D:
|
|
command ^= CX10D_FLAG_LAND;
|
|
break;
|
|
}
|
|
}
|
|
// auto take off
|
|
else if(CH5_SW && !(BTN_state & Q303_BTN_TAKEOFF))
|
|
{
|
|
BTN_state |= Q303_BTN_TAKEOFF;
|
|
BTN_state &= ~Q303_BTN_DESCEND;
|
|
command ^= CX10WD_FLAG_TAKEOFF;
|
|
}
|
|
|
|
return command;
|
|
}
|
|
|
|
static uint8_t __attribute__((unused)) cx35_lastButton()
|
|
{
|
|
#define CX35_CMD_RATE 0x09
|
|
#define CX35_CMD_TAKEOFF 0x0e
|
|
#define CX35_CMD_DESCEND 0x0f
|
|
#define CX35_CMD_SNAPSHOT 0x0b
|
|
#define CX35_CMD_VIDEO 0x0c
|
|
#define CX35_CMD_RTH 0x11
|
|
#define CX35_CMD_VTX 0x10
|
|
|
|
static uint8_t BTN_state;
|
|
static uint8_t command;
|
|
// simulate 2 keypress on rate button just after bind
|
|
if(packet_count < 50)
|
|
{
|
|
BTN_state = 0;
|
|
packet_count++;
|
|
command = 0x00; // startup
|
|
}
|
|
else if(packet_count < 150)
|
|
{
|
|
packet_count++;
|
|
command = CX35_CMD_RATE; // 1st keypress
|
|
}
|
|
else if(packet_count < 250)
|
|
{
|
|
packet_count++;
|
|
command |= 0x20; // 2nd keypress
|
|
}
|
|
// descend
|
|
else if(!(GET_FLAG(CH5_SW, 1)) && !(BTN_state & Q303_BTN_DESCEND))
|
|
{
|
|
BTN_state |= Q303_BTN_DESCEND;
|
|
BTN_state &= ~Q303_BTN_TAKEOFF;
|
|
command = CX35_CMD_DESCEND;
|
|
}
|
|
// take off
|
|
else if(GET_FLAG(CH5_SW,1) && !(BTN_state & Q303_BTN_TAKEOFF))
|
|
{
|
|
BTN_state |= Q303_BTN_TAKEOFF;
|
|
BTN_state &= ~Q303_BTN_DESCEND;
|
|
command = CX35_CMD_TAKEOFF;
|
|
}
|
|
// RTH
|
|
else if(GET_FLAG(CH10_SW,1) && !(BTN_state & Q303_BTN_RTH))
|
|
{
|
|
BTN_state |= Q303_BTN_RTH;
|
|
if(command == CX35_CMD_RTH)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_RTH;
|
|
}
|
|
else if(!(GET_FLAG(CH10_SW,1)) && (BTN_state & Q303_BTN_RTH))
|
|
{
|
|
BTN_state &= ~Q303_BTN_RTH;
|
|
if(command == CX35_CMD_RTH)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_RTH;
|
|
}
|
|
// video
|
|
else if(GET_FLAG(CH8_SW,1) && !(BTN_state & Q303_BTN_VIDEO))
|
|
{
|
|
BTN_state |= Q303_BTN_VIDEO;
|
|
if(command == CX35_CMD_VIDEO)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_VIDEO;
|
|
}
|
|
else if(!(GET_FLAG(CH8_SW,1)) && (BTN_state & Q303_BTN_VIDEO))
|
|
{
|
|
BTN_state &= ~Q303_BTN_VIDEO;
|
|
if(command == CX35_CMD_VIDEO)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_VIDEO;
|
|
}
|
|
// snapshot
|
|
else if(GET_FLAG(CH7_SW,1) && !(BTN_state & Q303_BTN_SNAPSHOT))
|
|
{
|
|
BTN_state |= Q303_BTN_SNAPSHOT;
|
|
if(command == CX35_CMD_SNAPSHOT)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_SNAPSHOT;
|
|
}
|
|
// vtx channel
|
|
else if(GET_FLAG(CH6_SW,1) && !(BTN_state & Q303_BTN_VTX))
|
|
{
|
|
BTN_state |= Q303_BTN_VTX;
|
|
if(command == CX35_CMD_VTX)
|
|
command |= 0x20;
|
|
else
|
|
command = CX35_CMD_VTX;
|
|
}
|
|
|
|
if(!(GET_FLAG(CH7_SW,1)))
|
|
BTN_state &= ~Q303_BTN_SNAPSHOT;
|
|
if(!(GET_FLAG(CH6_SW,1)))
|
|
BTN_state &= ~Q303_BTN_VTX;
|
|
|
|
return command;
|
|
}
|
|
|
|
static void __attribute__((unused)) Q303_send_packet(uint8_t bind)
|
|
{
|
|
uint16_t aileron, elevator, throttle, rudder, slider;
|
|
if(bind)
|
|
{
|
|
packet[0] = 0xaa;
|
|
memcpy(&packet[1], rx_tx_addr + 1, 4);
|
|
memset(&packet[5], 0, packet_length-5);
|
|
}
|
|
else
|
|
{
|
|
packet[0] = 0x55;
|
|
// sticks
|
|
switch(sub_protocol)
|
|
{
|
|
case Q303:
|
|
case CX35:
|
|
aileron = convert_channel_16b_limit(AILERON, 0, 1000);
|
|
elevator = convert_channel_16b_limit(ELEVATOR, 1000, 0);
|
|
throttle = convert_channel_16b_limit(THROTTLE, 0, 1000);
|
|
rudder = convert_channel_16b_limit(RUDDER, 1000, 0);
|
|
if(sub_protocol == CX35)
|
|
aileron = 1000 - aileron;
|
|
packet[1] = aileron >> 2; // 8 bits
|
|
packet[2] = (aileron & 0x03) << 6 // 2 bits
|
|
| (elevator >> 4); // 6 bits
|
|
packet[3] = (elevator & 0x0f) << 4 // 4 bits
|
|
| (throttle >> 6); // 4 bits
|
|
packet[4] = (throttle & 0x3f) << 2 // 6 bits
|
|
| (rudder >> 8); // 2 bits
|
|
packet[5] = rudder & 0xff; // 8 bits
|
|
break;
|
|
case CX10D:
|
|
case CX10WD:
|
|
aileron = convert_channel_16b_limit(AILERON, 2000, 1000);
|
|
elevator = convert_channel_16b_limit(ELEVATOR, 2000, 1000);
|
|
throttle = convert_channel_16b_limit(THROTTLE, 1000, 2000);
|
|
rudder = convert_channel_16b_limit(RUDDER, 1000, 2000);
|
|
packet[1] = aileron & 0xff;
|
|
packet[2] = aileron >> 8;
|
|
packet[3] = elevator & 0xff;
|
|
packet[4] = elevator >> 8;
|
|
packet[5] = throttle & 0xff;
|
|
packet[6] = throttle >> 8;
|
|
packet[7] = rudder & 0xff;
|
|
packet[8] = rudder >> 8;
|
|
break;
|
|
}
|
|
|
|
// buttons
|
|
switch(sub_protocol)
|
|
{
|
|
case Q303:
|
|
packet[6] = 0x10; // trim(s) ?
|
|
packet[7] = 0x10; // trim(s) ?
|
|
packet[8] = 0x03 // high rate (0-3)
|
|
| GET_FLAG(CH5_SW, 0x40)
|
|
| GET_FLAG(CH10_SW, 0x80);
|
|
packet[9] = 0x40 // always set
|
|
| GET_FLAG(CH9_SW,0x08)
|
|
| GET_FLAG(CH6_SW, 0x80)
|
|
| GET_FLAG(CH7_SW,0x10)
|
|
| GET_FLAG(CH8_SW, 0x01);
|
|
if(Channel_data[CH11] < CHANNEL_MIN_COMMAND)
|
|
packet[9] |= 0x04; // gimbal down
|
|
else if(CH11_SW)
|
|
packet[9] |= 0x20; // gimbal up
|
|
break;
|
|
|
|
case CX35:
|
|
slider = convert_channel_16b_limit(CH11, 731, 342);
|
|
packet[6] = slider >> 2;
|
|
packet[7] = ((slider & 3) << 6)
|
|
| 0x3e; // ?? 6 bit left (always 111110 ?)
|
|
packet[8] = 0x80; // always set
|
|
packet[9] = cx35_lastButton();
|
|
break;
|
|
|
|
case CX10D:
|
|
packet[8] |= GET_FLAG(CH6_SW, 0x10);
|
|
packet[9] = 0x02; // rate (0-2)
|
|
packet[10]= cx10wd_getButtons(); // auto land / take off management
|
|
break;
|
|
|
|
case CX10WD:
|
|
packet[8] |= GET_FLAG(CH6_SW, 0x10);
|
|
packet[9] = 0x02 // rate (0-2)
|
|
| cx10wd_getButtons(); // auto land / take off management
|
|
packet[10] = 0x00;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Power on, TX mode, CRC enabled
|
|
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
|
|
|
NRF24L01_WriteReg(NRF24L01_05_RF_CH, bind ? Q303_RF_BIND_CHANNEL : hopping_frequency[hopping_frequency_no++]);
|
|
hopping_frequency_no %= rf_ch_num;
|
|
|
|
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
|
NRF24L01_FlushTx();
|
|
|
|
XN297_WritePayload(packet, packet_length);
|
|
|
|
NRF24L01_SetPower(); // Set tx_power
|
|
}
|
|
|
|
static void __attribute__((unused)) Q303_init()
|
|
{
|
|
const uint8_t bind_address[] = {0xcc,0xcc,0xcc,0xcc,0xcc};
|
|
|
|
NRF24L01_Initialize();
|
|
NRF24L01_SetTxRxMode(TX_EN);
|
|
switch(sub_protocol)
|
|
{
|
|
case CX35:
|
|
case CX10D:
|
|
case CX10WD:
|
|
XN297_SetScrambledMode(XN297_SCRAMBLED);
|
|
NRF24L01_SetBitrate(NRF24L01_BR_1M);
|
|
break;
|
|
case Q303:
|
|
XN297_SetScrambledMode(XN297_UNSCRAMBLED);
|
|
NRF24L01_SetBitrate(NRF24L01_BR_250K);
|
|
break;
|
|
}
|
|
XN297_SetTXAddr(bind_address, 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);
|
|
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03);
|
|
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
|
|
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); // Set feature bits on
|
|
NRF24L01_Activate(0x73);
|
|
}
|
|
|
|
static void __attribute__((unused)) Q303_initialize_txid()
|
|
{
|
|
uint8_t i,offset;
|
|
|
|
rx_tx_addr[0] = 0x55;
|
|
|
|
switch(sub_protocol)
|
|
{
|
|
case Q303:
|
|
case CX10WD:
|
|
offset = rx_tx_addr[1] & 3;
|
|
for(i=0; i<4; i++)
|
|
hopping_frequency[i] = 0x46 + i*2 + offset;
|
|
break;
|
|
case CX35:
|
|
case CX10D:
|
|
// not thoroughly figured out rx_tx_addr/channels mapping yet
|
|
// for now 5 msb of rx_tx_addr[1] must be cleared
|
|
rx_tx_addr[1] &= 7;
|
|
offset = 6+(rx_tx_addr[1]*3);
|
|
hopping_frequency[0] = 0x14; // works only if rx_tx_addr[1] < 8
|
|
for(i=1; i<16; i++)
|
|
{
|
|
hopping_frequency[i] = hopping_frequency[i-1] + offset;
|
|
if(hopping_frequency[i] > 0x41)
|
|
hopping_frequency[i] -= 0x33;
|
|
if(hopping_frequency[i] < 0x14)
|
|
hopping_frequency[i] += offset;
|
|
}
|
|
// CX35 tx uses only 4 of those channels (#0,3,6,9)
|
|
if(sub_protocol == CX35)
|
|
for(i=0; i<4; i++)
|
|
hopping_frequency[i] = hopping_frequency[i*3];
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint16_t Q303_callback()
|
|
{
|
|
if(IS_BIND_DONE)
|
|
Q303_send_packet(0);
|
|
else
|
|
{
|
|
if (bind_counter == 0)
|
|
{
|
|
XN297_SetTXAddr(rx_tx_addr, 5);
|
|
packet_count = 0;
|
|
BIND_DONE;
|
|
}
|
|
else
|
|
{
|
|
Q303_send_packet(1);
|
|
bind_counter--;
|
|
}
|
|
}
|
|
return packet_period;
|
|
}
|
|
|
|
uint16_t initQ303()
|
|
{
|
|
Q303_initialize_txid();
|
|
Q303_init();
|
|
bind_counter = Q303_BIND_COUNT;
|
|
switch(sub_protocol)
|
|
{
|
|
case Q303:
|
|
packet_period = 1500;
|
|
packet_length = 10;
|
|
rf_ch_num = 4;
|
|
break;
|
|
case CX35:
|
|
packet_period = 3000;
|
|
packet_length = 10;
|
|
rf_ch_num = 4;
|
|
break;
|
|
case CX10D:
|
|
packet_period = 3000;
|
|
packet_length = 11;
|
|
rf_ch_num = 16;
|
|
break;
|
|
case CX10WD:
|
|
packet_period = 3000;
|
|
packet_length = 11;
|
|
rf_ch_num = 4;
|
|
break;
|
|
}
|
|
hopping_frequency_no = 0;
|
|
BIND_IN_PROGRESS; // autobind protocol
|
|
return Q303_INITIAL_WAIT;
|
|
}
|
|
|
|
#endif
|