Extensions to the project might include:
- the ability to select the file to play using buttons
- the inclusion of play, pause and stop buttons
- the option to play files in a random order
All of the above would be relatively straightforward. There is ample code space still available, since on a PIC18F452 the code only occupies 6677 bytes of the 32768 available.
Circuit Diagram
Project Code
{ ******************************************************************************** * Name : WavPlayer.BAS * * Author : S Wright * * Notice : Copyright (c) 2006 S Wright * * : All Rights Reserved * * Date : 25/10/2006 * * Version : 1.0 * * Notes : * * : * ******************************************************************************** } Device = 18F452 Clock = 32 Config OSC = HSPLL #option SD_SPI = MSSP #option SD_SUPPORT_SUB_DIRECTORIES = True #option LCD_DATA = PORTB.4 #option LCD_RS = PORTB.2 #option LCD_EN = PORTB.3 #option LCD_INIT_DELAY = 300 Include "SDFileSystem.bas" Include "LCD.bas" Const BufferSize = 500 // buffer to avoid glitches Const TimerReload = 65036 // alter for different sample rates Dim Timer As TMR1L.AsWord // alias to Timer1 Dim TimerOn As T1CON.Booleans(0) // start and stop Dim TimerEnabled As PIE1.Booleans(0) // enable interrupts Dim TimerInterrupt As PIR1.Booleans(0) // interrupt flag Dim File As String(13) Dim WritePointer, ReadPointer As Word // read/write pointers in Dim Buffer(BufferSize) As Byte // circular buffer Dim DataSize As LongWord // size of WAV data in file Dim Progress As LongWord // increment progress bar when zero Dim ProgressIncrement As LongWord // progress bar step { ******************************************************************************** * Name : OnTimer (Interupt) * * Purpose : Sends bytes to R-2R DAC on PORT D to play file * ******************************************************************************** } Interrupt OnTimer() // ISR Timer = TimerReload Save(0) // context save If ReadPointer = WritePointer Then // buffer empty PORTE.0 = 1 // set buffer empty indicator Else Inc(ReadPointer) // increment read pointer If ReadPointer = BufferSize Then // if end of buffer then... ReadPointer = 0 // move back to beginning of buffer EndIf PORTD = Buffer(ReadPointer) // move byte to PORTD (R-2R ladder DAC) EndIf TimerInterrupt = False // clear interrupt flag Restore End Interrupt { ******************************************************************************** * Name : ReadFmt * * Purpose : Reads header of WAV file and checks compatibility * ******************************************************************************** } Function ReadFmt() As Boolean // read fmt block of WAV file Dim Index As Byte ReadFmt = True Index = 0 Repeat // skip over RIFF header SD.ReadLongWord Inc(Index) Until Index = 3 If SD.ReadChar <> "f" Then // check in fmt block ReadFmt = False EndIf If SD.ReadChar <> "m" Then ReadFmt = False EndIf If SD.ReadChar <> "t" Then ReadFmt = False EndIf If SD.ReadChar <> " " Then ReadFmt = False EndIf SD.ReadLongWord // skip over fmt size If SD.ReadWord <> 1 Then // check PCM (un-compressed) file ReadFmt = False EndIf If SD.ReadWord <> 1 Then // check 1 channel (mono) file ReadFmt = False EndIf If SD.ReadLongWord <> 16000 Then // check sample rate (16kHz) ReadFmt = False EndIf Index = 0 Repeat // skip to data size SD.ReadByte Inc(Index) Until Index = 12 DataSize = SD.ReadLongWord SD.ReadLongWord // skip to data section ProgressIncrement = DataSize / 16 End Function { ******************************************************************************** * Name : ProgressBar * * Purpose : Updates progress bar during playback * ******************************************************************************** } Sub ProgressBar() Dec(Progress) If Progress = 0 Then Progress = ProgressIncrement LCD.Write("=") EndIf End Sub { ******************************************************************************** * Name : SoftStart * * Purpose : Ramps DAC output to new value to avoid clicks * ******************************************************************************** } Sub SoftStart(pNewValue As Byte) Dim Value As Byte Value = PORTD Repeat If Value < pNewValue Then Inc(Value) ElseIf Value > pNewValue Then Dec(Value) EndIf PORTD = Value DelayUS(20) Until Value = pNewValue End Sub { ******************************************************************************** * Name : PlayFile * * Purpose : Plays file * ******************************************************************************** } Sub PlayFile(pFile As String) Dim TempWritePointer As Word LCD.Cls LCD.Writeat(1, 1, "{ ", pFile, " }") LCD.WriteAt(2, 1, " OPENING FILE ") SD.OpenFile(pFile) Timer = TimerReload // set timer one ReadPointer = 0 // initialise pointers WritePointer = 0 If Not(ReadFmt()) Then // incompatible file format LCD.WriteAt(2, 1, "WRONG FILE TYPE!") Else LCD.WriteAt(2, 1, "----------------") Progress = ProgressIncrement LCD.MoveCursor(2,1) Repeat // fill buffer initially Inc(WritePointer) Buffer(WritePointer) = SD.ReadByte Dec(DataSize) ProgressBar() Until (WritePointer = BufferSize - 1) Or SD.EOF SoftStart(Buffer(1)) // ramp up to first byte to play TimerOn = True // start timer - play starts Repeat TempWritePointer = WritePointer // use a temp pointer in order Inc(TempWritePointer) // to prevent interrupt If TempWritePointer = BufferSize Then // detecting a buffer underun TempWritePointer = 0 // when buffer is full EndIf While TempWritePointer = ReadPointer PORTE.1 = 1 Wend PORTE.1 = 0 WritePointer = TempWritePointer Buffer(WritePointer) = SD.ReadByte // write next byte to buffer Dec(DataSize) ProgressBar() Until SD.EOF Or (DataSize = 0) EndIf SD.CloseFile Repeat // wait until buffer is empty Until ReadPointer = WritePointer TimerOn = False // stop timer PORTE = 0 End Sub // Main program... TRISD = 0 TRISE = 0 PORTE = 0 SoftStart(127) TimerOn = False // stop timer initially Enable(OnTimer) // assign the interrupt handler TimerEnabled = True // enable timer interrupts LCD.Cls LCD.WriteAt(1, 1, "PLEASE INSERT SD") Repeat Until SD.Init(spiOscDiv4) Repeat File = SD.Dir(dirNext, sdFile) // search for next file on SD card If File <> Null Then // file found PlayFile(File) EndIf Until File = Null LCD.Cls LCD.WriteAt(1, 5, "FINISHED")