1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-24 16:55:29 +03:00

Tidy up of all nrf24 protocols.

This commit is contained in:
Martin Budden 2016-05-26 15:34:29 +01:00
parent 04205e2cda
commit dda73ff689
9 changed files with 580 additions and 306 deletions

View file

@ -405,6 +405,8 @@ COMMON_SRC = \
drivers/pwm_mapping.c \
drivers/pwm_output.c \
drivers/pwm_rx.c \
drivers/rx_nrf24l01.c \
drivers/rx_xn297.c \
drivers/serial.c \
drivers/serial_uart.c \
drivers/sound_beeper.c \

View file

@ -383,6 +383,7 @@ static const char * const lookupTableSerialRX[] = {
#endif
#ifdef USE_RX_NRF24
// sync with nrf24_protocol_t
static const char * const lookupTableNRF24RX[] = {
"V202_250K",
"V202_1M",
@ -391,6 +392,7 @@ static const char * const lookupTableNRF24RX[] = {
"CX10",
"CX10A",
"H8_3D",
"H8_3D_DEV",
};
#endif

View file

@ -30,6 +30,7 @@ typedef enum {
NRF24RX_CX10,
NRF24RX_CX10A,
NRF24RX_H8_3D,
NRF24RX_H8_3D_DEVIATION,
NRF24RX_PROTOCOL_COUNT
} nrf24_protocol_t;
@ -61,6 +62,13 @@ typedef enum {
NRF24_AUX14
} nrf24_AETR_t;
// RC channels as used by deviation
#define RC_CHANNEL_RATE NRF24_AUX1
#define RC_CHANNEL_FLIP NRF24_AUX2
#define RC_CHANNEL_PICTURE NRF24_AUX3
#define RC_CHANNEL_VIDEO NRF24_AUX4
#define RC_CHANNEL_HEADLESS NRF24_AUX5
#define RC_CHANNEL_RTH NRF24_AUX6
bool rxNrf24DataReceived(void);
bool rxNrf24Init(nfr24l01_spi_type_e spiType, const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig, rcReadRawDataPtr *callback);

View file

@ -54,12 +54,13 @@
* hops between 4 channels that are set from the txId sent in the bind packet
*/
#define CX10_RC_CHANNEL_COUNT 9
#define RC_CHANNEL_COUNT 9
#define CX10_PROTOCOL_PAYLOAD_SIZE 15
#define CX10A_PROTOCOL_PAYLOAD_SIZE 19
#define CX10_RF_BIND_CHANNEL 0x02
enum {
RATE_LOW = 0,
RATE_MID = 1,
RATE_HIGH= 2,
};
#define FLAG_FLIP 0x10 // goes to rudder channel
// flags1
@ -79,10 +80,10 @@ typedef enum {
STATIC_UNIT_TESTED protocol_state_t protocolState;
#define ACK_TO_SEND_COUNT 8
#define CX10_PROTOCOL_PAYLOAD_SIZE 15
#define CX10A_PROTOCOL_PAYLOAD_SIZE 19
static uint8_t payloadSize;
#define ACK_TO_SEND_COUNT 8
#define RX_TX_ADDR_LEN 5
//STATIC_UNIT_TESTED uint8_t rxTxAddr[RX_TX_ADDR_LEN] = {0xcc, 0xcc, 0xcc, 0xcc, 0xcc};
@ -91,46 +92,20 @@ STATIC_UNIT_TESTED uint8_t rxAddr[RX_TX_ADDR_LEN] = {0x49, 0x26, 0x87, 0x7d, 0x2
#define TX_ID_LEN 4
STATIC_UNIT_TESTED uint8_t txId[TX_ID_LEN];
STATIC_UNIT_TESTED uint8_t cx10RfChannelIndex = 0;
#define CX10_RF_BIND_CHANNEL 0x02
#define RF_CHANNEL_COUNT 4
STATIC_UNIT_TESTED uint8_t cx10RfChannelIndex = 0;
STATIC_UNIT_TESTED uint8_t cx10RfChannels[RF_CHANNEL_COUNT]; // channels are set using txId from bind packet
static uint32_t timeOfLastHop;
static const uint32_t hopTimeout = 5000; // 5ms
void cx10Nrf24Init(nrf24_protocol_t protocol)
{
cx10Protocol = protocol;
protocolState = STATE_BIND;
payloadSize = (protocol == NRF24RX_CX10) ? CX10_PROTOCOL_PAYLOAD_SIZE : CX10A_PROTOCOL_PAYLOAD_SIZE;
NRF24L01_Initialize(0); // sets PWR_UP, no CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0);
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0)); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_SetChannel(CX10_RF_BIND_CHANNEL);
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
// RX_ADDR for pipes P2 to P5 are left at default values
NRF24L01_FlushRx();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, txAddr, RX_TX_ADDR_LEN);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxAddr, RX_TX_ADDR_LEN);
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payloadSize + 2); // payload + 2 bytes CRC
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
/*
* Returns true if it is a bind packet.
*/
STATIC_UNIT_TESTED bool cx10CheckBindPacket(const uint8_t *packet)
{
const bool bindPacket = (packet[0] == 0xaa);
const bool bindPacket = (packet[0] == 0xaa); // 10101010
if (bindPacket) {
txId[0] = packet[1];
txId[1] = packet[2];
@ -157,22 +132,22 @@ void cx10SetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload)
rcData[NRF24_YAW] = cx10ConvertToPwmUnsigned(&payload[11 + offset]); // rudder
const uint8_t flags1 = payload[13 + offset];
const uint8_t rate = flags1 & FLAG_MODE_MASK; // takes values 0, 1, 2
if (rate == 0) {
rcData[NRF24_AUX1] = PWM_RANGE_MIN;
} else if (rate == 1) {
rcData[NRF24_AUX1] = PWM_RANGE_MIDDLE;
if (rate == RATE_LOW) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIN;
} else if (rate == RATE_MID) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIDDLE;
} else {
rcData[NRF24_AUX1] = PWM_RANGE_MAX;
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MAX;
}
// flip flag is in YAW byte
rcData[NRF24_AUX2] = payload[12 + offset] & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_FLIP] = payload[12 + offset] & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
const uint8_t flags2 = payload[14 + offset];
rcData[NRF24_AUX3] = flags2 & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX4] = flags2 & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX5] = flags1 & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_PICTURE] = flags2 & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_VIDEO] = flags2 & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_HEADLESS] = flags1 & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
}
static void hopToNextChannel(void)
static void cx10HopToNextChannel(void)
{
++cx10RfChannelIndex;
if (cx10RfChannelIndex >= RF_CHANNEL_COUNT) {
@ -182,7 +157,7 @@ static void hopToNextChannel(void)
}
// The hopping channels are determined by the txId
STATIC_UNIT_TESTED void setHoppingChannels(const uint8_t* txId)
STATIC_UNIT_TESTED void cx10SetHoppingChannels(const uint8_t* txId)
{
cx10RfChannelIndex = 0;
cx10RfChannels[0] = 0x03 + (txId[0] & 0x0F);
@ -209,7 +184,7 @@ nrf24_received_t cx10DataReceived(uint8_t *payload)
const bool bindPacket = cx10CheckBindPacket(payload);
if (bindPacket) {
// set the hopping channels as determined by the txId received in the bind packet
setHoppingChannels(txId);
cx10SetHoppingChannels(txId);
ret = NRF24_RECEIVED_BIND;
protocolState = STATE_ACK;
ackCount = 0;
@ -259,21 +234,48 @@ nrf24_received_t cx10DataReceived(uint8_t *payload)
// read the payload, processing of payload is deferred
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize + 2)) {
XN297_UnscramblePayload(payload, payloadSize + 2);
hopToNextChannel();
cx10HopToNextChannel();
timeOfLastHop = timeNowUs;
ret = NRF24_RECEIVED_DATA;
}
if (timeNowUs > timeOfLastHop + hopTimeout) {
hopToNextChannel();
cx10HopToNextChannel();
timeOfLastHop = timeNowUs;
}
}
return ret;
}
void cx10Nrf24Init(nrf24_protocol_t protocol)
{
cx10Protocol = protocol;
protocolState = STATE_BIND;
payloadSize = (protocol == NRF24RX_CX10) ? CX10_PROTOCOL_PAYLOAD_SIZE : CX10A_PROTOCOL_PAYLOAD_SIZE;
NRF24L01_Initialize(0); // sets PWR_UP, no CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0);
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0)); // Enable data pipe 0 only
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_SetChannel(CX10_RF_BIND_CHANNEL);
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
// RX_ADDR for pipes P2 to P5 are left at default values
NRF24L01_FlushRx();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, txAddr, RX_TX_ADDR_LEN);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxAddr, RX_TX_ADDR_LEN);
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payloadSize + 2); // payload + 2 bytes CRC
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
void cx10Init(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
rxRuntimeConfig->channelCount = CX10_RC_CHANNEL_COUNT;
rxRuntimeConfig->channelCount = RC_CHANNEL_COUNT;
cx10Nrf24Init((nrf24_protocol_t)rxConfig->nrf24rx_protocol);
}
#endif

