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.
Diff: fat_lib.cpp
- Revision:
- 0:9a5c2fb0d96f
- Child:
- 10:1108261dabe8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fat_lib.cpp Tue Jan 15 12:45:24 2019 +0000 @@ -0,0 +1,241 @@ +#include "sys.h" +#include "sd_card.h" // -- include the sd card ide hard disk library +#include "fat_lib.h" + +//;sd_init() // -- initialize startup settings + +extern Serial pc; + +/****************** + 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 i; + 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(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 +} +