Core and all protocols have been updated

Lot of changes in this new master
ChangeLog:
- Core: LED flashing when an invalid protocol has been selected
- Core: Channels 5 to 12 available as switches for all protocols: code
and size optimization
- Documentation (readme.md): fully updated, all protocols/sub
protocols/channels described, models example, many improvements
- All protocols have been updated in some way, here are some highlights:
* Bayang: added picture, video and inverted channels
* CG023->H8_3D: added light and calibration channels
* CX10: added sub protocols Q282, JC3015_1, JC3015_2, MK33041
* ESky: added new protocol - untested
* Hubsan: added compatibility with the new Hubsan Plus protocol
* KN: fully rewritten protocol: added sub protocols WLTOYS and FEILUN,
11 channels support

New version successfully tested on all my models: Flysky RX/F939/V911
protocol Flysky, Frsky RX protocol Frsky, Hubsan X4 protocol Hubsan,
Hisky HCP100/HCP80 protocol Hisky, HK-3000/HK3100 RX protocol
Hisky/HK310, XINXUN X39 protocol YD717/XINXUN, Symax X5C-1 protocol
SymaX/SYMAX, Cheerson CX-10A protocol CX10/BLUE, Eachine 3D-X4 protocol
CG023.

To access new protocols from er9x/ersky9x, you need to build a version
from this github repository https://github.com/pascallanger/mbtx based
on the latest er9x r820 and ersky9x r218.
This commit is contained in:
pascallanger
2016-01-20 10:50:56 +01:00
parent 481d4c15d6
commit bc42dbf88a
23 changed files with 1199 additions and 798 deletions

View File