View file

@ -56,11 +56,7 @@
* hops between 4 channels generated from txId received in bind packets
*
*/
#define H8_3D_RC_CHANNEL_COUNT 14
#define H8_3D_X_PROTOCOL_PAYLOAD_SIZE 20
#define H8_3D_RF_CHANNEL_COUNT 4
#define RC_CHANNEL_COUNT 14
#define FLAG_FLIP 0x01
#define FLAG_RATE_MID 0x02
@ -72,6 +68,8 @@
#define FLAG_CAMERA_UP 0x04 // on payload[18]
#define FLAG_CAMERA_DOWN 0x08 // on payload[18]
STATIC_UNIT_TESTED nrf24_protocol_t h8_3dProtocol;
typedef enum {
STATE_BIND = 0,
STATE_DATA
@ -79,6 +77,7 @@ typedef enum {
STATIC_UNIT_TESTED protocol_state_t protocolState;
#define H8_3D_PROTOCOL_PAYLOAD_SIZE 20
STATIC_UNIT_TESTED uint8_t payloadSize;
#define RX_TX_ADDR_LEN 5
@ -88,8 +87,9 @@ STATIC_UNIT_TESTED uint8_t rxTxAddrXN297[RX_TX_ADDR_LEN] = {0x41, 0xbd, 0x42, 0x
STATIC_UNIT_TESTED uint8_t txId[TX_ID_LEN];
// radio channels for frequency hopping
STATIC_UNIT_TESTED uint8_t h8_3dRfChannelIndex;
#define H8_3D_RF_CHANNEL_COUNT 4
STATIC_UNIT_TESTED uint8_t h8_3dRfChannelCount = H8_3D_RF_CHANNEL_COUNT;
STATIC_UNIT_TESTED uint8_t h8_3dRfChannelIndex;
STATIC_UNIT_TESTED uint8_t h8_3dRfChannels[H8_3D_RF_CHANNEL_COUNT];
#define H8_3D_RF_BIND_CHANNEL_START 0x06
#define H8_3D_RF_BIND_CHANNEL_END 0x26
@ -99,37 +99,6 @@ STATIC_UNIT_TESTED uint8_t h8_3dRfChannels[H8_3D_RF_CHANNEL_COUNT];
static uint32_t hopTimeout = BIND_HOP_TIMEOUT;
static uint32_t timeOfLastHop;
void h8_3dSetBound(const uint8_t* txId);
void h8_3dNrf24Init(nrf24_protocol_t protocol, const uint8_t* nrf24_id)
{
UNUSED(protocol);
protocolState = STATE_BIND;
NRF24L01_Initialize(0); // sets PWR_UP, no CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0); // No auto acknowledgment
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
// RX_ADDR for pipes P1-P5 are left at default values
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddrXN297, RX_TX_ADDR_LEN);
if ((nrf24_id[0] | nrf24_id[1] | nrf24_id[2] | nrf24_id[3]) == 0) {
h8_3dRfChannelIndex = H8_3D_RF_BIND_CHANNEL_START;
NRF24L01_SetChannel(H8_3D_RF_BIND_CHANNEL_START);
} else {
h8_3dSetBound(nrf24_id);
}
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
payloadSize = H8_3D_X_PROTOCOL_PAYLOAD_SIZE + 2; // payload + 2 bytes CRC
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payloadSize); // payload + 2 bytes CRC
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
STATIC_UNIT_TESTED bool h8_3dCheckBindPacket(const uint8_t *payload)
{
bool bindPacket = false;
@ -164,12 +133,19 @@ void h8_3dSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload)
const uint8_t flags = payload[17];
const uint8_t flags2 = payload[18];
if (flags & FLAG_RATE_HIGH) {
rcData[NRF24_AUX1] = PWM_RANGE_MAX;
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MAX;
} else if (flags & FLAG_RATE_MID) {
rcData[NRF24_AUX1] = PWM_RANGE_MIDDLE;
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIDDLE;
} else {
rcData[NRF24_AUX1] = PWM_RANGE_MIN;
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIN;
}
rcData[RC_CHANNEL_FLIP] = flags & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_PICTURE] = flags2 & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_VIDEO] = flags2 & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_HEADLESS] = flags & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_RTH] = flags & FLAG_RTH ? PWM_RANGE_MAX : PWM_RANGE_MIN;
if (flags2 & FLAG_CAMERA_UP) {
rcData[NRF24_AUX7] = PWM_RANGE_MAX;
} else if (flags2 & FLAG_CAMERA_DOWN) {
@ -177,17 +153,12 @@ void h8_3dSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload)
} else {
rcData[NRF24_AUX7] = PWM_RANGE_MIDDLE;
}
rcData[NRF24_AUX2] = flags & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX3] = flags2 & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX4] = flags2 & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX5] = flags & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX6] = flags & FLAG_RTH ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX8] = h8_3dConvertToPwm(payload[14], 0x10, 0x30);
rcData[NRF24_AUX9] = h8_3dConvertToPwm(payload[15], 0x30, 0x10);
rcData[NRF24_AUX10] = h8_3dConvertToPwm(payload[16], 0x10, 0x30);
}
static void hopToNextChannel(void)
static void h8_3dHopToNextChannel(void)
{
++h8_3dRfChannelIndex;
if (protocolState == STATE_BIND) {
@ -206,23 +177,24 @@ static void hopToNextChannel(void)
// The hopping channels are determined by the txId
void h8_3dSetHoppingChannels(const uint8_t* txId)
{
h8_3dRfChannels[0] = 0x06 + ((txId[0]>>4) +(txId[0] & 0x0f)) % 0x0f;
h8_3dRfChannels[1] = 0x15 + ((txId[1]>>4) +(txId[1] & 0x0f)) % 0x0f;
// Kludge to allow bugged deviation TX H8_3D implementation to work
//h8_3dRfChannels[2] = 0x24 + ((txId[2]>>4) +(txId[2] & 0x0f)) % 0x0f;
//h8_3dRfChannels[3] = 0x33 + ((txId[3]>>4) +(txId[3] & 0x0f)) % 0x0f;
h8_3dRfChannels[2] = 0x06 + ((txId[0]>>8) +(txId[0] & 0x0f)) % 0x0f;
h8_3dRfChannels[3] = 0x15 + ((txId[1]>>8) +(txId[1] & 0x0f)) % 0x0f;
const int bitShift = h8_3dProtocol == NRF24RX_H8_3D_DEVIATION ? 8 : 4;
h8_3dRfChannels[0] = 0x06 + ((txId[0]>>bitShift) +(txId[0] & 0x0f)) % 0x0f;
h8_3dRfChannels[1] = 0x15 + ((txId[1]>>bitShift) +(txId[1] & 0x0f)) % 0x0f;
h8_3dRfChannels[2] = 0x24 + ((txId[2]>>bitShift) +(txId[2] & 0x0f)) % 0x0f;
h8_3dRfChannels[3] = 0x33 + ((txId[3]>>bitShift) +(txId[3] & 0x0f)) % 0x0f;
}
void h8_3dSetBound(const uint8_t* txId)
{
h8_3dSetHoppingChannels(txId);
protocolState = STATE_DATA;
h8_3dSetHoppingChannels(txId);
hopTimeout = DATA_HOP_TIMEOUT;
timeOfLastHop = micros();
h8_3dRfChannelIndex = 0;
NRF24L01_SetChannel(h8_3dRfChannels[0]);
}
/*
* This is called periodically by the scheduler.
* Returns NRF24L01_RECEIVED_DATA if a data packet was received.
@ -230,11 +202,6 @@ void h8_3dSetBound(const uint8_t* txId)
nrf24_received_t h8_3dDataReceived(uint8_t *payload)
{
nrf24_received_t ret = NRF24_RECEIVED_NONE;
const uint32_t timeNow = micros();
if (timeNow > timeOfLastHop + hopTimeout) {
hopToNextChannel();
timeOfLastHop = timeNow;
}
switch (protocolState) {
case STATE_BIND:
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
@ -250,18 +217,50 @@ nrf24_received_t h8_3dDataReceived(uint8_t *payload)
// read the payload, processing of payload is deferred
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
XN297_UnscramblePayload(payload, payloadSize);
hopToNextChannel();
timeOfLastHop = timeNow;
ret = NRF24_RECEIVED_DATA;
}
break;
}
const uint32_t timeNowUs = micros();
if ((ret == NRF24_RECEIVED_DATA) || (timeNowUs > timeOfLastHop + hopTimeout)) {
h8_3dHopToNextChannel();
timeOfLastHop = timeNowUs;
}
return ret;
}
void h8_3dNrf24Init(nrf24_protocol_t protocol, const uint8_t* nrf24_id)
{
h8_3dProtocol = protocol;
protocolState = STATE_BIND;
NRF24L01_Initialize(0); // sets PWR_UP, no CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0); // No auto acknowledgment
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
// RX_ADDR for pipes P1-P5 are left at default values
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddrXN297, RX_TX_ADDR_LEN);
if ((nrf24_id[0] | nrf24_id[1] | nrf24_id[2] | nrf24_id[3]) == 0) {
h8_3dRfChannelIndex = H8_3D_RF_BIND_CHANNEL_START;
NRF24L01_SetChannel(H8_3D_RF_BIND_CHANNEL_START);
} else {
h8_3dSetBound(nrf24_id);
}
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
payloadSize = H8_3D_PROTOCOL_PAYLOAD_SIZE + 2; // payload + 2 bytes CRC
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payloadSize); // payload + 2 bytes CRC
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
void h8_3dInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
rxRuntimeConfig->channelCount = H8_3D_RC_CHANNEL_COUNT;
rxRuntimeConfig->channelCount = RC_CHANNEL_COUNT;
h8_3dNrf24Init((nrf24_protocol_t)rxConfig->nrf24rx_protocol, rxConfig->nrf24rx_id);
}
#endif

250
src/main/rx/nrf24_ref.c Normal file
View file

@ -0,0 +1,250 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "platform.h"
#include "build_config.h"
#ifdef USE_RX_REF
#include "drivers/rx_nrf24l01.h"
#include "drivers/system.h"
#include "rx/nrf24.h"
//#include "rx/nrf24_ref.h"
/*
* Reference Protocol
* No auto acknowledgment
* Data rate is 250Kbps - lower data rate for better reliability and range
* Payload size is 16, static, small payload is read more quickly (marginal benefit)
*
* Bind Phase
* uses address {0x4b,0x5c,0x6d,0x7e,0x8f}
* uses channel 0x4c
*
* Data Phase
* uses address received in bind packet
* hops between 4 channels generated from address received in bind packet
*/
#define RC_CHANNEL_COUNT 16
enum {
RATE_LOW = 0,
RATE_MID = 1,
RATE_HIGH = 2,
};
enum {
FLAG_FLIP = 0x01,
FLAG_PICTURE = 0x02,
FLAG_VIDEO = 0x04,
FLAG_RTH = 0x08,
FLAG_HEADLESS = 0x10,
};
typedef enum {
STATE_BIND = 0,
STATE_DATA
} protocol_state_t;
STATIC_UNIT_TESTED protocol_state_t protocolState;
#define REF_PROTOCOL_PAYLOAD_SIZE 16
STATIC_UNIT_TESTED const uint8_t payloadSize = REF_PROTOCOL_PAYLOAD_SIZE;
#define RX_TX_ADDR_LEN 5
// set rxTxAddr to the bind values
STATIC_UNIT_TESTED uint8_t rxTxAddr[RX_TX_ADDR_LEN] = {0x4b,0x5c,0x6d,0x7e,0x8f};
// radio channels for frequency hopping
#define REF_RF_CHANNEL_COUNT 4
STATIC_UNIT_TESTED const uint8_t refRfChannelCount = REF_RF_CHANNEL_COUNT;
STATIC_UNIT_TESTED uint8_t refRfChannelIndex;
STATIC_UNIT_TESTED uint8_t refRfChannels[REF_RF_CHANNEL_COUNT];
#define REF_RF_BIND_CHANNEL 0x4c
//static uint32_t packetCount = 0;
static uint32_t timeOfLastHop;
static const uint32_t hopTimeout = 5000; // 5ms
STATIC_UNIT_TESTED bool refCheckBindPacket(const uint8_t *payload)
{
bool bindPacket = false;
if (payload[0] == 0xae && payload[1] == 0xc9) {
bindPacket = true;
rxTxAddr[0] = payload[2];
rxTxAddr[1] = payload[3];
rxTxAddr[2] = payload[4];
rxTxAddr[3] = payload[5];
rxTxAddr[4] = payload[6];
}
return bindPacket;
}
void refSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload)
{
// the AETR channels have 10 bit resolution
uint8_t lowBits = payload[6]; // least significant bits for AETR
rcData[NRF24_ROLL] = PWM_RANGE_MIN + ((payload[2] << 2) | (lowBits & 0x03)); // Aileron
lowBits >>= 2;
rcData[NRF24_PITCH] = PWM_RANGE_MIN + ((payload[3] << 2) | (lowBits & 0x03)); // Elevator
lowBits >>= 2;
rcData[NRF24_THROTTLE] = PWM_RANGE_MIN + ((payload[4] << 2) | (lowBits & 0x03)); // Throttle
lowBits >>= 2;
rcData[NRF24_YAW] = PWM_RANGE_MIN + ((payload[5] << 2) | (lowBits & 0x03)); // Rudder
// channel AUX1 is used for rate, as per the deviation convention
const uint8_t rate = payload[7];
if (rate == RATE_HIGH) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MAX;
} else if (rate == RATE_MID) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIDDLE;
} else {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIN;
}
// channels AUX2 to AUX7 use the deviation convention
const uint8_t flags = payload[8];
rcData[RC_CHANNEL_FLIP]= (flags & FLAG_FLIP) ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_PICTURE]= (flags & FLAG_PICTURE) ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_VIDEO]= (flags & FLAG_VIDEO) ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_HEADLESS]= (flags & FLAG_HEADLESS) ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_RTH]= (flags & FLAG_RTH) ? PWM_RANGE_MAX : PWM_RANGE_MIN;
// channels AUX7 to AUX10 have 10 bit resolution
lowBits = payload[13]; // least significant bits for AUX7 to AUX10
rcData[NRF24_AUX7] = PWM_RANGE_MIN + ((payload[9] << 2) | (lowBits & 0x03));
lowBits >>= 2;
rcData[NRF24_AUX8] = PWM_RANGE_MIN + ((payload[10] << 2) | (lowBits & 0x03));
lowBits >>= 2;
rcData[NRF24_AUX9] = PWM_RANGE_MIN + ((payload[11] << 2) | (lowBits & 0x03));
lowBits >>= 2;
rcData[NRF24_AUX10] = PWM_RANGE_MIN + ((payload[12] << 2) | (lowBits & 0x03));
lowBits >>= 2;
// channels AUX11 and AUX12 have 8 bit resolution
rcData[NRF24_AUX11] = PWM_RANGE_MIN + (payload[14] << 2);
rcData[NRF24_AUX12] = PWM_RANGE_MIN + (payload[15] << 2);
}
static void refHopToNextChannel(void)
{
++refRfChannelIndex;
if (refRfChannelIndex >= refRfChannelCount) {
refRfChannelIndex = 0;
}
NRF24L01_SetChannel(refRfChannels[refRfChannelIndex]);
}
// The hopping channels are determined by the low bits of rxTxAddr
STATIC_UNIT_TESTED void refSetHoppingChannels(uint32_t addr)
{
addr = addr & 0x1f;
const uint32_t inc = (addr << 24) | (addr << 16) | (addr << 8) | addr;
uint32_t * const prfChannels = (uint32_t *)refRfChannels;
*prfChannels = 0x10314259 + inc;
}
void refSetBound(const uint8_t* rxTxAddr)
{
protocolState = STATE_DATA;
refSetHoppingChannels(rxTxAddr[0]);
timeOfLastHop = micros();
refRfChannelIndex = 0;
NRF24L01_SetChannel(refRfChannels[0]);
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddr, RX_TX_ADDR_LEN);
}
/*
* This is called periodically by the scheduler.
* Returns NRF24L01_RECEIVED_DATA if a data packet was received.
*/
nrf24_received_t refDataReceived(uint8_t *payload)
{
nrf24_received_t ret = NRF24_RECEIVED_NONE;
uint32_t timeNowUs;
switch (protocolState) {
case STATE_BIND:
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
const bool bindPacket = refCheckBindPacket(payload);
if (bindPacket) {
ret = NRF24_RECEIVED_BIND;
// got a bind packet, so set the hopping channels and the rxTxAddr and start listening for data
refSetBound(rxTxAddr);
}
}
break;
case STATE_DATA:
timeNowUs = micros();
// read the payload, processing of payload is deferred
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
ret = NRF24_RECEIVED_DATA;
}
if ((ret == NRF24_RECEIVED_DATA) || (timeNowUs > timeOfLastHop + hopTimeout)) {
refHopToNextChannel();
timeOfLastHop = timeNowUs;
}
break;
}
return ret;
}
void refNrf24Init(nrf24_protocol_t protocol, const uint8_t* nrf24_id)
{
UNUSED(protocol);
NRF24L01_Initialize(BV(NRF24L01_00_CONFIG_EN_CRC) | BV( NRF24L01_00_CONFIG_CRCO)); // sets PWR_UP, EN_CRC, CRCO - 2 byte CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No auto acknowledgment
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0));
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
if ((nrf24_id[0] | nrf24_id[1] | nrf24_id[2] | nrf24_id[3] | nrf24_id[4]) == 0) {
protocolState = STATE_BIND;
NRF24L01_SetChannel(REF_RF_BIND_CHANNEL);
} else {
rxTxAddr[0] = nrf24_id[0];
rxTxAddr[1] = nrf24_id[1];
rxTxAddr[2] = nrf24_id[2];
rxTxAddr[3] = nrf24_id[3];
rxTxAddr[4] = nrf24_id[4];
refSetBound(nrf24_id);
}
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_250Kbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
// RX_ADDR for pipes P1-P5 are left at default values
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddr, RX_TX_ADDR_LEN);
NRF24L01_WriteReg(NRF24L01_08_OBSERVE_TX, 0x00);
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, payloadSize);
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
void refInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
rxRuntimeConfig->channelCount = RC_CHANNEL_COUNT;
refNrf24Init((nrf24_protocol_t)rxConfig->nrf24rx_protocol, rxConfig->nrf24rx_id);
}
#endif

