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)
http://mpu.up.seesaa.net/image/16bit-wave-player-output-schema.png
USER_BUTTON: PC_13(default button)
Next song: One click in Play mode.
Pause : Push long time .
Play : One click from Pause.

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
+}
+