wac_v1.c
#include <Mega32.h>

#include <stdio.h>
#include <delay.h> 
#include <string.h>

#define LCDwidth 16
//port A
#asm
	.equ __lcd_port = 0x1B 
#endasm

#include <lcd.h>

#define button0 0x01
#define button1 0x02
#define button2 0x04
#define button3 0x08
#define button4 0x10
#define button5 0x20
#define button6 0x40
#define button7 0x80

#define CHOICE 0
#define PLAY 1
#define STOP 2   
#define PAUSE 3
#define VOLUMEUP 4
#define VOLUMEDOWN 5 
#define RWD 6
#define FFWD 7 
#define TOGGLE_MODE 8

#define NO_PUSH 0
#define MAYBE_PUSH 1
#define PUSHED 2
#define MAYBE_NO_PUSHED 3 

#define PLAY_STATUS 0
#define SONG_TITLE  1  
#define TIME_STATUS 2
#define PRINT_STATUS 3
#define PLAYLIST_PRINT 4  
#define PRINT_PL_0 5
#define PRINT_PL_1 6
#define BITRATE 7

#define DOWN 0
#define UP 1
#define NONE 2

//mode defines
#define NORMAL		0
#define PLAYLIST	1
 
void debounce(int);
void init(void);
void toggleLED(unsigned int, char); 
void checkStatus(void);   
char * format(char[]);   
unsigned char multi_space(char[]);  

unsigned int pit_count;
unsigned char debounce_state;
unsigned char push_flag;
char debounced;  
char adjust_done_flag; 
char button_state;
char wait_for_input;
char status;
char c;
char cmd_str[250];
char song_title_full_prev[250];
char song_title_full[250];
char song_title_print[17];
char cmd_ready;
int char_count;    
char play_status_str[6];
char lcd_buffer0[17];
char repeat_flag;
char mode;          
char song_time[7]; 
unsigned char song_pos=0;
char pl_line0[17], pl_line1[17];
char bit_rate[6];  
char output[12];
unsigned char counter, song_pos_timer;
unsigned int pl_pos;  
unsigned int time_out_counter;
unsigned char start_counter;
unsigned char scroll;

//receive interrupt service routine
interrupt[USART_RXC] void uartRx(void)
{
    c = UDR;	//copy character from UDR to global C
	if(c!='\v') cmd_str[char_count++] = c;
	else 
	{
		cmd_str[char_count] = 0;
		cmd_ready = 1;
		UCSRB.7 = 0;	//kill interrupt until software is ready
	}
}

//init Rx interrupt servicing
void get_cmd_init(void)
{
	char_count = 0;
	cmd_ready = 0;  
	UCSRB.7 = 1;	//enable Rx interrupt
	time_out_counter=0;
	start_counter=1;
}