View file

@ -66,26 +66,23 @@
* (common channels between both phases are: 0x27, 0x39, 0x24, 0x22, 0x2d)
*/
#define SYMA_RC_CHANNEL_COUNT 9
#define RC_CHANNEL_COUNT 9
#define SYMA_X_PROTOCOL_PAYLOAD_SIZE 10
#define SYMA_X5C_PROTOCOL_PAYLOAD_SIZE 16
enum {
RATE_LOW = 0,
RATE_MID = 1,
RATE_HIGH= 2,
};
#define SYMA_X_RF_BIND_CHANNEL 8
#define SYMA_X_RF_CHANNEL_COUNT 4
#define FLAG_PICTURE 0x40
#define FLAG_VIDEO 0x80
#define FLAG_FLIP 0x40
#define FLAG_HEADLESS 0x80
#define SYMA_X5C_RF_BIND_CHANNEL_COUNT 16
#define SYMA_X5C_RF_CHANNEL_COUNT 15
#define FLAG_PICTURE 0x40
#define FLAG_VIDEO 0x80
#define FLAG_FLIP 0x40
#define FLAG_HEADLESS 0x80
#define FLAG_FLIP_X5C 0x01
#define FLAG_FLIP_X5C 0x01
#define FLAG_PICTURE_X5C 0x08
#define FLAG_VIDEO_X5C 0x10
#define FLAG_RATE_X5C 0x04
#define FLAG_VIDEO_X5C 0x10
#define FLAG_RATE_X5C 0x04
STATIC_UNIT_TESTED nrf24_protocol_t symaProtocol;
@ -97,6 +94,8 @@ typedef enum {
STATIC_UNIT_TESTED protocol_state_t protocolState;
// X11, X12, X5C-1 have 10-byte payload, X5C has 16-byte payload
#define SYMA_X_PROTOCOL_PAYLOAD_SIZE 10
#define SYMA_X5C_PROTOCOL_PAYLOAD_SIZE 16
STATIC_UNIT_TESTED uint8_t payloadSize;
#define RX_TX_ADDR_LEN 5
@ -105,16 +104,170 @@ STATIC_UNIT_TESTED uint8_t rxTxAddr[RX_TX_ADDR_LEN] = {0xab, 0xac, 0xad, 0xae, 0
STATIC_UNIT_TESTED const uint8_t rxTxAddrX5C[RX_TX_ADDR_LEN] = {0x6d, 0x6a, 0x73, 0x73, 0x73}; // X5C uses same address for bind and data
// radio channels for frequency hopping
static int packetCount = 0;
STATIC_UNIT_TESTED uint8_t symaRfChannelIndex = 0;
#define SYMA_X_RF_BIND_CHANNEL 8
#define SYMA_X_RF_CHANNEL_COUNT 4
#define SYMA_X5C_RF_BIND_CHANNEL_COUNT 16
#define SYMA_X5C_RF_CHANNEL_COUNT 15
STATIC_UNIT_TESTED uint8_t symaRfChannelCount = SYMA_X_RF_CHANNEL_COUNT;
STATIC_UNIT_TESTED uint8_t symaRfChannelIndex = 0;
// set rfChannels to SymaX bind channels, reserve enough space for SymaX5C channels
STATIC_UNIT_TESTED uint8_t symaRfChannels[SYMA_X5C_RF_BIND_CHANNEL_COUNT] = {0x4b, 0x30, 0x40, 0x20};
STATIC_UNIT_TESTED const uint8_t symaRfChannelsX5C[SYMA_X5C_RF_CHANNEL_COUNT] = {0x1d, 0x2f, 0x26, 0x3d, 0x15, 0x2b, 0x25, 0x24, 0x27, 0x2c, 0x1c, 0x3e, 0x39, 0x2d, 0x22};
static uint32_t packetCount = 0;
static uint32_t timeOfLastHop;
static uint32_t hopTimeout = 10000; // 10ms
STATIC_UNIT_TESTED bool symaCheckBindPacket(const uint8_t *packet)
{
bool bindPacket = false;
if (symaProtocol == NRF24RX_SYMA_X) {
if ((packet[5] == 0xaa) && (packet[6] == 0xaa) && (packet[7] == 0xaa)) {
bindPacket = true;
rxTxAddr[4] = packet[0];
rxTxAddr[3] = packet[1];
rxTxAddr[2] = packet[2];
rxTxAddr[1] = packet[3];
rxTxAddr[0] = packet[4];
}
} else {
if ((packet[0] == 0) && (packet[1] == 0) && (packet[14] == 0xc0) && (packet[15] == 0x17)) {
bindPacket = true;
}
}
return bindPacket;
}
STATIC_UNIT_TESTED uint16_t symaConvertToPwmUnsigned(uint8_t val)
{
uint32_t ret = val;
ret = ret * (PWM_RANGE_MAX - PWM_RANGE_MIN) / UINT8_MAX + PWM_RANGE_MIN;
return (uint16_t)ret;
}
STATIC_UNIT_TESTED uint16_t symaConvertToPwmSigned(uint8_t val)
{
int32_t ret = val & 0x7f;
ret = (ret * (PWM_RANGE_MAX - PWM_RANGE_MIN)) / (2 * INT8_MAX);
if (val & 0x80) {// sign bit set
ret = -ret;
}
return (uint16_t)(PWM_RANGE_MIDDLE + ret);
}
void symaSetRcDataFromPayload(uint16_t *rcData, const uint8_t *packet)
{
rcData[NRF24_THROTTLE] = symaConvertToPwmUnsigned(packet[0]); // throttle
rcData[NRF24_ROLL] = symaConvertToPwmSigned(packet[3]); // aileron
if (symaProtocol == NRF24RX_SYMA_X) {
rcData[NRF24_PITCH] = symaConvertToPwmSigned(packet[1]); // elevator
rcData[NRF24_YAW] = symaConvertToPwmSigned(packet[2]); // rudder
const uint8_t rate = (packet[5] & 0xc0) >> 6;
if (rate == RATE_LOW) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIN;
} else if (rate == RATE_MID) {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MIDDLE;
} else {
rcData[RC_CHANNEL_RATE] = PWM_RANGE_MAX;
}
rcData[RC_CHANNEL_FLIP] = packet[6] & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_PICTURE] = packet[4] & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_VIDEO] = packet[4] & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_HEADLESS] = packet[14] & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
} else {
rcData[NRF24_PITCH] = symaConvertToPwmSigned(packet[2]); // elevator
rcData[NRF24_YAW] = symaConvertToPwmSigned(packet[1]); // rudder
const uint8_t flags = packet[14];
rcData[RC_CHANNEL_RATE] = flags & FLAG_RATE_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_FLIP] = flags & FLAG_FLIP_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_PICTURE] = flags & FLAG_PICTURE_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[RC_CHANNEL_VIDEO] = flags & FLAG_VIDEO_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
}
}
static void symaHopToNextChannel(void)
{
// hop channel every second packet
++packetCount;
if ((packetCount & 0x01) == 0) {
++symaRfChannelIndex;
if (symaRfChannelIndex >= symaRfChannelCount) {
symaRfChannelIndex = 0;
}
}
NRF24L01_SetChannel(symaRfChannels[symaRfChannelIndex]);
}
// The SymaX hopping channels are determined by the low bits of rxTxAddress
void setSymaXHoppingChannels(uint32_t addr)
{
addr = addr & 0x1f;
if (addr == 0x06) {
addr = 0x07;
}
const uint32_t inc = (addr << 24) | (addr << 16) | (addr << 8) | addr;
uint32_t * const prfChannels = (uint32_t *)symaRfChannels;
if (addr == 0x16) {
*prfChannels = 0x28481131;
} else if (addr == 0x1e) {
*prfChannels = 0x38184121;
} else if (addr < 0x10) {
*prfChannels = 0x3A2A1A0A + inc;
} else if (addr < 0x18) {
*prfChannels = 0x1231FA1A + inc;
} else {
*prfChannels = 0x19FA2202 + inc;
}
}
void symaSetBound(const uint8_t* rxTxAddr)
{
protocolState = STATE_DATA;
// using protocol NRF24L01_SYMA_X, since NRF24L01_SYMA_X5C went straight into data mode
// set the hopping channels as determined by the rxTxAddr received in the bind packet
setSymaXHoppingChannels(rxTxAddr[0]);
timeOfLastHop = micros();
packetCount = 0;
// set the NRF24 to use the rxTxAddr received in the bind packet
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddr, RX_TX_ADDR_LEN);
symaRfChannelIndex = 0;
NRF24L01_SetChannel(symaRfChannels[0]);
}
/*
* This is called periodically by the scheduler.
* Returns NRF24_RECEIVED_DATA if a data packet was received.
*/
nrf24_received_t symaDataReceived(uint8_t *payload)
{
nrf24_received_t ret = NRF24_RECEIVED_NONE;
uint32_t timeNowUs;
switch (protocolState) {
case STATE_BIND:
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
const bool bindPacket = symaCheckBindPacket(payload);
if (bindPacket) {
ret = NRF24_RECEIVED_BIND;
symaSetBound(rxTxAddr);
}
}
break;
case STATE_DATA:
// read the payload, processing of payload is deferred
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
ret = NRF24_RECEIVED_DATA;
}
timeNowUs = micros();
if ((ret == NRF24_RECEIVED_DATA) || (timeNowUs > timeOfLastHop + hopTimeout)) {
symaHopToNextChannel();
timeOfLastHop = timeNowUs;
}
break;
}
return ret;
}
void symaNrf24Init(nrf24_protocol_t protocol)
{
symaProtocol = protocol;
@ -149,152 +302,9 @@ void symaNrf24Init(nrf24_protocol_t protocol)
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
STATIC_UNIT_TESTED uint16_t symaConvertToPwmUnsigned(uint8_t val)
{
uint32_t ret = val;
ret = ret * (PWM_RANGE_MAX - PWM_RANGE_MIN) / UINT8_MAX + PWM_RANGE_MIN;
return (uint16_t)ret;
}
STATIC_UNIT_TESTED uint16_t symaConvertToPwmSigned(uint8_t val)
{
int32_t ret = val & 0x7f;
ret = (ret * (PWM_RANGE_MAX - PWM_RANGE_MIN)) / (2 * INT8_MAX);
if (val & 0x80) {// sign bit set
ret = -ret;
}
return (uint16_t)(PWM_RANGE_MIDDLE + ret);
}
STATIC_UNIT_TESTED bool symaCheckBindPacket(const uint8_t *packet)
{
bool bindPacket = false;
if (symaProtocol == NRF24RX_SYMA_X) {
if ((packet[5] == 0xaa) && (packet[6] == 0xaa) && (packet[7] == 0xaa)) {
bindPacket = true;
rxTxAddr[4] = packet[0];
rxTxAddr[3] = packet[1];
rxTxAddr[2] = packet[2];
rxTxAddr[1] = packet[3];
rxTxAddr[0] = packet[4];
}
} else {
if ((packet[0] == 0) && (packet[1] == 0) && (packet[14] == 0xc0) && (packet[15] == 0x17)) {
bindPacket = true;
}
}
return bindPacket;
}
void symaSetRcDataFromPayload(uint16_t *rcData, const uint8_t *packet)
{
rcData[NRF24_THROTTLE] = symaConvertToPwmUnsigned(packet[0]); // throttle
rcData[NRF24_ROLL] = symaConvertToPwmSigned(packet[3]); // aileron
if (symaProtocol == NRF24RX_SYMA_X) {
rcData[NRF24_PITCH] = symaConvertToPwmSigned(packet[1]); // elevator
rcData[NRF24_YAW] = symaConvertToPwmSigned(packet[2]); // rudder
const uint8_t rate = (packet[5] & 0xc0) >> 6; // takes values 0, 1, 2
if (rate == 0) {
rcData[NRF24_AUX1] = PWM_RANGE_MIN;
} else if (rate == 1) {
rcData[NRF24_AUX1] = PWM_RANGE_MIDDLE;
} else {
rcData[NRF24_AUX1] = PWM_RANGE_MAX;
}
rcData[NRF24_AUX2] = packet[6] & FLAG_FLIP ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX3] = packet[4] & FLAG_PICTURE ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX4] = packet[4] & FLAG_VIDEO ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX5] = packet[14] & FLAG_HEADLESS ? PWM_RANGE_MAX : PWM_RANGE_MIN;
} else {
rcData[NRF24_PITCH] = symaConvertToPwmSigned(packet[2]); // elevator
rcData[NRF24_YAW] = symaConvertToPwmSigned(packet[1]); // rudder
const uint8_t flags = packet[14];
rcData[NRF24_AUX1] = flags & FLAG_RATE_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX2] = flags & FLAG_FLIP_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX3] = flags & FLAG_PICTURE_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
rcData[NRF24_AUX4] = flags & FLAG_VIDEO_X5C ? PWM_RANGE_MAX : PWM_RANGE_MIN;
}
}
static void hopToNextChannel(void)
{
// hop channel every second packet
++packetCount;
if ((packetCount & 0x01) == 0) {
++symaRfChannelIndex;
if (symaRfChannelIndex >= symaRfChannelCount) {
symaRfChannelIndex = 0;
}
}
NRF24L01_SetChannel(symaRfChannels[symaRfChannelIndex]);
}
// The SymaX hopping channels are determined by the low bits of rxTxAddress
void setSymaXHoppingChannels(uint32_t addr)
{
addr = addr & 0x1f;
if (addr == 0x06) {
addr = 0x07;
}
const uint32_t inc = (addr << 24) | (addr << 16) | (addr << 8) | addr;
uint32_t * const prfChannels = (uint32_t *)symaRfChannels;
if (addr == 0x16) {
*prfChannels = 0x28481131;
} else if (addr == 0x1e) {
*prfChannels = 0x38184121;
} else if (addr < 0x10) {
*prfChannels = 0x3A2A1A0A + inc;
} else if (addr < 0x18) {
*prfChannels = 0x1231FA1A + inc;
} else {
*prfChannels = 0x19FA2202 + inc;
}
}
/*
* This is called periodically by the scheduler.
* Returns NRF24_RECEIVED_DATA if a data packet was received.
*/
nrf24_received_t symaDataReceived(uint8_t *payload)
{
nrf24_received_t ret = NRF24_RECEIVED_NONE;
switch (protocolState) {
case STATE_BIND:
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
const bool bindPacket = symaCheckBindPacket(payload);
if (bindPacket) {
ret = NRF24_RECEIVED_BIND;
protocolState = STATE_DATA;
// using protocol NRF24L01_SYMA_X, since NRF24L01_SYMA_X5C went straight into data mode
// set the hopping channels as determined by the rxTxAddr received in the bind packet
setSymaXHoppingChannels(rxTxAddr[0]);
// set the NRF24 to use the rxTxAddr received in the bind packet
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rxTxAddr, RX_TX_ADDR_LEN);
packetCount = 0;
symaRfChannelIndex = 0;
NRF24L01_SetChannel(symaRfChannels[0]);
}
}
break;
case STATE_DATA:
// read the payload, processing of payload is deferred
if (NRF24L01_ReadPayloadIfAvailable(payload, payloadSize)) {
hopToNextChannel();
timeOfLastHop = micros();
ret = NRF24_RECEIVED_DATA;
}
if (micros() > timeOfLastHop + hopTimeout) {
hopToNextChannel();
timeOfLastHop = micros();
}
break;
}
return ret;
}
void symaInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
rxRuntimeConfig->channelCount = SYMA_RC_CHANNEL_COUNT;
rxRuntimeConfig->channelCount = RC_CHANNEL_COUNT;
symaNrf24Init((nrf24_protocol_t)rxConfig->nrf24rx_protocol);
}
#endif

