예제 #1
0
void CalibrateGyrosFast(void)
{
	uint8_t i;

	// Clear gyro zeros
	memset(&Config.gyroZero[ROLL],0,(sizeof(int16_t) * NUMBEROFAXIS));

	// Calculate average over 32 reads
	for (i=0; i<32; i++)
	{
		get_raw_gyros();				// Updates gyroADC[]

		Config.gyroZero[ROLL] 	+= gyroADC[ROLL];						
		Config.gyroZero[PITCH] 	+= gyroADC[PITCH];	
		Config.gyroZero[YAW] 	+= gyroADC[YAW];
	}

	// Average readings for all axis
	for (i=0; i<NUMBEROFAXIS; i++)
	{
		Config.gyroZero[i] 	= (Config.gyroZero[i] >> 5);	// Divide by 32	
	}

	Save_Config_to_EEPROM();
}
예제 #2
0
bool Initial_EEPROM_Config_Load(void)
{
	bool updated = false;
	
	// Read eeProm data into RAM
	eeprom_read_block((void*)&Config, (const void*)EEPROM_DATA_START_POS, sizeof(CONFIG_STRUCT));
	
	// See if we know what to do with the current eeprom data
	// Config.setup holds the magic number from the current EEPROM
	switch(Config.setup)
	{
		case V1_0_SIGNATURE:				// V1.0 detected
			Update_V1_0_to_V1_1();
			updated = true;
			// Fall through...

		case V1_1_SIGNATURE:				// V1.1 detected
			Update_V1_1_to_V1_2();
			updated = true;
			// Fall through...

		case V1_2_SIGNATURE:				// V1.2 detected
			Update_V1_2_to_V1_3B1();
			updated = true;
			// Fall through...

		case V1_3_B1_SIGNATURE:				// V1.3 B1 detected
			Update_V1_3B1_to_V1_3B14();
			updated = true;
			// Fall through...

		case V1_3_B14_SIGNATURE:			// V1.3 B14 detected
			Update_V1_3B14_to_V1_3B15();
			updated = true;
			// Fall through...
			
		case V1_3_B15_SIGNATURE:			// V1.3 B15 detected
			Update_V1_3B15_to_V1_3B17();
			updated = true;
			// Fall through...

		case V1_3_B17_SIGNATURE:			// V1.3 B15 detected
			break;			
			
		default:							// Unknown solution - restore to factory defaults
			// Load factory defaults
			Set_EEPROM_Default_Config();
			break;
	}
	
	// Save back to eeprom	
	Save_Config_to_EEPROM();
	
	// Return info regarding eeprom structure changes 
	return updated;
}
예제 #3
0
void menu_rc_setup(void)
{
	uint8_t cursor = LINE0;
	uint8_t top = RCSTART;
	uint8_t temp = 0;
	int8_t values[RCITEMS];
	menu_range_t range;
	uint8_t i = 0;
	uint8_t text_link;
	
	while(button != BACK)
	{
		// Load values from eeprom
		memcpy(&values[0],&Config.TxSeq,sizeof(int8_t) * RCITEMS);

		// Print menu
		print_menu_items(top, RCSTART, &values[0], RCITEMS, (prog_uchar*)rc_menu_ranges, RCOFFSET, (prog_uchar*)RCMenuText, cursor);

		// Handle menu changes
		update_menu(RCITEMS, RCSTART, button, &cursor, &top, &temp);
		range = get_menu_range ((prog_uchar*)rc_menu_ranges, temp - RCSTART);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&RCMenuText[temp - RCSTART]);
			values[temp - RCSTART] = do_menu_item(temp, values[temp - RCSTART], range, 0, text_link);
		}

		// Update value in config structure
		memcpy(&Config.TxSeq,&values[0],sizeof(int8_t) * RCITEMS);

		// Update Ch7. mixer with source from Config.FlapChan if in Aeroplane mode
		if (Config.MixMode == AEROPLANE)
		{
			Config.Channel[CH7].source_a = Config.FlapChan;
		}

		if (button == ENTER)
		{
			// Update channel sequence
			for (i = 0; i < MAX_RC_CHANNELS; i++)
			{
				if (Config.TxSeq == JRSEQ) 
				{
					Config.ChannelOrder[i] = pgm_read_byte(&JR[i]);
				}
				else if (Config.TxSeq == FUTABASEQ)
				{
					Config.ChannelOrder[i] = pgm_read_byte(&FUTABA[i]);
				}
			}
			Save_Config_to_EEPROM(); // Save value and return
		}
	}
}
예제 #4
0
void Initial_EEPROM_Config_Load(void)
{
	// Load last settings from EEPROM
	if(eeprom_read_byte((uint8_t*) EEPROM_DATA_START_POS )!=0x47)
	{
		Config.setup = 0x47;
		Set_EEPROM_Default_Config();
		// Write to eeProm
		Save_Config_to_EEPROM();
	} else {
		// Read eeProm
		eeprom_read_block(&Config, (void*) EEPROM_DATA_START_POS, sizeof(CONFIG_STRUCT)); 
	}
}
예제 #5
0
void menu_battery(void)
{
	uint8_t cursor = LINE0;
	uint8_t top = BATTSTART;
	uint8_t temp = 0;
	int16_t values[BATTITEMS];
	menu_range_t range;
	uint8_t text_link = 0;
	uint8_t temp_cells = 0;
	uint16_t temp_minvoltage = 0;
	
	while(button != BACK)
	{
		// Load values from eeprom
		memcpy(&values[0],&Config.BatteryType,sizeof(int16_t) * BATTITEMS);

		// Save pre-edited values
		temp_cells = Config.BatteryCells;
		temp_minvoltage = Config.MinVoltage;

		// Print menu
		print_menu_items_16(top, BATTSTART, &values[0], (prog_uchar*)batt_menu_ranges, BATTOFFSET, (prog_uchar*)BattMenuText, cursor);

		// Handle menu changes
		update_menu(BATTITEMS, BATTSTART, button, &cursor, &top, &temp);
		range = get_menu_range ((prog_uchar*)batt_menu_ranges, temp - BATTSTART);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&BattMenuText[temp - BATTSTART]);
			values[temp - BATTSTART] = do_menu_item(temp, values[temp - BATTSTART], range, 0, text_link);
		}

		// See if cell number or min_volts has changed
		if ((temp_cells != values[1]) || (temp_minvoltage != values[4]))
		{
			values[2] = values[1] * values[4];
			Config.PowerTrigger = values[2];
		}

		// Update value in config structure
		memcpy(&Config.BatteryType,&values[0],sizeof(int16_t) * BATTITEMS);

		if (button == ENTER)
		{
			Save_Config_to_EEPROM(); // Save value and return
		}
	}
}
예제 #6
0
void menu_offsets(void)
{
	int8_t *value_ptr;
	uint16_t reference = OFFSETSSTART;

	// If sub-menu item has changed, reset sub-menu positions
	if (menu_flag)
	{
		sub_top = OFFSETSSTART;			
		menu_flag = 0;
	}

	while(button != BACK)
	{
		value_ptr = &Config.Offsets[0].Point1;

		// Print top level menu
		print_menu_items(sub_top, reference, value_ptr, (const unsigned char*)Offsets_menu_ranges, 0, (const uint16_t*)OffsetsMenuOffsets, (const uint16_t*)OffsetsMenuText, cursor);

		// Handle menu navigation
		update_menu(MAX_OUTPUTS, reference, 0, button, &cursor, &sub_top, &menu_temp);

		// Edit selected curve
		if (button == ENTER)
		{
			edit_curve_item(menu_temp - reference, OFFSET); // Curves after NUMBEROFCURVES are offsets
		}

		// Stop unwanted exit to main menu
		if (button == ABORT)
		{
			Wait_BUTTON1();			 // Wait for user's finger off the button
			button = NONE;
		}

		// Update limits when exiting
		if (button == ENTER)
		{
			Save_Config_to_EEPROM(); // Save value and return
			Wait_BUTTON4();			 // Wait for user's finger off the button
			Wait_BUTTON1();	
		}
	}
}
예제 #7
0
void menu_mixer(uint8_t i)
{
	uint8_t cursor = LINE0;
	uint8_t top = MIXERSTART;
	int8_t values[MIXERITEMS];
	menu_range_t range;
	uint8_t temp = 0;
	uint8_t text_link = 0;

	while(button != BACK)
	{
		// Load values from eeprom
		memcpy(&values[0],&Config.Channel[i].source_a,sizeof(int8_t) * MIXERITEMS);

		// Print menu
		print_menu_items(top, MIXERSTART, &values[0], MIXERITEMS,(prog_uchar*)mixer_menu_ranges, MIXOFFSET, (prog_uchar*)MixerMenuText, cursor);

		// Handle menu changes
		update_menu(MIXERITEMS, MIXERSTART, button, &cursor, &top, &temp);
		range = get_menu_range ((prog_uchar*)mixer_menu_ranges, temp - MIXERSTART);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&MixerMenuText[temp - MIXERSTART]);
			values[temp - MIXERSTART] = do_menu_item(temp, values[temp - MIXERSTART], range, 0, text_link);
		}

		// Update value in config structure
		memcpy(&Config.Channel[i].source_a,&values[0],sizeof(int8_t) * MIXERITEMS);

		if (button == ENTER)
		{
			UpdateLimits();			 // Update travel limits based on percentages
			Save_Config_to_EEPROM(); // Save value and return
		}
	}
	menu_beep(1);
	_delay_ms(200);
}
예제 #8
0
// Center sticks on request from Menu
void CenterSticks(void)		
{
	uint8_t i, j;
	uint16_t RxChannelZeroOffset[MAX_RC_CHANNELS] = {0,0,0,0,0,0,0,0};

	// Take an average of eight readings
	// RxChannel will auto-update every RC frame (normally 46Hz or so)
	for (i=0; i<8; i++)
	{
		for (j=0; j<MAX_RC_CHANNELS; j++)
		{
			RxChannelZeroOffset[j] += RxChannel[j];
		}
		_delay_ms(100);
	}

	for (i=0;i<MAX_RC_CHANNELS;i++)
	{
		Config.RxChannelZeroOffset[i] = ((RxChannelZeroOffset[i] + 4) >> 3); // Round and divide by 8
	}

	Save_Config_to_EEPROM();
}
예제 #9
0
void menu_al_control(void)
{
	uint8_t cursor = LINE0;
	uint8_t top = AUTOSTART;
	uint8_t temp = 0;
	int8_t values[AUTOITEMS];
	menu_range_t range;
	uint8_t text_link = 0;
	
	while(button != BACK)
	{
		// Load values from eeprom
		memcpy(&values[0],&Config.AutoMode,sizeof(int8_t) * AUTOITEMS);

		// Print menu
		print_menu_items(top, AUTOSTART, &values[0], AUTOITEMS, (prog_uchar*)auto_menu_ranges, AUTOOFFSET, (prog_uchar*)AutoMenuText, cursor);

		// Handle menu changes
		update_menu(AUTOITEMS, AUTOSTART, button, &cursor, &top, &temp);
		range = get_menu_range ((prog_uchar*)auto_menu_ranges, temp - AUTOSTART);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&AutoMenuText[temp - AUTOSTART]);
			values[temp - AUTOSTART] = do_menu_item(temp, values[temp - AUTOSTART], range, 0, text_link);
		}

		// Update value in config structure
		memcpy(&Config.AutoMode,&values[0],sizeof(int8_t) * AUTOITEMS);

		if (button == ENTER)
		{
			UpdateLimits();			 // Update I-term limits and triggers based on percentages
			Save_Config_to_EEPROM(); // Save value and return
		}
	}
}
예제 #10
0
void menu_mixer(uint8_t section)
{
	static uint8_t mix_top = MIXERSTART;
	static	uint8_t old_section;
	int8_t *value_ptr;

	int8_t values[INPUTITEMS+1];
	menu_range_t range;
	uint8_t text_link = 0;
	uint8_t offset;			// Index into channel structure
	uint8_t	items;			// Items in group

	// If submenu item has changed, reset submenu positions
	if (section != old_section)
	{
		mix_top = MIXERSTART;
		old_section = section;
	}

	// Get mixer menu offsets
	// 1 = input mixer data, 2 = output mixer data
	switch(section)
	{
		case 1:
			items = INPUTITEMS+1;
			offset = 0;
			value_ptr = &values[0];
			break;
		case 2:
			items = OUTPUTITEMS+1;
			offset = INPUTITEMS+1;
			value_ptr = &values[0];
			break;
		default:
			items = INPUTITEMS+1;
			offset = 0;
			value_ptr = &values[0];
			break;
	}

	while(button != BACK)
	{
		// Load values from eeprom and insert channel number at the top of each - messy
		values[0] = Config.MenuChannel;
		if (section == 1)
		{
			memcpy(&values[1],&Config.Channel[Config.MenuChannel].source_a,(sizeof(int8_t) * INPUTITEMS));
		}
		else
		{
			memcpy(&values[1],&Config.Channel[Config.MenuChannel].switcher,(sizeof(int8_t) * OUTPUTITEMS));
		}

		// Print menu
		print_menu_items(mix_top + offset, MIXERSTART + offset, value_ptr, 1, (prog_uchar*)mixer_menu_ranges[section - 1], 0, MIXOFFSET, (prog_uchar*)MixerMenuText[section - 1], cursor);

		// Handle menu changes
		update_menu(items, MIXERSTART, offset, button, &cursor, &mix_top, &menu_temp);
		range = get_menu_range ((prog_uchar*)mixer_menu_ranges[section - 1], menu_temp - MIXERSTART - offset);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&MixerMenuText[section - 1][menu_temp - MIXERSTART - offset]);
			do_menu_item(menu_temp, value_ptr + (menu_temp - MIXERSTART - offset), 1, range, 0, text_link, false, 0);
		}

		// Save modified data back to Config
		// Copy channel number back to global
		switch(section)
		{
			case 1:
				memcpy(&Config.Channel[Config.MenuChannel].source_a,&values[1],(sizeof(int8_t) * INPUTITEMS));
				Config.MenuChannel = values[0];
				break;
			case 2:
				memcpy(&Config.Channel[Config.MenuChannel].switcher,&values[1],(sizeof(int8_t) * OUTPUTITEMS));
				Config.MenuChannel = values[0];
				break;
			default:
				break;
		}

		// Save and exit
		if (button == ENTER)
		{
			UpdateLimits();			 // Update travel limits based on percentages
			Save_Config_to_EEPROM(); // Save value and return
		}

	}
	_delay_ms(200);
}
예제 #11
0
void menu_rc_setup(uint8_t section)
{
	int8_t *value_ptr;
	menu_range_t range;
	uint8_t text_link;
	uint8_t i = 0;
	uint8_t mult = 1;		// Multiplier
	uint8_t offset;			// Index into channel structure
	uint8_t	items;			// Items in group

	// If submenu item has changed, reset submenu positions
	if (menu_flag)
	{
		sub_top = RCSTART;
		menu_flag = 0;
	}

	while(button != BACK)
	{
		// Get menu offsets and load values from eeprom
		// 1 = RC, 2 = Failsafe, 3 = General
		switch(section)
		{
			case 1:				// RC setup menu
				offset = 0;
				items = RCITEMS;
				value_ptr = &Config.RxModeIn;
				mult = 1;
				break;
			case 2:				// Failsafe menu
				offset = RCITEMS;
				items = FSITEMS;
				value_ptr = &Config.FailsafeType;
				mult = 1;
				break;
			case 3:				// General menu
				offset = RCITEMS + FSITEMS;
				items = GENERALITEMS;
				value_ptr = &Config.MixMode;
				mult = 1;
				break;
			default:
				offset = 0;
				items = RCITEMS;
				value_ptr = &Config.RxModeIn;
				mult = 1;
				break;
		}
		// Save pre-edited values
		int8_t temp_type = Config.MixMode;
		int8_t temp_flapchan = Config.FlapChan;
		int8_t temp_RxModeIn = Config.RxModeIn;

		// Print menu
		print_menu_items(sub_top + offset, RCSTART + offset, value_ptr, mult, (const unsigned char*)rc_menu_ranges[section - 1], 0, RCOFFSET, (const unsigned char*)RCMenuText[section - 1], cursor);

		// Handle menu changes
		update_menu(items, RCSTART, offset, button, &cursor, &sub_top, &menu_temp);
		range = get_menu_range ((const unsigned char*)rc_menu_ranges[section - 1], (menu_temp - RCSTART - offset)); 

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&RCMenuText[section - 1][menu_temp - RCSTART - offset]);
			do_menu_item(menu_temp, value_ptr + (menu_temp - RCSTART - offset), mult, range, 0, text_link, false, 0);
		}

		if (button == ENTER)
		{
			// Update Ch5. mixer with source from Config.FlapChan if in Aeroplane mode and source changed
			if ((Config.MixMode == AEROPLANE) && (Config.FlapChan != temp_flapchan))
			{
				Config.Channel[CH5].source_a = Config.FlapChan;
			}

			// Reset serial in channel masks every time the input type is changed
			if (temp_RxModeIn != Config.RxModeIn)
			{
				Xtreme_Chanmask = 0;
				Xtreme_RSS = 0;
				Spektrum_Chanmask_0 = 0;	
				Spektrum_Chanmask_1 = 0;		
				Spektrum_frameloss = 0;
				SBUS_Flags = 0;
				
				// Clear channel data
				for (i = 0; i < MAX_RC_CHANNELS; i++)
				{
					RxChannel[i] = 0;
					
					// Unused Spektrum channels set to NULL
					if (Config.RxModeOut == SPEKTRUM)
					{
						ExtChannel[i] = 0xFFFF;
					}	
					// Unused channels set to mid-way
					else if (Config.RxModeOut == SBUS)
					{
						ExtChannel[i] = 0x400;
					}
					// Xtreme doesn't care
					else
					{
						ExtChannel[i] = 0;
					}
				}
			}

			// If model type has changed, reload preset
			if ((section == 3) && (temp_type != Config.MixMode)) 
			{
				switch(Config.MixMode)  // Load selected mix
				{
					case AEROPLANE:
						get_preset_mix(AEROPLANE_MIX);
						break;	
					case FWING:
						get_preset_mix(FLYING_WING_MIX);
						break;
					case MANUAL:
						// Clear all channel info
						memset(&Config.Channel[0].value,0,(sizeof(channel_t) * MAX_OUTPUTS));

						// Preset important settings
						for (i = 0; i < MAX_OUTPUTS; i++)
						{
							Config.Channel[i].source_a = i;			// Set to mirror the inputs
							Config.Channel[i].source_a_volume = 100;
							Config.Channel[i].source_b = NOCHAN;
							Config.Channel[i].output_b = UNUSED;
							Config.Channel[i].output_c = UNUSED;
						}

						break;
					default:
						break;
				}
			}

			init_int();				// In case RC type has changed, reinitialise interrupts
			init_uart();			// and UART
			UpdateLimits();			// Update I-term limits and triggers based on percentages

#ifdef KK21
			// Update MPU6050 LPF
			writeI2Cbyte(MPU60X0_DEFAULT_ADDRESS, MPU60X0_RA_CONFIG, (6 - Config.MPU6050_LPF));
#endif
			// Update channel sequence
			for (i = 0; i < MAX_RC_CHANNELS; i++)
			{
				if (Config.TxSeq == FUTABASEQ)
				{
					Config.ChannelOrder[i] = pgm_read_byte(&FUTABA[i]);
				}
				else
				{
					Config.ChannelOrder[i] = pgm_read_byte(&JR[i]);
				}
			}

			Save_Config_to_EEPROM(); // Save value and return
			
			Wait_BUTTON4();			 // Wait for user's finger off the button
		}
	}
}
예제 #12
0
void menu_flight(uint8_t mode)
{
    int8_t *value_ptr;
    menu_range_t range;
    uint8_t text_link;
    int8_t temp_gyro_roll = 0;
    int8_t temp_gyro_pitch = 0;
    int8_t temp_gyro_yaw = 0;

    // If submenu item has changed, reset submenu positions
    if (menu_flag)
    {
        sub_top = FLIGHTSTART;
        menu_flag = 0;
    }

    while(button != BACK)
    {
        value_ptr = &Config.FlightMode[mode-1].StabMode;

        // Save pre-edited value for gyro types
        temp_gyro_roll = Config.FlightMode[mode - 1].Roll_type;
        temp_gyro_pitch = Config.FlightMode[mode - 1].Pitch_type;
        temp_gyro_yaw = Config.FlightMode[mode - 1].Yaw_type;

        // Print menu
        print_menu_items(sub_top, FLIGHTSTART, value_ptr, 1, (const unsigned char*)flight_menu_ranges, 0, FLIGHTOFFSET, (const unsigned char*)FlightMenuText, cursor);

        // Handle menu changes
        update_menu(FLIGHTITEMS, FLIGHTSTART, 0, button, &cursor, &sub_top, &menu_temp);
        range = get_menu_range ((const unsigned char*)flight_menu_ranges, (menu_temp - FLIGHTSTART));

        if (button == ENTER)
        {
            text_link = pgm_read_byte(&FlightMenuText[menu_temp - FLIGHTSTART]);
            do_menu_item(menu_temp, value_ptr + (menu_temp - FLIGHTSTART), 1, range, 0, text_link, false, 0);
        }

        // Preset I-limits when gyro mode changes
        if (button == ENTER)
        {
            // If roll gyro type has changed, reset to an appropriate start point
            if (temp_gyro_roll != Config.FlightMode[mode-1].Roll_type)
            {
                // Use Gyro type value to preset limits
                if(Config.FlightMode[mode-1].Roll_type == LOCK)
                {
                    Config.FlightMode[mode - 1].Roll_limit = 125;
                }
                else
                {
                    Config.FlightMode[mode - 1].Roll_limit = 0;
                }
            }

            if (temp_gyro_pitch != Config.FlightMode[mode-1].Pitch_type)
            {
                if(Config.FlightMode[mode-1].Pitch_type == LOCK)
                {
                    Config.FlightMode[mode - 1].Pitch_limit = 125;
                }
                else
                {
                    Config.FlightMode[mode - 1].Pitch_limit = 0;
                }
            }

            if (temp_gyro_yaw != Config.FlightMode[mode-1].Yaw_type)
            {
                if(Config.FlightMode[mode-1].Yaw_type == LOCK)
                {
                    Config.FlightMode[mode - 1].Yaw_limit = 125;
                }
                else
                {
                    Config.FlightMode[mode - 1].Yaw_limit = 0;
                }
            }

            UpdateLimits();			 // Update I-term limits and triggers based on percentages

            Save_Config_to_EEPROM(); // Save value and return

            Wait_BUTTON4();			 // Wait for user's finger off the button
        }
    }
}
예제 #13
0
void menu_servo_setup(uint8_t section)
{
	int8_t *value_ptr = &Config.Servo_reverse[0];

	menu_range_t range;
	uint8_t text_link;
	uint8_t i = 0;
	bool	servo_enable = false;
	bool	zero_setting = false;

	// If submenu item has changed, reset submenu positions
	if (menu_flag)
	{
		sub_top = SERVOSTART;
		menu_flag = 0;
	}

	// Get menu offsets
	// 1 = Reverse, 2 = Min, 3 = Max
	while(button != BACK)
	{
		// Load values from eeprom
		for (i = 0; i < SERVOITEMS; i++)
		{
			switch(section)
			{
				case 1:
					break;
				case 2:
					value_ptr = &Config.min_travel[0];
					servo_enable = true;
					zero_setting = true;
					break;
				case 3:
					value_ptr = &Config.max_travel[0];
					servo_enable = true;
					zero_setting = true;
					break;
				default:
					break;
			}
		}

		// Print menu
		print_menu_items(sub_top, SERVOSTART, value_ptr, 1, (const unsigned char*)servo_menu_ranges[section - 1], 1, SERVOOFFSET, (const unsigned char*)ServoMenuText[section - 1], cursor);

		// Handle menu changes
		update_menu(SERVOITEMS, SERVOSTART, 0, button, &cursor, &sub_top, &menu_temp);
		range = get_menu_range ((const unsigned char*)servo_menu_ranges[section - 1], 0);

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&ServoMenuText[section - 1][menu_temp - SERVOSTART]);

			// Zero limits if adjusting
			if (zero_setting)
			{
				value_ptr[menu_temp - SERVOSTART] = 0;
			}

			// Do not allow servo enable for throttle if in CPPM mode
			if ((Config.Channel[menu_temp - SERVOSTART].P1_source_a == THROTTLE) && (Config.RxMode == CPPM_MODE))
			{
				servo_enable = false;
			}

			do_menu_item(menu_temp, value_ptr + (menu_temp - SERVOSTART), 1, range, 0, text_link, servo_enable, (menu_temp - SERVOSTART));
		}

		// Disable servos
		servo_enable = false;

		if (button == ENTER)
		{
			UpdateLimits();				// Update actual servo trims
			Save_Config_to_EEPROM();	// Save value and return
		}
	}
}
예제 #14
0
void init(void)
{
	uint8_t i;
	bool	updated;
	
	//***********************************************************
	// I/O setup
	//***********************************************************
	// Set port directions
	DDRA		= 0x30;		// Port A
	DDRB		= 0x0A;		// Port B
	DDRC		= 0xFC;		// Port C
	DDRD		= 0xF2;		// Port D

	// Hold all PWM outputs low to stop glitches
	// M5 and M6 are on PortA for KK2.1
	MOTORS		= 0;
	M5			= 0;
	M6			= 0;

	// Preset I/O pins
	LED1 		= 0;		// LED1 off
	LVA 		= 0; 		// LVA alarm OFF
	LCD_SCL		= 1;		// GLCD clock high

	// Set/clear pull-ups (1 = set, 0 = clear)
	PINB		= 0xF5;		// Set PB pull-ups
	PIND		= 0x0C;		// Set PD pull-ups (Don't pull up RX yet)

	//***********************************************************
	// Spektrum receiver binding. Must be done immediately on power-up
	// 
	// 3 low pulses: DSM2 1024/22ms
	// 5 low pulses: DSM2 2048/11ms
	// 7 low pulses: DSMX 1024/22ms
	// 9 low pulses: DSMX 2048/11ms
	//***********************************************************

	PIND	= 0x0C;			// Release RX pull up on PD0
	_delay_ms(63);			// Pause while satellite wakes up
							// and pull-ups have time to rise.
							// Tweak until bind pulses about 68ms after power-up		
		
	// Bind as master if any single button pressed.
	// NB: Have to wait until the button pull-ups rise before testing for a button press.
	// Button 1
	if ((PINB & 0xf0) == 0x70)
	{
		DDRD	= 0xF3;		// Switch PD0 to output
		bind_master(3);
		
	}
	// Button 2	
	if ((PINB & 0xf0) == 0xb0)
	{
		DDRD	= 0xF3;		// Switch PD0 to output
		bind_master(5);
	}
	// Button 3	
	if ((PINB & 0xf0) == 0xd0)
	{
		DDRD	= 0xF3;		// Switch PD0 to output
		bind_master(7);
	}
	
	// Button 4
	if ((PINB & 0xf0) == 0xE0)
	{
		DDRD	= 0xF3;		// Switch PD0 to output
		bind_master(9);
	}
	
	DDRD	= 0xF2;			// Reset Port D directions
	PIND	= 0x0D;			// Set PD pull-ups (now pull up RX as well)

	//***********************************************************
	// Timers
	//***********************************************************

	// Timer0 (8bit) - run @ 20MHz / 1024 = 19.531kHz or 51.2us - max 13.1ms
	// Slow timer to extend Timer 1
	TCCR0A = 0;								// Normal operation
	TCCR0B = 0x05;							// Clk / 1024 = 19.531kHz or 51.2us - max 13.1ms
	TIMSK0 |= (1 << TOIE0);					// Enable interrupts
	TCNT0 = 0;								// Reset counter
	
	// Timer1 (16bit) - run @ 2.5MHz (400ns) - max 26.2ms
	// Used to measure Rx Signals & control ESC/servo output rate
	TCCR1A = 0;
	TCCR1B |= (1 << CS11);					// Clk/8 = 2.5MHz

	// Timer2 8bit - run @ 20MHz / 1024 = 19.531kHz or 51.2us - max 13.1ms
	// Used to time arm/disarm intervals
	TCCR2A = 0;	
	TCCR2B = 0x07;							// Clk/1024 = 19.531kHz
	TIMSK2 = 0;
	TIFR2 = 0;
	TCNT2 = 0;								// Reset counter

	//***********************************************************
	// Interrupts and pin function setup
	//***********************************************************

	// Pin change interrupt enables PCINT1, PCINT2 and PCINT3 (Throttle, AUX and CPPM input)
	PCICR  = 0x0A;							// PCINT8  to PCINT15 (PCINT1 group - AUX)
											// PCINT24 to PCINT31 (PCINT3 group - THR)
	PCIFR  = 0x0F;							// Clear PCIF0 interrupt flag 
											// Clear PCIF1 interrupt flag 
											// Clear PCIF2 interrupt flag 
											// Clear PCIF3 interrupt flag 

	// External interrupts INT0 (Elevator) and INT1 (Aileron) and INT2 (Rudder)
	EICRA = 0x15;							// Any change INT0
											// Any change INT1
											// Any change INT2
	EIFR  = 0x07; 							// Clear INT0 interrupt flag (Elevator)
											// Clear INT1 interrupt flag (Aileron)
											// Clear INT2 interrupt flag (Rudder/CPPM)

	//***********************************************************
	// Start up
	//***********************************************************

	// Preset important flags
	Interrupted = false;						

	// Load EEPROM settings
	updated = Initial_EEPROM_Config_Load(); // Config now contains valid values

	//***********************************************************
	// RX channel defaults for when no RC connected
	// Not doing this can result in the FC trying (unsuccessfully) to arm
	// and makes entry into the menus very hard
	//***********************************************************

	for (i = 0; i < MAX_RC_CHANNELS; i++)
	{
		RxChannel[i] = 3750;
	}
	
	RxChannel[THROTTLE] = 2500; // Min throttle
	
	//***********************************************************
	// GLCD initialisation
	//***********************************************************

	// Initialise the GLCD
	st7565_init();

	// Make sure the LCD is blank without clearing buffer (and so no logo)
	clear_screen();

	//***********************************************************
	// ESC calibration
	//***********************************************************
	
	// Calibrate ESCs if ONLY buttons 1 and 4 pressed
	if ((PINB & 0xf0) == 0x60)
	{
		// Display calibrating message
		st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
		clear_buffer(buffer);
		LCD_Display_Text(59,(const unsigned char*)Verdana14,10,25);
		write_buffer(buffer);
		clear_buffer(buffer);
				
		// For each output
		for (i = 0; i < MAX_OUTPUTS; i++)
		{
			// Check for motor marker
			if (Config.Channel[i].Motor_marker == MOTOR)
			{
				// Set output to maximum pulse width
				ServoOut[i] = MOTOR_100;
			}
			else
			{
				ServoOut[i] = SERVO_CENTER;
			}
		}
					
		// Output HIGH pulse (1.9ms) until buttons released
		while ((PINB & 0xf0) == 0x60)
		{
			// Pass address of ServoOut array and select all outputs
			output_servo_ppm_asm(&ServoOut[0], 0xFF);

			// Loop rate = 20ms (50Hz)
			_delay_ms(20);			
		}

		// Output LOW pulse (1.1ms) after buttons released
		// For each output
		for (i = 0; i < MAX_OUTPUTS; i++)
		{
			// Check for motor marker
			if (Config.Channel[i].Motor_marker == MOTOR)
			{
				// Set output to maximum pulse width
				ServoOut[i] = MOTOR_0;
			}
		}		

		// Loop forever here
		while(1)
		{
			// Pass address of ServoOut array and select all outputs
			output_servo_ppm_asm(&ServoOut[0], 0xFF);

			// Loop rate = 20ms (50Hz)
			_delay_ms(20);			
		}
	}

	//***********************************************************
	// Reset EEPROM settings
	//***********************************************************

	// This delay prevents the GLCD flashing up a ghost image of old data
	_delay_ms(300);

	// Reload default eeprom settings if middle two buttons are pressed
	if ((PINB & 0xf0) == 0x90)
	{
		// Display reset message
		st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
		clear_buffer(buffer);
		LCD_Display_Text(262,(const unsigned char*)Verdana14,40,25); // "Reset"
		write_buffer(buffer);
		clear_buffer(buffer);
		
		// Reset EEPROM settings
		Set_EEPROM_Default_Config();
		Save_Config_to_EEPROM();

		// Set contrast to the default value
		st7565_set_brightness(Config.Contrast);

		_delay_ms(500);		// Save is now too fast to show the "Reset" text long enough
	}

	// Display message in place of logo when updating eeprom structure
	if (updated)
	{
		st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
		clear_buffer(buffer);
		LCD_Display_Text(259,(const unsigned char*)Verdana14,30,13); // "Updating"
		LCD_Display_Text(260,(const unsigned char*)Verdana14,33,37); // "settings"
		write_buffer(buffer);
		clear_buffer(buffer);		
		_delay_ms(1000);	
	}
	else
	{
		// Write logo from buffer
		write_buffer(buffer);
		_delay_ms(1000);
	}

	clear_buffer(buffer);
	write_buffer(buffer);
	
	st7565_init(); // Seems necessary for KK2 mini
	
	//***********************************************************
	// i2c init
	//***********************************************************	

	i2c_init();
	init_i2c_gyros();
	init_i2c_accs();

	//***********************************************************
	// Remaining init tasks
	//***********************************************************

	// Display "Hold steady" message
	clear_buffer(buffer);
	st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
	LCD_Display_Text(263,(const unsigned char*)Verdana14,18,25);	// "Hold steady"
	write_buffer(buffer);	
	clear_buffer(buffer);
		
	// Do startup tasks
	Init_ADC();
	init_int();								// Initialise interrupts based on RC input mode
	init_uart();							// Initialise UART

	// Initial gyro calibration
	if (!CalibrateGyrosSlow())
	{
		clear_buffer(buffer);
		LCD_Display_Text(61,(const unsigned char*)Verdana14,25,25); // "Cal. failed"
		write_buffer(buffer);
		_delay_ms(1000);
		
		// Reset
		cli();
		wdt_enable(WDTO_15MS);				// Watchdog on, 15ms
		while(1);							// Wait for reboot
	}

	// Update voltage detection
	SystemVoltage = GetVbat();				// Check power-up battery voltage
	UpdateLimits();							// Update travel and trigger limits

	// Disarm on start-up if Armed setting is ARMABLE
	if (Config.ArmMode == ARMABLE)
	{
		General_error |= (1 << DISARMED); 	// Set disarmed bit
	}

	// Check to see that throttle is low if RC detected
	if (Interrupted)
	{
		RxGetChannels();
		if (MonopolarThrottle > THROTTLEIDLE) // THROTTLEIDLE = 50
		{
			General_error |= (1 << THROTTLE_HIGH); 	// Set throttle high error bit
		}
	}

	// Reset IMU
	reset_IMU();

	// Beep that init is complete
	LVA = 1;
	_delay_ms(25);
	LVA = 0;

#ifdef ERROR_LOG	
	// Log reboot
	add_log(REBOOT);
#endif
} // init()
예제 #15
0
void init(void)
{
	//***********************************************************
	// I/O setup
	//***********************************************************
	// Set port directions
	// KK2.0 and KK2.1 are different
#ifdef KK21
	DDRA		= 0x30;		// Port A
	DDRC		= 0xFC;		// Port C
#else
	DDRA		= 0x00;		// Port A
	DDRC		= 0xFF;		// Port C
#endif
	DDRB		= 0x0A;		// Port B
	DDRD		= 0xF2;		// Port D

	// Hold all PWM outputs low to stop glitches
	// M5 and M6 are on PortA for KK2.1
	MOTORS		= 0;
	M5			= 0;
	M6			= 0;		

	// Preset I/O pins
	LED1 		= 0;		// LED1 off
	LVA 		= 0; 		// LVA alarm OFF
	LCD_SCL		= 1;		// GLCD clock high

	// Set/clear pull-ups (1 = set, 0 = clear)
	PINB		= 0xF5;		// Set PB pull-ups
	PIND		= 0x0C;		// Set PD pull-ups (Don't pull up RX yet)

	//***********************************************************
	// Spektrum receiver binding
	//***********************************************************

	_delay_ms(63);				// Pause while satellite wakes up	
								// and pull-ups have time to rise.
								// Tweak until bind pulses about 68ms after power-up

	// Bind as master if ONLY button 4 pressed
	if ((PINB & 0xf0) == 0xE0)
	{
		DDRD		= 0xF3;		// Switch PD0 to output
		bind_master();
	}

	DDRD		= 0xF2;			// Reset Port D directions

	// Set/clear pull-ups (1 = set, 0 = clear)
	PIND		= 0x0D;			// Set PD pull-ups (now pull up RX as well)

	//***********************************************************
	// Timers
	//***********************************************************
	// Timer0 (8bit) - run @ 20MHz (50ns) - max 12.8us
	// Fast timer for small, precise interval timing
	TCCR0A = 0;								// Normal operation
	TCCR0B = (1 << CS00);					// Clk / 1 = 20MHz = 50ns
	TIMSK0 = 0; 							// No interrupts

	// Timer1 (16bit) - run @ 2.5MHz (400ns) - max 26.2ms
	// Used to measure Rx Signals & control ESC/servo output rate
	TCCR1A = 0;
	TCCR1B = (1 << CS11);					// Clk/8 = 2.5MHz

	// Timer2 8bit - run @ 20MHz / 1024 = 19.531kHz or 51.2us - max 13.1ms
	// Used to time arm/disarm intervals
	TCCR2A = 0;	
	TCCR2B = 0x07;							// Clk/1024 = 19.531kHz
	TIMSK2 = 0;
	TIFR2 = 0;
	TCNT2 = 0;								// Reset counter

	//***********************************************************
	// Interrupts and pin function setup
	//***********************************************************

	// Pin change interrupt enables PCINT1, PCINT2 and PCINT3 (Throttle, AUX and CPPM input)
	PCICR  = 0x0A;							// PCINT8  to PCINT15 (PCINT1 group - AUX)
											// PCINT24 to PCINT31 (PCINT3 group - THR)
	PCIFR  = 0x0F;							// Clear PCIF0 interrupt flag 
											// Clear PCIF1 interrupt flag 
											// Clear PCIF2 interrupt flag 
											// Clear PCIF3 interrupt flag 

	// External interrupts INT0 (Elevator) and INT1 (Aileron) and INT2 (Rudder)
	EICRA = 0x15;							// Any change INT0
											// Any change INT1
											// Any change INT2
	EIFR  = 0x07; 							// Clear INT0 interrupt flag (Elevator)
											// Clear INT1 interrupt flag (Aileron)
											// Clear INT2 interrupt flag (Rudder/CPPM)

	//***********************************************************
	// i2c init for KK2.1
	//***********************************************************	

#ifdef KK21
	i2c_init();
	init_i2c_gyros();
	init_i2c_accs();
#endif

	//***********************************************************
	// Start up
	//***********************************************************

	// Preset important flags
	Interrupted  = false;						
	Main_flags |= (1 << FirstTimeIMU);
	Main_flags |= (1 << FirstTimeFlightMode);

	// Initialise the GLCD
	st7565_init();
	st7565_command(CMD_DISPLAY_ON);
	st7565_command(CMD_SET_ALLPTS_NORMAL);
	st7565_set_brightness(0x26);
	st7565_command(CMD_SET_COM_REVERSE); 	// For logo

	// Make sure the LCD is blank
	clear_screen();

	// This delay prevents the GLCD flashing up a ghost image of old data
	_delay_ms(300);	

	// Reload default eeprom settings if middle two buttons are pressed (or all, for older users)
	if (((PINB & 0xf0) == 0x90) || ((PINB & 0xf0) == 0x00))
	{
		// Display reset message
		st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
		clear_buffer(buffer);
		LCD_Display_Text(1,(prog_uchar*)Verdana14,40,25);
		write_buffer(buffer,1);
		clear_buffer(buffer);

		Set_EEPROM_Default_Config();
		Save_Config_to_EEPROM();
	}
	// Load "Config" global data structure
	else
	{
		Initial_EEPROM_Config_Load();
	}		

	// Now set contrast to the previously saved value
	st7565_set_brightness((uint8_t)Config.Contrast);
						
#ifdef KK21
	// Write logo from buffer
	write_buffer(buffer,0);
	_delay_ms(500);
#endif

#ifndef KK21
	// Display "Hold steady" message for KK2.0
	st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)
	clear_buffer(buffer);
	LCD_Display_Text(2,(prog_uchar*)Verdana14,18,25);
	write_buffer(buffer,1);
	clear_buffer(buffer);