//main function
void main(void)
{
	int x;
	//init variables
	init();  

	while(1)
	{ 
		
	//poll for button presses
	switch (button_state)
		{ 
			case CHOICE:
				if (~PINB == 0x0) break; //no button pushed
				else if(~PINB == button6) 
				{
					toggleLED(button6, 'C'); 
					button_state = PLAY;
				}
				else if(~PINB == button5 && mode==NORMAL)
				{
					toggleLED(button5, 'C');
					button_state = PAUSE;
				}
				else if(~PINB == button4 && mode==NORMAL) 
				{
					toggleLED(button4, 'C');
					button_state = STOP;
				}
				else if(~PINB == button2) 
				{
					toggleLED(button2, 'C');
					button_state = VOLUMEUP;
				}
				else if(~PINB == button1) 
				{
					toggleLED(button1, 'C');
					button_state = VOLUMEDOWN;
				} 
				else if(~PINB == button7 && mode==NORMAL) 
				{
					toggleLED(button7, 'C');
					button_state = RWD;
				}                       
				else if(~PINB == button3 && mode==NORMAL) 
				{
					toggleLED(button3, 'C');
					button_state = FFWD;
				} 
				else if(~PINB == button0)
				{
					button_state = TOGGLE_MODE;
				}
			break;
			
			case PLAY:
				if (pit_count%30 == 0) debounce(button6);
				if (mode==NORMAL)
				{
					if (debounced) 
					{ 
						putchar('1'); 
						button_state = CHOICE;
						debounced = 0; 
						toggleLED(button6, 'C');
					}
				}
				else if (mode==PLAYLIST)
				{
					//play the current song viewed on the playlist
					if (debounced) 
					{ 
 						putchar('m');
						button_state = CHOICE;
						debounced = 0; 
						mode = NORMAL;
						toggleLED(button6, 'C');
					}
				}
				break;
			
			case PAUSE:
				if (pit_count%30 == 0) debounce(button5);
				if (debounced)
				{
					putchar('3');
					button_state = CHOICE;
					debounced = 0;  

					toggleLED(button5, 'C');
				}
				break;

			case STOP:
				if (pit_count%30 == 0) debounce(button4);
			 	if (debounced)
				{
					putchar('2');
					button_state = CHOICE;
					debounced = 0;
					toggleLED(button4, 'C'); 
				}
				break;  
			//volume up and scroll up in playlist mode
			case VOLUMEUP:
				if (pit_count%30 == 0) debounce(button2);
				if(mode==NORMAL)
				{
					if (push_flag)
					{   
						if(pit_count%100 == 0)
						{
							putchar('4');  
			//delay to prevent putchar from occuring mutliple times on the same tick
							delay_ms(1);
						}       
					}
					else if(debounced)
					{
						button_state = CHOICE;
						debounced = 0; 
						toggleLED(button2, 'C');
					}
				}
				else if (mode==PLAYLIST)
				{
					//scroll playlist up
					if(debounced)
					{
						//scroll playlist up
						scroll=UP; 
						button_state = CHOICE;
						debounced = 0; 
						toggleLED(button2, 'C');
					}
				}
				break; 
			//volume down and scroll down in playlist mode
			case VOLUMEDOWN:
				if (pit_count%30 == 0) debounce(button1);
				if (mode==NORMAL)
				{
					if (push_flag)
					{   
						if(pit_count%100 == 0)
						{
							putchar('5'); 
			//delay to prevent putchar from occuring mutliple times on the same tick
							delay_ms(1); 
						}       
					}
					else if(debounced)
					{
						button_state = CHOICE;
						debounced = 0; 
						toggleLED(button1, 'C');
					}
				}
				else if(mode==PLAYLIST)
				{
					//scroll playlist down
					if(debounced)
					{
						//scroll playlist down
						scroll = DOWN;
						button_state = CHOICE;
						debounced = 0; 
						toggleLED(button1, 'C');
					}
				}
				break;    
			//fast forward and skip to next song		
			case FFWD:
				if (pit_count%30 == 0) debounce(button3);
				if (push_flag)
				{   
					if(pit_count%400 == 0)
					{
						putchar('d'); 
						repeat_flag = 1;
			//delay to prevent putchar from occuring mutliple times on the same tick
						delay_ms(1); 
					}       
				}
				else if(debounced)
				{
					if(repeat_flag == 0)
						putchar('b');
					else repeat_flag = 0;
					button_state = CHOICE;
					debounced = 0; 
					toggleLED(button3, 'C');
				}
				break;  
			//rewind and skip to prev song
			case RWD:
				if (pit_count%30 == 0) debounce(button7);
				if (push_flag)
				{   
					if(pit_count%400 == 0)
					{
						putchar('e'); 
						repeat_flag = 1;
		//delay to prevent putchar from occuring mutliple times on the same tick
						delay_ms(1); 
					}       
				}
				else if(debounced)
				{
					if(repeat_flag == 0)
						putchar('c');
					else repeat_flag = 0;
					button_state = CHOICE;
					debounced = 0; 
					toggleLED(button7, 'C');
				}
				break; 
			//change mode between playlist and normal mode
			case TOGGLE_MODE:
				if (pit_count%30 == 0) debounce(button0);
				if (debounced)
				{
					if(mode==PLAYLIST)
					{
						mode = NORMAL;       
						//set the start state of print to the play status
						status = PLAY_STATUS;
					}
					else
					{ 
						mode=PLAYLIST;          
						//set the start state of print to print the playlist
						status = PLAYLIST_PRINT;
					}
					button_state = CHOICE;
					debounced = 0; 
					toggleLED(button0, 'C');
				}
				break;  

		} 
		//call check status every 50ms
		if(pit_count%50==0) 
			checkStatus(); 
	}
} 