View file

@ -116,37 +116,6 @@ static void switch_channel(void)
if (++rf_ch_num >= V2X2_NFREQCHANNELS) rf_ch_num = 0;
}
void v202Nrf24Init(nrf24_protocol_t protocol)
{
NRF24L01_Initialize(BV(NRF24L01_00_CONFIG_EN_CRC) | BV(NRF24L01_00_CONFIG_CRCO)); // 2-bytes CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgment
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0)); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xFF); // 4ms retransmit t/o, 15 tries
if (protocol == NRF24RX_V202_250K) {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_250Kbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
} else {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
}
NRF24L01_WriteReg(NRF24L01_07_STATUS, BV(NRF24L01_07_STATUS_RX_DR) | BV(NRF24L01_07_STATUS_TX_DS) | BV(NRF24L01_07_STATUS_MAX_RT)); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, V2X2_PAYLOAD_SIZE); // bytes of data payload for pipe 0
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
#define RX_TX_ADDR_LEN 5
const uint8_t rx_tx_addr[RX_TX_ADDR_LEN] = {0x66, 0x88, 0x68, 0x68, 0x68};
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, RX_TX_ADDR_LEN);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, RX_TX_ADDR_LEN);
NRF24L01_FlushTx();
NRF24L01_FlushRx();
rf_ch_num = 0;
bind_phase = PHASE_NOT_BOUND;
prepare_to_bind();
switch_channel();
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
void v2x2_set_tx_id(uint8_t *id)
{
uint8_t sum;
@ -249,6 +218,37 @@ nrf24_received_t v202DataReceived(uint8_t *packet)
return readrx(packet);
}
void v202Nrf24Init(nrf24_protocol_t protocol)
{
NRF24L01_Initialize(BV(NRF24L01_00_CONFIG_EN_CRC) | BV(NRF24L01_00_CONFIG_CRCO)); // 2-bytes CRC
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgment
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, BV(NRF24L01_02_EN_RXADDR_ERX_P0)); // Enable data pipe 0
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, NRF24L01_03_SETUP_AW_5BYTES); // 5-byte RX/TX address
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xFF); // 4ms retransmit t/o, 15 tries
if (protocol == NRF24RX_V202_250K) {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_250Kbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
} else {
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, NRF24L01_06_RF_SETUP_RF_DR_1Mbps | NRF24L01_06_RF_SETUP_RF_PWR_n12dbm);
}
NRF24L01_WriteReg(NRF24L01_07_STATUS, BV(NRF24L01_07_STATUS_RX_DR) | BV(NRF24L01_07_STATUS_TX_DS) | BV(NRF24L01_07_STATUS_MAX_RT)); // Clear data ready, data sent, and retransmit
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, V2X2_PAYLOAD_SIZE); // bytes of data payload for pipe 0
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
#define RX_TX_ADDR_LEN 5
const uint8_t rx_tx_addr[RX_TX_ADDR_LEN] = {0x66, 0x88, 0x68, 0x68, 0x68};
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, RX_TX_ADDR_LEN);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, RX_TX_ADDR_LEN);
NRF24L01_FlushTx();
NRF24L01_FlushRx();
rf_ch_num = 0;
bind_phase = PHASE_NOT_BOUND;
prepare_to_bind();
switch_channel();
NRF24L01_SetRxMode(); // enter receive mode to start listening for packets
}
void v202Init(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
rxRuntimeConfig->channelCount = V2X2_RC_CHANNEL_COUNT;

View file

@ -103,6 +103,7 @@
#define USE_RX_V202
#define USE_RX_CX10
#define USE_RX_H8_3D
#define USE_RX_REF
#endif
//#define NRF24_DEFAULT_PROTOCOL NRF24RX_SYMA_X5C
//#define NRF24_DEFAULT_PROTOCOL NRF24RX_V202_1M