16bit resolution PWM wave player with SD card, super lite version.
Dependencies: mbed DirectSPI FastPWM
Supported boards (confirmed):
Nucleo-F030R8
Nucleo-L152RE
Nucleo-F401RE
Nucleo-F411RE
Only compilation is OK (unchecked, but may work):
Nucleo-L073RZ
Nucleo-F334R8
Nucleo-F303RE
Nucleo-F429ZI
Nucleo-F446RE
Nucleo-F446ZE
Nucleo-L476RG
Supported SD card:
SDSC/SDHC card,
FAT16 and FAT32.
(1) At first, format SD card using SD Card Formatter
https://www.sdcard.org/downloads/formatter_4/index.html
(2) Copy PCM wav files to the SD card.
Supported file:
PCM wave file that have file extension ".wav" on root directory.
16bit/8bit, fs(sampling rate)=32kHz,44.1kHz,48kHz.
Stereo/Mono.
Hardware setting:
Refer to the file port_setting.txt
PWM output port:
Left upper(Hi) PWM 8bit out: PB_5 (TM3_CH2)
Right upper(Hi) PWM 8bit out: PB4 (TM3_CH1)
Left lower(Low) PWM 8bit out: PC_9 (TM3_CH4)
Right lower(Low) PWM 8bit out: PC_8 (TM3_CH3)
USER_BUTTON: PC_13(default button)
Next song: One click in Play mode.
Pause : Push long time .
Play : One click from Pause.
fat_lib.cpp
- Committer:
- mimi3
- Date:
- 2019-01-21
- Revision:
- 10:1108261dabe8
- Parent:
- 0:9a5c2fb0d96f
File content as of revision 10:1108261dabe8:
#include "sys.h" #include "sd_card.h" // -- include the sd card ide hard disk library #include "fat_lib.h" //;sd_init() // -- initialize startup settings #if DEBUG_FAT_LIB extern Serial pc; #endif /****************** definition of global variables *******************/ byte gDirEntryBuff[32]; byte gbBPB_SecPerClus; dword gdwBPB_FileSize; word lgwBPB_BytesPerSec; word gwBPB_RootEntCnt; //-- dword gdwRootdir_sector, gdwTargetFileSector; word gwSize_of_root; sbit gfFat32 ; /****************** FAT_init *******************/ /* ; Refer to ChaN's FAT info page, ; http://elm-chan.org/docs/fat.html#bpb ; Thank you, ChaN san. */ void FAT_init() { byte bi; #define bpbBuff gDirEntryBuff //; shares as temporaly data memory dword dwBPB_SecPerFats32; word wBPB_RsvdSecCnt; dword dwBPB_HiddSec; dword dwBPB_Sector; #if HAVE_FAT32 byte bBPB_NumFATs; #else #define bBPB_NumFATs 2 //; to reduce Flash size #endif sd_start_read(0); // -- read MBR sector #if 0 printf("\n"); for(i=0;i<512;i++){ if((i%16)==0){ printf("\n %04X:",i);}; printf("%02X ",sd_data_byte()); } fflush(stdout); while(1); #endif sd_read_pulse_byte(454); // -- go to partition info low(dwBPB_Sector)[0] = sd_data_byte(); //-- get BPB info sector number low(dwBPB_Sector)[1] = sd_data_byte(); low(dwBPB_Sector)[2] = sd_data_byte(); low(dwBPB_Sector)[3] = sd_data_byte(); sd_stop_read(); sd_start_read(dwBPB_Sector);// -- go to BPB info sector // printf("\n%04X",dwBPB_Sector); // printf("\n "); for(bi=0;bi<32;bi++){ bpbBuff[bi] = sd_data_byte();// -- read BPB info to bpbBuff[] // printf("%02X ",bpbBuff[bi]); } //; register BPB info to each variable low(lgwBPB_BytesPerSec)[0] = bpbBuff[11]; low(lgwBPB_BytesPerSec)[1] = bpbBuff[12]; gbBPB_SecPerClus = bpbBuff[13]; low(wBPB_RsvdSecCnt)[0] = bpbBuff[14]; low(wBPB_RsvdSecCnt)[1] = bpbBuff[15]; #if HAVE_FAT32 bBPB_NumFATs = bpbBuff[16]; #endif low(gwBPB_RootEntCnt)[0] = bpbBuff[17]; low(gwBPB_RootEntCnt)[1] = bpbBuff[18]; //; BPB_FATSz16 low(dwBPB_SecPerFats32)[0]= bpbBuff[22] ; //for FAT16 low(dwBPB_SecPerFats32)[1]= bpbBuff[23] ; //for FAT16 low(dwBPB_SecPerFats32)[2]= 0 ; //for FAT16 low(dwBPB_SecPerFats32)[3]= 0 ; //for FAT16 low(dwBPB_HiddSec)[0] = bpbBuff[28]; low(dwBPB_HiddSec)[1] = bpbBuff[29]; low(dwBPB_HiddSec)[2] = bpbBuff[30]; low(dwBPB_HiddSec)[3] = bpbBuff[31]; #if HAVE_FAT32 if ((low(dwBPB_SecPerFats32)[0] == 0) && (low(dwBPB_SecPerFats32)[1]==0)){ //----- enable FAT32 mode #if DEBUG_FAT_LIB pc.printf("\n FAT32"); #endif gfFat32 = true; bi = sd_data_byte() ;// dummy read bi = sd_data_byte(); bi = sd_data_byte(); bi = sd_data_byte(); //; BPB_FATSz32 low(dwBPB_SecPerFats32)[0] = sd_data_byte(); low(dwBPB_SecPerFats32)[1] = sd_data_byte(); low(dwBPB_SecPerFats32)[2] = sd_data_byte(); low(dwBPB_SecPerFats32)[3] = sd_data_byte(); } #endif //; /* Root DIR start sector : (absolute sector) */ gdwRootdir_sector = ( dwBPB_SecPerFats32 * bBPB_NumFATs ) + wBPB_RsvdSecCnt + dwBPB_HiddSec; gwSize_of_root = gwBPB_RootEntCnt * 32; //;include debug_info1 sd_stop_read(); } /****************** readDirEntry *******************/ void readDirEntry(word wDestDirEntryIndex){ byte i; word wCurrentDirEntryIndex = 0; sd_start_read( gdwRootdir_sector ); #if DEBUG_FAT_LIB pc.printf("\n"); #endif while( true ){ for(i=0;i<32;i++){ //; read one dir entry gDirEntryBuff[i] = sd_data_byte(); #if DEBUG_FAT_LIB pc.printf("%02X ",gDirEntryBuff[i]); #endif } if( wCurrentDirEntryIndex == wDestDirEntryIndex) { break; } wCurrentDirEntryIndex = wCurrentDirEntryIndex + 32; if( !gfFat32 ){ if (wCurrentDirEntryIndex >= gwSize_of_root){ break; } } } sd_stop_read(); } /****************** searchNexFile *******************/ void searchNextFile(){ byte topChar; dword dwTargetClusterNumber; dword ldwRootdir_sector_size; static word wNextDirEntryIndex = 0; // ; initialize if( wNextDirEntryIndex != 0) {// ; if 0 , search first song wNextDirEntryIndex = wNextDirEntryIndex + 32 ; //search next song } while (true) { //; start dir entry search //; skip deleted entry while (true) { readDirEntry(wNextDirEntryIndex); topChar = gDirEntryBuff[0]; if( topChar == 0xE5) { //; skip deleted entry wNextDirEntryIndex = wNextDirEntryIndex + 32; }else if(topChar == 0){ //; dir entry table is end, so return to the top entry wNextDirEntryIndex = 0; }else{ break; } } //; check long file name if((topChar >= 0x42) && (topChar <= 0x54)) { if( ( gDirEntryBuff[11] & 0x0F ) == 0x0F) { //; long file name ID(=0x0F) or not //; this is long file name, so set index to short file name address wNextDirEntryIndex = wNextDirEntryIndex + ((word)((topChar - 0x40))<<5); readDirEntry(wNextDirEntryIndex); } } //;get the file extention if( (gDirEntryBuff[8] =='W') && (gDirEntryBuff[9] =='A') && (gDirEntryBuff[10]=='V') && (gDirEntryBuff[11]==' ')) { // ; gDirEntryBuff[11]=" ": (space) is the mark of archive attribute // ; if the file extention matches 'WAV ', break // ----------- break; //----------- } //; for read next entry wNextDirEntryIndex = wNextDirEntryIndex + 32; } //; end while: dir entry search #if UART_INFO //-- print out music file name(short file name) to UART static int ix=0; byte fname[13]; for(int i=0;i<8;i++){ fname[i] = gDirEntryBuff[i]; } fname[8] = '.'; fname[9] = gDirEntryBuff[8]; fname[10] = gDirEntryBuff[9]; fname[11] = gDirEntryBuff[10]; fname[12] = 0; printf("\n[%3d]: %s",ix++,fname); #endif if (gfFat32) { low(dwTargetClusterNumber)[2] = (gDirEntryBuff[20]); low(dwTargetClusterNumber)[3] = (gDirEntryBuff[21]); }else{ low(dwTargetClusterNumber)[2] = 0; low(dwTargetClusterNumber)[3] = 0; } low(dwTargetClusterNumber)[0] = (gDirEntryBuff[26]); low(dwTargetClusterNumber)[1] = (gDirEntryBuff[27]); low(gdwBPB_FileSize)[0] = (gDirEntryBuff[28]); // file size low(gdwBPB_FileSize)[1] = (gDirEntryBuff[29]); low(gdwBPB_FileSize)[2] = (gDirEntryBuff[30]); low(gdwBPB_FileSize)[3] = (gDirEntryBuff[31]); ldwRootdir_sector_size = (32 * (dword)(gwBPB_RootEntCnt) + lgwBPB_BytesPerSec - 1 ) / lgwBPB_BytesPerSec; //; calculate start sector of target song file gdwTargetFileSector = gdwRootdir_sector + ldwRootdir_sector_size + (dwTargetClusterNumber - 2) * gbBPB_SecPerClus; //;include debug_info2 }