mirror of
				https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
				synced 2025-10-29 18:11:05 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2346 lines
		
	
	
		
			64 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2346 lines
		
	
	
		
			64 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*********************************************************
 | |
| 					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/>.
 | |
| */
 | |
| #include <avr/pgmspace.h>
 | |
| 
 | |
| //#define DEBUG_PIN		// Use pin TX for AVR and SPI_CS for STM32 => DEBUG_PIN_on, DEBUG_PIN_off, DEBUG_PIN_toggle
 | |
| //#define DEBUG_SERIAL	// Only for STM32_BOARD, compiled with Upload method "Serial"->usart1, "STM32duino bootloader"->USB serial
 | |
| 
 | |
| #ifdef __arm__			// Let's automatically select the board if arm is selected
 | |
| 	#define STM32_BOARD
 | |
| #endif
 | |
| #if defined (ARDUINO_AVR_XMEGA32D4) || defined (ARDUINO_MULTI_ORANGERX)
 | |
| 	#include "MultiOrange.h"
 | |
| #endif
 | |
| 
 | |
| #include "Multiprotocol.h"
 | |
| 
 | |
| //Multiprotocol module configuration file
 | |
| #include "_Config.h"
 | |
| 
 | |
| //Personal config file
 | |
| #if defined(USE_MY_CONFIG)
 | |
| #include "_MyConfig.h"
 | |
| #endif
 | |
| 
 | |
| #include "Pins.h"
 | |
| #include "TX_Def.h"
 | |
| #include "Validate.h"
 | |
| 
 | |
| #ifndef STM32_BOARD
 | |
| 	#include <avr/eeprom.h>
 | |
| #else
 | |
| 	#include <libmaple/usart.h>
 | |
| 	#include <libmaple/timer.h>
 | |
| 	//#include <libmaple/spi.h>
 | |
| 	#include <SPI.h>
 | |
| 	#include <EEPROM.h>	
 | |
| 	HardwareTimer HWTimer2(2);
 | |
| 	#ifdef ENABLE_SERIAL
 | |
| 		HardwareTimer HWTimer3(3);
 | |
| 		void ISR_COMPB();
 | |
| 	#endif
 | |
| 
 | |
| 	void PPM_decode();
 | |
| 	extern "C"
 | |
| 	{
 | |
| 		void __irq_usart2(void);
 | |
| 		void __irq_usart3(void);
 | |
| 	}
 | |
| 	#ifdef SEND_CPPM
 | |
| 		HardwareTimer HWTimer1(1) ;
 | |
| 	#endif				 
 | |
| #endif
 | |
| 
 | |
| //Global constants/variables
 | |
| uint32_t MProtocol_id;//tx id,
 | |
| uint32_t MProtocol_id_master;
 | |
| uint32_t blink=0,last_signal=0;
 | |
| //
 | |
| uint16_t counter;
 | |
| uint8_t  channel;
 | |
| #if defined(ESKY150V2_CC2500_INO)
 | |
| 	uint8_t  packet[150];
 | |
| #else
 | |
| 	uint8_t  packet[50];
 | |
| #endif
 | |
| 
 | |
| #define NUM_CHN 16
 | |
| // Servo data
 | |
| uint16_t Channel_data[NUM_CHN];
 | |
| uint8_t  Channel_AUX;
 | |
| #ifdef FAILSAFE_ENABLE
 | |
| 	uint16_t Failsafe_data[NUM_CHN];
 | |
| #endif
 | |
| 
 | |
| // Protocol variables
 | |
| uint8_t  cyrfmfg_id[6];//for dsm2 and devo
 | |
| uint8_t  rx_tx_addr[5];
 | |
| uint8_t  rx_id[5];
 | |
| 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;
 | |
| #if defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO) || defined(MLINK_CYRF6936_INO)
 | |
| 	uint8_t  hopping_frequency[78];
 | |
| #else
 | |
| 	uint8_t  hopping_frequency[50];
 | |
| #endif
 | |
| 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;
 | |
| uint16_t crc16_polynomial;
 | |
| uint8_t  crc8;
 | |
| uint8_t  crc8_polynomial;
 | |
| uint16_t seed;
 | |
| uint16_t failsafe_count;
 | |
| uint16_t state;
 | |
| uint8_t  len;
 | |
| uint8_t  armed, arm_flags, arm_channel_previous;
 | |
| uint8_t  num_ch;
 | |
| uint32_t pps_timer;
 | |
| uint16_t pps_counter;
 | |
| 
 | |
| #ifdef CC2500_INSTALLED
 | |
| 	#ifdef SCANNER_CC2500_INO
 | |
| 		uint8_t calData[255];
 | |
| 	#elif defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO)
 | |
| 		uint8_t calData[75];
 | |
| 	#else
 | |
| 		uint8_t calData[50];
 | |
| 	#endif
 | |
| #endif
 | |
| 
 | |
| #ifdef CHECK_FOR_BOOTLOADER
 | |
| 	uint8_t BootTimer ;
 | |
| 	uint8_t BootState ;
 | |
| 	uint8_t NotBootChecking ;
 | |
| 	uint8_t BootCount ;
 | |
| 
 | |
| 	#define BOOT_WAIT_30_IDLE	0
 | |
| 	#define BOOT_WAIT_30_DATA	1
 | |
| 	#define BOOT_WAIT_20		2
 | |
| 	#define BOOT_READY			3
 | |
| #endif
 | |
| 
 | |
| //Channel mapping for protocols
 | |
