/* ECE 476 Final Project * Austin S. Lu & Albert Ren * asl45 awr8 */
#include <mega32.h> #include <stdio.h> #include <stdlib.h> for ltoa #include <delay.h> #asm .equ __lcd_port=0x15 #endasm #include <lcd.h> Bruce's definitions #define begin { #define end }
Define the states for the state machine #define Command 0 #define DecodeCommand 1 #define Send 2 #define button_release 3 #define hold 4 #define Receive 5 int state = 0; Define the commands we support MODE 2 iPod Remote Functions #define play 0x01 #define volup 0x02 #define voldown 0x04 #define skipfwd 0x08 #define skipback 0x10 #define menu 0x40 #define select 0x80 MODE 4 Advanced Remote #define type 0x12 #define name 0x14 #define current 0x1e #define title 0x20 #define artist 0x22 #define album 0x24
MODE 4 Send Commands #define Switch 0 #define GetTrack 1 #define UseTrack 2 #define SwitchtoMode2 3 #define SendPlay 4 Information on the Apple Accessory Protocol can be found at http://ipodlinux.org/Apple_Accessory_Protocol Here are some variables that we'll need char mstime = 0; char header1 = 0xff; char header2 = 0x55; char length; char mode=0x02; char command[2]; int checksum; int index = 0; int debouncecounter = 0; char lastpress = 0; char press = 0; int timeout = 0; unsigned long currentsong; int param_length = 0;
Here are the flags we're using char validcommand = 0; char commandtype = 0; char stopflag = 0; char releaseflag = 0; char transmitdone_flag = 0; char mode4flag = 0; unsigned char switchmode[16] = {0xff, 0x55, 0x03, 0x00, 0x01, 0x04, 0xF8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; unsigned char switchback[16] = {0xff, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; unsigned char currenttrackcommand[16] = {0xff, 0x55, 0x03, 0x04, 0x00, 0x1E, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int tempchecksum; unsigned char t_buffer_temp[16]; For LCD displays unsigned char lcdbuffer[50]; unsigned int lcdbufferlength = 16; unsigned int lcdstart = 0; int lcdtime = 0;
char hold_timer = 0; int timeoutcounter = 0; int failurecount = 0; int mode4sequence = 0;
variables we use for transmitting unsigned char t_index; current string index unsigned char t_buffer[16]; output string unsigned char t_ready; flag for transmit done unsigned char t_char; current character RXC ISR variables unsigned char r_index=0; current string index unsigned char r_buffer[128]; input string unsigned char r_ready; flag for receive done unsigned char r_char; current character unsigned char r_checksum; received checksum unsigned char prev_char = 0; unsigned char start_checksum = 0; unsigned char r_length = 0xF5; Functions we use void initialize(void); void puts_int(void); void buttons (void); void gets_int(void); void lcd_scroll(void);
timer 0 overflow ISR interrupt [TIM0_COMP] void timer0_overflow(void) begin mstime++; lcdtime ++; end UART xmit-empty ISR TRANSMITS LSB FIRST (Little Endian is stupid!) http://en.wikipedia.org/wiki/Serial_port interrupt [USART_DRE] void uart_send(void) {
t_index++; t_char = t_buffer[t_index]; if(t_char == (char)checksum){//at this point, we know what the end is UDR = t_char; stopflag = 1; stopflag = 0; UCSRB.5=0; //kill isr t_ready=1; //transmit done checksum = 0; } else {UDR = t_char;} //send the char
}
UART character-ready ISR interrupt [USART_RXC] void uart_rec(void) begin r_char=UDR; get a char
//build the input string //iPod takes the form of 0xFF 0x55 Data Checksum //We are assuming idle high, so we should check for 0x55 instead if((r_char == (char)(r_checksum & 0xff)) && ((r_index-3)==r_length)) { r_ready=1; //signal cmd processor UCSRB.7=0; //stop rec ISR start_checksum = 0; }
if(start_checksum == 1) { r_checksum = r_checksum - r_char; }
if(r_char == 0x55 && prev_char == 0xff) { start_checksum = 1; r_checksum = 0x100; }
r_buffer[r_index]=r_char; if(r_index == 2) //Make the length so! r_length = r_buffer[r_index];
r_index++; prev_char = r_char;
end
void main(void) begin
int k, temp, ReturnToSender; unsigned char songnumber;
State machine updates every ms for now initialize(); while(1) begin if(lcdtime >= 300) { lcdtime = 0; lcd_scroll(); } if(mstime) begin buttons(); mstime = 0; switch (state) begin case(Command): check if there's a command. If so, we will decode it
mode4sequence = Switch; timeoutcounter = 0; failurecount = 0; if(validcommand == 1) begin validcommand = 0; state = DecodeCommand; end if((releaseflag == 1) && (validcommand == 0) && (mode == 0x02)) //the button was released begin releaseflag = 0; state = button_release; end break;
case(DecodeCommand): //We must decode the command! If statements galore! t_buffer[0] = header1; //0xff t_buffer[1] = header2; //0x55 if((mode == 0x02) || (commandtype == current)) { length = 0x03; } else begin length = 7; //Otherwise length is 7 bytes, cause it's a mode 4 command end t_buffer[2] = length; t_buffer[3] = mode; if(mode == 0x04) { mode4flag = 1; } t_buffer[4] = 0x00; //Beginning of command if(commandtype == play) t_buffer[5] = play; if(commandtype == volup) t_buffer[5] = volup; if(commandtype == voldown) t_buffer[5] = voldown; if(commandtype == skipfwd) t_buffer[5] = skipfwd; if(commandtype == skipback) t_buffer[5] = skipback; if(commandtype == album) t_buffer[5] = album; if(commandtype == title) t_buffer[5] = title; if (commandtype == artist) t_buffer[5] = artist;
if((mode == 0x04) && (commandtype != current)) //We need to send ipod the current song begin t_buffer[6] = (currentsong >> 24) & 0xff ; //& with 0xFF cause we're cautious people! t_buffer[7] = (currentsong >> 16) & 0xff; t_buffer[8] = (currentsong >> 8) & 0xff; t_buffer[9] = currentsong & 0xff; param_length = 4; //Mode 4 and Mode 2 have different lengths end for (index = 2; index < 6 + param_length; index ++) { //for checksum checksum = checksum + t_buffer[index];
}
checksum = (0x100 - checksum & 0xff); t_buffer[6+param_length] = (char) checksum & 0xff; //as a char */ param_length = 0; //We always reset to Mode 2 commands state = Send; break;
case(Send): stopflag = 0; timeoutcounter = 0;
if (mode == 0x02) begin puts_int(); commandtype = 0; state = Command; end
//Otherwise, the command is going to depend on //switching the iPod into Mode 4 zone //So, first we switch else begin //Switch to Mode 4 if((mode4sequence == Switch)) begin for(k=0; k<16; k++) begin //t_buffer_temp holds original mode4 command t_buffer_temp[k] = t_buffer[k]; t_buffer[k] = switchmode[k]; end tempchecksum = checksum; checksum = 0xF8; //Mode switching checksum //Now send the sucker stopflag = 0; puts_int(); state = Receive; //Now we receive a response end
//Now we gotta get the current song if(mode4sequence == GetTrack) begin for(k = 0; k<16; k++) begin //Put in the current command! t_buffer[k] = currenttrackcommand[k]; end checksum = 0xDB; //Current track checksum stopflag = 0; puts_int(); state = Receive; //Now we receive a response end
//Now we send the command we wanted to send! if(mode4sequence == UseTrack) begin for (k=0; k<16; k++) begin t_buffer[k] = t_buffer_temp[k]; //Bring back the real command end //Placing the current song into the command //& with 0xFF cause we're SCARED! t_buffer[6] = (currentsong >> 24) & 0xff; t_buffer[7] = (currentsong >> 16) & 0xff; t_buffer[8] = (currentsong >> 8) & 0xff; t_buffer[9] = currentsong & 0xff;
//recreating the checksum for (index = 2; index < 10; index ++) begin checksum = checksum + t_buffer[index]; end checksum = (0x100-checksum)&0xFF; t_buffer[10] = (char)checksum; //Now we transmit the command we wanted! stopflag = 0; puts_int(); state = Receive; end //Switch back to mode 2 if(mode4sequence == SwitchtoMode2) begin for(k=0; k<16; k++) begin t_buffer[k] = switchback[k]; end checksum = 0xFA; //This is the checksum stopflag = 0; puts_int(); state = Receive; end end break;
case(Receive): //Before we can receive, we need to wait for transmission to complete while(!t_ready){};
if(mode4sequence == SwitchtoMode2) begin //Success! failurecount = 0; mode4sequence = Switch; //Originally Switch state = Command; end
if(mode4sequence == Switch) begin if(timeoutcounter >= 1000) { mode4sequence = GetTrack; state = Send; timeoutcounter = 0; } end
timeoutcounter ++; //Base case: timeoutcounter = 1000 or we failed 5x if(timeoutcounter >= 2000 || failurecount >= 5) begin failurecount = 0; timeoutcounter = 0; if(mode4sequence != SwitchtoMode2) begin //Try to bring it back to initial conditions mode4sequence = SwitchtoMode2; state = Send; end else begin //Looks like all hope is lost. mode4sequence = Switch; state = Command;
end end
//Otherwise, we're still waiting to receive stuff if(r_ready) begin //r_index holds the last thing //What did we receive? Depends on the sequence if(mode4sequence == UseTrack) begin //Check for success if((r_buffer[4] == 0x00) && ((r_buffer[5] != 0) || (r_buffer[5] != 1))) begin
failurecount = 0; //the string is null terminated and starts at r_buffer[6] lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Num "); ltoa(currentsong+1,&songnumber); lcd_puts(&songnumber); lcd_putsf(" - "); //Declare artist, album, or title if(r_buffer[5] == 0x21) lcd_putsf("Title "); if(r_buffer[5] == 0x23) lcd_putsf("Artist"); if(r_buffer[5] ==0x25) lcd_putsf("Album ");
lcdbufferlength = r_index - 8; lcdstart = 0; for(k=6; k< 6+lcdbufferlength; k++) { lcdbuffer[k-6] = r_buffer[k]; } lcdbufferlength = lcdbufferlength+1; lcdbuffer[lcdbufferlength-1] = ' '; lcdbuffer[lcdbufferlength] = ' '; lcdbufferlength = lcdbufferlength + 1; state = Send; mode4sequence = SwitchtoMode2; end else begin //Failure! failurecount ++; state = Send; end end if(mode4sequence == GetTrack) begin if((r_buffer[3] == 0x04) && (r_buffer[4]==0x00) && (r_buffer[5] == 0x1f)) begin failurecount = 0; currentsong = r_buffer[6]; currentsong = (currentsong << 8) | r_buffer[7]; currentsong = (currentsong << 8) | r_buffer[8]; currentsong = (currentsong << 8) | r_buffer[9]; state = Send; mode4sequence = UseTrack;
end else //FAILURE! begin failurecount++; state = Send; end end gets_int(); //Start listening for the Serial TX again! end break;
case(button_release): //Send a button release;
t_buffer[0] = header1; //0xff t_buffer[1] = header2; //0x55 t_buffer[2] = length; //0x03 at the moment (not always true) t_buffer[3] = 0x02; //This is the mode we support right now. TWO t_buffer[4] = 0x00; //Beginning of command t_buffer[5] = 0x00; //button release command checksum = 0xFB; t_buffer[6] = 0xFB; //as a char, should be 0xfb here
state= Send; break; end end end
end
void buttons(void) {
press = ~PINA;
if((press != lastpress) && (lastpress == 0)) { // Start debouncing debouncecounter++; lastpress = press; } if((press == lastpress) && (lastpress != 0)) //Held button debouncecounter ++; if ((press != lastpress) && (lastpress != 0)) { //Released button releaseflag = 1; validcommand = 0; debouncecounter = 0; lastpress = 0; press = 0; } if((debouncecounter >= 40) && (lastpress != 0)) { //40ms //we have a solid press validcommand = 1; debouncecounter = -460; //To account for a .5 second holding time, Vol+ switch(press) { case(0x01): //Play commandtype = play; mode = 0x02; break; case(0x02): commandtype = volup; mode = 0x02; break; case(0x04): commandtype = voldown; mode = 0x02; break; case(0x08): commandtype = skipfwd; mode = 0x02; break; case(0x10): commandtype = skipback; mode = 0x02; break; case(0x20): commandtype = album; mode = 0x04; break; case(0x40): commandtype = title; mode = 0x04; break; case(0x80): commandtype = artist; mode = 0x04; break; default: commandtype = 0; break; } }
}
Set it all up void initialize(void) begin DDRA = 0x00; PORT A is an input - button switches for now
DDRB = 0xff; //PORTB LED output
PORTB = 0xff; //LEDs off for our sanity
//serial setup for debugging using printf, etc. UCSRB = 0x18 ; UBRRL = 51 ; //set the oscillator, we need 19200 baud for iPod comm.
//set up timer 0 OCR0=249; //1 mSec TIMSK=2; //turn on timer 0 cmp-match ISR TCCR0=0b00001011; //prescalar to 64 and Clr-on-match
lcd_init(16); //initialize the display lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("start");
//crank up the ISRs #asm sei #endasm gets_int(); //Start listening on the serial port
end
void puts_int(void) {
t_ready=0; t_index=0; stopflag =0; if (t_buffer[0]>0) begin putchar(t_buffer[0]); UCSRB.5=1; end
}
void gets_int(void) {
r_ready = 0; r_index = 0; r_length = 0xF5; UCSRB.7 = 1;
}
void lcd_scroll(void){
unsigned char displaybuffer[16]; int lcd_index; lcd_gotoxy(0,1); lcd_putsf(" "); //We clear it for scrolling lcd_gotoxy(0,1); lcd_index=0; while(lcd_index<15) { displaybuffer[lcd_index] = lcdbuffer[((lcdstart + lcd_index)%lcdbufferlength)]; if(lcd_index>=lcdbufferlength) //Prevents repeating of small strings { displaybuffer[lcd_index] = ' '; } lcd_index++; } displaybuffer[15] = 0x00; lcd_puts(&displaybuffer[0]); if(lcdbufferlength>16) //Prevents scrolling of small strings lcdstart = (lcdstart+1);
}