RCSwitch

From JimWIKI

Jump to: navigation, search

this project is based on the Wag Switch by the RC Groups member 'AleG' (Alejandro Garcia) and discussed in this Thread

//********* Wag Switch  *********//
//                               //
// Author   : Alejandro Garcia   //
// Target   : ATTiny13V          //
// Clk Speed: 9.6Mhz	:        //
// Version  : 1.00               //
// Date     : 26-12-08           //
//*******************************//

#define F_CPU 9600000UL // 9.6MHz

#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

volatile unsigned int Tick;	// Timer count variable
volatile unsigned char sPulse;	// Incoming servo pulse measured length
volatile unsigned int sPulseIn;	// Incoming servo pulse measurement
volatile unsigned char ServoCurrent;  // Current position of the output servo
volatile unsigned char ServoTarget;   // Target position of the output servo

unsigned char Right_1;	// Stick on right position flag
unsigned char Left_1;	// Stick on left position flag
unsigned int pRight;	// Right sPulse threshold
unsigned int pLeft;		// Left sPulse threshold
unsigned char window;	// Window of opportunity to switch states
unsigned char restore_SREG;	// SREG status temporary store
unsigned char ServoStep;	// Step the servo in one direction
unsigned char ServoSubStep;	// Controls speed of servo rotation

int main (void)
{

	sei(); //  Enable global interrupts
	DDRB &= ~(1<<PB1); // PB1 as input
	DDRB |= (1<<PB0) | (1<<PB2) | (1<<PB3) | (1<<PB4); // PB0 and PB2 as output
	TCCR0A |= (1<<WGM01); // Configure timer 1 for CTC mode
	TIMSK0 |= (1<<OCIE0A); // Enable CTC interrupt
	OCR0A = 95; // Set CTC compare value
	TCCR0B |= (1<<CS00); // No prescaler
	GIMSK |= (1<<INT0); // Enable INT0 interrupt

	MCUCR |= (1<<ISC00) | (1<<ISC01);	// INT0 interrupt on rising edge

	Tick = 0;	// Initialize timer at 0
	ServoCurrent = 100;	// Initialize output servo at far left position
	ServoTarget = 100;	// Initialize output servo target at far left position
	sPulse = 150;	// Set initial state of pulse width
	pRight = 175;	// Long pulse threshold
	pLeft = 125;	// Short pulse threshold
	window = 5;	// Time window for stick motion

	while(1)
	{
		sei();	// Enable interrupts

/*Monitor if stick is left or right*/
		if(sPulse >= pRight)	// True if stick right past threshold
		{
			Right_1 = 1;
		}
		if(sPulse <= pLeft)	// True if stick left past threshold
		{
			Left_1 = 1;
		}

/*Evaluate Right-Left-Right condition*/
		while(Right_1)	// While stick is right
		{
			Left_1 = 0;
			for(int i = 0; i<=window; i++)	// Wait for stick to move left (R-?-?)
			{
				PORTB |= (1<<PB2);	// Wait loop, display blinking LED on pin 7
				_delay_loop_2(10000);
				PORTB &= ~(1<<PB2);
				_delay_loop_2(10000);

				if(sPulse <= pLeft)	// If stick moved left before end of wait period
				{
					Left_1 = 1;
					i=window;	// Reset countdown variable
				}
			}
			while(Left_1)	// While stick is left (R-L-?)
				{
					Right_1 = 0;
					for(int i = 0; i<=window; i++)	// Wait for stick to move right again
					{
						PORTB |= (1<<PB2);	// Wait loop
						_delay_loop_2(10000);
						PORTB &= ~(1<<PB2);
						_delay_loop_2(10000);

						if(sPulse >= pRight)	// If stick moved right again then toggle output (R-L-R)
						{
							PORTB ^= (1<<PB3);	// Toggle Output A, pin 2
							if(ServoTarget != 200)	// Toggle servo targe position
							{
								ServoTarget = 200;	// Move to one extreme
							}
							else
							{
								ServoTarget = 100;	// Move to the other
							}
							i=window;	// Reset window time
						}
					}
					Right_1 = 0;	// Clear stick position flags
					Left_1 = 0;
				}
			Right_1 = 0;
			Left_1 = 0;
		}

/*Evaluate Left-Right-Left condition*/
		while(Left_1)	// While stick is Left 
		{
			Left_1 = 0;
			for(int i = 0; i<=window; i++)	// Wait for stick to move right (L-?-?)
			{
				PORTB |= (1<<PB2);	// Wait loop
				_delay_loop_2(10000);
				PORTB &= ~(1<<PB2);
				_delay_loop_2(10000);

				if(sPulse >= pRight)	// If stick moved right before end of wait period
				{
					Right_1 = 1;
					i=window;	// Reset countdown
				}
			}
			while(Right_1)	// While stick is right (L-R-?)
				{
					Left_1 = 0;
					for(int i = 0; i<=window; i++)	// Wait for stick to move left again
					{
						PORTB |= (1<<PB2);
						_delay_loop_2(10000);
						PORTB &= ~(1<<PB2);
						_delay_loop_2(10000);

						if(sPulse <= pLeft)	// If stick moved left again then toggle output (L-R-L)
						{
							PORTB ^= (1<<PB4);	// Toggle Output B, pin 3
							i=window;
						}
					}
					Right_1 = 0;	// Clear stick position flags
					Left_1 = 0;
				}
			Right_1 = 0;
			Left_1 = 0;
		}
	}
}