| uint8_t CH_AETR[]={AILERON, ELEVATOR, THROTTLE, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
 | |
| uint8_t CH_TAER[]={THROTTLE, AILERON, ELEVATOR, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
 | |
| //uint8_t CH_RETA[]={RUDDER, ELEVATOR, THROTTLE, AILERON, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
 | |
| uint8_t CH_EATR[]={ELEVATOR, AILERON, THROTTLE, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
 | |
| 
 | |
| // Mode_select variables
 | |
| uint8_t mode_select;
 | |
| uint8_t protocol_flags=0,protocol_flags2=0,protocol_flags3=0;
 | |
| uint8_t option_override;
 | |
| 
 | |
| #ifdef ENABLE_PPM
 | |
| // PPM variable
 | |
| volatile uint16_t PPM_data[NUM_CHN];
 | |
| volatile uint8_t  PPM_chan_max=0;
 | |
| uint32_t chan_order=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 prev_power=0xFD; // unused power value
 | |
| uint8_t RX_num;
 | |
| 
 | |
| //Serial RX variables
 | |
| #define BAUD 100000
 | |
| #define RXBUFFER_SIZE 36	// 26+1+9
 | |
| volatile uint8_t rx_buff[RXBUFFER_SIZE];
 | |
| volatile uint8_t rx_ok_buff[RXBUFFER_SIZE];
 | |
| volatile bool discard_frame = false;
 | |
| volatile uint8_t rx_idx=0, rx_len=0;
 | |
| 
 | |
| // Callback
 | |
| uint16_function_t remote_callback = 0;
 | |
| 
 | |
| // Telemetry
 | |
| #define TELEMETRY_BUFFER_SIZE 32
 | |
| uint8_t packet_in[TELEMETRY_BUFFER_SIZE];//telemetry receiving packets
 | |
| #if defined(TELEMETRY)
 | |
| 	#ifdef MULTI_SYNC
 | |
| 		uint16_t last_serial_input=0;
 | |
| 		uint16_t inputRefreshRate=0;
 | |
| 	#endif
 | |
| 	#ifdef INVERT_TELEMETRY
 | |
| 		#if not defined(ORANGE_TX) && not defined(STM32_BOARD)
 | |
| 			// enable bit bash for serial
 | |
| 			#define	BASH_SERIAL 1
 | |
| 		#endif
 | |
| 		#define	INVERT_SERIAL 1
 | |
| 	#endif
 | |
| 	uint8_t telemetry_in_buffer[TELEMETRY_BUFFER_SIZE];//telemetry receiving packets
 | |
| 	#ifdef BASH_SERIAL
 | |
| 	// For bit-bashed serial output
 | |
| 		#define TXBUFFER_SIZE 192
 | |
| 		volatile struct t_serial_bash
 | |
| 		{
 | |
| 			uint8_t head ;
 | |
| 			uint8_t tail ;
 | |
| 			uint8_t data[TXBUFFER_SIZE] ;
 | |
| 			uint8_t busy ;
 | |
| 			uint8_t speed ;
 | |
| 		} SerialControl ;
 | |
| 	#else
 | |
| 		#define TXBUFFER_SIZE 96
 | |
| 		volatile uint8_t tx_buff[TXBUFFER_SIZE];
 | |
| 		volatile uint8_t tx_head=0;
 | |
| 		volatile uint8_t tx_tail=0;
 | |
| 	#endif // BASH_SERIAL
 | |
| 	uint8_t v_lipo1;
 | |
| 	uint8_t v_lipo2;
 | |
| 	uint8_t RX_RSSI;
 | |
| 	uint8_t TX_RSSI;
 | |
| 	uint8_t RX_LQI;
 | |
| 	uint8_t TX_LQI;
 | |
| 	uint8_t telemetry_link=0; 
 | |
| 	uint8_t telemetry_counter=0;
 | |
| 	uint8_t telemetry_lost;
 | |
| 	#ifdef SPORT_SEND
 | |
| 		#define MAX_SPORT_BUFFER 64
 | |
| 		uint8_t	SportData[MAX_SPORT_BUFFER];
 | |
| 		uint8_t	SportHead=0, SportTail=0;
 | |
| 	#endif
 | |
| 
 | |
| 	// Functions definition when required
 | |
| 	#ifdef HUB_TELEMETRY
 | |
| 		static void __attribute__((unused)) frsky_send_user_frame(uint8_t, uint8_t, uint8_t);
 | |
| 	#endif
 | |
| 
 | |
| 	//RX protocols
 | |
| 	#if defined(AFHDS2A_RX_A7105_INO) || defined(FRSKY_RX_CC2500_INO) || defined(BAYANG_RX_NRF24L01_INO) || defined(DSM_RX_CYRF6936_INO)
 | |
| 		bool rx_data_started;
 | |
| 		bool rx_data_received;
 | |
| 		bool rx_disable_lna;
 | |
| 		uint16_t rx_rc_chan[16];
 | |
| 	#endif
 | |
| 	
 | |
| 	#ifdef HOTT_FW_TELEMETRY
 | |
| 		uint8_t HoTT_SerialRX_val=0;
 | |
| 		bool HoTT_SerialRX=false;
 | |
| 	#endif
 | |
| 	#ifdef DSM_FWD_PGM
 | |
| 		uint8_t DSM_SerialRX_val[7];
 | |
| 		bool DSM_SerialRX=false;
 | |
| 	#endif
 | |
| 	#ifdef MULTI_CONFIG_INO
 | |
| 		uint8_t CONFIG_SerialRX_val[7];
 | |
| 		bool CONFIG_SerialRX=false;
 | |
| 	#endif
 | |
| #endif // TELEMETRY
 | |
| 
 | |
| uint8_t multi_protocols_index=0xFF;
 | |
| 
 | |
| // Init
 | |
| void setup()
 | |
| {
 | |
| 	// Setup diagnostic uart before anything else
 | |
| 	#ifdef DEBUG_SERIAL
 | |
| 		Serial.begin(115200,SERIAL_8N1);
 | |
| 
 | |
| 		// Wait up to 30s for a serial connection; double-blink the LED while we wait
 | |
| 		unsigned long currMillis = millis();
 | |
| 		unsigned long initMillis = currMillis;
 | |
| 		pinMode(LED_pin,OUTPUT);
 | |
| 		LED_off;
 | |
| 		while (!Serial && (currMillis - initMillis) <= 30000) {
 | |
| 			LED_on;
 | |
| 			delay(100);
 | |
| 			LED_off;
 | |
| 			delay(100);
 | |
| 			LED_on;
 | |
| 			delay(100);
 | |
| 			LED_off;
 | |
| 			delay(500);
 | |
| 			currMillis = millis();
 | |
| 		}
 | |
| 
 | |
| 		delay(250);  // Brief delay for FTDI debugging
 | |
| 		debugln("Multiprotocol version: %d.%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH_LEVEL);
 | |
| 	#endif
 | |
| 
 | |
| 	// General pinout
 | |
| 	#ifdef ORANGE_TX
 | |
| 		//XMEGA
 | |
| 		PORTD.OUTSET = 0x17 ;
 | |
| 		PORTD.DIRSET = 0xB2 ;
 | |
| 		PORTD.DIRCLR = 0x4D ;
 | |
| 		PORTD.PIN0CTRL = 0x18 ;
 | |
| 		PORTD.PIN2CTRL = 0x18 ;
 | |
| 		PORTE.DIRSET = 0x01 ;
 | |
| 		PORTE.DIRCLR = 0x02 ;
 | |
| 		// Timer1 config
 | |
| 		// TCC1 16-bit timer, clocked at 0.5uS
 | |
| 		EVSYS.CH3MUX = 0x80 + 0x04 ;				// Prescaler of 16
 | |
| 		TCC1.CTRLB = 0; TCC1.CTRLC = 0; TCC1.CTRLD = 0; TCC1.CTRLE = 0;
 | |
| 		TCC1.INTCTRLA = 0; TIMSK1 = 0;
 | |
| 		TCC1.PER = 0xFFFF ;
 | |
| 		TCNT1 = 0 ;
 | |
| 		TCC1.CTRLA = 0x0B ;							// Event3 (prescale of 16)
 | |
| 	#elif defined STM32_BOARD
 | |
| 		//STM32
 | |
| 		afio_cfg_debug_ports(AFIO_DEBUG_NONE);
 | |
| 		pinMode(LED_pin,OUTPUT);
 | |
| 		pinMode(LED2_pin,OUTPUT);
 | |
| 		pinMode(A7105_CSN_pin,OUTPUT);
 | |
| 		pinMode(CC25_CSN_pin,OUTPUT);
 | |
| 		pinMode(NRF_CSN_pin,OUTPUT);
 | |
| 		pinMode(CYRF_CSN_pin,OUTPUT);
 | |
| 		pinMode(SPI_CSN_pin,OUTPUT);
 | |
| 		pinMode(CYRF_RST_pin,OUTPUT);
 | |
| 		pinMode(PE1_pin,OUTPUT);
 | |
| 		pinMode(PE2_pin,OUTPUT);
 | |
| 		pinMode(TX_INV_pin,OUTPUT);
 | |
| 		pinMode(RX_INV_pin,OUTPUT);
 | |
| 		#if defined TELEMETRY
 | |
| 			#if defined INVERT_SERIAL
 | |
| 				TX_INV_on;							// activate inverter for both serial TX and RX signals
 | |
| 				RX_INV_on;
 | |
| 			#else
 | |
| 				TX_INV_off;
 | |
| 				RX_INV_off;
 | |
| 			#endif	
 | |
| 		#endif
 | |
| 		pinMode(BIND_pin,INPUT_PULLUP);
 | |
| 		pinMode(PPM_pin,INPUT);
 | |
| 		pinMode(S1_pin,INPUT_PULLUP);				// dial switch
 | |
| 		pinMode(S2_pin,INPUT_PULLUP);
 | |
| 		pinMode(S3_pin,INPUT_PULLUP);
 | |
| 		pinMode(S4_pin,INPUT_PULLUP);
 | |
| 		
 | |
| 		#ifdef MULTI_5IN1_INTERNAL
 | |
| 			//pinMode(SX1276_RST_pin,OUTPUT);		// already done by LED2_pin
 | |
| 			pinMode(SX1276_TXEN_pin,OUTPUT);		// PB0
 | |
| 			pinMode(SX1276_DIO0_pin,INPUT_PULLUP);
 | |
| 		#else
 | |
| 			//Random pin
 | |
| 			pinMode(RND_pin, INPUT_ANALOG);			// set up PB0 pin for analog input
 | |
| 		#endif
 | |
| 	
 | |
| 		#if defined ENABLE_DIRECT_INPUTS
 | |
| 			#if defined (DI1_PIN)
 | |
| 				pinMode(DI1_PIN,INPUT_PULLUP);
 | |
| 			#endif
 | |
| 			#if defined (DI2_PIN)
 | |
| 				pinMode(DI2_PIN,INPUT_PULLUP);
 | |
| 			#endif
 | |
| 			#if defined (DI3_PIN)
 | |
| 				pinMode(DI3_PIN,INPUT_PULLUP);
 | |
| 			#endif
 | |
| 			#if defined (DI4_PIN)
 | |
| 				pinMode(DI4_PIN,INPUT_PULLUP);
 | |
| 			#endif
 | |
| 		#endif
 | |
| 
 | |
| 		#ifdef SEND_CPPM
 | |
| 			pinMode(PA9,INPUT);						// make sure the USART1.TX pin is released for heartbeat use
 | |
| 		#endif
 | |
| 
 | |
| 		//Timers
 | |
| 		init_HWTimer();								//0.5us
 | |
| 
 | |
| 		//Read module flash size
 | |
| 		#ifndef DISABLE_FLASH_SIZE_CHECK
 | |
| 			unsigned short *flashSize = (unsigned short *) (0x1FFFF7E0);// Address register 
 | |
| 			debugln("Module Flash size: %dKB",(int)(*flashSize & 0xffff));
 | |
| 			if((int)(*flashSize & 0xffff) < MCU_EXPECTED_FLASH_SIZE)  // Not supported by this project
 | |
| 				while (true) { //SOS
 | |
| 					for(uint8_t i=0; i<3;i++)
 | |
| 					{
 | |
| 						LED_on;
 | |
| 						delay(100);
 | |
| 						LED_off;
 | |
| 						delay(100);
 | |
| 					}
 | |
| 					for(uint8_t i=0; i<3;i++)
 | |
| 					{
 | |
| 						LED_on;
 | |
| 						delay(500);
 | |
| 						LED_off;
 | |
| 						delay(100);
 | |
| 					}
 | |
| 					for(uint8_t i=0; i<3;i++)
 | |
| 					{
 | |
| 						LED_on;
 | |
| 						delay(100);
 | |
| 						LED_off;
 | |
| 						delay(100);
 | |
| 					}
 | |
| 					LED_off;
 | |
| 					delay(1000);
 | |
| 				}
 | |
| 		#endif
 | |
| 
 | |
| 		// Initialize the EEPROM
 | |
| 		uint16_t eepromStatus = EEPROM.init();
 | |
| 		debugln("EEPROM initialized: %d",eepromStatus);
 | |
| 
 | |
| 		// If there was no valid EEPROM page the EEPROM is corrupt or uninitialized and should be formatted
 | |
| 		if( eepromStatus == EEPROM_NO_VALID_PAGE )
 | |
| 		{
 | |
| 			EEPROM.format();
 | |
| 			debugln("No valid EEPROM page, EEPROM formatted");
 | |
| 		}
 | |
| 	#else
 | |
| 		//ATMEGA328p
 | |
| 		// all inputs
 | |
| 		DDRB=0x00;DDRC=0x00;DDRD=0x00;
 | |
| 		// outputs
 | |
| 		SDI_output;
 | |
| 		SCLK_output;
 | |
| 		#ifdef A7105_CSN_pin
 | |
| 			A7105_CSN_output;
 | |
| 		#endif
 | |
| 		#ifdef CC25_CSN_pin
 | |
| 			CC25_CSN_output;
 | |
| 		#endif
 | |
| 		#ifdef CYRF_CSN_pin
 | |
| 			CYRF_RST_output;
 | |
| 			CYRF_CSN_output;
 | |
| 		#endif
 | |
| 		#ifdef NRF_CSN_pin
 | |
| 			NRF_CSN_output;
 | |
| 		#endif
 | |
| 		PE1_output;
 | |
| 		PE2_output;
 | |
| 		SERIAL_TX_output;
 | |
| 
 | |
| 		// pullups
 | |
| 		PROTO_DIAL1_port |= _BV(PROTO_DIAL1_pin);
 | |
| 		PROTO_DIAL2_port |= _BV(PROTO_DIAL2_pin);
 | |
| 		PROTO_DIAL3_port |= _BV(PROTO_DIAL3_pin);
 | |
| 		PROTO_DIAL4_port |= _BV(PROTO_DIAL4_pin);
 | |
| 		BIND_port |= _BV(BIND_pin);
 | |
| 
 | |
| 		// Timer1 config
 | |
| 		TCCR1A = 0;
 | |
| 		TCCR1B = (1 << CS11);	//prescaler8, set timer1 to increment every 0.5us(16Mhz) and start timer
 | |
| 
 | |
| 		// Random
 | |
| 		random_init();
 | |
| 	#endif
 | |
| 
 | |
| 	LED2_on;
 | |
| 	
 | |
| 	// Set Chip selects
 | |
| 	#ifdef A7105_CSN_pin
 | |
| 		A7105_CSN_on;
 | |
| 	#endif
 | |
| 	#ifdef CC25_CSN_pin
 | |
| 		CC25_CSN_on;
 | |
| 	#endif
 | |
| 	#ifdef CYRF_CSN_pin
 | |
| 		CYRF_CSN_on;
 | |
| 	#endif
 | |
| 	#ifdef NRF_CSN_pin
 | |
| 		NRF_CSN_on;
 | |
| 	#endif
 | |
| 	#ifdef SPI_CSN_pin
 | |
| 		SPI_CSN_on;
 | |
| 	#endif
 | |
| 
 | |
| 	//	Set SPI lines
 | |
| 	#ifdef	STM32_BOARD
 | |
| 		initSPI2();
 | |
| 	#else
 | |
| 		SDI_on;
 | |
| 		SCLK_off;
 | |
| 	#endif
 | |
| 
 | |
| 	//Wait for every component to start
 | |
| 	delayMilliseconds(100);
 | |
| 	
 | |
| 	// Read status of bind button
 | |
| 	if( IS_BIND_BUTTON_on )
 | |
| 	{
 | |
| 		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}
 | |
| 	#ifndef ENABLE_PPM
 | |
| 		mode_select = MODE_SERIAL ;	// force serial mode
 | |
| 	#elif defined STM32_BOARD
 | |
| 		mode_select= 0x0F -(uint8_t)(((GPIOA->regs->IDR)>>4)&0x0F);
 | |
| 	#else
 | |
| 		mode_select =
 | |
| 			((PROTO_DIAL1_ipr & _BV(PROTO_DIAL1_pin)) ? 0 : 1) + 
 | |
| 			((PROTO_DIAL2_ipr & _BV(PROTO_DIAL2_pin)) ? 0 : 2) +
 | |
| 			((PROTO_DIAL3_ipr & _BV(PROTO_DIAL3_pin)) ? 0 : 4) +
 | |
| 			((PROTO_DIAL4_ipr & _BV(PROTO_DIAL4_pin)) ? 0 : 8);
 | |
| 	#endif
 | |
| 	//mode_select=1;
 | |
|     debugln("Protocol selection switch reads as %d", mode_select);
 | |
| 
 | |
| 	#ifdef ENABLE_PPM
 | |
| 		uint8_t bank=bank_switch();
 | |
| 	#endif
 | |
| 
 | |
| 	// Set default channels' value
 | |
| 	for(uint8_t i=0;i<NUM_CHN;i++)
 | |
| 		Channel_data[i]=1024;
 | |
| 	Channel_data[THROTTLE]=0;	//0=-125%, 204=-100%
 | |
| 
 | |
| 	#ifdef ENABLE_PPM
 | |
| 		// Set default PPMs' value
 | |
| 		for(uint8_t i=0;i<NUM_CHN;i++)
 | |
| 			PPM_data[i]=PPM_MAX_100+PPM_MIN_100;
 | |
| 		PPM_data[THROTTLE]=PPM_MIN_100*2;
 | |
| 	#endif
 | |
| 
 | |
| 	// Update LED
 | |
| 	LED_off;
 | |
| 	LED_output;
 | |
| 
 | |
| 	//Init RF modules
 | |
| 	modules_reset();
 | |
| 
 | |
| #ifndef ORANGE_TX
 | |
| 	#ifdef STM32_BOARD
 | |
| 		uint32_t seed=0;
 | |
| 		for(uint8_t i=0;i<4;i++)
 | |
| 		#ifdef RND_pin
 | |
| 			seed=(seed<<8) | (analogRead(RND_pin)& 0xFF);
 | |
| 		#else
 | |
| 		//TODO find something to randomize...
 | |
| 			seed=(seed<<8);
 | |
| 		#endif
 | |
| 		randomSeed(seed);
 | |
| 	#else
 | |
| 		//Init the seed with a random value created from watchdog timer for all protocols requiring random values
 | |
| 		randomSeed(random_value());
 | |
| 	#endif
 | |
| #endif
 | |
| 
 | |
| 	// Read or create protocol id
 | |
| 	MProtocol_id_master=random_id(EEPROM_ID_OFFSET,false);
 | |
| 
 | |
| 	debugln("Module Id: %lx", MProtocol_id_master);
 | |
| 
 | |
| #ifdef ENABLE_PPM
 | |
| 	//Protocol and interrupts initialization
 | |
| 	if(mode_select != MODE_SERIAL)
 | |
| 	{ // PPM
 | |
| 		#ifndef MY_PPM_PROT
 | |
| 			const PPM_Parameters *PPM_prot_line=&PPM_prot[bank*14+mode_select-1];
 | |
| 		#else
 | |
| 			const PPM_Parameters *PPM_prot_line=&My_PPM_prot[bank*14+mode_select-1];
 | |
| 		#endif
 | |
| 		
 | |
| 		protocol		=	PPM_prot_line->protocol;
 | |
| 		cur_protocol[1] =	protocol;
 | |
| 		sub_protocol   	=	PPM_prot_line->sub_proto;
 | |
| 		RX_num			=	PPM_prot_line->rx_num;
 | |
| 		chan_order		=	PPM_prot_line->chan_order;
 | |
| 
 | |
| 		//Forced frequency tuning values for CC2500 protocols
 | |
| 		#if defined(FORCE_FRSKYD_TUNING) && defined(FRSKYD_CC2500_INO)
 | |
| 			if(protocol==PROTO_FRSKYD) 
 | |
| 				option			=	FORCE_FRSKYD_TUNING;		// Use config-defined tuning value for FrSkyD
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_FRSKYL_TUNING) && defined(FRSKYL_CC2500_INO)
 | |
| 			if(protocol==PROTO_FRSKYL) 
 | |
| 				option			=	FORCE_FRSKYL_TUNING;		// Use config-defined tuning value for FrSkyL
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_FRSKYV_TUNING) && defined(FRSKYV_CC2500_INO)
 | |
| 			if(protocol==PROTO_FRSKYV)
 | |
| 				option			=	FORCE_FRSKYV_TUNING;		// Use config-defined tuning value for FrSkyV
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_FRSKYX_TUNING) && defined(FRSKYX_CC2500_INO)
 | |
| 			if(protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2)
 | |
| 				option			=	FORCE_FRSKYX_TUNING;		// Use config-defined tuning value for FrSkyX
 | |
| 			else
 | |
| 		#endif 
 | |
| 		#if defined(FORCE_FUTABA_TUNING) && defined(FUTABA_CC2500_INO)
 | |
| 			if (protocol==PROTO_FUTABA)
 | |
| 				option			=	FORCE_FUTABA_TUNING;			// Use config-defined tuning value for SFHSS
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_CORONA_TUNING) && defined(CORONA_CC2500_INO)
 | |
| 			if (protocol==PROTO_CORONA)
 | |
| 				option			=	FORCE_CORONA_TUNING;		// Use config-defined tuning value for CORONA
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_SKYARTEC_TUNING) && defined(SKYARTEC_CC2500_INO)
 | |
| 			if (protocol==PROTO_SKYARTEC)
 | |
| 				option			=	FORCE_SKYARTEC_TUNING;		// Use config-defined tuning value for SKYARTEC
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_REDPINE_TUNING) && defined(REDPINE_CC2500_INO)
 | |
