/* * MP3Player version 1.4 * Last updated: 04-Nov-2004, 17:00 */ #include "mp3player.h" struct BootRecord br; struct FileRecord flist[MAX_FILES]; int numfiles; unsigned int randtime; int main(void) { initboard(); initlcd(); initmp3(); initmmc(); readboot(); readfilenames(); welcome(); userinterface(); return 0; } void initboard(void) { *MMCControl = 0xF8; *ButtonsControl = 0x3F; *MP3Control = 0xC1; } void initlcd(void) { lcdwrite(0x0C, LCD_IR); // Display ON, Cursor OFF, Blink OFF. lcdwrite(0x06, LCD_IR); // Increment address mode. lcdwrite(0x01, LCD_IR); // Clear screen. } void initmp3(void) { *MP3Port = (MP3_PCS); delay(MP3_DELAY); *MP3Port = (MP3_POR | MP3_PCS); delay(MP3_DELAY); *MP3Port = (MP3_POR | MP3_PR); delay(MP3_DELAY); } void initmmc(void) { int i; byte resp; // Raise the CS *MMCPort = (*MMCPort | MMC_CS); // Send 10 dummy bytes for (i = 0; i < 10; i++) mmcrw(0xFF); // Lower the CS - Makes the card active *MMCPort = (*MMCPort & ~MMC_CS); // Send CMD0 sendcmd(0, 0x00000000, 0x95); // Wait for Idle state response for (i = 0; (i < MMC_TIMEOUT) && (mmcrw(0xFF) != 0x01); i++); if (i == MMC_TIMEOUT) { cls(); lcdwritemsg(MSG_NOMMC1, 1, 0); lcdwritemsg(MSG_NOMMC2, 2, 0); while (1); } // Send CMD1 until card is initialized do { sendcmd(1, 0x00000000, 0xFF); while ((resp = mmcrw(0xFF)) == 0xFF); } while (resp != 0x00); } void readboot(void) { int i; int firstsector = 0; // Read the partition table to find // where the boot sector is sendcmd(17, 0x00000000, 0xFF); waitfor(MMC_OKDATA); // Read slack readslack(454); // Read the first sector position of the first partition firstsector = readnumber(4); // Read the rest of the slack + CRC + extra 1 byte. readslack(57); // Now that we know where the boot sector is // lets read it! sendcmd(17, (firstsector * MMC_SECTOR_SIZE), 0xFF); waitfor(MMC_OKDATA); for (i = 0; i < 3; i++) br.JumpCommand[i] = mmcrw(0xFF); for (i = 0; i < 8; i++) br.OEMName[i] = mmcrw(0xFF); br.BytesPerSector = readnumber(2); br.SectorsPerCluster = readnumber(1); br.ReservedSectors = readnumber(2); br.NumberOfFATs = readnumber(1); br.NumberOfRootEntries = readnumber(2); br.NumberOfMediaSectors = readnumber(2); br.Media = mmcrw(0xFF); br.SectorsPerFAT = readnumber(2); br.SectorsPerTrack = readnumber(2); br.NumberOfHeads = readnumber(2); br.NumberOfHiddenSectors = readnumber(4); br.NumberOfTotalSectors = readnumber(4); br.DriveNumber = mmcrw(0xFF); br.Reserved = mmcrw(0xFF); br.ExtendedSignature = mmcrw(0xFF); for (i = 0; i < 4; i++) br.VolumeID[i] = mmcrw(0xFF); for (i = 0; i < 11; i++) br.VolumeLabel[i] = mmcrw(0xFF); for (i = 0; i < 8; i++) br.FileSystem[i] = mmcrw(0xFF); readslack(448); br.Signature1 = mmcrw(0xFF); br.Signature2 = mmcrw(0xFF); readslack(3); } void readfilenames(void) { const int ROOT_BASE_SECTOR = (br.NumberOfHiddenSectors + br.ReservedSectors + (br.NumberOfFATs * br.SectorsPerFAT)); int i, j, seqnum, nameoffset; int bytesread = 0; byte dirrecord[DIR_RECORD_LENGTH]; numfiles = 0; while (bytesread < (br.NumberOfRootEntries * DIR_RECORD_LENGTH)) { // Are we at the begining of a new sector? if (!(bytesread % MMC_SECTOR_SIZE)) { // Read CRC from previous sector read readslack(3); // Send read request for a root sector sendcmd(17, ((ROOT_BASE_SECTOR * br.BytesPerSector) + bytesread), 0xFF); waitfor(MMC_OKDATA); } // Read one directory record for (i = 0; i < DIR_RECORD_LENGTH; i++) { dirrecord[i] = mmcrw(0xFF); } bytesread = bytesread + DIR_RECORD_LENGTH; // Is this not the end of the file list? if (dirrecord[FAT_SEQ_OFFSET] != FAT_EOF) { // Is it the begining of a long file name? if (dirrecord[FAT_ATTR_OFFSET] == FAT_LFN) { // Is it not an erased file? if (!(dirrecord[FAT_SEQ_OFFSET] & FAT_LFN_ERASED)) { // Clear any previous junk from the filename record for (i = 0; i < (FILENAME_RECORDS * FN_RECORD_LENGTH + 1); i++) { flist[numfiles].FileName[i] = '\0'; } do { seqnum = dirrecord[FAT_SEQ_OFFSET] & FAT_LFN_SEQNUM; if (seqnum <= FILENAME_RECORDS) { nameoffset = (seqnum - 1) * FN_RECORD_LENGTH; flist[numfiles].FileName[nameoffset++] = dirrecord[1]; flist[numfiles].FileName[nameoffset++] = dirrecord[3]; flist[numfiles].FileName[nameoffset++] = dirrecord[5]; flist[numfiles].FileName[nameoffset++] = dirrecord[7]; flist[numfiles].FileName[nameoffset++] = dirrecord[9]; flist[numfiles].FileName[nameoffset++] = dirrecord[14]; flist[numfiles].FileName[nameoffset++] = dirrecord[16]; flist[numfiles].FileName[nameoffset++] = dirrecord[18]; flist[numfiles].FileName[nameoffset++] = dirrecord[20]; flist[numfiles].FileName[nameoffset++] = dirrecord[22]; flist[numfiles].FileName[nameoffset++] = dirrecord[24]; flist[numfiles].FileName[nameoffset++] = dirrecord[28]; flist[numfiles].FileName[nameoffset++] = dirrecord[30]; } // Read the next directory record if (!(bytesread % MMC_SECTOR_SIZE)) { readslack(3); sendcmd(17, ((ROOT_BASE_SECTOR * br.BytesPerSector) + bytesread), 0xFF); waitfor(MMC_OKDATA); } for (i = 0; i < DIR_RECORD_LENGTH; i++) { dirrecord[i] = mmcrw(0xFF); } bytesread = bytesread + DIR_RECORD_LENGTH; } while (seqnum != 1); flist[numfiles].FirstCluster = dirrecord[27]; flist[numfiles].FirstCluster = (flist[numfiles].FirstCluster << 8) + dirrecord[26]; // If after all this, we find that the file is erased, // hidden, system, volume or directory - ignore it. if ((dirrecord[FAT_SEQ_OFFSET] != FAT_SFN_ERASED) && (!(dirrecord[FAT_ATTR_OFFSET] & FAT_HSVD))) { numfiles = (numfiles + 1) % MAX_FILES; } } } else { // Is it not an erased, hidden, system, volume or directory file ? if ((dirrecord[FAT_SEQ_OFFSET] != FAT_SFN_ERASED) && (!(dirrecord[FAT_ATTR_OFFSET] & FAT_HSVD))) { // Clear any previous junk from the filename record for (i = 0; i < (FILENAME_RECORDS * FN_RECORD_LENGTH + 1); i++) { flist[numfiles].FileName[i] = '\0'; } j = 0; for (i = 0; i < 8; i++) { if (dirrecord[i] == ' ') break; flist[numfiles].FileName[j++] = dirrecord[i]; } if (dirrecord[8] != ' ') { flist[numfiles].FileName[j++] = '.'; for (i = 8; i < 11; i++) { if (dirrecord[i] == ' ') break; flist[numfiles].FileName[j++] = dirrecord[i]; } } flist[numfiles].FirstCluster = dirrecord[27]; flist[numfiles].FirstCluster = (flist[numfiles].FirstCluster << 8) + dirrecord[26]; numfiles = (numfiles + 1) % MAX_FILES; } } } else { // Its the end of the file list // Read whatevers left to finish the sector readslack(MMC_SECTOR_SIZE - (bytesread % MMC_SECTOR_SIZE)); // Don't try to read any more records break; } } readslack(3); } void welcome(void) { cls(); lcdwritemsg(MSG_WLC1, 1, 0); lcdwritemsg(MSG_WLC2, 2, 0); randtime = 0; while (!(*Buttons & PB_ALL)) { randtime++; } delay(PB_DELAY); } void userinterface(void) { byte pressed; int cursor = 0; int topsong = 0; int nameoffset = 0; cls(); displaysongs(topsong); displaycursor(cursor); while (1) { while (*Buttons & PB_ALL); while (!(pressed = (*Buttons & PB_ALL))); delay(PB_DELAY); switch (pressed) { case PB_UP: if ((topsong + cursor) > 0) { nameoffset = 0; lcdwritemsg(flist[topsong + cursor].FileName, cursor, SONG_OFFSET); if (cursor > 0) { cursor--; displaycursor(cursor); } else { topsong--; displaysongs(topsong); } } break; case PB_DOWN: if ((topsong + cursor + 1) < numfiles) { nameoffset = 0; lcdwritemsg(flist[topsong + cursor].FileName, cursor, SONG_OFFSET); if ((cursor + 1) < LCD_LINES) { cursor++; displaycursor(cursor); } else { topsong++; displaysongs(topsong); } } break; case PB_LEFT: nameoffset = nameoffset - (LCD_CHARS - SONG_OFFSET); if (nameoffset >= 0) { lcdwritemsg((flist[topsong + cursor].FileName + nameoffset), cursor, SONG_OFFSET); } else { nameoffset = nameoffset + (LCD_CHARS - SONG_OFFSET); } break; case PB_RIGHT: nameoffset = nameoffset + (LCD_CHARS - SONG_OFFSET); if ((nameoffset < (FILENAME_RECORDS * FN_RECORD_LENGTH)) && (flist[topsong + cursor].FileName[nameoffset] != '\0')) { lcdwritemsg((flist[topsong + cursor].FileName + nameoffset), cursor, SONG_OFFSET); } else { nameoffset = nameoffset - (LCD_CHARS - SONG_OFFSET); } break; case PB_STOP: nameoffset = 0; lcdwritemsg(flist[topsong + cursor].FileName, cursor, SONG_OFFSET); break; case PB_PLAY: topsong = play(topsong + cursor); cursor = 0; cls(); displaysongs(topsong); displaycursor(cursor); break; default: break; } } } int getnextcluster(int thiscluster) { const int FAT_BASE_SECTOR = (br.NumberOfHiddenSectors + br.ReservedSectors); int nextcluster; // Set the MMC block length so we read only one entry and not a whole sector sendcmd(16, FAT_ENTRY_LENGTH, 0xFF); waitfor(MMC_OK); // Request the entry sendcmd(17, ((FAT_BASE_SECTOR * br.BytesPerSector) + (thiscluster * FAT_ENTRY_LENGTH)), 0xFF); waitfor(MMC_OKDATA); // Read the next cluster number nextcluster = readnumber(FAT_ENTRY_LENGTH); // Get rid of CRC readslack(3); // Set the MMC block length to a whole sector sendcmd(16, MMC_SECTOR_SIZE, 0xFF); waitfor(MMC_OK); return nextcluster; } int play(int song) { const int DATA_BASE_ADDR = (((br.NumberOfHiddenSectors + br.ReservedSectors + (br.NumberOfFATs * br.SectorsPerFAT)) * br.BytesPerSector) + (br.NumberOfRootEntries * DIR_RECORD_LENGTH)); static int random = 0; static int repeat = 0; int cluster, sectorofcluster; unsigned int playedclusters; byte pressed; enum SongBreak sb = NO_BREAK; enum PlayState ps = PLAY; int i; int poll = 1; for (i = 0; i < numfiles; i++) { flist[i].Played = 0; } cls(); while (1) { if (ps == PLAY) { lcdwritemsg(MSG_PLAY, 0, PP_OFFSET); } else { lcdwritemsg(MSG_PAUSE, 0, PP_OFFSET); } lcdwritemsg(flist[song].FileName, 1, 0); if (random) lcdwritemsg(MSG_RND, 3, RND_OFFSET); if (repeat) lcdwritemsg(MSG_RPT, 3, RPT_OFFSET); cluster = flist[song].FirstCluster; flist[song].Played = 1; playedclusters = 0; while ((cluster < 0xFFF8) && (!sb)) { if (ps) { // For each cluster, read all its sectors for (sectorofcluster = 0; sectorofcluster < br.SectorsPerCluster; sectorofcluster++) { // Request a sector sendcmd(17, (DATA_BASE_ADDR + ((((cluster - 2) * br.SectorsPerCluster) + sectorofcluster) * br.BytesPerSector)), 0xFF); waitfor(MMC_OKDATA); // Stream the sector to the MP3 decoder for (i = 0; i < br.BytesPerSector; i++) { mp3write(mmcrw(0xFF)); } // Get rid of the CRC readslack(3); } playedclusters++; cluster = getnextcluster(cluster); } if ((pressed = (*Buttons & PB_ALL))) { if (poll) { delay(PB_DELAY); poll = 0; switch (pressed) { case PB_UP: if (random) { random = 0; lcdwritemsg(MSG_BLANK, 3, RND_OFFSET); if (repeat) lcdwritemsg(MSG_RPT, 3, RPT_OFFSET); } else { random = 1; lcdwritemsg(MSG_RND, 3, RND_OFFSET); if (repeat) lcdwritemsg(MSG_RPT, 3, RPT_OFFSET); } break; case PB_DOWN: if (repeat) { repeat = 0; lcdwritemsg(MSG_BLANK, 3, RPT_OFFSET); } else { repeat = 1; lcdwritemsg(MSG_RPT, 3, RPT_OFFSET); } break; case PB_LEFT: if (playedclusters < PREV_LIMIT) { if (!random) { if (song > 0) { song--; sb = PLAY_THIS; } else { if (repeat) { song = numfiles - 1; sb = PLAY_THIS; } } } } else { sb = PLAY_THIS; } break; case PB_RIGHT: if (((song + 1) < numfiles) || repeat || random) { sb = PLAY_NEXT; } break; case PB_STOP: return song; break; case PB_PLAY: if (ps == PLAY) { ps = PAUSE; lcdwritemsg(MSG_PAUSE, 0, PP_OFFSET); } else { ps = PLAY; lcdwritemsg(MSG_PLAY, 0, PP_OFFSET); } break; } } } else { poll = 1; } } if (sb == PLAY_THIS) { sb = NO_BREAK; continue; } else { sb = NO_BREAK; } if (random) { song = randomsong((randtime + playedclusters), repeat); if (song == -1) return 0; } else { song++; if (song == numfiles) { song = 0; if (!repeat) return song; } } } return song; } int randomsong(unsigned int seed, int repeat) { int avail = 0; int song; int i; if (repeat) { avail = numfiles; } else { for (i = 0; i < numfiles; i++) { if (!(flist[i].Played)) avail++; } if (!avail) return -1; } seed = seed & 0xFF; song = mod(seed, avail); if (!repeat) { for (i = 0, avail = -1; ; i++) { if (!(flist[i].Played)) { avail++; if (avail == song) return i; } } } return song; } int mod(int op1, int op2) { while (op1 >= op2) { op1 = op1 - op2; } return op1; } void mp3write(byte writebyte) { int i; byte data; for (i = 7; i >= 0; i--) { while (!(*MP3Port & MP3_PI19)); data = ((((writebyte >> i) << 6) & MP3_SID) | MP3_SIC); *MP3Data = data; *MP3Data = (data & ~MP3_SIC); } } void sendcmd(byte command, unsigned int argument, byte crc) { byte cmd[6]; int i; cmd[0] = (command | 0x40); for (i = 3; i >=0; i--) { cmd[4 - i] = ((byte) ((argument >> (i*8)) & 0xFF)); } cmd[5] = crc; for (i = 0; i < 6; i++) { mmcrw(cmd[i]); } } void displaysongs(int topsong) { int i; for (i = 0; ((i < LCD_LINES) && ((topsong + i) < numfiles)); i++) { lcdwritemsg(flist[topsong + i].FileName, i, SONG_OFFSET); } } void displaycursor(int line) { int i; for (i = 0; i < LCD_LINES; i++) { lcdwrite((LCD_SET_DDRAM | LCD_LINE_ADR[i]), LCD_IR); lcdwrite(((i == line) ? CH_CURSOR : CH_SPACE), LCD_DR); } } void cls(void) { lcdwrite(0x01, LCD_IR); // Clear screen. } void lcdwritemsg(char *msg, int line, int charoffset) { int len = (LCD_CHARS - charoffset); lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[line] + charoffset)), LCD_IR); while ((*msg != '\0') && (len > 0)) { lcdwrite(*msg, LCD_DR); msg++; len--; } for ( ; len > 0; len--) { lcdwrite(CH_SPACE, LCD_DR); } } void lcdwrite(byte data, enum LCDRegSel RS) { lcdwait(); if (RS == LCD_IR) { lcdwritectrl(data); } else { lcdwritedata(data); } } void waitfor(enum MMCResp resp) { // Wait for OK from card while (mmcrw(0xFF) != 0x00); // Wait for Data Start if (resp == MMC_OKDATA) { while (mmcrw(0xFF) != 0xFE); } } void readslack(int numbytes) { int i; for (i = 0; i < numbytes; i++) { mmcrw(0xFF); } } int readnumber(int numbytes) { int i; int num = 0; for (i = 0; i < numbytes; i++) { num = (num | (((int) mmcrw(0xFF)) << (i*8))); } return num; }