The iPod controller that I submitted for the mbed challenge
Dependencies: mbed Motordriver PID
Diff: ipod.cpp
- Revision:
- 0:371773dd3dd1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipod.cpp Wed May 04 15:41:13 2011 +0000 @@ -0,0 +1,380 @@ +#include "mbed.h" +#include "ipod.h" + +ipod::ipod(PinName tx, PinName rx): led(*new DigitalOut(DEBUGPIN)) { + com = new Serial(tx, rx); + com->baud(19200);//should work upto 57600 + rx_ready = false; + tx_ready = true; + state = 0; + tx_buffer[0] = 0xff; + tx_buffer[1] = 0x55; + com->attach(this, &ipod::handlerx); +#ifdef TX_IRQ + com->attach(this, &ipod::handletx, Serial::TxIrq); +#endif + lastcommand = -1; + error = err_success; + replies = 0; +} + +void ipod::handlerx() {//Serial port interrupt handler + led = 1; + unsigned short c = com->getc(); +// printf("%d: %02X; ", state, chk); + switch (state) { + case 0: + if (c==0xff) state++; + break; + case 1: + if (c==0x55) state++; + else state = 0; + break; + case 2: + if (rx_ready) { + state = 0; + break;//buffer not free, ignore the message + } + length = c; + chk = c; + rx_buffer = new char[length]; + state++; + break; + case 3: + if (c > 4) { + state = 0; + break;//mode not known or not supported + } + mode = c; + chk += c; + state++; + break; + case 4: + command = c<<8; + chk += c; + state++; + break; + case 5: + command += c; + chk += c; + state++; + break; + default: + chk += c; + if (state < length+3) { + rx_buffer[state-6] = c; + state++; + } else { + if (chk==0) { + rx_ready = true; + replies--; + if (command-1 == lastcommand) {//it is a reply to lastcommand + //printf("reply to %02X\n", lastcommand); + lastcommand = -1; + } + //printf("ready %02X\n", command); + } else + printf("checksum error %02x state=%d\n", chk, state); + state = 0; + break; + } + } + led = 0; +} + +void ipod::handletx() { + if (tx_index < tx_size) + com->putc(tx_buffer[tx_index++]); + else + tx_ready = true; +} + +bool ipod::waitForReply() {//busy waits for a reply to the last issued command, all other replies are ignored + for (int i = 0; i < 10000000; i++) { + if (ready()) { + if (lastcommand == -1) {//indicates that a reply to the last issued command has been received + parse();//parse the last reply + return true;//return to caller for further processing + } + release();//release the buffer and hence ignore the message + } + } + printf("timeout: last received %02X(%d, %d, %d, \"%s\")\n", command, arg1, arg2, arg3, string); + error = err_timeout; + return false; +} + +#define BIGENDIAN +void ipod::copy(char *b, union conv p) {//copy an UINT32 argument bytewise to the buffer +#ifdef BIGENDIAN + for (int i = 0; i < 4; i++) + *(b+3-i) = p.asBytes[i]; +#else + for (int i = 0; i < 4; i++) + *(b+i) = p.asBytes[i]; +#endif +} + +void ipod::SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//send an advanced ipod remote command, unused arguments are optional + union conv par1, par2, par3; + par1.asInt = arg1; + par2.asInt = arg2; + par3.asInt = arg3; + tx_buffer[3] = 4; //AiR mode + tx_buffer[4] = 0; + tx_buffer[5] = cmd & 0xff; + unsigned expect = 1; //typically expect 1 reply per request + switch (cmd) { + case 0x12: //get ipod type + tx_buffer[2] = 3; + //expect 2 bytes return + break; + case 0x14: //get iPod name + tx_buffer[2] = 3; + //expect string return + break; + case 0x16: //main lib + tx_buffer[2] = 3; + expect = 0; + break; + case 0x17: //goto item + tx_buffer[6] = arg1; + tx_buffer[2] = 8; + copy(tx_buffer+7, par2); + expect = 0; + break; + case 0x18: //get count + tx_buffer[6] = arg1; + tx_buffer[2] = 4; + //expect integer return + break; + case 0x1A: //get names + tx_buffer[6] = arg1; + tx_buffer[2] = 12; + copy(tx_buffer+7, par2); + copy(tx_buffer+11, par3); + //expect many offset,string pairs + expect = arg3; + break; + case 0x1C: //get time/stat + tx_buffer[2] = 3; + //expect int,int,byte + break; + case 0x1E: //get position + tx_buffer[2] = 3; + //expect int + break; + case 0x20: //get title + tx_buffer[2] = 7; + copy(tx_buffer+6, par1); + //expect string + break; + case 0x22: //get artist + tx_buffer[2] = 7; + copy(tx_buffer+6, par1); + //expect string + break; + case 0x24: //get album + tx_buffer[2] = 7; + copy(tx_buffer+6, par1); + //expect string + break; + case 0x26: //poll mode + tx_buffer[6] = arg1; + tx_buffer[2] = 4; + //expect many byte,int pairs + expect = 0; //the number to expect is unknown here, so either stop polling or just insert command anyway + break; + case 0x28: //run PL + tx_buffer[2] = 7; + copy(tx_buffer+6, par1); + expect = 0; + break; + case 0x29: //command + tx_buffer[6] = arg1; + tx_buffer[2] = 4; + expect = 0; + break; + case 0x2C: //get shuffle + tx_buffer[2] = 3; + //expect byte + break; + case 0x2E: //set shuffle + tx_buffer[6] = arg1; + tx_buffer[2] = 4; + expect = 0; + break; + case 0x2F: //get repeat + tx_buffer[2] = 3; + //expect byte + break; + case 0x31: //set repeat + tx_buffer[6] = arg1; + tx_buffer[2] = 4; + expect = 0; + break; + case 0x35: //get nr in PL + tx_buffer[2] = 3; + //expect int + break; + case 0x37: //goto song + tx_buffer[2] = 7; + copy(tx_buffer+6, par1); + expect = 0; + break; + case 0x38: //select + tx_buffer[6] = arg1; + tx_buffer[2] = 9; + copy(tx_buffer+7, par2); + tx_buffer[11] = 0;//unknown + break; + default: + return; + } + tx_size = tx_buffer[2] + 4; + tx_buffer[tx_size-1] = 0; + for (int i = 2; i < tx_size-1; i++) + tx_buffer[tx_size-1] -= tx_buffer[i];//compute checksum + tx_index = 1; + replies = expect; +#ifdef TX_IRQ + while (!tx_ready) /* wait */; + tx_ready = false; + com->putc(tx_buffer[0]);//kick-off writing buffer +#else + write(); +#endif + lastcommand = cmd; + printf("%02X (%d, %d, %d)\n", cmd, arg1, arg2, arg3); +} + +void ipod::guarded_SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//same as SendAirCmd but waits until all previous expected replies have been received + for (int i = 0; i<1000000; i++) + if (replies==0) { + SendAirCmd(cmd, arg1, arg2, arg3); + return; + } + printf("Timeout while waiting for reply to %02X, command %02X cannot be send\n", lastcommand, cmd); +} + +void ipod::SetMode(int m) { //char buf[] = {0xff, 0x55, 0x03, 0x00, 0x01, 0x00, 0x00}; + tx_buffer[2] = 3; //length + tx_buffer[3] = 0; //mode 0, mode switching + tx_buffer[4] = 1; //cmd high byte + tx_buffer[5] = m; //cmd low byte + tx_buffer[6] = 0x100 - 3 - 1 - m;//checksum + tx_index = 1; + tx_size = 7; +#ifdef TX_IRQ + while (!tx_ready) /* wait */; + tx_ready = false; + com->putc(tx_buffer[0]); +#else + write(); +#endif +} + +unsigned ipod::copy(char* s) {//return a bytewise argument from the buffer as a UINT32 + union conv p; +#ifdef BIGENDIAN + for (int i = 0; i < 4; i++) + p.asBytes[3-i] = s[i]; +#else + for (int i = 0; i < 4; i++) + p.asBytes[i] = s[i]; +#endif + return p.asInt; +} + +void ipod::parse() { + error = err_success; + switch (mode) { + case 0: //mode 0 reply + if (command & 0xFF == 4) {//reply to mode status request + printf("mode 0 reply = %04X\n", command); + } else + printf( "unexpected mode 0 reply\n"); + break; //assume s.c_str()[4] == 0 + case 4://reply to AiR command + switch (command) { + case 0: + if (rx_buffer[0]==0x04) { + error = err_unknown; + printf("command %04X is not understood\n", *(unsigned short*)&rx_buffer[1]); + } + break; + case 1: + error = (errors)rx_buffer[0]; + switch (error) { + case err_success: + //printf("command %04X succeeded\n",*(unsigned short*)&rx_buffer[1]); + break; + case err_failure: + printf("command %04X failed\n",*(unsigned short*)&rx_buffer[1]); + break; + case err_limit: + printf("command %04X had wrong parameter(s)\n",*(unsigned short*)&rx_buffer[1]); + break; + case err_answer: + printf("command %04X is an answer\n",*(unsigned short*)&rx_buffer[1]); + break; + default: + printf("unknown error\n"); + break; + } + break; + //2 bytes + case get_ipod_size+1: //ipod type + break; + //single string + case get_ipod_name+1: + case get_title+1: + case get_artist+1: + case get_album+1: + string = rx_buffer; + printf("%04X: %s\n", command, string); + break; + //number+string + case get_names+1: + string = rx_buffer + 4; + arg1 = copy(rx_buffer); + printf("%04X: %d %s\n", command, arg1, string); + break; + //number+number+byte + case get_time_status+1: + arg1 = copy(rx_buffer); + arg2 = copy(rx_buffer+4); + arg3 = rx_buffer[8]; + printf("%04X: %d %d %02X\n", command, arg1, arg2, arg3); + break; + //number + case get_count+1: + case get_position+1: + case get_nr_in_playlist+1: + arg1 = copy(rx_buffer); + printf("%04X: %d\n", command, arg1); + break; + //byte + number + case polling+1: + arg1 = rx_buffer[0]; + arg2 = copy(rx_buffer+1); + break; + //byte + case get_shuffle+1: + case get_repeat+1: + arg1 = rx_buffer[0]; + printf("%04X: %02X\n", command, arg1); + break; + //10 bytes + case select+1: //select + break; + default: + printf("unsupported reply"); + break; + } + break; + default: + printf("unsupported mode\n"); + break; + } +} \ No newline at end of file