//cheap mmc mp3 2006 #include <16f88.H> #use delay(clock=8000000) #use fast_io(a) #use fast_io(b) #use rs232(baud=9600, xmit=PIN_a7,rcv=PIN_b5) #byte port_a = 0x05 //port a #byte port_b = 0x06 //b #byte tris_a = 0x85 //port a #byte tris_b = 0x86 //b #byte option_reg = 0x81 #byte ADRESL = 0x9E #byte ADRESH = 0x1E #byte ADCON0 = 0x1F #bit GODONE = ADCON0.2 #byte ADCON1 = 0x9F #byte ANSEL = 0x9B #byte OSCCON = 0x8F #byte CVRCON = 0x9D #byte CMCON = 0x9C #byte SSPCON = 0x14 #byte SSPSTAT = 0x94 #byte SSPBUF = 0x13 #byte intcon = 0X0B #bit SSPEN = SSPCON.5 #bit SS = port_a.0 #bit XCS = port_a.1 #bit XDCS =port_a.3 #bit XCLK = port_b.4 #bit XDI = port_b.1 #bit XDREQ = port_a.4 #bit MCLR = port_a.5 #bit XRESET = port_a.2 #bit XDO = port_a.6 #bit MOSI =port_b.2 #bit XDI_TRIS =tris_b.1 #bit TRACKUP = port_b.6 #bit TRACKDOWN = port_b.7 #bit VOLUMEUP = port_b.5 #bit VOLUMEDOWN = port_b.3 #bit PAUSE = port_b.0 ///////////////////prototypes void find_bpb(void); void mmc_skip(char count); void mmc_read(void); char getsongstart(int32 filenumber); char song(int32 startsector,int32 endsector); void printout_rootdir(void); void find_highest_song_number(void); int32 get_next_shuffle(void); char rand(void); //super fat functions:::: int32 readfat(int32 fatoffset); int mmc_init(); int mmc_response(unsigned char response); int mmc_read_block_to_serial(int32 block_number); int mmc_get_status(); char mmc_open_block(int32 block_number); char mmc_get_byte(void); void mmc_close_block(void); void mmc_cancel_block(void); int mmc_read_block_to_vs1011(int32 block_number); #inline void init(void); void resetvs1011_hard(void); void resetvs1011_soft(void); void morezeroes(void); int16 vs_command(char inout,address,a,b); char vs_spi_write(char aa); void play_range(int32 start, int32 end); ///////////////////////end prototypes /////////////////////globals char volume; char data_lo, data_hi; char BPB_SecPerClus; int16 bpbstart; int16 RootDirSectors; int16 FirstDataSector; int16 BPB_ResvdSecCnt; int16 BPB_BytsPerSec; int32 FirstRootDirSecNum; //;, songstarter, songlength, currentsong; int32 songstarter; int32 songlength; int32 currentsong; int32 fatstart, datsec; int32 ccl, pcl, root_ccl; int32 root_p_cl; int32 highestsong; int32 BPB_RootEntCnt; int32 BPB_FATSz16; int32 BPB_FATSz32; int32 BPB_RootClus; char BPB_NumFATs, fatmode; char offset; char column_pointer[16]; //char pointerh, pointerl, globalpointer; char random_byte;//global variable for the random number generator (keeps the last value around for seeding purposes?) ////////////////////end globals //BYTE CONST RNDL [256]= {208,75,137,110,218,247,165,23,248,216,204,59,118,35,193,29,83,158,93,57,224,194,3,207,205,149,203,22,129,171,10, #rom 0x2100={208,75,137,110,218,247,165,23,248,216,204,59,118,35,193,29,83,158,93,57,224,194,3,207,205,149,203,22,129,171,10, 142,251,99,249,244,144,16,17,238,37,197,111,151,4,192,104,7,95,150,67,101,69,15,132,186,87,156,206,27,166,20,182, 163,92,198,119,213,180,61,152,39,185,19,58,45,33,64,24,160,40,222,172,131,227,221,105,108,177,183,73,43,157,12,199, 113,9,245,126,130,106,120,210,215,31,167,246,76,139,195,239,50,229,169,55,122,212,30,112,6,85,141,82,236,21,32,89, 219,173,188,127,74,214,187,190,176,62,123,189,121,184,148,225,34,1,196,28,49,46,79,77,54,117,240,175,242,140,231, 13,128,25,97,146,228,179,116,70,53,178,18,254,78,109,155,241,233,81,255,159,202,102,2,174,14,147,234,60,8,84,52,98, 100,230,145,211,125,11,181,164,26,90,134,161,36,71,223,143,232,136,63,68,51,96,243,66,65,235,47,154,237,252,72,209, 114,138,115,86,250,5,94,133,42,44,88,226,103,253,124,168,91,48,217,56,80,135,220,107,191,201,162,153,38,170,41,200,0} //////////////////////////////////////////////////////////////////////////////////////////////// #int_timer0 void volume_handler(void){ if (!VOLUMEUP){ // volume = spicommand(0x03,0x0b,0x00,0x00); if (volume<255){ volume++; } // spicommand(0x02,0x0b,volume,volume); } if (!VOLUMEDOWN){ // volume = spicommand(0x03,0x0b,0x00,0x00); if (volume>0){ volume--; } // spicommand(0x02,0x0b,volume,volume); } } void main(void){ char flag, result, n, shufflemode; init(); enable_interrupts(GLOBAL); enable_interrupts(INT_TIMER0); xcs=1; xdcs=1; // while(1){ volume=0x23; // delay_ms(250); // printf("IKF");//putc(13); putc(10); result=mmc_init(); // if (result==1){printf("nogo"); while(1){;}} if (result==1){while(1){;}} // BPB_BytsPerSec=0; find_bpb(); resetvs1011_hard(); // if(BPB_BytsPerSec!=0x200){ // printf("POP"); putc(13);putc(10); // find_bpb(); // } // printf("card parameters found"); putc(13);putc(10); currentsong=0x01; find_highest_song_number(); root_ccl= BPB_RootClus; flag='s'; result=0x00; // shufflemode=0; if (!TRACKUP){ for(n=0;n<16;n++){ column_pointer[n]=0; } while(!TRACKUP){random_byte++; offset++;} while(1){ if ((TRACKUP)&&(TRACKDOWN)){ currentsong=get_next_shuffle(); result=getsongstart(currentsong); if(result==0x01){ // printf("playing: %lX ",currentsong); putc(13);putc(10); putc(13);putc(10); flag=song( songstarter,(songstarter+(songlength/512)) ); } } } } while(1){ ////////////////////////////////////////////////////////auto track increment. press and hold... //single increment/decrement of track is within the "song" function if (!TRACKUP){ for(n=120;n>0;n--){ delay_ms(3); if (TRACKUP){ break; } } while (!TRACKUP){ delay_ms(1); n++; if (n==15){ n=0; currentsong++; if (currentsong>highestsong){currentsong=0x01;}//putc('s');} //putc('f'); } } } if (!TRACKDOWN){ for(n=120;n>0;n--){ delay_ms(3); if (TRACKDOWN){ break; } } while (!TRACKDOWN){ delay_ms(1); n++; if (n==15){ n=0; currentsong--; if (currentsong==0x00){currentsong=highestsong;}// putc('s');} //putc('f'); } } } //////////////////////////end AUTO TRACK INCREMENT if ((TRACKUP)&&(TRACKDOWN)){ n=0; result=getsongstart(currentsong); if(result==0x02){ currentsong=0x01; flag='s'; // printf("got to end of card, going back to track one"); putc(13);putc(10); } else if(result==0x01){ // printf("playing: %lX ",currentsong); putc(13);putc(10); flag=song( songstarter,(songstarter+(songlength/512)) ); } if ((flag=='n')||(flag=='s')){ currentsong++; // printf("auto advanced"); putc(13);putc(10); } else if (flag=='p'){ if (currentsong>1){ currentsong--; // printf("previous track attempt"); putc(13);putc(10); } else{currentsong=highestsong;}//loop around to the last track on the card } } } } char getsongstart(int32 filenumber){ char Emm,Pee,Three; int32 Largeccl, ccltemp, eocmark; int16 x, y, z; // printf("getting song paramaters for: %lX ",filenumber); eocmark=0; // if (fatmode==16){ // mmc_open_block((Firstrootdirsecnum+(filenumber/16))+bpbstart); // }else{// if 32 mode: root_ccl= BPB_RootClus; y=filenumber/16; z=y; y=y/BPB_SecPerClus; x=y*BPB_SecPerClus; x=z-x; while(y>0){ root_p_cl=root_ccl; root_ccl=readfat(root_p_cl); // printf("current root cluster:%4LX ",root_ccl); eocmark=root_ccl & 0x0FFFFFFF; y--; } if (eocmark>0x0fffffef){ return (0x02); } ccltemp=root_ccl-2; ccltemp=ccltemp * (int32)BPB_SecPerClus; ccltemp=ccltemp + (int32)datsec; ccltemp=ccltemp + (int32)x; mmc_open_block(ccltemp); // } mmc_skip((filenumber & 0x000F)*16); mmc_read(); // printf("firstCharOf File Name is:%X ",data_lo); if(data_lo==0x00){ mmc_cancel_block(); return(0x00);//was 0x02 } if(data_lo != 0xe5){ // putc(254);putc(1); // putc(data_lo);putc(data_hi); // mmc_skip(6); // mmc_read(); // putc(data_lo);putc(data_hi); // mmc_read(); // putc(data_lo);putc(data_hi); // mmc_read(); // putc(data_lo);putc(data_hi); // putc('.'); mmc_skip(3); mmc_read(); // putc(data_lo);putc(data_hi); Emm=data_lo; Pee=data_hi; mmc_read(); // putc(data_lo);putc(data_hi); putc(13);putc(10); Three=data_lo; if (((Emm=='m') || (Emm=='M')|| (Emm=='W')|| (Emm=='w')) && ((Pee=='P') || (Pee=='p')|| (Pee=='A')|| (Pee=='a')) && (Three=='3')||(Three=='V')||(Three=='v')){ mmc_skip(4); mmc_read(); if (fatmode==32){ Largeccl= ((int16)data_hi<<8)+data_lo;// clusterf high bytes. they're in a funny place to maintain compatibility with fat16 Largeccl=Largeccl<<16; } mmc_skip(2); mmc_read(); ccl= ((int32)data_hi<<8)+data_lo; if (fatmode==32){ ccl = ccl+ Largeccl; //add on the high bytes of the cluster address } songstarter=( ((((int32)data_lo+((int32)data_hi<<8))-0) *BPB_SecPerClus) +bpbstart+Firstdatasector); mmc_read(); *(((char*)&songlength)+0)=data_lo; *(((char*)&songlength)+1)=data_hi; //songlength=(int16)data_lo + ((int16)data_hi<<8); mmc_read(); *(((char*)&songlength)+2)=data_lo; *(((char*)&songlength)+3)=data_hi; //songlength=songlength + (((int16)data_lo+((int16)data_hi<<8)) <<16); mmc_cancel_block(); return(0x01); } } mmc_cancel_block(); return(0x00); } char song(int32 startsector,int32 endsector){ int32 ccltemp; short int hardflag; char insanity;//, sectprint; char shutdowncounter, x ; x=0; insanity=0; // sectprint=1; resetvs1011_soft(); startsector = startsector/BPB_SecPerClus; endsector = endsector/BPB_SecPerClus; for(;startsector<=endsector;startsector++){ for (x=0;x0){return('p');} } } pcl=ccl; ccl=readfat(pcl); vs_command(0x02,0x0b,volume,volume); if(!PAUSE){ delay_ms(20); while (!PAUSE){delay_ms(20);} // delay_ms(20); while (PAUSE){delay_ms(20);} // delay_ms(20); while (!PAUSE){;} delay_ms(20); } } return('s'); } //////////////////////////////////////////////////////////////////////////////////////////////// void play_range(int32 start, int32 end){ for(;starthighestsong){ b = rand(); b = b/16; // printf("column number: %X ",b); //putc(13);putc(10); // printf("is up to: %X ",column_pointer[b]); // putc(13);putc(10); if (column_pointer[b]<255){ c = column_pointer[b]++; // printf(" c is: %X ",c); c=c+offset; work=read_eeprom(c); // work=RNDL[c]; // printf("work is: %lX ",work); a = (int16) b * 256; work=work+a; // printf("for a final number: %lX ",work); } } // printf("random hopeful!: %lX ",work); return (work); } char rand(void) { char sum; sum = 0; // This calculates parity on the selected bits (mask = 0xb4). if(random_byte & 0x80){ sum = 1;} if(random_byte & 0x20){ sum ^= 1;} if(random_byte & 0x10){ sum ^= 1;} if(random_byte & 0x04){ sum ^= 1;} random_byte <<= 1; random_byte |= sum; return(random_byte); } //super fat ::::: //----------------------------------- //////////////////////////// int32 readfat(int32 fatoffset){ int16 temp; int32 tempb; int8 los; temp=0; // if (fatmode==32){ fatoffset=fatoffset*2; // } los = *(((char*)&fatoffset)+0); //the bottom byte of the address goes directly to a word in the FAT fatoffset=fatoffset / 256; fatoffset+=fatstart; mmc_open_block(fatoffset); mmc_skip(los); mmc_read(); temp = ((int16) data_hi * 256)+ (int16) data_lo; // if (fatmode==16){ // return temp;//only two bytes per entry // } mmc_read();//for fat32, four bytes per entry tempb=0; tempb = ((int16) data_hi * 256)+ (int16) data_lo; tempb=tempb<<16; tempb=tempb+(int32) temp; mmc_cancel_block(); // printf(" cluster getter returns:::%4LX ",tempb); return tempb; } //----------------------------------- //////////////////////////////////////////////////////////////////////////////////////////////// void resetvs1011_hard(void){ SS=1; xcs=1; xdcs=1; delay_ms(500); do{ xreset=0;//hard reset delay_cycles(100); xreset=1; delay_cycles(500); vs_command(0x02,0x00,0x00,0x04);//sets new mode w/o shared pin and reset bit delay_ms(100); }while(!XDREQ); delay_ms(1); vs_command(0x02,0x00,0x08,0x00);//go into new mode (w/o pin sharing, w/o sdi tests) delay_ms(1); vs_command(0x02,0x0b,volume,volume);//set volume delay_ms(1); vs_command(0x02,0x03,0x99,0x50);// set xtal 12.960MHZ + doubler // printf("_%x",vs_command(0x03,0x03,0x99,0x50)); } //----------------------------------- void resetvs1011_soft(void){ vs_command(0x02,0x00,0x08,0x04);//go into new mode (w/o pin sharing, w/o sdi tests) delay_ms(1); vs_command(0x02,0x0b,volume,volume);//set volume delay_ms(1); vs_command(0x02,0x03,0x99,0x50);// set xtal 12.960MHZ + doubler delay_ms(1); // morezeroes(); } /*/////////////////////////////////////////////////////////////////////////////////////////////// void morezeroes(void){ int16 c; xcs=1; if (XDREQ){ xdcs=0; for(c=0;c<2048;c++){ vs_spi_write(0); while (!XDREQ){;} } xdcs=1; } } *//////////////////////////////////////////////////////////////////////////////////////////////// char vs_spi_write(char aa){ char h; char i; h=0; i=7; XCLK=0;delay_cycles(10); do{ XDI=bit_test(aa,i); XCLK=1; if(XDO==1){bit_set(h,i);} XCLK=0; }while((i--)>0); return (h); } //////////////////////////////////////////////////////////////////////////////////////////////// int16 vs_command(char inout, address,a,b){//for sending non-song related data to the decoder.. int16 result; SS=1; xdcs=1; SSPEN=0; XDI_TRIS=0; xcs=0; vs_spi_write(inout); vs_spi_write(address);//load the buffer with address *(((char*)&result)+1)=vs_spi_write(a); //load buffer with hi data(ignored on read command) *(((char*)&result)+0)=vs_spi_write(b); //load buffer with data low value xcs=1; //deselect the vs1011 command registers (go into sdi mode) XDI_TRIS=1; SSPEN=1; return (result); } void mmc_read(void){ data_lo=SPI_READ(0xFF); data_hi=SPI_READ(0xFF); } ///////////////////////////////////////////////////////////////////////////////////////////////// void mmc_skip(char count){ for (;count>0;count--){ SPI_WRITE(0xFF); SPI_WRITE(0xFF); } } //////////////////////////////////////////////////////////////////////////////////////////////// char mmc_open_block(int32 block_number){ block_number*=512; SS=0; // set SS = 0 (on) SPI_WRITE(0x51); // send mmc read single block command SPI_WRITE(*(((char*)&block_number)+3)); // arguments are address SPI_WRITE(*(((char*)&block_number)+2)); SPI_WRITE(*(((char*)&block_number)+1)); SPI_WRITE(0x00); SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF if((mmc_response(0x00))==1) return 1; // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative) if((mmc_response(0xFE))==1) return 1; // wait for data token return(0); } void mmc_close_block(void){ SPI_READ(0xFF); // CRC bytes that are not needed SPI_READ(0xFF); SS=1; // set SS = 1 (off) SPI_WRITE(0xFF); // give mmc the clocks it needs to finish off } //////////////////////////////////////////////////////////////////////////////////////////////////// int mmc_init(){ //Initialises the MMC into SPI mode and sets block size char p; int i; SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED); SSPSTAT |= 0x40; // set CKE = 1 - clock idle low SSPCON &= 0xEF; // set CKP = 0 - data valid on rising edge SS=1; // // set SS = 1 (off) for(i=0;i<10;i++){ // initialise the MMC card into SPI mode by sending clks on SPI_WRITE(0xFF); } SS=0; // set SS = 0 (on) tells card to go to spi mode when it receives reset SPI_WRITE(0x40); // send reset command for(p=4;p>0;p--){ SPI_WRITE(0x00); // all the arguments are 0x00 for the reset command } SPI_WRITE(0x95); // precalculated checksum as we are still in MMC mode // puts("Sent go to SPI\n\r"); if(mmc_response(0x01)==1) return 1; // if = 1 then there was a timeout waiting for 0x01 from the mmc (bad!) // puts("Got response from MMC\n\r"); i = 0; while((i < 255) && (mmc_response(0x00)==1)){ // must keep sending command if response SPI_WRITE(0x41); // send mmc command one to bring out of idle state for(p=4;p>0;p--){ SPI_WRITE(0x00); } SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF i++; // printf("%d ",i); } if(i >= 254) return 1; // if >= 254 then there was a timeout waiting for 0x00 from the mmc (bad!) // puts("Got out of idle response from MMC\n\r"); SS=1; // set SS = 1 (off) SPI_WRITE(0xFF); // extra clocks to allow mmc to finish off what it is doing SS=0; // set SS = 0 (on) SPI_WRITE(0x50); // send mmc command one to bring out of idle state SPI_WRITE(0x00); SPI_WRITE(0x00); SPI_WRITE(0x02); // high block length bits - 512 bytes SPI_WRITE(0x00); // low block length bits SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF if((mmc_response(0x00))==1) return 1; //(bad!) SS=1; // set SS = 1 (off) // puts("Got set block length response from MMC\n\r"); return 0; //(good) } //////////////////////////////////////////////////////////////////////////////////////////////// int mmc_get_status(){ // Get the status register of the MMC, for debugging char p; xcs=1; xdcs=1; SS=0; // set SS = 0 (on) SPI_WRITE(0x58); // send mmc command one for(p=4;p>0;p--){ SPI_WRITE(0x00); } SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF SS=1; // set SS = 1 (off) return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// int mmc_response(unsigned char response){ //reads the MMC until we get the response we want or timeout unsigned long count = 0x0FFF; // 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point while(SPI_READ(0xFF) != response && --count > 0); if(count==0) return 1; // loop was exited due to timeout (bad) else return 0; // loop was exited before timeout (good) } //////////////////////////////////////////////////////////////////////////////////////////////// int mmc_read_block_to_serial(int32 block_number){ //read block from the MMC and outputs each byte to RS232. just for fun unsigned long i; mmc_open_block(block_number); for(i=0;i<512;i++){ putc(SPI_READ(0xFF)); // we should now receive 512 bytes } mmc_close_block(); // puts("\n\r----End of read block----\n\r"); return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// int mmc_read_block_to_vs1011(int32 block_number){ unsigned long i, ii; SSPEN=1; block_number*=2; SS=0; // set SS = 0 (on) SPI_WRITE(0x51); // send mmc read single block command SPI_WRITE(*(((char*)&block_number)+2)); // arguments are address SPI_WRITE(*(((char*)&block_number)+1)); SPI_WRITE(*(((char*)&block_number)+0)); SPI_WRITE(0x00); SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF if((mmc_response(0x00))==1) return 1; // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative) if((mmc_response(0xFE))==1) return 1; // wait for data token // MOSI=1; SSPEN=0;//switch to manual control of SPI port pins. the pic will not be reading these bytes into sspbuf XdCS=0;//allow vs1011 to see the spi data for(i=16;i>0;i--){ while(!XDREQ){;} for(ii=0;ii<32;ii++){ XCLK=1;XCLK=0; //doing it this way (without looping) to kill off a few cycles per byte XCLK=1;XCLK=0;//if you really want to go crazy, do this whole thing 32 times XCLK=1;XCLK=0;// that would save you about 1000 clock cycles or so, per block XCLK=1;XCLK=0; XCLK=1;XCLK=0; XCLK=1;XCLK=0; XCLK=1;XCLK=0; XCLK=1;XCLK=0; } } XdCS=1;//turn off vs1011 chip select SSPEN=1;//back to normal spi mode for pic to mmc communication SPI_READ(0xFF); // CRC bytes that are not needed, so we just use 0xFF SPI_READ(0xFF); SS=1; // set SS = 1 (off) SPI_WRITE(0xFF);// give mmc the clocks it needs to finish off return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// #inline void init(void){ tris_a=0b01110000; tris_b=0b11101011; // port_a=0; // XCS=1; option_reg =0b00000111; // option_reg= 0b00000000;//allow rb pullups so that external pullup resistors are not needed // ADCON0 = 0b00000000;//not using the adc, but here are the control bytes // ADCON1 = 0b00000000; // ANSEL = 0b00000000; OSCCON = 0b01110010; // 01110010 for internal 8MHz clock, 0b01110000 otherwise // CVRCON = 0b00000000; //00001101for 3.3 volt output in case you want it for some reason // CMCON = 0b00000111; }