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.

sd_card.cpp

Committer:
mimi3
Date:
2020-10-09
Revision:
21:dc161a192ba7
Parent:
4:8c1772c05eaf

File content as of revision 21:dc161a192ba7:

/*
-- Title: Library for communicating with SD memory cards
-- Author: Matthew Schinkel - borntechi.com, copyright (c) 2009, all rights reserved.
-- Adapted-by:
-- Compiler: >=2.4q2
-- Revision: $Revision: 3534 $
--
-- This file is part of jallib (http://jallib.googlecode.com)
-- Released under the ZLIB license (http://www.opensource.org/licenses/zlib-license.html)
--
-- Description: this library provides functions for SD memory cards.
--
-- Notes: SD card SPI mode is 1,1
--
--        This version works with standard capacity sd cards up to 4gb and
--        high capacity up to 32 gb. Extended Capacity up to 2TB
--        may be supported later on.
--
-- Sources:
-- SanDisk Secure Digital Card - http://www.cs.ucr.edu/~amitra/sdcard/ProdManualSDCardv1.9.pdf
-- How to use MMC/SDC - http://forums.parallax.com/forums/attach.aspx?a=32012
--
*/

#include "sys.h"
#include "sd_card.h"
#if HAVE_DIRECT_SPI
#   include "DirectSPI.h"
    DirectSPI spi(SPI_MOSI,SPI_MISO,SPI_SCK);
#   define spi_write(m)  spi.directWrite8(m)
#else
    SPI spi(SPI_MOSI,SPI_MISO,SPI_SCK);
#   define spi_write(m)  spi.write(m)
#endif
DigitalOut sd_chip_select(SPI_CS);

#define _usec_delay(m) wait_us(m)


#define SD_BYTE_PER_SECTOR  512

void sd_ready();

//-- counters
 word sd_byte_count = 0;
 word sd_sector_count = 0;
 dword sd_sector_select;

//-- Basic Commands
#define SD_GO_IDLE_STATE  0
#define SD_SEND_OP_COND  1
#define SD_SEND_IF_COND  8  //-- for SDHC only
#define SD_SEND_CSD  9      //-- sd sends "Card Specific Data" standard or high capacity
#define SD_SEND_CID  10
#define SD_STOP_TRANSMISSION  12
#define SD_SEND_STATUS  13

//-- Read Commands
#define SD_SET_BLOCKLEN  16
#define SD_READ_SINGLE_BLOCK  17
#define SD_READ_MULTIPLE_BLOCK  18

//-- Write Commands
#define SD_WRITE_BLOCK  24
#define SD_WRITE_MULTIPLE_BLOCK  25
#define SD_PROGRAM_CSD  27

//-- Write Protection Commands
#define SD_SET_WRITE_PROT  28
#define SD_CLR_WRITE_PROT  29
#define SD_SEND_WRITE_PROT  30

//-- Erase Commands
#define SD_ERASE_WR_BLK_START  32
#define SD_ERASE_WR_BLK_END  33
#define SD_ERASE  38

//-- Application Specific Commands
#define SD_APP_CMD  55 //-- indicate that the next command is a application specific command
#define SD_GEN_CMD  56

//-- Other Commands
#define SD_READ_OCR  58
#define SD_CRC_ON_OFF  59 //-- default is off

//-- application specific command, must write command 55 first
#define SD_SD_STATUS  13
#define SD_SEND_NUM_WR_BLOCKS  22
#define SD_SET_WR_BLK_ERASE_COUNT  23
#define SD_SD_APP_OP_COND  41
#define SD_SET_CLR_CARD_DETECT  42
#define SD_SEND_SCR  51

//-- R1 RESPONCE boolS
#define SD_IN_IDLE_STATE  0
#define SD_ERASE_RESET  1
#define SD_ILLEGAL_COMMAND  2
#define SD_COM_CRC_ERROR  3
#define SD_ERASE_SEQUENCE_ERROR  4
#define SD_ADDRESS_ERROR  5
#define SD_PARAMETER_ERROR  6

 bool sd_error = false;
 bool sd_card_type = 0;
#define SD_HIGH_CAPACITY  0
#define SD_STANDARD_CAPACITY  1

//-- carrier used to access SD-Card (pseudo-var dealing with SPI)

//-- number of sectors variable
//dword sd_number_of_sectors; //-- number of sectors * 512 = sd card size
void sd_get_number_of_sectors(){}