#endif
		
	// Do startup tasks
	UpdateLimits();							// Update travel limts	
	UpdateIMUvalues();						// Update IMU factors
	Init_ADC();
	init_int();								// Intialise interrupts based on RC input mode

	// Initialise UART
	init_uart();

	// Initial gyro calibration
	CalibrateGyrosSlow();

	// Check to see that gyros are stable
	ReadGyros();

	if ((gyroADC[ROLL] > GYROS_STABLE) || (gyroADC[ROLL] < -GYROS_STABLE) ||
	 	(gyroADC[PITCH] > GYROS_STABLE) || (gyroADC[PITCH] < -GYROS_STABLE) ||
		(gyroADC[YAW] > GYROS_STABLE) || (gyroADC[YAW] < -GYROS_STABLE))
	{
		General_error |= (1 << SENSOR_ERROR); 	// Set sensor error bit
	}

	// Check to see that throttle is low if in serial mode.
	// Don't bother if in CamStab mode
	_delay_ms(100);
	if (
		(
		 (Config.RxMode == CPPM_MODE) ||
		 (Config.RxMode == XTREME) ||
		 (Config.RxMode == SBUS) ||
		 (Config.RxMode == SPEKTRUM)
		)
		&& (Config.CamStab == OFF)
	   )
	{
		RxGetChannels();
		if (RCinputs[THROTTLE] > -900)
		{
			General_error |= (1 << THROTTLE_HIGH); 	// Set throttle high error bit
		}
	}

	// Flash LED
	LED1 = 1;
	_delay_ms(150);
	LED1 = 0;

	// Beep that all sensors have been handled
	menu_beep(1);

	// Set text display mode back to normal
	st7565_command(CMD_SET_COM_NORMAL); 	// For text (not for logo)

} // init()
void Display_sticks(void)
{
	int8_t	i;
	int8_t	offset;
	int8_t	temp_aileron, temp_elevator, temp_rudder;
	bool	CalibrateDone = false;

	// Save original settings in case user aborts
	temp_aileron = Config.AileronPol;
	temp_elevator = Config.ElevatorPol;
	temp_rudder = Config.RudderPol;

	// Reset to defaults - not ideal, but it works
	Config.AileronPol = NORMAL;
	Config.ElevatorPol =  NORMAL;
	Config.RudderPol = NORMAL;

	// Until exit button pressed
	while((BUTTON1 != 0) && (!CalibrateDone))
	{
		offset = 0;

		// Clear screen buffer
		clear_buffer(buffer);

		// Draw graphic
		for (i = 0; i < 2; i++)
		{
			drawrect(buffer, 17 + offset, 0, 40, 40, 1);			// Box
			drawline(buffer, 38 + offset,20, 48 + offset,  3, 1); 	// Line 1
			drawline(buffer, 41 + offset,21, 56 + offset,  6, 1); 	// Line 2
			fillcircle(buffer, 38 + offset, 21, 2, 1);				// Centre
			fillcircle(buffer, 51 + offset, 5, 4, 1);				// End

			offset = 52;
		}

		// Print bottom text and markers
		LCD_Display_Text(12, (const unsigned char*)Wingdings, 0, 57); 		// Left

		// If uncalibrated
		if (!CalibrateDone)
		{
			RxGetChannels();

			// Display "No RX signal" if no input detected
			if(RxChannel[AILERON] == 0)
			{
				LCD_Display_Text(135,(const unsigned char*)Verdana14,14,43); 	// "No RX signal"
			}

			// Sticks have not moved far enough
			else if ((RxChannel[AILERON] > 3000) && (RxChannel[AILERON] < 4500))
			{
				LCD_Display_Text(136,(const unsigned char*)Verdana14,9,43); 		// "Hold as shown"
			}

			// Sticks should now be in the right position
			// Reverse wrong input channels
			else
			{
				if (RCinputs[AILERON] < 0)
				{
					Config.AileronPol = REVERSED;
				}

				if (RCinputs[ELEVATOR] < 0)
				{
					Config.ElevatorPol = REVERSED;
				}

				if (RCinputs[RUDDER] < 0)
				{
					Config.RudderPol = REVERSED;
				}

				// If all positive - done!
				if ((RCinputs[AILERON] > 0) && (RCinputs[ELEVATOR] > 0) && (RCinputs[RUDDER] > 0))
				{
					CalibrateDone = true;
				}
			}
		}

		// Update buffer
		write_buffer(buffer,1);
		_delay_ms(100);
	}

	// Save value and return
	if (CalibrateDone)
	{
		LCD_Display_Text(137,(const unsigned char*)Verdana14,40,43); 	// "Done!"
		// Update buffer
		write_buffer(buffer,1);
		clear_buffer(buffer);
		Save_Config_to_EEPROM();
 	}
	else
	{
		// Restore old settings if failed
		Config.AileronPol = temp_aileron;
		Config.ElevatorPol = temp_elevator;
		Config.RudderPol = temp_rudder;
	}
}
예제 #17
0
파일: init.c 프로젝트: di9it/therminator
void init(void)
{
	//***********************************************************
	// I/O setup
	//***********************************************************

	// Set port directions
	DDRA		= 0x00;		// Port A
	DDRB		= 0x0A;		// Port B
	DDRC		= 0xFF;		// Port C
	DDRD		= 0xF2;		// Port D

	MOTORS		= 0;		// Hold all PWM outputs low to stop glitches

	// Preset I/O pins
	LED1 		= 0;		// LED1 off
	LVA 		= 0; 		// LVA alarm OFF
	LCD_CSI		= 1;
	LCD_SCL		= 1;
	LCD_RES		= 1;

	// Set/clear pull-ups (1 = set, 0 = clear)
	PINB		= 0xF5;		// Set PB pull-ups
	PIND		= 0x0D;		// Set PD pull-ups

	//***********************************************************
	// Timers
	//***********************************************************
	// Timer0 (8bit) - run @ 20MHz (50ns) - max 12.8us
	// Fast timer for small, precise interval timing
	TCCR0A = 0;							// Normal operation
	TCCR0B = (1 << CS00);				// Clk / 1 = 20MHz = 50ns
	TIMSK0 = 0; 						// No interrupts

	// Timer1 (16bit) - run @ 2.5MHz (400ns) - max 26.2ms
	// Used to measure Rx Signals & control ESC/servo output rate
	TCCR1A = 0;
	TCCR1B = (1 << CS11);				// Clk/8 = 2.5MHz

	// Timer2 8bit - run @ 20MHz / 1024 = 19.531kHz or 51.2us - max 13.1ms
	// Used to time arm/disarm intervals
	TCCR2A = 0;	
	TCCR2B = 0x07;						// Clk/1024 = 19.531kHz
	TIMSK2 = 0;
	TIFR2 = 0;
	TCNT2 = 0;							// Reset counter

	//***********************************************************
	// Interrupts and pin function setup
	//***********************************************************

	// Pin change interrupt enables PCINT1, PCINT2 and PCINT3 (Throttle, Aux and CPPM input)
	PCICR  = 0x0A;						// PCINT8  to PCINT15 (PCINT1 group - AUX)
										// PCINT24 to PCINT31 (PCINT3 group - THR)
	PCMSK1 |= (1 << PCINT8);			// PB0 (Aux pin change mask)
	PCMSK3 |= (1 << PCINT24);			// PD0 (Throttle pin change mask)
	PCIFR  = 0x0F;						// Clear PCIF0 interrupt flag 
										// Clear PCIF1 interrupt flag 
										// Clear PCIF2 interrupt flag 
										// Clear PCIF3 interrupt flag 

	// External interrupts INT0 (Elevator) and INT1 (Aileron) and INT2 (Rudder)
	EIMSK = 0x07;						// Enable INT0 (Elevator input)
										// Enable INT1 (Aileron input)
										// Enable INT2 (Rudder/CPPM input)
	EICRA = 0x15;						// Any change INT0
										// Any change INT1
										// Any change INT2
	EIFR  = 0x07; 						// Clear INT0 interrupt flag (Elevator)
										// Clear INT1 interrupt flag (Aileron)
										// Clear INT2 interrupt flag (Rudder/CPPM)

	//***********************************************************

	RC_Lock = false;						// Preset important flags
	Failsafe = false;
	AutoLevel = false;
	Stability = false;
	FirstTimeIMU = true;

	// Button acceleration
	button_multiplier = 1;

	Initial_EEPROM_Config_Load();			// Loads config at start-up 
	UpdateLimits();							// Update travel limts	
	UpdateIMUvalues();						// Update IMU factors
	Init_ADC();

	// Flash LED
	LED1 = 1;
	_delay_ms(150);
	LED1 = 0;

	// Initialise the GLCD
	st7565_init();
	st7565_command(CMD_DISPLAY_ON); 		// Check (AF)
	st7565_command(CMD_SET_ALLPTS_NORMAL);	// Check (A4)
	st7565_set_brightness(0x26);
	write_buffer(buffer,0);					// Display logo
	_delay_ms(1000);
	clear_buffer(buffer);					// Clear
	write_buffer(buffer,1);
	st7565_command(CMD_SET_COM_NORMAL); 	// For text
	clear_buffer(buffer);					// Clear

	// Reset I-terms
	IntegralGyro[ROLL] = 0;	
	IntegralGyro[PITCH] = 0;
	IntegralGyro[YAW] = 0;

	// Calibrate gyros, hopefully after motion minimised
	CalibrateGyros();			


	//***********************************************************
	//* Reload eeprom settings if all buttons are pressed 
	//***********************************************************

	if ((PINB & 0xf0) == 0)
	{

		LCD_Display_Text(1,(prog_uchar*)Verdana14,15,10);
		LCD_Display_Text(2,(prog_uchar*)Verdana14,31,30);

		write_buffer(buffer,1);
		clear_buffer(buffer);				// Clear
		Set_EEPROM_Default_Config();
		Save_Config_to_EEPROM();
	}

	//***********************************************************

	sei();									// Enable global Interrupts 

	// Check to see that gyros are stable
	ReadGyros();

	if ((gyroADC[ROLL] > GYROS_STABLE) || (gyroADC[ROLL] < -GYROS_STABLE) ||
	 	(gyroADC[PITCH] > GYROS_STABLE) || (gyroADC[PITCH] < -GYROS_STABLE) ||
		(gyroADC[YAW] > GYROS_STABLE) || (gyroADC[YAW] < -GYROS_STABLE))
	{
		General_error |= (1 << SENSOR_ERROR); 	// Set sensor error bit
	}

	// Check to see that throttle is low if in CPPM mode if RC detected
	// Don't bother if in CamStab mode
	_delay_ms(100);
	if ((Config.RxMode == CPPM_MODE) && RC_Lock && (Config.CamStab == OFF))
	{
		RxGetChannels();
		if (RCinputs[THROTTLE] > 300)
		{
			General_error |= (1 << THROTTLE_HIGH); 	// Set throttle high error bit
		}
	}

	// Beep that all sensors have been handled
	menu_beep(1);

} // init()
void menu_rc_setup(uint8_t section)
{
	static uint8_t rc_top = RCSTART;

	int8_t *value_ptr;

	menu_range_t range;
	uint8_t text_link;
	uint8_t i = 0;
	uint8_t mult = 1;		// Multiplier
	uint8_t offset;			// Index into channel structure
	uint8_t	items;			// Items in group
	uint16_t temp16_1;

	// If submenu item has changed, reset submenu positions
	if (menu_flag)
	{
		rc_top = RCSTART;
		menu_flag = 0;
	}

	while(button != BACK)
	{
		// Get menu offsets and load values from eeprom
		// 1 = RC, 2 = Failsafe, 3 = General, 4 = Battery
		switch(section)
		{
			case 1:				// RC setup menu
				offset = 0;
				items = RCITEMS;
				value_ptr = &Config.RxMode;
				mult = 1;
				break;
			case 2:				// Failsafe menu
				offset = RCITEMS;
				items = FSITEMS;
				value_ptr = &Config.FailsafeType;
				mult = 1;
				break;
			case 3:				// General menu
				offset = RCITEMS + FSITEMS;
				items = GENERALITEMS;
				value_ptr = &Config.MixMode;
				mult = 1;
				break;
			case 4:				// Battery menu
				offset = RCITEMS + FSITEMS + GENERALITEMS;
				items = BATTITEMS;
				value_ptr = &Config.BatteryType;
				mult = 4;
				break;
			default:
				offset = 0;
				items = RCITEMS;
				value_ptr = &Config.RxMode;
				mult = 1;
				break;
		}
		// Save pre-edited values
		int8_t temp_type = Config.MixMode;
		int8_t temp_cells = Config.BatteryCells;
		int8_t temp_minvoltage = Config.MinVoltage;
		int8_t temp_flapchan = Config.FlapChan;

		// Print menu
		print_menu_items(rc_top + offset, RCSTART + offset, value_ptr, mult, (prog_uchar*)rc_menu_ranges[section - 1], 0, RCOFFSET, (prog_uchar*)RCMenuText[section - 1], cursor);

		// Handle menu changes
		update_menu(items, RCSTART, offset, button, &cursor, &rc_top, &menu_temp);
		range = get_menu_range ((prog_uchar*)rc_menu_ranges[section - 1], (menu_temp - RCSTART - offset)); 

		if (button == ENTER)
		{
			text_link = pgm_read_byte(&RCMenuText[section - 1][menu_temp - RCSTART - offset]);
			do_menu_item(menu_temp, value_ptr + (menu_temp - RCSTART - offset), mult, range, 0, text_link, false, 0);
		}

		if (button == ENTER)
		{
			// Update Ch7. mixer with source from Config.FlapChan if in Aeroplane mode and source changed
			if ((Config.MixMode == AEROPLANE) && (Config.FlapChan != temp_flapchan))
			{
				Config.Channel[CH7].source_a = Config.FlapChan;
			}

			// See if cell number or min_volts has changed
			if ((temp_cells != Config.BatteryCells) || (temp_minvoltage != Config.MinVoltage))
			{
				// Recalculate if more cells
				temp16_1 = Config.MinVoltage;
				temp16_1 = temp16_1 * Config.BatteryCells;
				temp16_1 = temp16_1 / 10;
				Config.PowerTrigger = (int8_t)temp16_1;
			}

			// If model type has changed, reload preset
			if ((section == 3) && (temp_type != Config.MixMode)) 
			{
				switch(Config.MixMode)  // Load selected mix
				{
					case AEROPLANE:
						get_preset_mix(AEROPLANE_MIX);
						break;	
					case FWING:
						get_preset_mix(FLYING_WING_MIX);
						break;
					case CAMSTAB:
						get_preset_mix(CAM_STAB);
						break;
					case MANUAL:
						// Clear all channel info
						memset(&Config.Channel[0].value,0,(sizeof(channel_t) * PSUEDO_OUTPUTS));

						// Preset important settings
						for (i = 0; i < PSUEDO_OUTPUTS; i++)
						{
							Config.Channel[i].source_a = NOCHAN;
							Config.Channel[i].source_b = NOCHAN;
							Config.Channel[i].output_b = UNUSED;
							Config.Channel[i].output_c = UNUSED;
							Config.Channel[i].output_d = UNUSED;
						}

						break;
					default:
						break;
				}
			}

			init_int();				// In case RC type has changed, reinitialise interrupts
			init_uart();			// and UART

			UpdateIMUvalues();		// Update IMU variables
			UpdateLimits();			// Update I-term limits and triggers based on percentages

#ifdef KK21
			// Update MPU6050 LPF
			writeI2Cbyte(MPU60X0_DEFAULT_ADDRESS, MPU60X0_RA_CONFIG, Config.MPU6050_LPF);
#endif
			// Update channel sequence
			for (i = 0; i < MAX_RC_CHANNELS; i++)
			{
				if (Config.TxSeq == FUTABASEQ)
				{
					Config.ChannelOrder[i] = (uint8_t)pgm_read_byte(&FUTABA[i]);
				}
				else
				{
					Config.ChannelOrder[i] = (uint8_t)pgm_read_byte(&JR[i]);
				}
			}

			Save_Config_to_EEPROM(); // Save value and return
		}
	}
}