@@ -12,53 +12,248 @@
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Last sync with hexfet new_protocols/KN_nrf24l01.c dated 2015-11-09
#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)
// Wait for RX chip stable - 10ms
#define KN_INIT_WAIT_MS 10000
//#define PAYLOADSIZE 16
#define NFREQCHANNELS 4
#define KN_TXID_SIZE 4
//Payload(16 bytes) plus overhead(10 bytes) is 208 bits, takes about 0.4ms or 0.2ms
//to send for the rate of 500kb/s and 1Mb/s respectively.
// Callback timeout period for sending bind packets, minimum 250
#define KN_BINDING_PACKET_PERIOD 1000
// Timeout for sending data packets, in uSec, KN protocol requires 2ms
#define KN_WL_SENDING_PACKET_PERIOD 2000
// Timeout for sending data packets, in uSec, KNFX protocol requires 1.2 ms
#define KN_FX_SENDING_PACKET_PERIOD 1200
// packets to be sent during binding, last 0.5 seconds in WL Toys and 0.2 seconds in Feilun
#define KN_WL_BIND_COUNT 500
#define KN_FX_BIND_COUNT 200
#define KN_PAYLOADSIZE 16
//24L01 has 126 RF channels, can we use all of them?
#define KN_MAX_RF_CHANNEL 73
//KN protocol for WL Toys changes RF frequency every 10 ms, repeats with only 4 channels.
//Feilun variant uses only 2 channels, so we will just repeat the hopping channels later
#define KN_RF_CH_COUNT 4
//KN protocol for WL Toys sends 4 data packets every 2ms per frequency, plus a 2ms gap.
#define KN_WL_PACKET_SEND_COUNT 5
//KN protocol for Feilun sends 8 data packets every 1.2ms per frequency, plus a 0.3ms gap.
#define KN_FX_PACKET_SEND_COUNT 8
#define KN_TX_ADDRESS_SIZE 5
enum {
KN_FLAG_DR = 0x01, // Dual Rate
KN_FLAG_TH = 0x02, // Throttle Hold
KN_FLAG_IDLEUP = 0x04, // Idle up
KN_PHASE_PRE_BIND,
KN_PHASE_BINDING,
KN_PHASE_PRE_SEND,
KN_PHASE_SENDING,
};
enum {
KN_FLAG_DR = 0x01, // Dual Rate: 1 - full range
KN_FLAG_TH = 0x02, // Throttle Hold: 1 - hold
KN_FLAG_IDLEUP = 0x04, // Idle up: 1 - 3D
KN_FLAG_RES1 = 0x08,
KN_FLAG_RES2 = 0x10,
KN_FLAG_RES3 = 0x20,
KN_FLAG_GYRO3 = 0x40, // 00 - 6G mode, 01 - 3G mode
KN_FLAG_GYRO3 = 0x40, // 0 - 6G mode, 1 - 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()
//================================================================================================
// Private Functions
//================================================================================================
uint16_t initKN()
{
if(sub_protocol==WLTOYS)
{
packet_period = KN_WL_SENDING_PACKET_PERIOD;
bind_counter = KN_WL_BIND_COUNT;
packet_count = KN_WL_PACKET_SEND_COUNT;
}
else
{
packet_period = KN_FX_SENDING_PACKET_PERIOD;
bind_counter = KN_FX_BIND_COUNT;
packet_count = KN_FX_PACKET_SEND_COUNT;
}
kn_init();
phase = IS_AUTOBIND_FLAG_on ? KN_PHASE_PRE_BIND : KN_PHASE_PRE_SEND;
return KN_INIT_WAIT_MS;
}
uint16_t kn_callback()
{
switch (phase)
{
case KN_PHASE_PRE_BIND:
kn_bind_init();
phase=KN_PHASE_BINDING;
//Do once, no break needed
case KN_PHASE_BINDING:
if(bind_counter>0)
{
bind_counter--;
NRF24L01_WritePayload(packet, KN_PAYLOADSIZE);
return KN_BINDING_PACKET_PERIOD;
}
BIND_DONE;
//Continue
case KN_PHASE_PRE_SEND:
packet_sent = 0;
hopping_frequency_no = 0;
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, KN_TX_ADDRESS_SIZE);
phase = KN_PHASE_SENDING;
//Do once, no break needed
case KN_PHASE_SENDING:
if(packet_sent >= packet_count)
{
packet_sent = 0;
hopping_frequency_no++;
if(hopping_frequency_no >= KN_RF_CH_COUNT) hopping_frequency_no = 0;
kn_update_packet_control_data();
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
}
else
{
// Update sending count and RF channel index.
// The protocol sends 4 data packets every 2ms per frequency, plus a 2ms gap.
// Each data packet need a packet number and RF channel index
packet[13] = 0x00;
if(sub_protocol==WLTOYS)
packet[13] = (packet_sent << 5) | (hopping_frequency_no << 2);
}
NRF24L01_WritePayload(packet, KN_PAYLOADSIZE);
packet_sent++;
return packet_period;
} //switch
//Bad things happened, reset
packet_sent = 0;
phase = KN_PHASE_PRE_SEND;
return packet_period;
}
//-------------------------------------------------------------------------------------------------
// This function init 24L01 regs and packet data for binding
// Send tx address, hopping table (for Wl Toys), and data rate to the KN receiver during binding.
// It seems that KN can remember these parameters, no binding needed after power up.
// Bind uses fixed TX address "KNDZK", 1 Mbps data rate and channel 83
//-------------------------------------------------------------------------------------------------
static void kn_bind_init()
{
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"KNDZK", 5);
packet[0] = 'K';
packet[1] = 'N';
packet[2] = 'D';
packet[3] = 'Z';
//Use first four bytes of tx_addr
packet[4] = rx_tx_addr[0];
packet[5] = rx_tx_addr[1];
packet[6] = rx_tx_addr[2];
packet[7] = rx_tx_addr[3];
if(sub_protocol==WLTOYS)
{
packet[8] = hopping_frequency[0];
packet[9] = hopping_frequency[1];
packet[10] = hopping_frequency[2];
packet[11] = hopping_frequency[3];
}
else
{
packet[8] = 0x00;
packet[9] = 0x00;
packet[10] = 0x00;
packet[11] = 0x00;
}
packet[12] = 0x00;
packet[13] = 0x00;
packet[14] = 0x00;
packet[15] = 0x01; //(USE1MBPS_YES) ? 0x01 : 0x00;
//Set RF channel
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 83);
}
//-------------------------------------------------------------------------------------------------
// Update control data to be sent
// Do it once per frequency, so the same values will be sent 4 times
// KN uses 4 10-bit data channels plus a 8-bit switch channel
//
// The packet[0] is used for pitch/throttle, the relation is hard coded, not changeable.
// We can change the throttle/pitch range though.
//
// How to use trim? V977 stock controller can trim 6-axis mode to eliminate the drift.
//-------------------------------------------------------------------------------------------------
static void kn_update_packet_control_data()
{
uint16_t value;
value = convert_channel_10b(THROTTLE);
packet[0] = (value >> 8) & 0xFF;
packet[1] = value & 0xFF;
value = convert_channel_10b(AILERON);
packet[2] = (value >> 8) & 0xFF;
packet[3] = value & 0xFF;
value = convert_channel_10b(ELEVATOR);
packet[4] = (value >> 8) & 0xFF;
packet[5] = value & 0xFF;
value = convert_channel_10b(RUDDER);
packet[6] = (value >> 8) & 0xFF;
packet[7] = value & 0xFF;
// Trims, middle is 0x64 (100) range 0-200
packet[8] = convert_channel_8b_scale(AUX5,0,200); // 0x64; // T
packet[9] = convert_channel_8b_scale(AUX6,0,200); // 0x64; // A
packet[10] = convert_channel_8b_scale(AUX7,0,200); // 0x64; // E
packet[11] = 0x64; // R
flags=0;
if (Servo_data[AUX1] > PPM_SWITCH)
flags = KN_FLAG_DR;
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] = 0x00;
if(sub_protocol==WLTOYS)
packet[13] = (packet_sent << 5) | (hopping_frequency_no << 2);
packet[14] = 0x00;
packet[15] = 0x00;
NRF24L01_SetPower();
}
//-------------------------------------------------------------------------------------------------
// This function setup 24L01
// V977 uses one way communication, receiving only. 24L01 RX is never enabled.
// V977 needs payload length in the packet. We should configure 24L01 to enable Packet Control Field(PCF)
// Some RX reg settings are actually for enable PCF
//-------------------------------------------------------------------------------------------------
static void kn_init()
{
kn_calculate_tx_addr();
kn_calculate_freqency_hopping_channels();
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG);
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, 0x01); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
@@ -68,207 +263,84 @@ void kn_init()
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 0x20); // bytes of data payload for pipe 0
NRF24L01_Activate(0x73); // Activate feature register
NRF24L01_Activate(0x73);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
// Enable: Dynamic Payload Length, Payload with ACK , W_TX_PAYLOAD_NOACK
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, BV(NRF2401_1D_EN_DPL) | BV(NRF2401_1D_EN_ACK_PAY) | BV(NRF2401_1D_EN_DYN_ACK));
NRF24L01_Activate(0x73);
}
// Enable: Dynamic Payload Length to enable PCF
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, BV(NRF2401_1D_EN_DPL));
NRF24L01_SetPower();
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;
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_SetBitrate(NRF24L01_BR_1M); //USE1MBPS_YES ? NRF24L01_BR_1M : NRF24L01_BR_250K;
}
void set_tx_for_bind()
//-------------------------------------------------------------------------------------------------
// This function generate RF TX packet address
// V977 can remember the binding parameters; we do not need rebind when power up.
// This requires the address must be repeatable for a specific RF ID at power up.
//-------------------------------------------------------------------------------------------------
static void kn_calculate_tx_addr()
{
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)
if(sub_protocol==FEILUN)
{
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;
uint8_t addr2;
// Generate TXID with sum of minimum 256 and maximum 256+MAX_RF_CHANNEL-32
rx_tx_addr[1] = 1 + rx_tx_addr[0] % (KN_MAX_RF_CHANNEL-33);
addr2 = 1 + rx_tx_addr[2] % (KN_MAX_RF_CHANNEL-33);
if ((uint16_t)(rx_tx_addr[0] + rx_tx_addr[1]) < 256)
rx_tx_addr[2] = addr2;
else
rx_tx_addr[2] = 0x00;
rx_tx_addr[3] = 0x00;
while((uint16_t)(rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2] + rx_tx_addr[3]) < 257)
rx_tx_addr[3] += addr2;
}
return timeout;
//The 5th byte is a constant, must be 'K'
rx_tx_addr[4] = 'K';
}
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();
//-------------------------------------------------------------------------------------------------
// This function generates "random" RF hopping frequency channel numbers.
// These numbers must be repeatable for a specific seed
// The generated number range is from 0 to MAX_RF_CHANNEL. No repeat or adjacent numbers
//
// For Feilun variant, the channels are calculated from TXID, and since only 2 channels are used
// we copy them to fill up to MAX_RF_CHANNEL
//-------------------------------------------------------------------------------------------------
static void kn_calculate_freqency_hopping_channels()
{
if(sub_protocol==WLTOYS)
{
uint8_t idx = 0;
uint32_t rnd = MProtocol_id;
while (idx < KN_RF_CH_COUNT)
{
uint8_t i;
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
// Call callback in 50ms
return 50000;
// Use least-significant byte. 73 is prime, so channels 76..77 are unused
uint8_t next_ch = ((rnd >> 8) % KN_MAX_RF_CHANNEL) + 2;
// Keep the distance 2 between the channels - either odd or even
if (((next_ch ^ MProtocol_id) & 0x01 )== 0)
continue;
// Check that it's not duplicate and spread uniformly
for (i = 0; i < idx; i++)
if(hopping_frequency[i] == next_ch)
break;
if (i != idx)
continue;
hopping_frequency[idx++] = next_ch;
}
}
else
{//FEILUN
hopping_frequency[0] = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2] + rx_tx_addr[3]; // - 256; ???
hopping_frequency[1] = hopping_frequency[0] + 32;
hopping_frequency[2] = hopping_frequency[0];
hopping_frequency[3] = hopping_frequency[1];
}
}
#endif