You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
528 lines
15 KiB
528 lines
15 KiB
/*********************************************************
|
|
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, Hexfet, Goebish, Victzh and all protocol developers
|
|
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/>.
|
|
*/
|
|
|
|
|
|
#define DEBUG_SERIAL // Only for STM32_BOARD compiled with Upload method "Serial"->usart1, "STM32duino bootloader"->USB serial
|
|
|
|
#include <cstdint> //adds types like unit_16_t
|
|
|
|
#include "config.h"
|
|
#include "tx_def.h"
|
|
#include "Multiprotocol.h"
|
|
|
|
|
|
#include "pins.h"
|
|
#include "Validate.h"
|
|
#include "common.h"
|
|
#include "state.h"
|
|
#include "input.h"
|
|
#include "cc2500_spi.h"
|
|
|
|
//#include "FrSkyX_cc2500.h"
|
|
//#include "FrSkyV_cc2500.h"
|
|
#include "FrSkyD_cc2500.h"
|
|
|
|
//Global constants/variables
|
|
uint32_t MProtocol_id;//tx id,
|
|
uint32_t MProtocol_id_master;
|
|
uint32_t blink=0,last_signal=0;
|
|
uint8_t protocol_flags=0,protocol_flags2=0;
|
|
//
|
|
uint8_t channel;
|
|
uint8_t packet[40];
|
|
|
|
|
|
uint16_t seed;
|
|
|
|
// Protocol variables
|
|
uint8_t cyrfmfg_id[6];//for dsm2 and devo
|
|
uint8_t rx_tx_addr[5];
|
|
uint8_t rx_id[4];
|
|
uint8_t phase;
|
|
uint16_t bind_counter;
|
|
uint8_t bind_phase;
|
|
uint8_t binding_idx;
|
|
uint16_t packet_period;
|
|
uint8_t packet_count;
|
|
uint8_t packet_sent;
|
|
uint8_t packet_length;
|
|
uint8_t hopping_frequency[50];
|
|
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;
|
|
uint16_t crc;
|
|
uint8_t crc8;
|
|
uint16_t failsafe_count;
|
|
uint16_t state;
|
|
uint8_t len;
|
|
|
|
#if defined(FRSKYX_CC2500_INO) || defined(SFHSS_CC2500_INO)
|
|
uint8_t calData[48];
|
|
#endif
|
|
|
|
|
|
// Mode_select variables
|
|
uint8_t mode_select;
|
|
|
|
#ifdef ENABLE_PPM
|
|
// PPM variable
|
|
volatile uint8_t PPM_chan_max=0;
|
|
#endif
|
|
|
|
#if not defined (ORANGE_TX) && not defined (STM32_BOARD)
|
|
//Random variable
|
|
volatile uint32_t gWDT_entropy=0;
|
|
#endif
|
|
|
|
//Serial protocol
|
|
uint8_t sub_protocol;
|
|
uint8_t protocol;
|
|
uint8_t option;
|
|
uint8_t cur_protocol[3];
|
|
uint8_t prev_option;
|
|
uint8_t RX_num;
|
|
|
|
//Serial RX variables
|
|
#define BAUD 100000
|
|
#define RXBUFFER_SIZE 26
|
|
volatile uint8_t rx_buff[RXBUFFER_SIZE];
|
|
volatile uint8_t rx_ok_buff[RXBUFFER_SIZE];
|
|
volatile uint8_t discard_frame = 0;
|
|
|
|
// Telemetry
|
|
uint8_t pkt[MAX_PKT];//telemetry receiving packets
|
|
float TIMER_PRESCALE = 5.82;
|
|
// 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;
|
|
|
|
//forward declarations
|
|
void modules_reset();
|
|
uint32_t random_id(bool create_new);
|
|
static void protocol_init();
|
|
uint8_t Update_All();
|
|
|
|
// Init
|
|
void setup()
|
|
{
|
|
// Setup diagnostic uart before anything else
|
|
#ifdef ENABLE_DBEUG
|
|
Serial.begin(115200,SERIAL_8N1);
|
|
while (!Serial); // Wait for ever for the serial port to connect...
|
|
debugln("Multiprotocol startup");
|
|
debugln("time %s ", __TIME__);
|
|
#endif
|
|
|
|
// all inputs
|
|
// outputs
|
|
SDI_output;
|
|
SCLK_output;
|
|
CC25_CSN_output;
|
|
|
|
// Random
|
|
//random_init();
|
|
CC25_CSN_on;
|
|
SDI_on;
|
|
SCLK_off;
|
|
|
|
//Wait for every component to start
|
|
delay(100);
|
|
|
|
// Read status of bind button
|
|
if( /*IS_BIND_BUTTON_on */ true)
|
|
{
|
|
BIND_BUTTON_FLAG_on; // If bind button pressed save the status
|
|
BIND_IN_PROGRESS; // Request bind
|
|
}
|
|
else
|
|
BIND_DONE;
|
|
|
|
// Read status of mode select binary switch
|
|
// after this mode_select will be one of {0000, 0001, ..., 1111}
|
|
|
|
|
|
String str;
|
|
debugln("select bank:");
|
|
str="";
|
|
while(true) {
|
|
if (Serial.available() > 0) {
|
|
char recieved = Serial.read();
|
|
if (recieved == '\n') {
|
|
int new_bank = atoi(str.c_str());
|
|
curr_bank = new_bank;
|
|
debugln("Bank selection %d", new_bank);
|
|
break;
|
|
}else {
|
|
str += recieved;
|
|
}
|
|
}
|
|
}
|
|
|
|
debugln("select mode:");
|
|
str="";
|
|
while(true) {
|
|
if (Serial.available() > 0) {
|
|
char recieved = Serial.read();
|
|
if (recieved == '\n') {
|
|
int new_mode = atoi(str.c_str());
|
|
debugln("Protocol selection switch reads as %d with \'%s\'", new_mode,str.c_str());
|
|
mode_select = new_mode;
|
|
break;
|
|
}else {
|
|
str += recieved;
|
|
}
|
|
}
|
|
}
|
|
debugln("Protocol selection switch reads as %d", mode_select);
|
|
|
|
// Update LED
|
|
LED_off;
|
|
LED_output;
|
|
|
|
//Init RF modules
|
|
modules_reset();
|
|
|
|
{
|
|
seed = analogRead(PA0);
|
|
randomSeed(seed);
|
|
}
|
|
|
|
// Read or create protocol id
|
|
MProtocol_id_master=random_id(false);
|
|
|
|
debugln("Module Id: %lx", MProtocol_id_master);
|
|
|
|
#ifdef ENABLE_PPM
|
|
//Protocol and interrupts initialization
|
|
{
|
|
uint8_t line=curr_bank*14+mode_select-1;
|
|
protocol = PPM_prot[line].protocol;
|
|
cur_protocol[1] = protocol;
|
|
sub_protocol = PPM_prot[line].sub_proto;
|
|
RX_num = PPM_prot[line].rx_num;
|
|
|
|
debugln("protocol: %d", protocol);
|
|
switch(protocol) {
|
|
case PROTO_FRSKYD:
|
|
debugln("PROTO_FRSKYD");
|
|
break;
|
|
case PROTO_FRSKYX:
|
|
debugln("PROTO_FRSKYX");
|
|
break;
|
|
case PROTO_FRSKYV:
|
|
debugln("PROTO_FRSKYV");
|
|
break;
|
|
}
|
|
debugln("sub_protocol: %d", sub_protocol);
|
|
option = PPM_prot[line].option; // Use radio-defined option value
|
|
debugln("freq offset: %d", option);
|
|
if(PPM_prot[line].power)
|
|
POWER_FLAG_on;
|
|
if(PPM_prot[line].autobind) {
|
|
AUTOBIND_FLAG_on;
|
|
BIND_IN_PROGRESS; // Force a bind at protocol startup
|
|
}
|
|
line++;
|
|
|
|
protocol_init();
|
|
|
|
}
|
|
#endif //ENABLE_PPM
|
|
debugln("Init complete");
|
|
input.init();
|
|
input.update();
|
|
init_state();
|
|
|
|
|
|
debugln("do calibration start moving sticks please");
|
|
input.do_calibration();
|
|
}
|
|
|
|
// Main
|
|
// Protocol scheduler
|
|
void loop()
|
|
{
|
|
uint32_t next_callback;
|
|
|
|
if(remote_callback==0 || IS_WAIT_BIND_on ) {
|
|
do {
|
|
Update_All();
|
|
} while(remote_callback==0 || IS_WAIT_BIND_on);
|
|
}
|
|
|
|
uint32_t end__ = micros();
|
|
uint32_t start = micros();
|
|
|
|
while(1) {
|
|
start = end__;
|
|
next_callback = remote_callback();
|
|
|
|
if (next_callback > 4000) {
|
|
uint32_t s;
|
|
|
|
s =micros();
|
|
input.update();
|
|
debugln("input took %lu", (micros()-s));
|
|
|
|
s =micros();
|
|
update_state();
|
|
debugln("state took %lu", (micros()-s));
|
|
}
|
|
|
|
uint32_t wait_until = start + next_callback;
|
|
end__ = micros();
|
|
|
|
if (end__-start < next_callback) {
|
|
uint32_t wait = next_callback;
|
|
wait -= ((end__-start));
|
|
delayMicroseconds(wait);
|
|
end__ += wait;
|
|
}
|
|
end__ = micros();
|
|
}
|
|
}
|
|
|
|
uint8_t Update_All() {
|
|
|
|
#ifdef ENABLE_BIND_CH
|
|
if(IS_AUTOBIND_FLAG_on &&
|
|
IS_BIND_CH_PREV_off &&
|
|
Channel_data[BIND_CH-1] > CHANNEL_MAX_COMMAND &&
|
|
Channel_data[Input::CH_THROTTLE] < (CHANNEL_MIN_100+50)
|
|
) { // Autobind is on and BIND_CH went up and Throttle is low
|
|
CHANGE_PROTOCOL_FLAG_on; //reload protocol
|
|
BIND_IN_PROGRESS; //enable bind
|
|
|
|
debugln("%s:%d set bind prog",__func__, __LINE__);
|
|
BIND_CH_PREV_on;
|
|
}
|
|
if(IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_on && Channel_data[BIND_CH-1]<CHANNEL_MIN_COMMAND) {
|
|
// Autobind is on and BIND_CH went down
|
|
BIND_CH_PREV_off;
|
|
//Request protocol to terminate bind
|
|
#if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO)
|
|
if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV)
|
|
BIND_DONE;
|
|
else
|
|
#endif
|
|
if(bind_counter>2)
|
|
bind_counter=2;
|
|
}
|
|
#endif //ENABLE_BIND_CH
|
|
|
|
input.update();
|
|
|
|
if(IS_CHANGE_PROTOCOL_FLAG_on) {
|
|
debugln("%s:%d set bind prog",__func__, __LINE__);
|
|
// Protocol needs to be changed or relaunched for bind
|
|
protocol_init(); //init new protocol
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Protocol start
|
|
static void protocol_init() {
|
|
static uint16_t next_callback;
|
|
if(IS_WAIT_BIND_off) {
|
|
remote_callback = 0; // No protocol
|
|
next_callback=0; // Default is immediate call back
|
|
modules_reset(); // Reset all modules
|
|
|
|
//Set global ID and rx_tx_addr
|
|
MProtocol_id = RX_num + MProtocol_id_master;
|
|
set_rx_tx_addr(MProtocol_id);
|
|
|
|
blink=millis();
|
|
|
|
debugln("Protocol selected: %d, sub proto %d, rxnum %d, option %d", protocol, sub_protocol, RX_num, option);
|
|
|
|
switch(protocol) // Init the requested protocol
|
|
{
|
|
case PROTO_FRSKYD:
|
|
next_callback = initFrSky_2way();
|
|
remote_callback = ReadFrSky_2way;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if defined(WAIT_FOR_BIND) && defined(ENABLE_BIND_CH)
|
|
if( IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_off && (cur_protocol[1]&0x80)==0 && mode_select == MODE_SERIAL) {
|
|
// Autobind is active but no bind requested by either BIND_CH or BIND. But do not wait if in PPM mode...
|
|
WAIT_BIND_on;
|
|
return;
|
|
}
|
|
#endif
|
|
WAIT_BIND_off;
|
|
CHANGE_PROTOCOL_FLAG_off;
|
|
|
|
delayMicroseconds(next_callback);
|
|
debugln("%s BIND_BUTTON_FLAG_off",__func__);
|
|
}
|
|
|
|
void update_serial_data()
|
|
{
|
|
if(rx_ok_buff[1]&0x20) //check range
|
|
RANGE_FLAG_on;
|
|
else
|
|
RANGE_FLAG_off;
|
|
|
|
if(rx_ok_buff[1]&0x40) //check autobind
|
|
AUTOBIND_FLAG_on;
|
|
else
|
|
AUTOBIND_FLAG_off;
|
|
|
|
if(rx_ok_buff[2]&0x80) //if rx_ok_buff[2] ==1,power is low ,0-power high
|
|
POWER_FLAG_off; //power low
|
|
else
|
|
POWER_FLAG_on; //power high
|
|
|
|
//Forced frequency tuning values for CC2500 protocols
|
|
#if defined(FORCE_FRSKYD_TUNING)
|
|
if(protocol==PROTO_FRSKYD)
|
|
option=FORCE_FRSKYD_TUNING; // Use config-defined tuning value for FrSkyD
|
|
else
|
|
#endif
|
|
option=rx_ok_buff[3]; // Use radio-defined option value
|
|
|
|
#ifdef FAILSAFE_ENABLE
|
|
bool failsafe=false;
|
|
if(rx_ok_buff[0]&0x02) { // Packet contains failsafe instead of channels
|
|
failsafe=true;
|
|
rx_ok_buff[0]&=0xFD; //remove the failsafe flag
|
|
}
|
|
#endif
|
|
if( (rx_ok_buff[0] != cur_protocol[0]) || ((rx_ok_buff[1]&0x5F) != (cur_protocol[1]&0x5F)) || ( (rx_ok_buff[2]&0x7F) != (cur_protocol[2]&0x7F) ) )
|
|
{ // New model has been selected
|
|
CHANGE_PROTOCOL_FLAG_on; //change protocol
|
|
WAIT_BIND_off;
|
|
if((rx_ok_buff[1]&0x80)!=0 || IS_AUTOBIND_FLAG_on)
|
|
BIND_IN_PROGRESS; //launch bind right away if in autobind mode or bind is set
|
|
else
|
|
BIND_DONE;
|
|
protocol=(rx_ok_buff[0]==0x55?0:32) + (rx_ok_buff[1]&0x1F); //protocol no (0-63) bits 4-6 of buff[1] and bit 0 of buf[0]
|
|
sub_protocol=(rx_ok_buff[2]>>4)& 0x07; //subprotocol no (0-7) bits 4-6
|
|
RX_num=rx_ok_buff[2]& 0x0F; // rx_num bits 0---3
|
|
} else if( ((rx_ok_buff[1]&0x80)!=0) && ((cur_protocol[1]&0x80)==0) ) // Bind flag has been set
|
|
{ // Restart protocol with bind
|
|
CHANGE_PROTOCOL_FLAG_on;
|
|
BIND_IN_PROGRESS;
|
|
} else {
|
|
if( ((rx_ok_buff[1]&0x80)==0) && ((cur_protocol[1]&0x80)!=0) ) // Bind flag has been reset
|
|
{ // Request protocol to end bind
|
|
#if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO)
|
|
if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV)
|
|
BIND_DONE;
|
|
else
|
|
#endif
|
|
if(bind_counter>2)
|
|
bind_counter=2;
|
|
}
|
|
}
|
|
|
|
//store current protocol values
|
|
for(uint8_t i=0;i<3;i++)
|
|
cur_protocol[i] = rx_ok_buff[i];
|
|
|
|
// decode channel/failsafe values
|
|
volatile uint8_t *p=rx_ok_buff+3;
|
|
uint8_t dec=-3;
|
|
for(uint8_t i=0;i<NUM_TX_CHN;i++) {
|
|
dec+=3;
|
|
if(dec>=8) {
|
|
dec-=8;
|
|
p++;
|
|
}
|
|
p++;
|
|
uint16_t temp=((*((uint32_t *)p))>>dec)&0x7FF;
|
|
if(failsafe)
|
|
Failsafe_data[i]=temp; //value range 0..2047, 0=no pulses, 2047=hold
|
|
else
|
|
Channel_data[i]=temp; //value range 0..2047, 0=-125%, 2047=+125%
|
|
}
|
|
cli();
|
|
if(IS_RX_MISSED_BUFF_on) // If the buffer is still valid
|
|
{ memcpy((void*)rx_ok_buff,(const void*)rx_buff,RXBUFFER_SIZE);// Duplicate the buffer
|
|
RX_MISSED_BUFF_off;
|
|
}
|
|
sei();
|
|
}
|
|
|
|
void modules_reset()
|
|
{
|
|
CC2500_Reset();
|
|
|
|
//Wait for every component to reset
|
|
delay(100);
|
|
}
|
|
|
|
// 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] = (rx_tx_addr[2]&0xF0)|(rx_tx_addr[3]&0x0F);
|
|
}
|
|
|
|
uint32_t random_id(bool create_new)
|
|
{
|
|
#ifdef FORCE_GLOBAL_ID
|
|
(void)create_new;
|
|
return FORCE_GLOBAL_ID;
|
|
#else
|
|
uint32_t id=0;
|
|
|
|
if (eeprom_read_byte((EE_ADDR)(10))==0xf0 && !create_new) {
|
|
// TXID exists in EEPROM
|
|
for(uint8_t i=4;i>0;i--) {
|
|
id<<=8;
|
|
id|=eeprom_read_byte((EE_ADDR)i-1);
|
|
}
|
|
if(id!=0x2AD141A7) //ID with seed=0
|
|
{
|
|
debugln("Read ID from EEPROM");
|
|
return id;
|
|
}
|
|
}
|
|
// Generate a random ID from UUID
|
|
#define STM32_UUID ((uint32_t *)0x1FFFF7E8)
|
|
if (!create_new) {
|
|
id = STM32_UUID[0] ^ STM32_UUID[1] ^ STM32_UUID[2];
|
|
debugln("Generated ID from STM32 UUID %x", id);
|
|
} else {
|
|
id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
|
|
}
|
|
|
|
#if 0
|
|
for(uint8_t i=0;i<4;i++)
|
|
eeprom_write_byte((EE_ADDR)address+i,id >> (i*8));
|
|
eeprom_write_byte((EE_ADDR)(address+10),0xf0);//write bind flag in eeprom.
|
|
#endif
|
|
return id;
|
|
#endif
|
|
|
|
}
|