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.

fat_lib.cpp

Committer:
mimi3
Date:
2019-01-15
Revision:
0:9a5c2fb0d96f
Child:
10:1108261dabe8

File content as of revision 0:9a5c2fb0d96f:

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