| 			if (protocol==PROTO_REDPINE)
 | |
| 				option			=	FORCE_REDPINE_TUNING;		// Use config-defined tuning value for REDPINE
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_RADIOLINK_TUNING) && defined(RADIOLINK_CC2500_INO)
 | |
| 			if (protocol==PROTO_RADIOLINK)
 | |
| 				option			=	FORCE_RADIOLINK_TUNING;		// Use config-defined tuning value for RADIOLINK
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_HITEC_TUNING) && defined(HITEC_CC2500_INO)
 | |
| 			if (protocol==PROTO_HITEC)
 | |
| 				option			=	FORCE_HITEC_TUNING;		// Use config-defined tuning value for HITEC
 | |
| 			else
 | |
| 		#endif
 | |
| 		#if defined(FORCE_HOTT_TUNING) && defined(HOTT_CC2500_INO)
 | |
| 			if (protocol==PROTO_HOTT)
 | |
| 				option			=	FORCE_HOTT_TUNING;			// Use config-defined tuning value for HOTT
 | |
| 			else
 | |
| 		#endif
 | |
| 				option			=	(uint8_t)PPM_prot_line->option;	// Use radio-defined option value
 | |
| 
 | |
| 		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
 | |
| 		}
 | |
| 
 | |
| 		protocol_init();
 | |
| 
 | |
| 		#ifndef STM32_BOARD
 | |
| 			//Configure PPM interrupt
 | |
| 			#if PPM_pin == 2
 | |
| 				EICRA |= _BV(ISC01);	// The rising edge of INT0 pin D2 generates an interrupt request
 | |
| 				EIMSK |= _BV(INT0);		// INT0 interrupt enable
 | |
| 			#elif PPM_pin == 3
 | |
| 				EICRA |= _BV(ISC11);	// The rising edge of INT1 pin D3 generates an interrupt request
 | |
| 				EIMSK |= _BV(INT1);		// INT1 interrupt enable
 | |
| 			#else
 | |
| 				#error PPM pin can only be 2 or 3
 | |
| 			#endif
 | |
| 		#else
 | |
| 			attachInterrupt(PPM_pin,PPM_decode,FALLING);
 | |
| 		#endif
 | |
| 
 | |
| 		#if defined(TELEMETRY)
 | |
| 			PPM_Telemetry_serial_init();// Configure serial for telemetry
 | |
| 		#endif
 | |
| 	}
 | |
| 	else
 | |
| #endif //ENABLE_PPM
 | |
| 	{ // Serial
 | |
| 		#ifdef ENABLE_SERIAL
 | |
| 			for(uint8_t i=0;i<3;i++)
 | |
| 				cur_protocol[i]=0;
 | |
| 			protocol=0;
 | |
| 			#ifdef CHECK_FOR_BOOTLOADER
 | |
| 				Mprotocol_serial_init(1); 	// Configure serial and enable RX interrupt
 | |
| 			#else
 | |
| 				Mprotocol_serial_init(); 	// Configure serial and enable RX interrupt
 | |
| 			#endif
 | |
| 		#endif //ENABLE_SERIAL
 | |
| 	}
 | |
| 	debugln("Init complete");
 | |
| 	LED2_on;
 | |
| }
 | |
| 
 | |
| // Main
 | |
| // Protocol scheduler
 | |
| void loop()
 | |
| { 
 | |
| 	uint16_t next_callback, diff;
 | |
| 	uint8_t count=0;
 | |
| 
 | |
| 	while(1)
 | |
| 	{
 | |
| 		while(remote_callback==0 || IS_WAIT_BIND_on || IS_INPUT_SIGNAL_off)
 | |
| 			if(!Update_All())
 | |
| 			{
 | |
| 				cli();								// Disable global int due to RW of 16 bits registers
 | |
| 				OCR1A=TCNT1;						// Callback should already have been called... Use "now" as new sync point.
 | |
| 				sei();								// Enable global int
 | |
| 			}
 | |
| 		TX_MAIN_PAUSE_on;
 | |
| 		tx_pause();
 | |
| 		next_callback=remote_callback()<<1;
 | |
| 		TX_MAIN_PAUSE_off;
 | |
| 		tx_resume();
 | |
| 		cli();										// Disable global int due to RW of 16 bits registers
 | |
| 		OCR1A+=next_callback;						// Calc when next_callback should happen
 | |
| 		#ifndef STM32_BOARD			
 | |
| 			TIFR1=OCF1A_bm;							// Clear compare A=callback flag
 | |
| 		#else
 | |
| 			TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC1IF;	// Clear Timer2/Comp1 interrupt flag
 | |
| 		#endif		
 | |
| 		diff=OCR1A-TCNT1;							// Calc the time difference
 | |
| 		sei();										// Enable global int
 | |
| 		if((diff&0x8000) && !(next_callback&0x8000))
 | |
| 		{ // Negative result=callback should already have been called... 
 | |
| 			debugln("Short CB:%d",next_callback);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			if(IS_RX_FLAG_on || IS_PPM_FLAG_on)
 | |
| 			{ // Serial or PPM is waiting...
 | |
| 				if(++count>10)
 | |
| 				{ //The protocol does not leave enough time for an update so forcing it
 | |
| 					count=0;
 | |
| 					debugln("Force update");
 | |
| 					Update_All();
 | |
| 				}
 | |
| 			}
 | |
| 			#ifndef STM32_BOARD
 | |
| 				while((TIFR1 & OCF1A_bm) == 0)
 | |
| 			#else
 | |
| 				while((TIMER2_BASE->SR & TIMER_SR_CC1IF )==0)
 | |
| 			#endif
 | |
| 			{
 | |
| 				if(diff>900*2)
 | |
| 				{	//If at least 1ms is available update values 
 | |
| 					if((diff&0x8000) && !(next_callback&0x8000))
 | |
| 					{//Should never get here...
 | |
| 						debugln("!!!BUG!!!");
 | |
| 						break;
 | |
| 					}
 | |
| 					count=0;
 | |
| 					Update_All();
 | |
| 					#ifdef DEBUG_SERIAL
 | |
| 						if(TIMER2_BASE->SR & TIMER_SR_CC1IF )
 | |
| 							debugln("Long update");
 | |
| 					#endif
 | |
| 					if(remote_callback==0)
 | |
| 						break;
 | |
| 					cli();							// Disable global int due to RW of 16 bits registers
 | |
| 					diff=OCR1A-TCNT1;				// Calc the time difference
 | |
| 					sei();							// Enable global int
 | |
| 				}
 | |
| 			}
 | |
| 		}			
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void End_Bind()
 | |
| {
 | |
| 	//Request protocol to terminate bind
 | |
| 	if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYL || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 || protocol==PROTO_FRSKYV || protocol==PROTO_FRSKY_R9
 | |
| 	|| protocol==PROTO_DSM_RX || protocol==PROTO_AFHDS2A_RX || protocol==PROTO_FRSKY_RX || protocol==PROTO_BAYANG_RX
 | |
| 	|| protocol==PROTO_AFHDS2A || protocol==PROTO_BUGS || protocol==PROTO_BUGSMINI || protocol==PROTO_HOTT || protocol==PROTO_ASSAN)
 | |
| 		BIND_DONE;
 | |
| 	else
 | |
| 		if(bind_counter>2)
 | |
| 			bind_counter=2;
 | |
| }
 | |
| 
 | |
| void Update_Telem()
 | |
| {
 | |
| 	#if defined(TELEMETRY)
 | |
| 		#ifndef MULTI_TELEMETRY
 | |
| 			if((protocol == PROTO_BAYANG_RX) || (protocol == PROTO_AFHDS2A_RX) || (protocol == PROTO_FRSKY_RX) || (protocol == PROTO_SCANNER) || (protocol==PROTO_FRSKYD) || (protocol==PROTO_BAYANG) || (protocol==PROTO_NCC1701) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_FRSKYX) || (protocol==PROTO_FRSKYX2) || (protocol==PROTO_DSM) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_HOTT) || (protocol==PROTO_PROPEL) || (protocol==PROTO_OMP) || (protocol==PROTO_DEVO) || (protocol==PROTO_DSM_RX) || (protocol==PROTO_FRSKY_R9) || (protocol==PROTO_RLINK) || (protocol==PROTO_WFLY2) || (protocol==PROTO_LOLI) || (protocol==PROTO_MLINK) || (protocol==PROTO_MT99XX))
 | |
| 		#endif
 | |
| 				if(IS_DISABLE_TELEM_off)
 | |
| 					TelemetryUpdate();
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| bool Update_All()
 | |
