2016-03-01 22:12:10 +01:00

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