//this function is the LCD routines.  
//it retrieves the needed info fromt the plugin
//and spits it out to the LCD one piece at a time
void checkStatus()
{
 	unsigned char x; 

	switch(status)
 	{
 		case PLAY_STATUS:
 	      //not waiting for any input
 	      if(wait_for_input == 0)
 	 		{
 	        	get_cmd_init();
 				putchar('7');  //send status request   
 				wait_for_input = 1;
 			}
 			//data is ready to be read
 			if(cmd_ready==1)
 			{    
				sprintf(play_status_str,"%-s", format(cmd_str));
				play_status_str[5]=0;
				lcd_gotoxy(0,1);
				lcd_puts(play_status_str); 
				wait_for_input = 0;			
 				status = SONG_TITLE; 
 				start_counter = 0;
 			}
 		break; 
 		case SONG_TITLE:
 			//not waiting for any input
 	      if(wait_for_input == 0)
 	      {
 	        	get_cmd_init();
 				putchar('h');  //send songtitle request   
 				wait_for_input = 1;
 			}
 			//data is ready to be read
 			if(cmd_ready==1)
 			{    
				strcpy(song_title_full,cmd_str); //copy the input data to the song title
				
			//if same song is still playing and the whol
         //e or at least the end of the title didn't show last time
				if(song_pos_timer==10)
				{  
					counter++;
					if(counter==2)
					{
		if(strcmp(song_title_full, song_title_full_prev)==0 
          && strpos(song_title_full_prev,'\0')>16 && multi_space(song_title_print)==0)
						{	
							//change index so the song will seem to scroll
							song_pos++;	
						}
						//reset index
						else 
						{
							song_pos=0;
							song_pos_timer=0; 
						}
						counter = 0;
					}
				
				}		
				else song_pos_timer++;
				//copy the first 16 characters of the song title.
				for(x=0;x<16;x++)
				{
					song_title_print[x]=song_title_full[x+song_pos];
				}
				song_title_print[16]=0; //null terminate the string
				
				strcpy(song_title_full_prev,song_title_full);
				
				sprintf(lcd_buffer0,"%-s", format(song_title_print));
				lcd_buffer0[16]=0;
			     	 
			 	lcd_gotoxy(0,0);
		    	lcd_puts(lcd_buffer0); 		
 				wait_for_input = 0;
 				status = TIME_STATUS;
 		  		start_counter = 0;
 			}
 		break;
		case TIME_STATUS:
			if(wait_for_input == 0)
 	 		{
 	        	get_cmd_init();
 				putchar('g');  //send songtitle request   
 				wait_for_input = 1;
 			}
 			//data is ready to be read
 			if(cmd_ready==1)
 			{
 				sprintf(song_time, " %s",cmd_str); 
 				wait_for_input=0;
 				status = BITRATE; 
 				start_counter = 0;
 			} 
			break;     
			
		case BITRATE:
			if(wait_for_input == 0)
 	 		{
 	        	get_cmd_init();
 				putchar('9');  //send bitrate request  
 				wait_for_input = 1;
 			}
 			//data is ready to be read
 			if(cmd_ready==1)
 			{
 				sprintf(bit_rate, "%s",cmd_str);
 				bit_rate[5]=0; 
				sprintf(output, "%s  %s",song_time,bit_rate);
				lcd_gotoxy(5,1);
				lcd_puts(output);				
 				wait_for_input=0;
 				status = PRINT_STATUS; 
 				start_counter = 0;
 			} 
			break;     
		
		case PLAYLIST_PRINT:
			//if(pit_count%300==0){
			if(wait_for_input == 0)
 	      {
 	        	get_cmd_init();
 	        	if(scroll==NONE)
 	        	{
 	        		putchar('l');
				}
 				else if(scroll==DOWN)
 				{
 					putchar('j'); 
				}
 				else if(scroll==UP)
 				{
 					putchar('k');
 				}
 				
 				scroll = NONE;
				wait_for_input = 1;
 			}
 			//data is ready to be read
 			if(cmd_ready==1)
 			{
				for(x=0;x<16;x++)
				{
					pl_line0[x]=cmd_str[x];
					pl_line1[x]=cmd_str[x+16];
				}

 				pl_line0[16]=0;
				pl_line1[16]=0;
 			
 				wait_for_input=0;
 				status = PRINT_PL_0; 
 				start_counter = 0;
 			}
			break;
		//print first song of playlist
		case PRINT_PL_0:
			lcd_gotoxy(0,0);
			lcd_puts(pl_line0);
			status = PRINT_PL_1; 
			break;              
		//print second song of playlist
		case PRINT_PL_1:
			lcd_gotoxy(0,1);
			lcd_puts(pl_line1);
			status = PRINT_STATUS;
			break;     
		//resest to beginning state
 		case PRINT_STATUS:  
			if(mode == PLAYLIST)
				status = PLAYLIST_PRINT;
			else  status = PLAY_STATUS;
			break;
 	}
}