| {
 | |
| 	#ifdef ENABLE_SERIAL
 | |
| 		#ifdef CHECK_FOR_BOOTLOADER
 | |
| 			if ( (mode_select==MODE_SERIAL) && (NotBootChecking == 0) )
 | |
| 				pollBoot() ;
 | |
| 			else
 | |
| 		#endif
 | |
| 		if(mode_select==MODE_SERIAL && IS_RX_FLAG_on)		// Serial mode and something has been received
 | |
| 		{
 | |
| 			update_serial_data();							// Update protocol and data
 | |
| 			update_channels_aux();
 | |
| 			INPUT_SIGNAL_on;								//valid signal received
 | |
| 			last_signal=millis();
 | |
| 		}
 | |
| 	#endif //ENABLE_SERIAL
 | |
| 	#ifdef ENABLE_PPM
 | |
| 		if(mode_select!=MODE_SERIAL && IS_PPM_FLAG_on)		// PPM mode and a full frame has been received
 | |
| 		{
 | |
| 			uint32_t chan_or=chan_order;
 | |
| 			uint8_t ch;		
 | |
| 			uint8_t channelsCount = PPM_chan_max;
 | |
| 			
 | |
| 			#ifdef ENABLE_DIRECT_INPUTS				
 | |
| 				#ifdef DI_CH1_read
 | |
| 					PPM_data[channelsCount] = DI_CH1_read;
 | |
| 					channelsCount++;
 | |
| 				#endif
 | |
| 				#ifdef DI_CH2_read
 | |
| 					PPM_data[channelsCount] = DI_CH2_read;
 | |
| 					channelsCount++;
 | |
| 				#endif
 | |
| 				#ifdef DI_CH3_read
 | |
| 					PPM_data[channelsCount] = DI_CH3_read;
 | |
| 					channelsCount++;
 | |
| 				#endif
 | |
| 				#ifdef DI_CH4_read
 | |
| 					PPM_data[channelsCount] = DI_CH4_read;
 | |
| 					channelsCount++;
 | |
| 				#endif 
 | |
| 			#endif
 | |
| 			
 | |
| 			for(uint8_t i=0;i<channelsCount;i++)
 | |
| 			{ // update servo data without interrupts to prevent bad read
 | |
| 				uint16_t val;
 | |
| 				cli();										// disable global int
 | |
| 				val = PPM_data[i];
 | |
| 				sei();										// enable global int
 | |
| 				val=map16b(val,PPM_MIN_100*2,PPM_MAX_100*2,CHANNEL_MIN_100,CHANNEL_MAX_100);
 | |
| 				if(val&0x8000) 					val=CHANNEL_MIN_125;
 | |
| 				else if(val>CHANNEL_MAX_125)	val=CHANNEL_MAX_125;
 | |
| 				if(chan_or)
 | |
| 				{
 | |
| 					ch=chan_or>>28;
 | |
| 					if(ch)
 | |
| 						Channel_data[ch-1]=val;
 | |
| 					else
 | |
| 						Channel_data[i]=val;
 | |
| 					chan_or<<=4;
 | |
| 				}
 | |
| 				else
 | |
| 					Channel_data[i]=val;
 | |
| 			}
 | |
| 			PPM_FLAG_off;									// wait for next frame before update
 | |
| 			#ifdef FAILSAFE_ENABLE
 | |
| 				PPM_failsafe();
 | |
| 			#endif
 | |
| 			update_channels_aux();
 | |
| 			INPUT_SIGNAL_on;								// valid signal received
 | |
| 			last_signal=millis();
 | |
| 		}
 | |
| 	#endif //ENABLE_PPM
 | |
| 	update_led_status();
 | |
| 	
 | |
| 	#ifdef SEND_CPPM
 | |
| 		if ( telemetry_link & 0x80 )
 | |
| 		{ // Protocol requests telemetry to be disabled
 | |
| 			if( protocol == PROTO_FRSKY_RX || protocol == PROTO_AFHDS2A_RX || protocol == PROTO_BAYANG_RX || protocol == PROTO_DSM_RX )
 | |
| 			{ // RX protocol
 | |
| 				if(RX_LQI == 0)
 | |
| 					telemetry_link = 0x00;					// restore normal telemetry on connection loss
 | |
| 				else if(telemetry_link & 1)
 | |
| 				{ // New data available
 | |
| 					Send_CCPM_USART1();
 | |
| 					telemetry_link &= 0xFE;					// update done
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 	#endif
 | |
| 
 | |
| 	Update_Telem();
 | |
| 
 | |
| 	#ifdef ENABLE_BIND_CH
 | |
| 		if(IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_off && Channel_data[BIND_CH-1]>CHANNEL_MAX_COMMAND)
 | |
| 		{ // Autobind is on and BIND_CH went up
 | |
| 			CHANGE_PROTOCOL_FLAG_on;						// reload protocol
 | |
| 			BIND_IN_PROGRESS;								// enable bind
 | |
| 			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;
 | |
| 			End_Bind();
 | |
| 		}
 | |
| 	#endif //ENABLE_BIND_CH
 | |
| 	if(IS_CHANGE_PROTOCOL_FLAG_on)
 | |
| 	{ // Protocol needs to be changed or relaunched for bind
 | |
| 		protocol_init();									// init new protocol
 | |
| 		return true;
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| #if defined(FAILSAFE_ENABLE) && defined(ENABLE_PPM)
 | |
| void PPM_failsafe()
 | |
| {
 | |
| 	static uint8_t counter=0;
 | |
| 	
 | |
| 	if(IS_BIND_IN_PROGRESS || IS_FAILSAFE_VALUES_on) 	// bind is not finished yet or Failsafe already being sent
 | |
| 		return;
 | |
| 	BIND_SET_INPUT;
 | |
| 	BIND_SET_PULLUP;
 | |
| 	if(IS_BIND_BUTTON_on)
 | |
| 	{// bind button pressed
 | |
| 		counter++;
 | |
| 		if(counter>227)
 | |
| 		{ //after 5s with PPM frames @22ms
 | |
| 			counter=0;
 | |
| 			for(uint8_t i=0;i<NUM_CHN;i++)
 | |
| 				Failsafe_data[i]=Channel_data[i];
 | |
| 			FAILSAFE_VALUES_on;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 		counter=0;
 | |
| 	BIND_SET_OUTPUT;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Update channels direction and Channel_AUX flags based on servo AUX positions
 | |
| static void update_channels_aux(void)
 | |
| {
 | |
| 	//Reverse channels direction
 | |
| 	#ifdef REVERSE_AILERON
 | |
| 		reverse_channel(AILERON);
 | |
| 	#endif
 | |
| 	#ifdef REVERSE_ELEVATOR
 | |
| 		reverse_channel(ELEVATOR);
 | |
| 	#endif
 | |
| 	#ifdef REVERSE_THROTTLE
 | |
| 		reverse_channel(THROTTLE);
 | |
| 	#endif
 | |
| 	#ifdef REVERSE_RUDDER
 | |
| 		reverse_channel(RUDDER);
 | |
| 	#endif
 | |
| 		
 | |
| 	//Calc AUX flags
 | |
| 	Channel_AUX=0;
 | |
| 	for(uint8_t i=0;i<8;i++)
 | |
| 		if(Channel_data[CH5+i]>CHANNEL_SWITCH)
 | |
| 			Channel_AUX|=1<<i;
 | |
| }
 | |
| 
 | |
| // Update led status based on binding and serial
 | |
| static void update_led_status(void)
 | |
| {
 | |
| 	if(IS_INPUT_SIGNAL_on)
 | |
| 		if(millis()-last_signal>70)
 | |
| 		{
 | |
| 			INPUT_SIGNAL_off;							//no valid signal (PPM or Serial) received for 70ms
 | |
| 			debugln("No input signal");
 | |
| 		}
 | |
| 	if(blink<millis())
 | |
| 	{
 | |
| 		if(IS_INPUT_SIGNAL_off)
 | |
| 		{
 | |
| 			if(mode_select==MODE_SERIAL)
 | |
| 				blink+=BLINK_SERIAL_TIME;				//blink slowly if no valid serial input
 | |
| 			else
 | |
| 				blink+=BLINK_PPM_TIME;					//blink more slowly if no valid PPM input
 | |
| 		}
 | |
| 		else
 | |
| 			if(remote_callback == 0)
 | |
| 			{ // Invalid protocol
 | |
| 				if(IS_LED_on)							//flash to indicate invalid protocol
 | |
| 					blink+=BLINK_BAD_PROTO_TIME_LOW;
 | |
| 				else
 | |
| 					blink+=BLINK_BAD_PROTO_TIME_HIGH;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if(IS_WAIT_BIND_on)
 | |
| 				{
 | |
| 					if(IS_LED_on)							//flash to indicate WAIT_BIND
 | |
| 						blink+=BLINK_WAIT_BIND_TIME_LOW;
 | |
| 					else
 | |
| 						blink+=BLINK_WAIT_BIND_TIME_HIGH;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if(IS_BIND_DONE)
 | |
| 						LED_off;							//bind completed force led on
 | |
| 					blink+=BLINK_BIND_TIME;					//blink fastly during binding
 | |
| 				}
 | |
| 			}
 | |
| 		LED_toggle;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #ifdef ENABLE_PPM
 | |
| uint8_t bank_switch(void)
 | |
| {
 | |
| 	uint8_t bank=eeprom_read_byte((EE_ADDR)EEPROM_BANK_OFFSET);
 | |
| 	if(bank>=NBR_BANKS)
 | |
| 	{ // Wrong number of bank
 | |
| 		eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,0x00);	// set bank to 0
 | |
| 		bank=0;
 | |
| 	}
 | |
| 	debugln("Using bank %d", bank);
 | |
| 
 | |
| 	phase=3;
 | |
| 	uint32_t check=millis();
 | |
| 	blink=millis();
 | |
| 	while(mode_select==15)
 | |
| 	{ //loop here if the dial is on position 15 for user to select the bank
 | |
| 		if(blink<millis())
 | |
| 		{
 | |
| 			switch(phase & 0x03)
 | |
| 			{ // Flash bank number of times
 | |
| 				case 0:
 | |
| 					LED_on;
 | |
| 					blink+=BLINK_BANK_TIME_HIGH;
 | |
| 					phase++;
 | |
| 					break;
 | |
| 				case 1:
 | |
| 					LED_off;
 | |
| 					blink+=BLINK_BANK_TIME_LOW;
 | |
| 					phase++;
 | |
| 					break;
 | |
| 				case 2:
 | |
| 					if( (phase>>2) >= bank)
 | |
| 					{
 | |
| 						phase=0;
 | |
| 						blink+=BLINK_BANK_REPEAT;
 | |
| 					}
 | |
| 					else
 | |
| 						phase+=2;
 | |
| 					break;
 | |
| 				case 3:
 | |
| 					LED_output;
 | |
| 					LED_off;
 | |
| 					blink+=BLINK_BANK_TIME_LOW;
 | |
| 					phase=0;
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 		if(check<millis())
 | |
| 		{
 | |
| 			//Test bind button: for AVR it's shared with the LED so some extra work is needed to check it...
 | |
| 			#ifndef STM32_BOARD
 | |
| 				bool led=IS_LED_on;
 | |
| 				BIND_SET_INPUT;
 | |
| 				BIND_SET_PULLUP;
 | |
| 			#endif
 | |
| 			bool test_bind=IS_BIND_BUTTON_on;
 | |
| 			#ifndef STM32_BOARD
 | |
| 				if(led)
 | |
| 					LED_on;
 | |
| 				else
 | |
| 					LED_off;
 | |
| 				LED_output;
 | |
| 			#endif
 | |
| 			if( test_bind )
 | |
| 			{	// Increase bank
 | |
| 				LED_on;
 | |
| 				bank++;
 | |
| 				if(bank>=NBR_BANKS)
 | |
| 					bank=0;
 | |
| 				eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,bank);
 | |
| 				debugln("Using bank %d", bank);
 | |
| 				phase=3;
 | |
| 				blink+=BLINK_BANK_REPEAT;
 | |
| 				check+=2*BLINK_BANK_REPEAT;
 | |
| 			}
 | |
| 			check+=1;
 | |
| 		}
 | |
| 	}
 | |
| 	return bank;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| inline void tx_pause()
 | |
| {
 | |
| 	#ifdef TELEMETRY
 | |
| 	// Pause telemetry by disabling transmitter interrupt
 | |
| 		#ifdef ORANGE_TX
 | |
| 			USARTC0.CTRLA &= ~0x03 ;
 | |
| 		#else
 | |
| 			#ifndef BASH_SERIAL
 | |
| 				#ifdef STM32_BOARD
 | |
| 					USART3_BASE->CR1 &= ~ USART_CR1_TXEIE;
 | |
| 				#else
 | |
| 					UCSR0B &= ~_BV(UDRIE0);
 | |
| 				#endif
 | |
| 			#endif
 | |
| 		#endif
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| inline void tx_resume()
 | |
| {
 | |
| 	#ifdef TELEMETRY
 | |
| 	// Resume telemetry by enabling transmitter interrupt
 | |
| 		if(IS_TX_PAUSE_off)
 | |
| 		{
 | |
| 			#ifdef ORANGE_TX
 | |
| 				cli() ;
 | |
| 				USARTC0.CTRLA = (USARTC0.CTRLA & 0xFC) | 0x01 ;
 | |
| 				sei() ;
 | |
| 			#else
 | |
| 				#ifndef BASH_SERIAL
 | |
| 					#ifdef STM32_BOARD
 | |
| 						USART3_BASE->CR1 |= USART_CR1_TXEIE;
 | |
| 					#else
 | |
| 						UCSR0B |= _BV(UDRIE0);			
 | |
| 					#endif
 | |
| 				#else
 | |
| 					resumeBashSerial();
 | |
| 				#endif
 | |
| 			#endif
 | |
| 		}
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| void rf_switch(uint8_t comp)
 | |
| {
 | |
| 	PE1_off;
 | |
| 	PE2_off;
 | |
| 	switch(comp)
 | |
| 	{
 | |
| 		case SW_CC2500:
 | |
| 			PE2_on;
 | |
| 			break;
 | |
| 		case SW_CYRF:
 | |
| 			PE2_on;
 | |
| 		case SW_NRF:
 | |
| 			PE1_on;
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Protocol start
 | |
| static void protocol_init()
 | |
| {
 | |
| 	if(IS_WAIT_BIND_off)
 | |
| 	{
 | |
| 		remote_callback = 0;			// No protocol
 | |
| 		LED_off;						// Led off during protocol init
 | |
| 		crc16_polynomial = 0x1021;		// Default CRC crc16_polynomial
 | |
| 		crc8_polynomial  = 0x31;		// Default CRC crc8_polynomial
 | |
| 		prev_option = option;
 | |
| 
 | |
| 		multi_protocols_index = 0xFF;
 | |
| 		// reset telemetry
 | |
| 		#ifdef TELEMETRY
 | |
| 			#ifdef MULTI_SYNC
 | |
| 				inputRefreshRate = 0;	// Don't do it unless the protocol asks for it
 | |
| 			#endif
 | |
| 			tx_pause();
 | |
| 			init_frskyd_link_telemetry();
 | |
| 			pps_timer=millis();
 | |
| 			pps_counter=0;
 | |
| 			#ifdef BASH_SERIAL
 | |
| 				TIMSK0 = 0 ;			// Stop all timer 0 interrupts
 | |
| 				#ifdef INVERT_SERIAL
 | |
| 					SERIAL_TX_off;
 | |
| 				#else
 | |
| 					SERIAL_TX_on;
 | |
| 				#endif
 | |
| 				SerialControl.tail=0;
 | |
| 				SerialControl.head=0;
 | |
| 				SerialControl.busy=0;
 | |
| 			#else
 | |
| 				tx_tail=0;
 | |
| 				tx_head=0;
 | |
| 			#endif
 | |
| 			TX_RX_PAUSE_off;
 | |
| 			TX_MAIN_PAUSE_off;
 | |
| 			tx_resume();
 | |
| 			#if defined(AFHDS2A_RX_A7105_INO) || defined(FRSKY_RX_CC2500_INO) || defined(BAYANG_RX_NRF24L01_INO) || defined(DSM_RX_CYRF6936_INO)
 | |
| 				for(uint8_t ch=0; ch<16; ch++)
 | |
| 					rx_rc_chan[ch] = 1024;
 | |
| 			#endif
 | |
| 		#endif
 | |
| 		binding_idx=0;
 | |
| 		
 | |
| 		//Stop CPPM if it was previously running
 | |
| 		#ifdef SEND_CPPM
 | |
| 			release_trainer_ppm();
 | |
| 		#endif
 | |
| 		
 | |
| 		//Set global ID and rx_tx_addr
 | |
| 		MProtocol_id = RX_num + MProtocol_id_master;
 | |
| 		set_rx_tx_addr(MProtocol_id);
 | |
| 		
 | |
| 		#ifdef FAILSAFE_ENABLE
 | |
| 			FAILSAFE_VALUES_off;
 | |
| 		#endif
 | |
| 		DATA_BUFFER_LOW_off;
 | |
| 
 | |
| 		SUB_PROTO_INVALID;
 | |
| 		option_override = 0xFF;
 | |
| 		
 | |
| 		blink=millis();
 | |
| 
 | |
| 		debugln("Protocol selected: %d, sub proto %d, rxnum %d, option %d", protocol, sub_protocol, RX_num, option);
 | |
| 		if(protocol)
 | |
| 		{
 | |
| 			//Reset all modules
 | |
| 			modules_reset();				
 | |
| 
 | |
| 			uint8_t index=0;
 | |
| 			//#if defined(FRSKYX_CC2500_INO) && defined(MULTI_EU)
 | |
| 			//	if( ! ( (protocol == PROTO_FRSKYX || protocol == PROTO_FRSKYX2) && sub_protocol < 2 ) )
 | |
| 			//#endif
 | |
| 			while(multi_protocols[index].protocol != 0xFF)
 | |
| 			{
 | |
| 				if(multi_protocols[index].protocol==protocol)
 | |
| 				{
 | |
| 					//Save index
 | |
| 					multi_protocols_index = index;
 | |
| 					//Check sub protocol validity
 | |
| 					if( ((sub_protocol&0x07) == 0) || (sub_protocol&0x07) < multi_protocols[index].nbrSubProto )
 | |
| 						SUB_PROTO_VALID;
 | |
| 					if(IS_SUB_PROTO_VALID)
 | |
| 					{//Start the protocol
 | |
| 						//Set the RF switch
 | |
| 						rf_switch(multi_protocols[index].rfSwitch);
 | |
| 						//Init protocol
 | |
| 						multi_protocols[index].Init();		// Init could invalidate the sub proto in case it is not suuported
 | |
| 						if(IS_SUB_PROTO_VALID)
 | |
| 							remote_callback = multi_protocols[index].CallBack;	//Save call back function address
 | |
| 					}
 | |
| 					#ifdef DEBUG_SERIAL
 | |
| 						debug("Proto=%s", multi_protocols[index].ProtoString);
 | |
| 						debug(", nbr_sub=%d, Sub=", multi_protocols[index].nbrSubProto);
 | |
| 						if(IS_SUB_PROTO_VALID)
 | |
| 						{
 | |
| 							uint8_t len=multi_protocols[index].SubProtoString[0];
 | |
| 							uint8_t offset=len*(sub_protocol&0x07)+1;
 | |
| 							for(uint8_t j=0;j<len;j++)
 | |
| 								debug("%c",multi_protocols[index].SubProtoString[j+offset]);
 | |
| 						}
 | |
| 						debug(", Opt=%d",multi_protocols[index].optionType);
 | |
| 						debug(", FS=%d",multi_protocols[index].failSafe);
 | |
| 						debug(", CHMap=%d",multi_protocols[index].chMap);
 | |
| 						debugln(", rfSw=%d",multi_protocols[index].rfSwitch);
 | |
| 					#endif
 | |
| 					break;
 | |
| 				}
 | |
| 				index++;
 | |
| 			}
 | |
| 			//Send a telemetry status right now
 | |
| 			SEND_MULTI_STATUS_on;
 | |
| 			Update_Telem();
 | |
| 		}
 | |
| 		#ifdef MULTI_TELEMETRY
 | |
| 			else
 | |
| 			{//protocol=PROTO_PROTOLIST=0
 | |
| 				remote_callback = PROTOLIST_callback;
 | |
| 				prev_option = option + 1;
 | |
| 			}
 | |
| 		#endif
 | |
| 	}
 | |
| 
 | |
| 	#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;
 | |
| 
 | |
| 	if(protocol)
 | |
| 	{
 | |
| 		//Wait 5ms after protocol init
 | |
| 		cli();										// disable global int
 | |
| 		OCR1A = TCNT1 + 5000*2;						// set compare A for callback
 | |
| 		#ifndef STM32_BOARD
 | |
| 			TIFR1 = OCF1A_bm ;						// clear compare A flag
 | |
| 		#else
 | |
| 			TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC1IF;	// Clear Timer2/Comp1 interrupt flag
 | |
| 		#endif	
 | |
| 		sei();										// enable global int
 | |
| 		BIND_BUTTON_FLAG_off;						// do not bind/reset id anymore even if protocol change
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void update_serial_data()
 | |
| {
 | |
| 	static bool prev_ch_mapping=false;
 | |
| 	#if defined(TELEMETRY) && defined(INVERT_TELEMETRY_TX)
 | |
| 		#ifdef INVERT_TELEMETRY
 | |
| 			static bool prev_inv_telem=true;
 | |
| 		#else
 | |
| 			static bool prev_inv_telem=false;
 | |
| 		#endif
 | |
| 	#endif
 | |
| 
 | |
| 	RX_DONOTUPDATE_on;
 | |
| 	RX_FLAG_off;								//data is being processed
 | |
| 
 | |
| 	#ifdef SAMSON	// Extremely dangerous, do not enable this unless you know what you are doing...
 | |
| 		if( rx_ok_buff[0]==0x55 && (rx_ok_buff[1]&0x1F)==PROTO_FRSKYD && rx_ok_buff[2]==0x7F && rx_ok_buff[24]==217 && rx_ok_buff[25]==202 )
 | |
| 		{//proto==FRSKYD+sub==7+rx_num==7+CH15==73%+CH16==73%
 | |
| 			rx_ok_buff[1]=(rx_ok_buff[1]&0xE0) | PROTO_FLYSKY;			// change the protocol to Flysky
 | |
| 			memcpy((void*)(rx_ok_buff+4),(void*)(rx_ok_buff+4+11),11);	// reassign channels 9-16 to 1-8
 | |
| 		}
 | |
| 	#endif
 | |
| 	#ifdef BONI	// Extremely dangerous, do not enable this!!! This is really for a special case...
 | |
| 		if(CH14_SW)
 | |
| 			rx_ok_buff[2]=(rx_ok_buff[2]&0xF0)|((rx_ok_buff[2]+1)&0x0F);
 | |
| 	#endif
 | |
| 
 | |
| 	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) && defined(FRSKYD_CC2500_INO)
 | |
| 		if(protocol==PROTO_FRSKYD)
 | |
| 			option=FORCE_FRSKYD_TUNING;			// Use config-defined tuning value for FrSkyD
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_FRSKYL_TUNING) && defined(FRSKYL_CC2500_INO)
 | |
| 		if(protocol==PROTO_FRSKYL)
 | |
| 			option=FORCE_FRSKYL_TUNING;			// Use config-defined tuning value for FrSkyL
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_FRSKYV_TUNING) && defined(FRSKYV_CC2500_INO)
 | |
| 		if(protocol==PROTO_FRSKYV)
 | |
| 			option=FORCE_FRSKYV_TUNING;			// Use config-defined tuning value for FrSkyV
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_FRSKYX_TUNING) && defined(FRSKYX_CC2500_INO)
 | |
| 		if(protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2)
 | |
| 			option=FORCE_FRSKYX_TUNING;			// Use config-defined tuning value for FrSkyX
 | |
| 		else
 | |
| 	#endif 
 | |
| 	#if defined(FORCE_FUTABA_TUNING) && defined(FUTABA_CC2500_INO)
 | |
| 		if (protocol==PROTO_FUTABA)
 | |
| 			option=FORCE_FUTABA_TUNING;			// Use config-defined tuning value for SFHSS
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_CORONA_TUNING) && defined(CORONA_CC2500_INO)
 | |
| 		if (protocol==PROTO_CORONA)
 | |
| 			option=FORCE_CORONA_TUNING;			// Use config-defined tuning value for CORONA
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_SKYARTEC_TUNING) && defined(SKYARTEC_CC2500_INO)
 | |
| 		if (protocol==PROTO_SKYARTEC)
 | |
| 			option=FORCE_SKYARTEC_TUNING;			// Use config-defined tuning value for SKYARTEC
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_REDPINE_TUNING) && defined(REDPINE_CC2500_INO)
 | |
| 		if (protocol==PROTO_REDPINE)
 | |
| 			option=FORCE_REDPINE_TUNING;		// Use config-defined tuning value for REDPINE
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_RADIOLINK_TUNING) && defined(RADIOLINK_CC2500_INO)
 | |
| 		if (protocol==PROTO_RADIOLINK)
 | |
| 			option			=	FORCE_RADIOLINK_TUNING;		// Use config-defined tuning value for RADIOLINK
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_HITEC_TUNING) && defined(HITEC_CC2500_INO)
 | |
| 		if (protocol==PROTO_HITEC)
 | |
| 			option=FORCE_HITEC_TUNING;			// Use config-defined tuning value for HITEC
 | |
| 		else
 | |
| 	#endif
 | |
| 	#if defined(FORCE_HOTT_TUNING) && defined(HOTT_CC2500_INO)
 | |
| 		if (protocol==PROTO_HOTT)
 | |
| 			option=FORCE_HOTT_TUNING;			// Use config-defined tuning value for HOTT
 | |
| 		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
 | |
| 			FAILSAFE_VALUES_on;					// Failsafe data has been received
 | |
| 			debugln("Failsafe received");
 | |
| 		}
 | |
| 	#endif
 | |
| 
 | |
| 	DISABLE_CH_MAP_off;
 | |
| 	DISABLE_TELEM_off;
 | |
| 	if(rx_len>26)
 | |
| 	{//Additional flag received at the end
 | |
| 		rx_ok_buff[0]=(rx_ok_buff[26]&0xF0) | (rx_ok_buff[0]&0x0F);	// Additional protocol numbers and RX_Num available -> store them in rx_ok_buff[0]
 | |
| 		if(rx_ok_buff[26]&0x02)
 | |
| 			DISABLE_TELEM_on;
 | |
| 		if(rx_ok_buff[26]&0x01)
 | |
| 			DISABLE_CH_MAP_on;
 | |
| 		#if defined(TELEMETRY) && defined(INVERT_TELEMETRY_TX)
 | |
| 			if(((rx_ok_buff[26]&0x08)!=0) ^ prev_inv_telem)
 | |
| 			{ //value changed
 | |
| 				if(rx_ok_buff[26]&0x08)
 | |
| 				{								// Invert telemetry
 | |
| 					debugln("Invert telem %d,%d",rx_ok_buff[26]&0x01,prev_inv_telem);
 | |
| 					#if defined (ORANGE_TX)
 | |
| 						PORTC.PIN3CTRL |= 0x40 ;
 | |
| 					#elif defined (STM32_BOARD)
 | |
| 						TX_INV_on;
 | |
| 						RX_INV_on;
 | |
| 					#endif
 | |
| 				}
 | |
| 				else
 | |
| 				{								// Normal telemetry
 | |
| 					debugln("Normal telem %d,%d",rx_ok_buff[26]&0x01,prev_inv_telem);
 | |
| 					#if defined (ORANGE_TX)
 | |
| 						PORTC.PIN3CTRL &= 0xBF ;
 | |
| 					#elif defined (STM32_BOARD)
 | |
| 						TX_INV_off;
 | |
| 						RX_INV_off;
 | |
| 					#endif
 | |
| 				}
 | |
| 				prev_inv_telem=rx_ok_buff[26]&0x08;
 | |
| 			}
 | |
| 		#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[1]&0x1F;			//protocol no (0-31)
 | |
| 		if(!(rx_ok_buff[0]&1))
 | |
| 			protocol+=32;						//protocol no (0-63)
 | |
| 		if(rx_len>26)
 | |
| 			protocol|=rx_ok_buff[26]&0xC0;		//protocol no (0-255)
 | |
| 		sub_protocol=(rx_ok_buff[2]>>4)& 0x07;	//subprotocol no (0-7) bits 4-6
 | |
| 		RX_num=rx_ok_buff[2]& 0x0F;				//rx_num no (0-15)
 | |
| 		if(rx_len>26)
 | |
| 			RX_num|=rx_ok_buff[26]&0x30;		//rx_num no (0-63)
 | |
| 	}
 | |
| 	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
 | |
| 				End_Bind();
 | |
| 			}
 | |
| 			
 | |
| 	//store current protocol values
 | |
| 	for(uint8_t i=0;i<3;i++)
 | |
| 		cur_protocol[i] =  rx_ok_buff[i];
 | |
| 
 | |
| 	//disable channel mapping
 | |
| 	if(multi_protocols[multi_protocols_index].chMap == 0)
 | |
| 		DISABLE_CH_MAP_off;						//not a protocol supporting ch map to be disabled
 | |
| 
 | |
| 	if(prev_ch_mapping!=IS_DISABLE_CH_MAP_on)
 | |
| 	{
 | |
| 		prev_ch_mapping=IS_DISABLE_CH_MAP_on;
 | |
| 		if(IS_DISABLE_CH_MAP_on)
 | |
| 		{
 | |
| 			for(uint8_t i=0;i<4;i++)
 | |
| 				CH_AETR[i]=CH_TAER[i]=CH_EATR[i]=i;
 | |
| 			debugln("DISABLE_CH_MAP_on");
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			CH_AETR[0]=AILERON;CH_AETR[1]=ELEVATOR;CH_AETR[2]=THROTTLE;CH_AETR[3]=RUDDER;
 | |
| 			CH_TAER[0]=THROTTLE;CH_TAER[1]=AILERON;CH_TAER[2]=ELEVATOR;CH_TAER[3]=RUDDER;
 | |
| 			CH_EATR[0]=ELEVATOR;CH_EATR[1]=AILERON;CH_EATR[2]=THROTTLE;CH_EATR[3]=RUDDER;
 | |
| 			debugln("DISABLE_CH_MAP_off");
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// decode channel/failsafe values
 | |
| 	volatile uint8_t *p=rx_ok_buff+3;
 | |
| 	uint8_t dec=-3;
 | |
| 	for(uint8_t i=0;i<NUM_CHN;i++)
 | |
| 	{
 | |
| 		dec+=3;
 | |
| 		if(dec>=8)
 | |
| 		{
 | |
| 			dec-=8;
 | |
| 			p++;
 | |
| 		}
 | |
| 		p++;
 | |
| 		uint16_t temp=((*((uint32_t *)p))>>dec)&0x7FF;
 | |
| 		#ifdef FAILSAFE_ENABLE
 | |
| 			if(failsafe)
 | |
| 				Failsafe_data[i]=temp;			//value range 0..2047, 0=no pulse, 2047=hold
 | |
| 			else
 | |
| 		#endif
 | |
| 				Channel_data[i]=temp;			//value range 0..2047, 0=-125%, 2047=+125%
 | |
| 	}
 | |
| 
 | |
| 	#ifdef HOTT_FW_TELEMETRY
 | |
| 		HoTT_SerialRX=false;
 | |
| 	#endif
 | |
| 	if(rx_len>27)
 | |
| 	{ // Data available for the current protocol
 | |
| 		#if defined(FRSKYX_CC2500_INO) || defined(FRSKYR9_SX1276_INO)
 | |
| 			if((protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 || protocol==PROTO_FRSKY_R9) && rx_len==28)
 | |
| 			{//Protocol waiting for 1 byte during bind
 | |
| 				binding_idx=rx_ok_buff[27];
 | |
| 			}
 | |
| 		#endif
 | |
| 		#ifdef SPORT_SEND
 | |
| 			if((protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 || protocol==PROTO_FRSKY_R9) && rx_len==27+8)
 | |
| 			{//Protocol waiting for 8 bytes
 | |
| 				#define BYTE_STUFF	0x7D
 | |
| 				#define STUFF_MASK	0x20
 | |
| 				//debug("SPort_in: ");
 | |
| 				boolean sport_valid=false;
 | |
| 				for(uint8_t i=28;i<28+7;i++)
 | |
| 					if(rx_ok_buff[i]!=0) sport_valid=true;	//Check that the payload is not full of 0
 | |
| 				if((rx_ok_buff[27]&0x1F) > 0x1B)				//Check 1st byte validity
 | |
| 					sport_valid=false;
 | |
| 				if(sport_valid)
 | |
| 				{
 | |
| 					SportData[SportTail]=0x7E;
 | |
| 					SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1);
 | |
| 					SportData[SportTail]=rx_ok_buff[27]&0x1F;
 | |
| 					SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1);
 | |
| 					for(uint8_t i=28;i<28+7;i++)
 | |
| 					{
 | |
| 						if( (rx_ok_buff[i]==BYTE_STUFF) || (rx_ok_buff[i]==0x7E) )
 | |
| 						{//stuff
 | |
| 							SportData[SportTail]=BYTE_STUFF;
 | |
| 							SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1);
 | |
| 							SportData[SportTail]=rx_ok_buff[i]^STUFF_MASK;
 | |
| 						}
 | |
| 						else
 | |
| 							SportData[SportTail]=rx_ok_buff[i];
 | |
| 						//debug("%02X ",SportData[SportTail]);
 | |
| 						SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1);
 | |
| 					}
 | |
| 					uint8_t used = SportTail;
 | |
| 					if ( SportHead > SportTail )
 | |
| 						used += MAX_SPORT_BUFFER - SportHead ;
 | |
| 					else
 | |
| 						used -= SportHead ;
 | |
| 					if ( used >= MAX_SPORT_BUFFER-(MAX_SPORT_BUFFER>>2) )
 | |
| 					{
 | |
| 						DATA_BUFFER_LOW_on;
 | |
| 						//Send Multi Status ASAP to inform the TX
 | |
| 						SEND_MULTI_STATUS_on;
 | |
| 						Update_Telem();
 | |
| 						debugln("Low buf=%d,h=%d,t=%d",used,SportHead,SportTail);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		#endif //SPORT_SEND
 | |
| 		#ifdef HOTT_FW_TELEMETRY
 | |
| 			if(protocol==PROTO_HOTT && rx_len==27+1)
 | |
| 			{//Protocol waiting for 1 byte
 | |
| 				HoTT_SerialRX_val=rx_ok_buff[27];
 | |
| 				HoTT_SerialRX=true;
 | |
| 			}
 | |
| 		#endif
 | |
| 		#ifdef DSM_FWD_PGM
 | |
| 			if(protocol==PROTO_DSM && rx_len==27+7)
 | |
| 			{//Protocol waiting for 7 bytes
 | |
| 				memcpy(DSM_SerialRX_val, (const void *)&rx_ok_buff[27],7);
 | |
| 				DSM_SerialRX=true;
 | |
| 			}
 | |
| 		#endif
 | |
| 		#ifdef MULTI_CONFIG_INO
 | |
| 			if(protocol==PROTO_CONFIG && rx_len==27+7)
 | |
| 			{//Protocol waiting for 7 bytes
 | |
| 				memcpy(CONFIG_SerialRX_val, (const void *)&rx_ok_buff[27],7);
 | |
| 				CONFIG_SerialRX=true;
 | |
| 			}
 | |
| 		#endif
 | |
| 	}
 | |
| 
 | |
| 	RX_DONOTUPDATE_off;
 | |
| 	#ifdef ORANGE_TX
 | |
| 		cli();
 | |
| 	#else
 | |
| 		UCSR0B &= ~_BV(RXCIE0);					// RX interrupt disable
 | |
| 	#endif
 | |
| 	if(IS_RX_MISSED_BUFF_on)					// If the buffer is still valid
 | |
| 	{	
 | |
| 		if(rx_idx>=26 && rx_idx<RXBUFFER_SIZE)
 | |
| 		{
 | |
| 			rx_len=rx_idx;
 | |
| 			memcpy((void*)rx_ok_buff,(const void*)rx_buff,rx_len);// Duplicate the buffer
 | |
| 			RX_FLAG_on;							// Data to be processed next time...
 | |
| 		}
 | |
| 		RX_MISSED_BUFF_off;
 | |
| 	}
 | |
| 	#ifdef ORANGE_TX
 | |
| 		sei();
 | |
| 	#else
 | |
| 		UCSR0B |= _BV(RXCIE0) ;					// RX interrupt enable
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| void modules_reset()
 | |
| {
 | |
| 	#ifdef	CC2500_INSTALLED
 | |
| 		CC2500_Reset();
 | |
| 	#endif
 | |
| 	#ifdef	A7105_INSTALLED
 | |
| 		A7105_Reset();
 | |
| 	#endif
 | |
| 	#ifdef	CYRF6936_INSTALLED
 | |
| 		CYRF_Reset();
 | |
| 	#endif
 | |
| 	#ifdef	NRF24L01_INSTALLED
 | |
| 		NRF24L01_Reset();
 | |
| 	#endif
 | |
| 	#ifdef	SX1276_INSTALLED
 | |
| 		SX1276_Reset();
 | |
| 	#endif
 | |
| 
 | |
| 	//Wait for every component to reset
 | |
| 	delayMilliseconds(100);
 | |
| 	prev_power=0xFD;		// unused power value
 | |
| }
 | |
| 
 | |
| #ifdef CHECK_FOR_BOOTLOADER
 | |
| 	void Mprotocol_serial_init( uint8_t boot )
 | |
| #else
 | |
| 	void Mprotocol_serial_init()
 | |
| #endif
 | |
| {
 | |
| 	#ifdef ORANGE_TX
 | |
| 		PORTC.OUTSET = 0x08 ;
 | |
| 		PORTC.DIRSET = 0x08 ;
 | |
| 
 | |
| 		USARTC0.BAUDCTRLA = 19 ;
 | |
| 		USARTC0.BAUDCTRLB = 0 ;
 | |
| 		
 | |
| 		USARTC0.CTRLB = 0x18 ;
 | |
| 		USARTC0.CTRLA = (USARTC0.CTRLA & 0xCC) | 0x11 ;
 | |
| 		USARTC0.CTRLC = 0x2B ;
 | |
| 		UDR0 ;
 | |
| 		#ifdef INVERT_SERIAL
 | |
| 			PORTC.PIN3CTRL |= 0x40 ;
 | |
| 		#endif
 | |
| 		#ifdef CHECK_FOR_BOOTLOADER
 | |
| 			if ( boot )
 | |
| 			{
 | |
| 				USARTC0.BAUDCTRLB = 0 ;
 | |
| 				USARTC0.BAUDCTRLA = 33 ;		// 57600
 | |
| 				USARTC0.CTRLA = (USARTC0.CTRLA & 0xC0) ;
 | |
| 				USARTC0.CTRLC = 0x03 ;			// 8 bit, no parity, 1 stop
 | |
| 				USARTC0.CTRLB = 0x18 ;			// Enable Tx and Rx
 | |
| 				PORTC.PIN3CTRL &= ~0x40 ;
 | |
| 			}
 | |
| 		#endif // CHECK_FOR_BOOTLOADER
 | |
| 	#elif defined STM32_BOARD
 | |
| 		#ifdef CHECK_FOR_BOOTLOADER
 | |
| 			if ( boot )
 | |
| 			{
 | |
| 				usart2_begin(57600,SERIAL_8N1);
 | |
| 				USART2_BASE->CR1 &= ~USART_CR1_RXNEIE ;
 | |
| 				(void)UDR0 ;
 | |
| 			}
 | |
| 			else
 | |
| 		#endif // CHECK_FOR_BOOTLOADER
 | |
| 		{
 | |
| 			usart2_begin(100000,SERIAL_8E2);
 | |
| 			USART2_BASE->CR1 |= USART_CR1_PCE_BIT;
 | |
| 		}
 | |
| 		USART2_BASE->CR1 &= ~ USART_CR1_TE;		//disable transmit
 | |
| 		usart3_begin(100000,SERIAL_8E2);
 | |
| 	#else
 | |
| 		//ATMEGA328p
 | |
| 		#include <util/setbaud.h>	
 | |
| 		UBRR0H = UBRRH_VALUE;
 | |
| 		UBRR0L = UBRRL_VALUE;
 | |
| 		UCSR0A = 0 ;	// Clear X2 bit
 | |
| 		//Set frame format to 8 data bits, even parity, 2 stop bits
 | |
| 		UCSR0C = _BV(UPM01)|_BV(USBS0)|_BV(UCSZ01)|_BV(UCSZ00);
 | |
| 		while ( UCSR0A & (1 << RXC0) )	//flush receive buffer
 | |
| 			UDR0;
 | |
| 		//enable reception and RC complete interrupt
 | |
| 		UCSR0B = _BV(RXEN0)|_BV(RXCIE0);//rx enable and interrupt
 | |
| 		#ifndef DEBUG_PIN
 | |
| 			#if defined(TELEMETRY)
 | |
| 				initTXSerial( SPEED_100K ) ;
 | |
| 			#endif //TELEMETRY
 | |
| 		#endif //DEBUG_PIN
 | |
| 		#ifdef CHECK_FOR_BOOTLOADER
 | |
| 			if ( boot )
 | |
| 			{
 | |
| 				UBRR0H = 0;
 | |
| 				UBRR0L = 33;			// 57600
 | |
| 				UCSR0C &= ~_BV(UPM01);	// No parity
 | |
| 				UCSR0B &= ~_BV(RXCIE0);	// No rx interrupt
 | |
| 				UCSR0A |= _BV(U2X0);	// Double speed mode USART0
 | |
| 			}
 | |
| 		#endif // CHECK_FOR_BOOTLOADER
 | |
| 	#endif //ORANGE_TX
 | |
| }
 | |
| 
 | |
| #ifdef STM32_BOARD
 | |
| 	void usart2_begin(uint32_t baud,uint32_t config )
 | |
| 	{
 | |
| 		usart_init(USART2); 
 | |
| 		usart_config_gpios_async(USART2,GPIOA,PIN_MAP[PA3].gpio_bit,GPIOA,PIN_MAP[PA2].gpio_bit,config);
 | |
| 		LED2_output;
 | |
| 		usart_set_baud_rate(USART2, STM32_PCLK1, baud);
 | |
| 		usart_enable(USART2);
 | |
| 	}
 | |
| 	void usart3_begin(uint32_t baud,uint32_t config )
 | |
| 	{
 | |
| 		usart_init(USART3);
 | |
| 		usart_config_gpios_async(USART3,GPIOB,PIN_MAP[PB11].gpio_bit,GPIOB,PIN_MAP[PB10].gpio_bit,config);
 | |
| 		usart_set_baud_rate(USART3, STM32_PCLK1, baud);
 | |
| 		USART3_BASE->CR3 &= ~USART_CR3_EIE & ~USART_CR3_CTSIE;	// Disable receive
 | |
| 		USART3_BASE->CR1 &= ~USART_CR1_RE & ~USART_CR1_RXNEIE & ~USART_CR1_PEIE & ~USART_CR1_IDLEIE ; // Disable RX and interrupts
 | |
|     	USART3_BASE->CR1 |= (USART_CR1_TE | USART_CR1_UE);		// Enable USART3 and TX
 | |
| 	}
 | |
| 	void init_HWTimer()
 | |
| 	{	
 | |
| 		HWTimer2.pause();										// Pause the timer2 while we're configuring it
 | |
| 		TIMER2_BASE->PSC = 35;									// 36-1;for 72 MHZ /0.5sec/(35+1)
 | |
| 		TIMER2_BASE->ARR = 0xFFFF;								// Count until 0xFFFF
 | |
| 		HWTimer2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);		// Main scheduler
 | |
| 		TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC2IF;				// Clear Timer2/Comp2 interrupt flag
 | |
| 		TIMER2_BASE->DIER &= ~TIMER_DIER_CC2IE;					// Disable Timer2/Comp2 interrupt
 | |
| 		HWTimer2.refresh();										// Refresh the timer's count, prescale, and overflow
 | |
| 		HWTimer2.resume();
 | |
| 
 | |
| 		#ifdef ENABLE_SERIAL
 | |
| 			HWTimer3.pause();									// Pause the timer3 while we're configuring it
 | |
| 			TIMER3_BASE->PSC = 35;								// 36-1;for 72 MHZ /0.5sec/(35+1)
 | |
| 			TIMER3_BASE->ARR = 0xFFFF;							// Count until 0xFFFF
 | |
| 			HWTimer3.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE);	// Serial check
 | |
| 			TIMER3_BASE->SR = 0x1E5F & ~TIMER_SR_CC2IF;			// Clear Timer3/Comp2 interrupt flag
 | |
| 			HWTimer3.attachInterrupt(TIMER_CH2,ISR_COMPB);		// Assign function to Timer3/Comp2 interrupt
 | |
| 			TIMER3_BASE->DIER &= ~TIMER_DIER_CC2IE;				// Disable Timer3/Comp2 interrupt
 | |
| 			HWTimer3.refresh();									// Refresh the timer's count, prescale, and overflow
 | |
| 			HWTimer3.resume();
 | |
| 		#endif
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| #ifdef CHECK_FOR_BOOTLOADER
 | |
| void pollBoot()
 | |
| {
 | |
| 	uint8_t rxchar ;
 | |
| 	uint8_t lState = BootState ;
 | |
| 	uint8_t millisTime = millis();				// Call this once only
 | |
| 
 | |
| 	#ifdef ORANGE_TX
 | |
| 	if ( USARTC0.STATUS & USART_RXCIF_bm )
 | |
| 	#elif defined STM32_BOARD
 | |
| 	if ( USART2_BASE->SR & USART_SR_RXNE )
 | |
| 	#else
 | |
| 	if ( UCSR0A & ( 1 << RXC0 ) )
 | |
| 	#endif
 | |
| 	{
 | |
| 		rxchar = UDR0 ;
 | |
| 		BootCount += 1 ;
 | |
| 		if ( ( lState == BOOT_WAIT_30_IDLE ) || ( lState == BOOT_WAIT_30_DATA ) )
 | |
| 		{
 | |
| 			if ( lState == BOOT_WAIT_30_IDLE )	// Waiting for 0x30
 | |
| 				BootTimer = millisTime ;		// Start timeout
 | |
| 			if ( rxchar == 0x30 )
 | |
| 				lState = BOOT_WAIT_20 ;
 | |
| 			else
 | |
| 				lState = BOOT_WAIT_30_DATA ;
 | |
| 		}
 | |
| 		else
 | |
| 			if ( lState == BOOT_WAIT_20 && rxchar == 0x20 )	// Waiting for 0x20
 | |
| 				lState = BOOT_READY ;
 | |
| 	}
 | |
| 	else // No byte received
 | |
| 	{
 | |
| 		if ( lState != BOOT_WAIT_30_IDLE )		// Something received
 | |
| 		{
 | |
| 			uint8_t time = millisTime - BootTimer ;
 | |
| 			if ( time > 5 )
 | |
| 			{
 | |
| 				#ifdef	STM32_BOARD
 | |
| 				if ( BootCount > 4 )
 | |
| 				#else
 | |
| 				if ( BootCount > 2 )
 | |
| 				#endif
 | |
| 				{ // Run normally
 | |
| 					NotBootChecking = 0xFF ;
 | |
| 					Mprotocol_serial_init( 0 ) ;
 | |
| 				}
 | |
| 				else if ( lState == BOOT_READY )
 | |
| 				{
 | |
| 					#ifdef	STM32_BOARD
 | |
| 						nvic_sys_reset();
 | |
| 						while(1);						/* wait until reset */
 | |
| 					#else
 | |
| 						cli();							// Disable global int due to RW of 16 bits registers
 | |
| 						void (*p)();
 | |
| 						#ifndef ORANGE_TX
 | |
| 							p = (void (*)())0x3F00 ;	// Word address (0x7E00 byte)
 | |
| 						#else
 | |
| 							p = (void (*)())0x4000 ;	// Word address (0x8000 byte)
 | |
| 						#endif
 | |
| 						(*p)() ;						// go to boot
 | |
| 					#endif
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					lState = BOOT_WAIT_30_IDLE ;
 | |
| 					BootCount = 0 ;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	BootState = lState ;
 | |
| }
 | |
| #endif //CHECK_FOR_BOOTLOADER
 | |
| 
 | |
| #if defined(TELEMETRY)
 | |
| void PPM_Telemetry_serial_init()
 | |
| {
 | |
| 	if( (protocol==PROTO_FRSKYD) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_BAYANG)|| (protocol==PROTO_NCC1701) || (protocol==PROTO_CABELL)  || (protocol==PROTO_HITEC) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) || (protocol==PROTO_PROPEL) || (protocol==PROTO_OMP) || (protocol==PROTO_RLINK) || (protocol==PROTO_WFLY2) || (protocol==PROTO_LOLI) || (protocol==PROTO_MT99XX)
 | |
| 	#ifdef TELEMETRY_FRSKYX_TO_FRSKYD
 | |
| 		 || (protocol==PROTO_FRSKYX) || (protocol==PROTO_FRSKYX2)
 | |
| 	#endif
 | |
| 	 )
 | |
| 		initTXSerial( SPEED_9600 ) ;
 | |
| 	#ifndef TELEMETRY_FRSKYX_TO_FRSKYD
 | |
| 		if(protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2)
 | |
| 			initTXSerial( SPEED_57600 ) ;
 | |
| 	#endif
 | |
| 	if(protocol==PROTO_DSM)
 | |
| 		initTXSerial( SPEED_125K ) ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Convert 32b id to rx_tx_addr
 | |
| static 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);
 | |
| }
 | |
| 
 | |
| static uint32_t random_id(uint16_t address, uint8_t create_new)
 | |
| {
 | |
| 	#ifndef FORCE_GLOBAL_ID
 | |
| 		uint32_t id=0;
 | |
| 
 | |
| 		if(eeprom_read_byte((EE_ADDR)(address+10))==0xf0 && !create_new)
 | |
| 		{  // TXID exists in EEPROM
 | |
| 			for(uint8_t i=4;i>0;i--)
 | |
| 			{
 | |
| 				id<<=8;
 | |
| 				id|=eeprom_read_byte((EE_ADDR)address+i-1);
 | |
| 			}
 | |
| 			if(id!=0x2AD141A7)	//ID with seed=0
 | |
| 			{
 | |
| 				//debugln("Read ID from EEPROM");
 | |
| 				return id;
 | |
| 			}
 | |
| 		}
 | |
| 		// Generate a random ID
 | |
| 		#if defined STM32_BOARD
 | |
| 			#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");
 | |
| 			}
 | |
| 			else
 | |
| 		#endif
 | |
| 				id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
 | |
| 
 | |
| 		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.
 | |
| 		return id;
 | |
| 	#else
 | |
| 		(void)address;
 | |
| 		(void)create_new;
 | |
| 		return FORCE_GLOBAL_ID;
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| // Generate frequency hopping sequence in the range [02..77]
 | |
| static void __attribute__((unused)) calc_fh_channels(uint8_t num_ch)
 | |
| {
 | |
| 	uint8_t idx = 0;
 | |
| 	uint32_t rnd = MProtocol_id;
 | |
| 	uint8_t max=(num_ch/3)+2;
 | |
| 	
 | |
| 	while (idx < num_ch)
 | |
| 	{
 | |
| 		uint8_t i;
 | |
| 		uint8_t count_2_26 = 0, count_27_50 = 0, count_51_74 = 0;
 | |
| 
 | |
| 		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 a distance of 5 between consecutive channels
 | |
| 		if (idx !=0)
 | |
| 		{
 | |
| 			if(hopping_frequency[idx-1]>next_ch)
 | |
| 			{
 | |
| 				if(hopping_frequency[idx-1]-next_ch<5)
 | |
| 					continue;
 | |
| 			}
 | |
| 			else
 | |
| 				if(next_ch-hopping_frequency[idx-1]<5)
 | |
| 					continue;
 | |
| 		}
 | |
| 		// Check that it's not duplicated and spread uniformly
 | |
| 		for (i = 0; i < idx; i++) {
 | |
| 			if(hopping_frequency[i] == next_ch)
 | |
| 				break;
 | |
| 			if(hopping_frequency[i] <= 26)
 | |
| 				count_2_26++;
 | |
| 			else if (hopping_frequency[i] <= 50)
 | |
| 				count_27_50++;
 | |
| 			else
 | |
| 				count_51_74++;
 | |
| 		}
 | |
| 		if (i != idx)
 | |
| 			continue;
 | |
| 		if ( (next_ch <= 26 && count_2_26 < max) || (next_ch >= 27 && next_ch <= 50 && count_27_50 < max) || (next_ch >= 51 && count_51_74 < max) )
 | |
| 			hopping_frequency[idx++] = next_ch;//find hopping frequency
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static uint8_t __attribute__((unused)) bit_reverse(uint8_t b_in)
 | |
| {
 | |
|     uint8_t b_out = 0;
 | |
|     for (uint8_t i = 0; i < 8; ++i)
 | |
| 	{
 | |
|         b_out = (b_out << 1) | (b_in & 1);
 | |
|         b_in >>= 1;
 | |
|     }
 | |
|     return b_out;
 | |
| }
 | |
| 
 | |
| static void __attribute__((unused)) crc16_update(uint8_t a, uint8_t bits)
 | |
| {
 | |
| 	crc ^= a << 8;
 | |
|     while(bits--)
 | |
|         if (crc & 0x8000)
 | |
|             crc = (crc << 1) ^ crc16_polynomial;
 | |
| 		else
 | |
|             crc = crc << 1;
 | |
| }
 | |
| 
 | |
| static void __attribute__((unused)) crc8_update(uint8_t byte)
 | |
| {
 | |
| 	crc8 = crc8 ^ byte;
 | |
| 	for ( uint8_t j = 0; j < 8; j++ )
 | |
| 		if ( crc8 & 0x80 )
 | |
| 			crc8 = (crc8<<1) ^ crc8_polynomial;
 | |
| 		else
 | |
| 			crc8 <<= 1;
 | |
| }
 | |
| 
 | |
| /**************************/
 | |
| /**************************/
 | |
| /**  Interrupt routines  **/
 | |
| /**************************/
 | |
| /**************************/
 | |
| 
 | |
| //PPM
 | |
| #ifdef ENABLE_PPM
 | |
| 	#ifdef ORANGE_TX
 | |
| 		#if PPM_pin == 2
 | |
| 			ISR(PORTD_INT0_vect)
 | |
| 		#else
 | |
| 			ISR(PORTD_INT1_vect)
 | |
| 		#endif
 | |
| 	#elif defined STM32_BOARD
 | |
| 		void PPM_decode()
 | |
| 	#else
 | |
| 		#if PPM_pin == 2
 | |
| 			ISR(INT0_vect, ISR_NOBLOCK)
 | |
| 		#else
 | |
| 			ISR(INT1_vect, ISR_NOBLOCK)
 | |
| 		#endif
 | |
| 	#endif
 | |
| 	{	// Interrupt on PPM pin
 | |
| 		static int8_t chan=0,bad_frame=1;
 | |
| 		static uint16_t Prev_TCNT1=0;
 | |
| 		uint16_t Cur_TCNT1;
 | |
| 
 | |
| 		Cur_TCNT1 = TCNT1 - Prev_TCNT1 ;	// Capture current Timer1 value
 | |
| 		if(Cur_TCNT1<1600)
 | |
| 			bad_frame=1;					// bad frame
 | |
| 		else
 | |
| 			if(Cur_TCNT1>4400)
 | |
| 			{  //start of frame
 | |
| 				if(chan>=MIN_PPM_CHANNELS)
 | |
| 				{
 | |
| 					PPM_FLAG_on;			// good frame received if at least 4 channels have been seen
 | |
| 					if(chan>PPM_chan_max) PPM_chan_max=chan;	// Saving the number of channels received
 | |
| 				}
 | |
| 				chan=0;						// reset channel counter
 | |
| 				bad_frame=0;
 | |
| 			}
 | |
| 			else
 | |
| 				if(bad_frame==0)			// need to wait for start of frame
 | |
| 				{  //servo values between 800us and 2200us will end up here
 | |
| 					PPM_data[chan]=Cur_TCNT1;
 | |
| 					if(chan++>=MAX_PPM_CHANNELS)
 | |
| 						bad_frame=1;		// don't accept any new channels
 | |
| 				}
 | |
| 		Prev_TCNT1+=Cur_TCNT1;
 | |
| 	}
 | |
| #endif //ENABLE_PPM
 | |
| 
 | |
| //Serial RX
 | |
| #ifdef ENABLE_SERIAL
 | |
| 	#ifdef ORANGE_TX
 | |
| 		ISR(USARTC0_RXC_vect)
 | |
| 	#elif defined STM32_BOARD
 | |
| 		void __irq_usart2()			
 | |
| 	#else
 | |
| 		ISR(USART_RX_vect)
 | |
| 	#endif
 | |
| 	{	// RX interrupt
 | |
| 		#ifdef ORANGE_TX
 | |
| 			if((USARTC0.STATUS & 0x1C)==0)							// Check frame error, data overrun and parity error
 | |
| 		#elif defined STM32_BOARD
 | |
| 			if((USART2_BASE->SR & USART_SR_RXNE) && (USART2_BASE->SR &0x0F)==0)					
 | |
| 		#else
 | |
| 			UCSR0B &= ~_BV(RXCIE0) ;								// RX interrupt disable
 | |
| 			sei() ;
 | |
| 			if((UCSR0A&0x1C)==0)									// Check frame error, data overrun and parity error
 | |
| 		#endif
 | |
| 		{ // received byte is ok to process
 | |
| 			if(rx_idx==0||discard_frame==true)
 | |
| 			{	// Let's try to sync at this point
 | |
| 				RX_MISSED_BUFF_off;									// If rx_buff was good it's not anymore...
 | |
| 				rx_idx=0;discard_frame=false;
 | |
| 				rx_buff[0]=UDR0;
 | |
| 				#ifdef FAILSAFE_ENABLE
 | |
| 					if((rx_buff[0]&0xFC)==0x54)						// If 1st byte is 0x54, 0x55, 0x56 or 0x57 it looks ok
 | |
| 				#else
 | |
| 					if((rx_buff[0]&0xFE)==0x54)						// If 1st byte is 0x54 or 0x55 it looks ok
 | |
| 				#endif
 | |
| 				{
 | |
| 					#if defined STM32_BOARD
 | |
| 						TIMER3_BASE->CCR2=TIMER3_BASE->CNT + 500;	// Next byte should show up within 250us (1 byte = 120us)
 | |
| 						TIMER3_BASE->SR = 0x1E5F & ~TIMER_SR_CC2IF;	// Clear Timer3/Comp2 interrupt flag
 | |
| 						TIMER3_BASE->DIER |= TIMER_DIER_CC2IE;		// Enable Timer3/Comp2 interrupt
 | |
| 					#else
 | |
| 						TX_RX_PAUSE_on;
 | |
| 						tx_pause();
 | |
| 						cli();										// Disable global int due to RW of 16 bits registers
 | |
| 						OCR1B = TCNT1 + 500;						// Next byte should show up within 250us (1 byte = 120us)
 | |
| 						sei();										// Enable global int
 | |
| 						TIFR1 = OCF1B_bm ;							// clear OCR1B match flag
 | |
| 						SET_TIMSK1_OCIE1B ;							// enable interrupt on compare B match
 | |
| 					#endif
 | |
| 					rx_idx++;
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if(rx_idx>=RXBUFFER_SIZE)
 | |
| 				{
 | |
| 					discard_frame=true; 								// Too many bytes being received...
 | |
| 					debugln("RX frame too long");
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					rx_buff[rx_idx++]=UDR0;							// Store received byte
 | |
| 					#if defined STM32_BOARD
 | |
| 						TIMER3_BASE->CCR2=TIMER3_BASE->CNT + 500;	// Next byte should show up within 250us (1 byte = 120us)
 | |
| 					#else
 | |
| 						cli();										// Disable global int due to RW of 16 bits registers
 | |
| 						OCR1B = TCNT1 + 500;						// Next byte should show up within 250us (1 byte = 120us)
 | |
| 						sei();										// Enable global int
 | |
| 					#endif
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			rx_idx=UDR0;											// Dummy read
 | |
| 			rx_idx=0;
 | |
| 			discard_frame=true;										// Error encountered discard full frame...
 | |
| 			debugln("Bad frame RX");
 | |
| 		}
 | |
| 		if(discard_frame==true)
 | |
| 		{
 | |
| 			#ifdef STM32_BOARD
 | |
| 				TIMER3_BASE->DIER &= ~TIMER_DIER_CC2IE;				// Disable Timer3/Comp2 interrupt
 | |
| 			#else							
 | |
| 				CLR_TIMSK1_OCIE1B;									// Disable interrupt on compare B match
 | |
| 				TX_RX_PAUSE_off;
 | |
| 				tx_resume();
 | |
| 			#endif
 | |
| 		}
 | |
| 		#if not defined (ORANGE_TX) && not defined (STM32_BOARD)
 | |
| 			cli() ;
 | |
| 			UCSR0B |= _BV(RXCIE0) ;									// RX interrupt enable
 | |
| 		#endif
 | |
| 	}
 | |
| 
 | |
| 	//Serial timer
 | |
| 	#ifdef ORANGE_TX
 | |
| 		ISR(TCC1_CCB_vect)
 | |
| 	#elif defined STM32_BOARD
 | |
| 		void ISR_COMPB()
 | |
| 	#else
 | |
| 		ISR(TIMER1_COMPB_vect)
 | |
| 	#endif
 | |
| 	{	// Timer1 compare B interrupt
 | |
| 		if(rx_idx>=26 && rx_idx<=RXBUFFER_SIZE)
 | |
| 		{
 | |
| 			// A full frame has been received
 | |
| 			if(!IS_RX_DONOTUPDATE_on)
 | |
| 			{ //Good frame received and main is not working on the buffer
 | |
| 				rx_len=rx_idx;
 | |
| 				memcpy((void*)rx_ok_buff,(const void*)rx_buff,rx_idx);	// Duplicate the buffer
 | |
| 				RX_FLAG_on;											// Flag for main to process data
 | |
| 			}
 | |
| 			else
 | |
| 				RX_MISSED_BUFF_on;									// Notify that rx_buff is good
 | |
| 			#ifdef MULTI_SYNC
 | |
| 				cli();
 | |
| 				last_serial_input=TCNT1;
 | |
| 				sei();
 | |
| 			#endif
 | |
| 		}
 | |
| 		#ifdef DEBUG_SERIAL
 | |
| 			else
 | |
| 				debugln("RX frame size incorrect");
 | |
| 		#endif
 | |
| 		discard_frame=true;
 | |
| 		#ifdef STM32_BOARD
 | |
| 			TIMER3_BASE->DIER &= ~TIMER_DIER_CC2IE;					// Disable Timer3/Comp2 interrupt
 | |
| 		#else
 | |
| 			CLR_TIMSK1_OCIE1B;										// Disable interrupt on compare B match
 | |
| 			TX_RX_PAUSE_off;
 | |
| 			tx_resume();
 | |
| 		#endif
 | |
| 	}
 | |
| #endif //ENABLE_SERIAL
 | |
| 
 | |
| /**************************/
 | |
| /**************************/
 | |
| /**    CPPM  routines    **/
 | |
| /**************************/
 | |
| /**************************/
 | |
| #ifdef SEND_CPPM
 | |
| 	#define PPM_CENTER 1500*2
 | |
| 	uint32_t TrainerTimer ;
 | |
| 	bool CppmInitialised = false;
 | |
| 	uint16_t *TrainerPulsePtr ;
 | |
| 	uint16_t TrainerPpmStream[10] ;
 | |
| 	int16_t CppmChannels[8] ;
 | |
| 
 | |
| 	void setupTrainerPulses()
 | |
| 	{
 | |
| 		uint32_t i ;
 | |
| 		uint32_t total ;
 | |
| 		uint32_t pulse ;
 | |
| 		uint16_t *ptr ;
 | |
| 		uint32_t p = 8 ;
 | |
| 		int16_t PPM_range = 512*2 ;										//range of 0.7..1.7msec
 | |
| 
 | |
| 		ptr = TrainerPpmStream ;
 | |
| 
 | |
| 		total = 22500u*2;												//Minimum Framelen=22.5 ms
 | |
| 
 | |
| 		if ( (millis() - TrainerTimer) < 400 )
 | |
| 		{
 | |
| 			for ( i = 0 ; i < p ; i += 1 )
 | |
| 			{
 | |
| 				pulse = max( (int)min(CppmChannels[i],PPM_range),-PPM_range) + PPM_CENTER ;
 | |
| 
 | |
| 				total -= pulse ;
 | |
| 				*ptr++ = pulse ;
 | |
| 			}
 | |
| 		}
 | |
| 		*ptr++ = total ;
 | |
| 		*ptr = 0 ;
 | |
| 		TIMER1_BASE->CCR1 = total - 1500 ;								// Update time
 | |
| 		TIMER1_BASE->CCR2 = 300*2 ;
 | |
| 	}
 | |
| 
 | |
| 	void init_trainer_ppm()
 | |
| 	{
 | |
| 		// Timer 1, channel 2 on PA9
 | |
| 		RCC_BASE->APB2ENR |= RCC_APB2ENR_TIM1EN ;						// Enable clock
 | |
| 		setupTrainerPulses() ;
 | |
| 		RCC_BASE->APB2ENR |= RCC_APB2ENR_IOPAEN ;						// Enable portA clock
 | |
| 		RCC_BASE->APB2ENR &= ~RCC_APB2ENR_USART1EN ;					// Disable USART1
 | |
| 
 | |
| 		GPIOA_BASE->CRH &= ~0x00F0 ;
 | |
| 		GPIOA_BASE->CRH |= 0x00A0 ;										// AF PP OP2MHz
 | |
| 
 | |
| 		HWTimer1.pause() ;												// Pause the timer1 while we're configuring it
 | |
| 		TIMER1_BASE->ARR = *TrainerPulsePtr++ ;
 | |
| 		TIMER1_BASE->PSC = 72000000  / 2000000 - 1 ;					// 0.5uS
 | |
| 		TIMER1_BASE->CCR2 = 600 ;										// 300 uS pulse
 | |
| 		TIMER1_BASE->CCR1 = 5000 ;										// 2500 uS pulse
 | |
| 		TIMER1_BASE->CCMR1 = 0x6000 ;									// PWM mode 1 (header file has incorrect bits)
 | |
| 		TIMER1_BASE->EGR = 1 ;
 | |
| 		TIMER1_BASE->CCER = TIMER_CCER_CC2E ;
 | |
| 		TIMER1_BASE->DIER |= TIMER_DIER_UIE ;
 | |
| 		TIMER1_BASE->CR1 = TIMER_CR1_CEN ;
 | |
| 		nvic_irq_set_priority(NVIC_TIMER1_CC, 4 ) ;
 | |
| 		nvic_irq_set_priority(NVIC_TIMER1_UP, 4 ) ;
 | |
| 		HWTimer1.attachInterrupt(TIMER_UPDATE_INTERRUPT,tim1_up);		// Assign function to Timer1/Comp2 interrupt
 | |
| 		HWTimer1.attachInterrupt(TIMER_CH1,tim1_cc);					// Assign function to Timer1/Comp2 interrupt
 | |
| 
 | |
| 		CppmInitialised = true ;
 | |
| 		HWTimer1.resume() ;
 | |
| 	}
 | |
| 
 | |
| 	void release_trainer_ppm()
 | |
| 	{
 | |
| 		if ( CppmInitialised )
 | |
| 		{
 | |
| 			TIMER1_BASE->CR1 = 0 ;
 | |
| 			pinMode(PA9,INPUT) ;
 | |
| 			CppmInitialised = false ;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void tim1_up()
 | |
| 	{
 | |
| 		#define TIMER1_SR_MASK	0x1FFF
 | |
| 		// PPM out update interrupt
 | |
| 		if ( (TIMER1_BASE->DIER & TIMER_DIER_UIE) && ( TIMER1_BASE->SR & TIMER_SR_UIF ) )
 | |
| 		{
 | |
| 			GPIOA_BASE->BRR = 0x0200 ;
 | |
| 			TIMER1_BASE->SR = TIMER1_SR_MASK & ~TIMER_SR_UIF ; 			// Clear flag
 | |
| 			TIMER1_BASE->ARR = *TrainerPulsePtr++ ;
 | |
| 			if ( *TrainerPulsePtr == 0 )
 | |
| 			{
 | |
| 				TIMER1_BASE->SR = 0x1FFF & ~TIMER_SR_CC1IF ;			// Clear this flag
 | |
| 				TIMER1_BASE->DIER |= TIMER_DIER_CC1IE ;					// Enable this interrupt
 | |
| 				TIMER1_BASE->DIER &= ~TIMER_DIER_UIE ;					// Stop this interrupt
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void tim1_cc()
 | |
| 	{
 | |
| 		if ( ( TIMER1_BASE->DIER & TIMER_DIER_CC1IE ) && ( TIMER1_BASE->SR & TIMER_SR_CC1IF ) )
 | |
| 		{
 | |
| 			// compare interrupt
 | |
| 			TIMER1_BASE->DIER &= ~TIMER_DIER_CC1IE ;					// Stop this interrupt
 | |
| 			TIMER1_BASE->SR = 0x1FFF & ~TIMER_SR_CC1IF ;				// Clear flag
 | |
| 
 | |
| 			setupTrainerPulses() ;
 | |
| 
 | |
| 			TrainerPulsePtr = TrainerPpmStream ;
 | |
| 			TIMER1_BASE->SR = 0x1FFF & ~TIMER_SR_UIF ;					// Clear this flag
 | |
| 			TIMER1_BASE->DIER |= TIMER_DIER_UIE ;						// Enable this interrupt
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void Send_CCPM_USART1()
 | |
| 	{
 | |
| 		if ( CppmInitialised == false )
 | |
| 			init_trainer_ppm() ;
 | |
| 		TrainerTimer = millis() ;
 | |
| 		len = packet_in[3] ;
 | |
| 		uint32_t bitsavailable = 0 ;
 | |
| 		uint32_t bits = 0 ; ;
 | |
| 		uint32_t i ;
 | |
| 		int16_t value ;
 | |
| 		uint8_t *packet ;
 | |
| 		packet = &packet_in[4] ;
 | |
| 		i = packet_in[2] ;	// Start channel
 | |
| 		// Load changed channels
 | |
| 		while ( len )
 | |
| 		{
 | |
| 			while ( bitsavailable < 11 )
 | |
| 			{
 | |
| 				bits |= *packet++ << bitsavailable ;
 | |
| 				bitsavailable += 8 ;
 | |
| 			}
 | |
| 			value = bits & 0x07FF ;
 | |
| 			value -= 0x0400 ;
 | |
| 			bitsavailable -= 11 ;
 | |
| 			bits >>= 11 ;
 | |
| 			if ( i < 8 )
 | |
| 				CppmChannels[i] = value * 5 / 4 ;
 | |
| 			i++ ;
 | |
| 			len-- ;
 | |
| 		}
 | |
| 	}
 | |
| #endif	
 | |
| 
 | |
| /**************************/
 | |
| /**************************/
 | |
| /**    Arduino random    **/
 | |
| /**************************/
 | |
| /**************************/
 | |
| #if not defined (ORANGE_TX) && not defined (STM32_BOARD)
 | |
| 	static void random_init(void)
 | |
| 	{
 | |
| 		cli();					// Temporarily turn off interrupts, until WDT configured
 | |
| 		MCUSR = 0;				// Use the MCU status register to reset flags for WDR, BOR, EXTR, and POWR
 | |
| 		WDTCSR |= _BV(WDCE);	// WDT control register, This sets the Watchdog Change Enable (WDCE) flag, which is  needed to set the prescaler
 | |
| 		WDTCSR = _BV(WDIE);		// Watchdog interrupt enable (WDIE)
 | |
| 		sei();					// Turn interupts on
 | |
| 	}
 | |
| 
 | |
| 	static uint32_t random_value(void)
 | |
| 	{
 | |
| 		while (!gWDT_entropy);
 | |
| 		return gWDT_entropy;
 | |
| 	}
 | |
| 
 | |
| 	// Random interrupt service routine called every time the WDT interrupt is triggered.
 | |
| 	// It is only enabled at startup to generate a seed.
 | |
| 	ISR(WDT_vect)
 | |
| 	{
 | |
| 		static uint8_t gWDT_buffer_position=0;
 | |
| 		#define gWDT_buffer_SIZE 32
 | |
| 		static uint8_t gWDT_buffer[gWDT_buffer_SIZE];
 | |
| 		gWDT_buffer[gWDT_buffer_position] = TCNT1L; // Record the Timer 1 low byte (only one needed) 
 | |
| 		gWDT_buffer_position++;                     // every time the WDT interrupt is triggered
 | |
| 		if (gWDT_buffer_position >= gWDT_buffer_SIZE)
 | |
| 		{
 | |
| 			// The following code is an implementation of Jenkin's one at a time hash
 | |
| 			for(uint8_t gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter)
 | |
| 			{
 | |
| 				gWDT_entropy += gWDT_buffer[gWDT_loop_counter];
 | |
| 				gWDT_entropy += (gWDT_entropy << 10);
 | |
| 				gWDT_entropy ^= (gWDT_entropy >> 6);
 | |
| 			}
 | |
| 			gWDT_entropy += (gWDT_entropy << 3);
 | |
| 			gWDT_entropy ^= (gWDT_entropy >> 11);
 | |
| 			gWDT_entropy += (gWDT_entropy << 15);
 | |
| 			WDTCSR = 0;	// Disable Watchdog interrupt
 | |
| 		}
 | |
| 	}
 | |
| #endif
 |