//--------------------------------------------------------------------------------
//-- send a command to the sd card (commands with 1 response only)
//--------------------------------------------------------------------------------
void send_command(byte command,dword _data, byte *response){
    byte x;
    uint16_t i;

   //-- send a valid CRC byte only for set idle command
   //-- right bool must always be 1 (stop bool)
   if( command == SD_GO_IDLE_STATE){
      x = 0x95;
   }else if( command == SD_SEND_IF_COND){
      x = 0x87;
   }else{
      x = 0xFF;
   }

   command = command + 64;//          -- left bools must be 01 (start bools)

   spi_write(0xFF);            //-- send 8 clock pulses

   spi_write(command);         //-- send the command
   spi_write((byte)(_data>>24) );   //-- send command parameters
   spi_write((byte)(_data>>16) );   //-- send command parameters
   spi_write((byte)(_data>>8 ) );  //-- send command parameters
   spi_write((byte)(_data    ) );   //-- send command parameters

   //-- CRC data byte, crc disabled in this lib, but required for SD_GO_IDLE_STATE & SD_SEND_IF_COND commands.
   spi_write( x );

   //-- Get a responce from the card after each command
#define RETRY 10
  for(i=0;i<RETRY;i++){
       *response = spi_read();
       //printf("\ncmd resp = %02X ",*response);
       //if( *response != 0xFF){
       if( !(*response & 0x80 ) ){
           break;
       }
   }
#if DEBUG_SD_CARD
  if(i>=RETRY){ printf("\n Fail Command [0x%02X]",command);}
#endif
}

//--------------------------------------------------------------------------------
//-- check if the sd card is ready after last command.
//--------------------------------------------------------------------------------
void sd_ready(){
    byte response = 1;
   while (response != 0) {//   -- wait till last command has been completed
      //;start sdcard initialize
      send_command(SD_SEND_OP_COND,1, &response); // CMD1
   }
}


//--------------------------------------------------------------------------------
//-- initalize the sd card in SPI data transfer mode.
//--------------------------------------------------------------------------------
void sd_init(){
   byte response = 0;//           -- shows if sd card init is ok
   byte i;
   uint16_t count1 = 0;
   spi.frequency(500*1000);//500khz
   spi.format(8,0);
   //bool illegal_command at response : SD_ILLEGAL_COMMAND;
    #define illegal_command ( response & (1<<SD_ILLEGAL_COMMAND) )
   //-- steps to set sd card to use SPI
   _usec_delay(1000);//        -- delay
   sd_chip_select = 1;//     -- chip select high
   for(i=0;i<10;i++){
       spi_write(0xFF);//    -- send clock pulses (0xFF 10 times)
   }
   //;-- try to contact the sd card
   while ( response == 0 ) {                      // -- try 100 times
      _usec_delay(1000);                         //      -- delay 1ms
      sd_chip_select = 0;                        //   -- enable the sd card
      _usec_delay(255);                          //-- delay 255us
      // -- command 0, Resets card to idle state, get a response
      send_command(SD_GO_IDLE_STATE,0,&response); // CMD0
      sd_chip_select = 1;                        //  -- disable the sd card
      count1 = count1 + 1;                       // -- increment count
      if( count1 == 100) {
         sd_error = true;
#if DEBUG_SD_CARD
         printf("\n Error SD card initialize !");
#endif
         return;
      }
      //printf("\n response = 0x%02X",response);
   }

   //-- send SD_SEND_IF_COND command
   sd_chip_select = 0; // -- enable the sd card
   send_command(SD_SEND_IF_COND, 0x1AA, &response) ; //CMD8

   //printf("\n sd_init");
   if ( illegal_command ) { //-- SD CARD SPEC 1
#if DEBUG_SD_CARD
       printf("\n SDv1");
#endif
#define print_string(sd,str) //printf(str)
      sd_card_type = SD_STANDARD_CAPACITY;

      sd_chip_select = 0;//  -- enable the sd card
      //;start sdcard initialize
      //sd_ready();//            -- CMD1 wait till sd card is ready
      sd_chip_select = 1;// -- disable the sd card
      sd_get_number_of_sectors();
      return;
   }else{ //-- SD CARD SPEC 2
#if DEBUG_SD_CARD
       printf("\n SDv2");
#endif
      //; read OCR 4 byte
      response = spi_read();
      response = spi_read();
      response = spi_read();//  ;0x01
      response = spi_read();//  ;0xAA
      sd_chip_select = 1;//    -- disable the sd card

      if (response == 0xAA) {
         sd_chip_select = 0;//  -- enable the sd card

         //-- check if it has completed init
         while(1){
            //; send ACMD41
            send_command(SD_APP_CMD, 0, &response);
            send_command(SD_SD_APP_OP_COND,0x40000000, &response);
            if( response == 0) {
              break;
            }
         }
#if DEBUG_SD_CARD
         printf("\n ACMD41 OK");
#endif
         //-- read OCR here
         send_command(SD_READ_OCR,0, &response);// CMD58
         if ((spi_read() & 0x40) > 0) {// ; ocr[0]
            sd_card_type = SD_HIGH_CAPACITY; //and BLOCK mode
#if DEBUG_SD_CARD
            printf("\nSDHC block mode");
#endif
         }else{// -- sd card spec 2 standard capacity??
            sd_card_type = SD_STANDARD_CAPACITY; //and none BLOCK mode
#if DEBUG_SD_CARD
            printf("\n SDSC none block mode");
#endif
         }
         response = spi_read();
         response = spi_read();
         response = spi_read();

         //-- set block size to 512
         send_command(SD_SET_BLOCKLEN,512, &response);
         //sd_ready();

         sd_chip_select = 1;//  -- disable the sd card
#if DEBUG_SD_CARD
         printf("\n Finished sd card initialize.");
#endif
      }else{
         sd_error = true;
         sd_chip_select = 1;//  -- disable the sd card
         sd_get_number_of_sectors();
         return;
      }
   }
   sd_get_number_of_sectors();
   spi.frequency(16*1000000);//
}