//remove all null characters and replace with spaces
char * format(char string[])
{
	char x=0; 
	char y;
	while(string[x]!='\0')
		x++;
	for(y=x;y<16;y++)
		string[y]=' ';
	string[16]=0;
	return string;
} 

//count the num of consecutive empty spaces
//used for song scrolling
unsigned char multi_space(char string[])
{        
	unsigned char x, count=0;
	for(x=13;x<16;x++)
	{
		if(string[x]==' ')
			count ++;
	}
	if(count==3)
		return 1;
	else return 0;
}

//debounce button
//30ms ticks  
void debounce(int button)
{
	switch(debounce_state)
	{
		case NO_PUSH:
			if(~PINB == button) debounce_state=MAYBE_PUSH;
			break;
		case MAYBE_PUSH:
			if(~PINB == button) 
			{
				debounce_state=PUSHED;
				push_flag=1;
			}
			else debounce_state=NO_PUSH;
			break;
		case PUSHED:
			if(~PINB == button){}
			else debounce_state=MAYBE_NO_PUSHED;
			break;
		case MAYBE_NO_PUSHED:
			if(~PINB == button) debounce_state=PUSHED;
			else 
			{
				push_flag=0;
				debounce_state=NO_PUSH;
				debounced = 1;       //flag set when completely debounced
			}
			break;
	}
}	

//timer 0 interrupt service routine
interrupt [TIM0_COMP] void timer0_compare(void)
{	
	//1ms ticks 
	pit_count++;	//counter for checking debounce state machine    

	//timeout counter
	if(start_counter==1)
		time_out_counter++;
	
	//if timeout
	if(time_out_counter==500)
	{
		//disable interrupt for now
		UCSRB.7 = 0; 
		TIMSK=0;
		//re-init
		init();
	}
  	   
	
		 
} 

//function to toggle leds.  
//easy to use, takes out some complexity in 
//writing code
void toggleLED(unsigned int led, char port)
{  
	if(port=='a' || port=='A')
		PORTA = PORTA^led;
	if(port=='b' || port=='B')
		PORTB = PORTB^led;
	if(port=='c' || port=='C')
		PORTC = PORTC^led;     
}

void init(void)
{
	//set up the ports
	DDRD=0x00;	// PORT D is an input
	DDRB=0x00;    // PORT B is an input - buttons   
	PORTB=0xff;   // PORT B off
	DDRC=0xff;    // PORT C is output - LEDs
	PORTC=0xff;  //port c is off
	DDRA=0xff;
  
	 //USART
	UCSRB = 0x18;
	UBRRL = 103;
  
	 //set up timer 0  
	TIMSK=2;	
	OCR0 = 250;  	//set the compare re to 250 time ticks
 	TCCR0=0b00001011; 
       
	wait_for_input = 0;     
	status = PLAY_STATUS;  
	repeat_flag=0;
	mode = NORMAL;
	scroll = NONE;

	pit_count=0;  
	counter = 0;
	song_pos_timer=0; 
  
	start_counter = 0;
	time_out_counter = 0;
	debounced=0;        //button is debounced
	adjust_done_flag=0; //done making changes to freq   
  
	debounce_state = NO_PUSH;
	button_state = CHOICE;     
  
	lcd_init(LCDwidth);
	lcd_gotoxy(0,0);
	lcd_putsf("WinAmp Cont v1.0");
	lcd_gotoxy(0,1);
	lcd_putsf(" no connection");
	//crank up the ISRs
	#asm
		sei
	#endasm
}