ISR(TIM0_COMPA_vect)	// 100KHz timer interrupt
{	
	restore_SREG = SREG;	// Save register status
	cli();	// Disable interrupts

/* Servo Out pulse generation */
	if(Tick <= ServoCurrent)	// While the Tick count is less than the maximum pulse length
	{
		PORTB |= (1<<PB0);	// Keep pin 5 high
	}
	else
	{
		PORTB &= ~(1<<PB0);	// Keep pin 5 low for the reminder of the servo frame
	}

	if(Tick >= 2000)	// One servo frame (20ms) completed
	{
		Tick = 0;	// Reset counter
	}
	Tick += 1;	// Increment counter
	sPulseIn += 1;	// Increment the incoming servo pulse value
	SREG = restore_SREG;	// Restore status register
	sei();	// Enable interrupts
}	

/* Servo In pulse measurement and servo Out position setting */
ISR(INT0_vect)	// Servo input interrupt
{
	restore_SREG = SREG;	// Save register status
	cli();	// Disable interrupts

	if(MCUCR & (1<<ISC00))	// If incoming servo pulse begins (rising edge)
	{
		sPulseIn = 0;	// Start pulse count

		/* Servo Out rotation speed*/
		ServoSubStep += 1;	// Increment servo position substep
		if(ServoSubStep >= 3)	// ServoSubStep >= *less faster, more slower*
			{
				ServoStep = 1;
				ServoSubStep = 0;
			}

		/*Set interrupt sense to falling edge*/
		MCUCR |= (1<<ISC01);
		MCUCR &= ~(1<<ISC00);
	}
	else	// If incoming servo pulse ends (falling edge)
	{
		if(ServoStep)	// If it's time to step Servo Out in one direction
			{
				if(ServoCurrent < ServoTarget)
				{
					ServoCurrent += 1;	// Step Servo Out right
				}

				if(ServoCurrent > ServoTarget)
				{
					ServoCurrent -= 1;	// Step Servo Out left
				}
				ServoStep = 0;	// Reset and wait for next servo step command
			}
		
		sPulse = sPulseIn; // Measure Servo In pulse length
		/*Set interrupt sense to rising edge*/
		MCUCR |= (1<<ISC00) | (1<<ISC01);
	}
	SREG = restore_SREG;
	sei();
}