//--------------------------------------------------------------------------------
//-- set the sd card to idle state
//--------------------------------------------------------------------------------
void sd_set_idle(){
    byte response = 0;
   sd_chip_select = 0;//  -- enable the sd card
   send_command(SD_STOP_TRANSMISSION,0,&response);// -- stop current transmission
   send_command(SD_STOP_TRANSMISSION,0,&response);//-- stop current transmission
   send_command(SD_STOP_TRANSMISSION,0,&response);// -- stop current transmission
   send_command(SD_STOP_TRANSMISSION,0,&response);// -- stop current transmission
   sd_chip_select = 1;//  -- disable the sd card
}

//--------------------------------------------------------------------------------
//-- tell sd card you will be reading data from a specified sector
//-- do not interupt read process by switching to another spi component
//--------------------------------------------------------------------------------
void  sd_start_read(dword address){
    byte response;

   sd_sector_select = address;
   //-- put spi into mode
   sd_chip_select = 0;//  -- enable the sd card
   if( sd_card_type == SD_STANDARD_CAPACITY) {
      address = address * SD_BYTE_PER_SECTOR;// -- make sd card sector addressable, sd cards are normally byte addressable.
   }

   // -- send read multi block command, ignore response.
   send_command(SD_READ_MULTIPLE_BLOCK,address,&response); //CMD18
   sd_byte_count = 0;
   sd_sector_count = 0;//     -- reset count
   //wait_idle();
}
//--------------------------------------------------------------------------------
//-- read 1 bytes from the sd card (pseudo )
//--------------------------------------------------------------------------------
byte sd_data_byte() {
    byte data_byte;

   if( sd_byte_count == 0) {         //   -- beginning of sector read
      while( spi_read() != 0xFE){} //      -- wait till data is ready to read
   }

   data_byte = spi_read();//                -- get data byte

   sd_byte_count = sd_byte_count + 1;//     -- increment byte_count
   if( sd_byte_count == SD_BYTE_PER_SECTOR) {//          -- end of sector read
      sd_byte_count = 0;
      sd_sector_count = sd_sector_count + 1;// -- increment sector number
      spi_read();//                     -- get junk crc data, crc is disabled
      spi_read();//                     -- get junk crc data, crc is disabled
   }

   return data_byte;
}

//--------------------------------------------------------------------------------
//-- tell sd card you are finished reading
//-- needed to be the same as other mass media libs
//--------------------------------------------------------------------------------
void sd_stop_read(){
   sd_set_idle();
   sd_chip_select = 1;//  -- disable the sd card
}

//--------------------------------------------------------------------------------
//-- send a read pulse to the sd card, go 1 bytes forward in current sector.
//--------------------------------------------------------------------------------
void sd_read_pulse_byte(word  count1){
    uint16_t i;
   for(i=0;i<count1;i++){//           -- loop specified number of times
      sd_data_byte();//       -- do a data read and ignore the incomming data
   }
}