summaryrefslogtreecommitdiff
path: root/boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
committerYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
commitc3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (patch)
treeadded370d1e356901f8579f076e3263fb0464db7 /boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c
parent990090a4cc9070837d31e66b58d40f0c3d038741 (diff)
Add more board models
Diffstat (limited to 'boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c')
-rw-r--r--boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c1554
1 files changed, 1554 insertions, 0 deletions
diff --git a/boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c b/boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c
new file mode 100644
index 0000000..d2bee89
--- /dev/null
+++ b/boards/dk-tm4c129x/usb_host_audio/usb_host_audio.c
@@ -0,0 +1,1554 @@
+//*****************************************************************************
+//
+// usb_host_audio.c - Main routine for the USB host audio example.
+//
+// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the DK-TM4C129X Firmware Package.
+//
+//*****************************************************************************
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "inc/hw_ints.h"
+#include "driverlib/flash.h"
+#include "driverlib/gpio.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/rom.h"
+#include "driverlib/rom_map.h"
+#include "driverlib/sysctl.h"
+#include "driverlib/udma.h"
+#include "driverlib/systick.h"
+#include "driverlib/pin_map.h"
+#include "grlib/grlib.h"
+#include "grlib/widget.h"
+#include "grlib/canvas.h"
+#include "grlib/listbox.h"
+#include "grlib/pushbutton.h"
+#include "utils/ustdlib.h"
+#include "third_party/fatfs/src/ff.h"
+#include "third_party/fatfs/src/diskio.h"
+#include "drivers/usb_sound.h"
+#include "utils/wavfile.h"
+#include "drivers/kentec320x240x16_ssd2119.h"
+#include "drivers/frame.h"
+#include "drivers/pinout.h"
+#include "drivers/touch.h"
+#include "usblib/usblib.h"
+#include "usblib/host/usbhost.h"
+
+//*****************************************************************************
+//
+//! \addtogroup example_list
+//! <h1>USB host audio example application using SD Card FAT file system
+//! (usb_host_audio)</h1>
+//!
+//! This example application demonstrates playing .wav files from an SD card
+//! that is formatted with a FAT file system using USB host audio class. The
+//! application can browse the file system on the SD card and displays
+//! all files that are found. Files can be selected to show their format and
+//! then played if the application determines that they are a valid .wav file.
+//! Only PCM format (uncompressed) files may be played.
+//!
+//! For additional details about FatFs, see the following site:
+//! http://elm-chan.org/fsw/ff/00index_e.html
+//!
+//! The application can be recompiled to run using and external USB phy to
+//! implement a high speed host using an external USB phy. To use the external
+//! phy the application must be built with \b USE_ULPI defined. This disables
+//! the internal phy and the connector on the DK-TM4C129X board and enables the
+//! connections to the external ULPI phy pins on the DK-TM4C129X board.
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// The error routine that is called if the driver library encounters an error.
+//
+//*****************************************************************************
+#ifdef DEBUG
+void
+__error__(char *pcFilename, uint32_t ui32Line)
+{
+}
+
+#endif
+
+//*****************************************************************************
+//
+// Graphics context used to show text on the display.
+//
+//*****************************************************************************
+tContext g_sContext;
+
+//*****************************************************************************
+//
+// Global needed by the FAT driver to know the processor speed of the system.
+//
+//*****************************************************************************
+uint32_t g_ui32SysClock;
+
+//*****************************************************************************
+//
+// Defines for the basic screen area used by the application.
+//
+//*****************************************************************************
+#define STATUS_HEIGHT 40
+#define BG_MIN_X 7
+#define BG_MAX_X (320 - 8)
+#define BG_MIN_Y 24
+#define BG_MAX_Y (240 - 8)
+#define BUTTON_HEIGHT (STATUS_HEIGHT - 8)
+
+//*****************************************************************************
+//
+// Forward declarations for functions called by the widgets used in the user
+// interface.
+//
+//*****************************************************************************
+void OnListBoxChange(tWidget *psWidget, int16_t i16Selected);
+static void PlayPause(tWidget *psWidget);
+static void Stop(tWidget *psWidget);
+
+//*****************************************************************************
+//
+// The following are data structures used by FatFs.
+//
+//*****************************************************************************
+static FATFS g_sFatFs;
+static DIR g_sDirObject;
+static FILINFO g_sFileInfo;
+
+//*****************************************************************************
+//
+// Define a pair of buffers that are used for holding path information.
+// The buffer size must be large enough to hold the longest expected
+// full path name, including the file name, and a trailing null character.
+// The initial path is set to root "/".
+//
+//*****************************************************************************
+#define PATH_BUF_SIZE 80
+static char g_pcCwdBuf[PATH_BUF_SIZE] = "/";
+static char g_pcTmpBuf[PATH_BUF_SIZE];
+
+//*****************************************************************************
+//
+// The number of SysTick ticks per second.
+//
+//*****************************************************************************
+#define TICKS_PER_SECOND 100
+#define MS_PER_SYSTICK (1000 / TICKS_PER_SECOND)
+
+//*****************************************************************************
+//
+// Audio buffering definitions, these are optimized to deal with USB audio.
+// AUDIO_TRANSFER_SIZE define one frame of audio at 48000 Stereo 16 bit
+// and AUDIO_BUFFERS declares 16 frames(16ms) of audio buffering.
+//
+//*****************************************************************************
+#define AUDIO_TRANSFER_SIZE (192)
+#define AUDIO_BUFFERS (64)
+#define AUDIO_BUFFER_SIZE (AUDIO_TRANSFER_SIZE * AUDIO_BUFFERS)
+uint32_t g_ui32TransferSize;
+uint32_t g_ui32BufferSize;
+
+//*****************************************************************************
+//
+// The main audio buffer and it's pointers.
+//
+//*****************************************************************************
+uint8_t g_pui8AudioBuffer[AUDIO_BUFFER_SIZE];
+volatile uint8_t *g_pui8Read;
+volatile uint8_t *g_pui8Write;
+volatile uint32_t g_ui32ValidBytes;
+
+//*****************************************************************************
+//
+// Holds global flags for the system.
+//
+//*****************************************************************************
+uint32_t g_ui32Flags;
+
+//
+// The last transfer has competed so a new one can be started.
+//
+#define FLAGS_TX_COMPLETE 1
+
+//
+// New audio device present.
+//
+#define FLAGS_DEVICE_CONNECT 2
+
+//*****************************************************************************
+//
+// The global playback state for the application.
+//
+//*****************************************************************************
+volatile enum
+{
+ AUDIO_PLAYING,
+ AUDIO_PAUSED,
+ AUDIO_STOPPED,
+ AUDIO_NONE
+}
+g_ePlayState;
+
+//*****************************************************************************
+//
+// These are the global .wav file states used by the application.
+//
+//*****************************************************************************
+tWavFile g_sWavFile;
+tWavHeader g_sWavHeader;
+
+//*****************************************************************************
+//
+// Widget definitions
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Storage for the filename listbox widget string table.
+//
+//*****************************************************************************
+#define NUM_LIST_STRINGS 48
+const char *g_ppcDirListStrings[NUM_LIST_STRINGS];
+
+//*****************************************************************************
+//
+// Storage for the names of the files in the current directory. Filenames
+// are stored in format "(D) filename.ext" for directories or "(F) filename.ext"
+// for files.
+//
+//*****************************************************************************
+#define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
+char g_pcFilenames[NUM_LIST_STRINGS][MAX_FILENAME_STRING_LEN];
+
+//*****************************************************************************
+//
+// The canvas widgets for the wav file information.
+//
+//*****************************************************************************
+extern tCanvasWidget g_sWaveInfoBackground;
+
+char g_pcTime[16] = "";
+Canvas(g_sWaveInfoTime, &g_sWaveInfoBackground, 0, 0,
+ &g_sKentec320x240x16_SSD2119, BG_MAX_X - 166, BG_MIN_Y + 28, 158, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT | CANVAS_STYLE_TEXT_RIGHT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrBlack, ClrWhite, ClrWhite,
+ g_psFontFixed6x8, g_pcTime, 0, 0);
+
+char g_pcFormat[24] = "";
+Canvas(g_sWaveInfoSample, &g_sWaveInfoBackground, &g_sWaveInfoTime, 0,
+ &g_sKentec320x240x16_SSD2119, BG_MAX_X - 166, BG_MIN_Y + 18, 158, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT | CANVAS_STYLE_TEXT_RIGHT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrBlack, ClrWhite, ClrWhite,
+ g_psFontFixed6x8, g_pcFormat, 0, 0);
+
+Canvas(g_sWaveInfoFileName, &g_sWaveInfoBackground, &g_sWaveInfoSample, 0,
+ &g_sKentec320x240x16_SSD2119, BG_MAX_X - 166, BG_MIN_Y + 8, 158, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT | CANVAS_STYLE_TEXT_RIGHT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrBlack, ClrWhite, ClrWhite,
+ g_psFontFixed6x8, "", 0, 0);
+
+//*****************************************************************************
+//
+// The canvas widget acting as the background for the wav file information.
+//
+//*****************************************************************************
+Canvas(g_sWaveInfoBackground, WIDGET_ROOT, 0,
+ &g_sWaveInfoFileName, &g_sKentec320x240x16_SSD2119,
+ BG_MAX_X - 170, BG_MIN_Y + 4, 166, 80,
+ CANVAS_STYLE_FILL, ClrBlack, ClrWhite, ClrWhite,
+ g_psFontFixed6x8, 0, 0, 0);
+
+extern tCanvasWidget g_sStatusPanel;
+
+//
+// Status text area.
+//
+Canvas(g_sStatusText, &g_sStatusPanel, 0, 0, &g_sKentec320x240x16_SSD2119,
+ BG_MIN_X + 112, BG_MAX_Y - STATUS_HEIGHT + 4, 189, BUTTON_HEIGHT,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT | CANVAS_STYLE_TEXT_RIGHT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrLightGrey, ClrDarkGray, ClrBlack,
+ g_psFontCmss16, "", 0, 0);
+
+//
+// Stop button.
+//
+RectangularButton(g_sStop, &g_sStatusPanel, &g_sStatusText, 0,
+ &g_sKentec320x240x16_SSD2119, BG_MIN_X + 58,
+ BG_MAX_Y - STATUS_HEIGHT + 4, 50, BUTTON_HEIGHT,
+ PB_STYLE_FILL | PB_STYLE_TEXT |
+ PB_STYLE_RELEASE_NOTIFY, ClrLightGrey, ClrDarkGray, 0,
+ ClrBlack, g_psFontCmss16, "Stop", 0, 0, 0 ,0 , Stop);
+
+//
+// Play/Pause button.
+//
+RectangularButton(g_sPlayPause, &g_sStatusPanel, &g_sStop, 0,
+ &g_sKentec320x240x16_SSD2119, BG_MIN_X + 4,
+ BG_MAX_Y - STATUS_HEIGHT + 4, 50, BUTTON_HEIGHT,
+ PB_STYLE_FILL | PB_STYLE_TEXT |
+ PB_STYLE_RELEASE_NOTIFY, ClrLightGrey, ClrDarkGray, 0,
+ ClrBlack, g_psFontCmss16, "Play", 0, 0, 0 ,0 , PlayPause);
+
+//
+// Background of the status area behind the buttons.
+//
+Canvas(g_sStatusPanel, WIDGET_ROOT, 0, &g_sPlayPause,
+ &g_sKentec320x240x16_SSD2119, BG_MIN_X, BG_MAX_Y - STATUS_HEIGHT,
+ BG_MAX_X - BG_MIN_X, STATUS_HEIGHT,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT_RIGHT |
+ CANVAS_STYLE_TEXT_TOP, ClrGray, ClrWhite, ClrBlack, 0,
+ 0, 0, 0);
+
+//
+// The file list box.
+//
+ListBox(g_sDirList, WIDGET_ROOT, 0, 0,
+ &g_sKentec320x240x16_SSD2119,
+ BG_MIN_X + 4, BG_MIN_Y + 4, 120,
+ BG_MAX_Y - BG_MIN_Y - STATUS_HEIGHT - 8,
+ LISTBOX_STYLE_OUTLINE, ClrBlack, ClrDarkBlue, ClrSilver, ClrWhite,
+ ClrWhite, g_psFontCmss12, g_ppcDirListStrings,
+ NUM_LIST_STRINGS, 0, OnListBoxChange);
+
+//*****************************************************************************
+//
+// State information for keep track of time.
+//
+//*****************************************************************************
+static uint32_t g_ui32BytesPlayed;
+static uint32_t g_ui32NextUpdate;
+
+//*****************************************************************************
+//
+// Globals used to track play back position.
+//
+//*****************************************************************************
+static uint16_t g_ui16Minutes;
+static uint16_t g_ui16Seconds;
+
+//*****************************************************************************
+//
+// Static constant strings used by the application.
+//
+//*****************************************************************************
+static const char g_pcPlay[] = "Play";
+static const char g_pcPause[] = "Pause";
+static const char g_pcNoDevice[] ="No USB Device ";
+static const char g_pcUnknownDevice[] ="Unknown Device ";
+static const char g_pcDirError[] ="Directory Read Error ";
+
+//*****************************************************************************
+//
+// This function is used to tell when to update the play back times for a file.
+// It will only update the screen at 1 second intervals but can be called more
+// often with no result.
+//
+//*****************************************************************************
+static void
+DisplayTime(uint32_t ui32ForceUpdate)
+{
+ uint32_t ui32Seconds;
+ uint32_t ui32Minutes;
+
+ //
+ // Only display on the screen once per second.
+ //
+ if((g_ui32BytesPlayed >= g_ui32NextUpdate) || (ui32ForceUpdate != 0))
+ {
+ //
+ // Set the next update time to one second later.
+ //
+ g_ui32NextUpdate = g_ui32BytesPlayed + g_sWavHeader.ui32AvgByteRate;
+
+ //
+ // Calculate the integer number of minutes and seconds.
+ //
+ ui32Seconds = g_ui32BytesPlayed / g_sWavHeader.ui32AvgByteRate;
+ ui32Minutes = ui32Seconds / 60;
+ ui32Seconds -= ui32Minutes * 60;
+
+ //
+ // Print the time string in the format mm.ss/mm.ss
+ //
+ usprintf(g_pcTime, "%2d:%02d/%d:%02d\0", ui32Minutes,ui32Seconds,
+ g_ui16Minutes, g_ui16Seconds);
+
+ //
+ // Display the updated time on the screen.
+ //
+ WidgetPaint((tWidget *)&g_sWaveInfoTime);
+ }
+}
+
+//*****************************************************************************
+//
+// This function will handle stopping the play back of audio. It will not do
+// this immediately but will defer stopping audio at a later time. This allows
+// this function to be called from an interrupt handler.
+//
+//*****************************************************************************
+static void
+WaveStop(void)
+{
+ uint32_t ui32Idx;
+ uint32_t *pui32Buffer;
+
+ //
+ // Stop playing audio.
+ //
+ g_ePlayState = AUDIO_STOPPED;
+
+ //
+ // Do unsigned long accesses to clear out the buffer.
+ //
+ pui32Buffer = (uint32_t *)g_pui8AudioBuffer;
+
+ //
+ // Zero out the buffer.
+ //
+ for(ui32Idx = 0; ui32Idx < (AUDIO_BUFFER_SIZE >> 2); ui32Idx++)
+ {
+ pui32Buffer[ui32Idx] = 0;
+ }
+
+ //
+ // Reset the number of bytes played and for a time update on the screen.
+ //
+ g_ui32BytesPlayed = 0;
+ g_ui32NextUpdate = 0;
+ DisplayTime(1);
+
+ //
+ // Change the play/pause button to say play.
+ //
+ PushButtonTextSet(&g_sPlayPause, g_pcPlay);
+ WidgetPaint((tWidget *)&g_sPlayPause);
+}
+
+//*****************************************************************************
+//
+// This function is used to change to a new directory in the file system.
+// It takes a parameter that specifies the directory to make the current
+// working directory.
+// Path separators must use a forward slash "/". The directory parameter
+// can be one of the following:
+// * root ("/")
+// * a fully specified path ("/my/path/to/mydir")
+// * a single directory name that is in the current directory ("mydir")
+// * parent directory ("..")
+//
+// It does not understand relative paths, so dont try something like this:
+// ("../my/new/path")
+//
+// Once the new directory is specified, it attempts to open the directory
+// to make sure it exists. If the new path is opened successfully, then
+// the current working directory (cwd) is changed to the new path.
+//
+// In cases of error, the pui32Reason parameter will be written with one of
+// the following values:
+//
+// NAME_TOO_LONG_ERROR - combination of paths are too long for the buffer
+// OPENDIR_ERROR - there is some problem opening the new directory
+//
+//*****************************************************************************
+static FRESULT
+ChangeToDirectory(char *pcDirectory)
+{
+ uint32_t ui32Idx;
+ FRESULT fresult;
+
+ //
+ // Copy the current working path into a temporary buffer so
+ // it can be manipulated.
+ //
+ strcpy(g_pcTmpBuf, g_pcCwdBuf);
+
+ //
+ // If the first character is /, then this is a fully specified
+ // path, and it should just be used as-is.
+ //
+ if(pcDirectory[0] == '/')
+ {
+ //
+ // Make sure the new path is not bigger than the cwd buffer.
+ //
+ if(strlen(pcDirectory) + 1 > sizeof(g_pcCwdBuf))
+ {
+ return(FR_OK);
+ }
+
+ //
+ // If the new path name (in argv[1]) is not too long, then
+ // copy it into the temporary buffer so it can be checked.
+ //
+ else
+ {
+ strncpy(g_pcTmpBuf, pcDirectory, sizeof(g_pcTmpBuf));
+ }
+ }
+
+ //
+ // If the argument is .. then attempt to remove the lowest level
+ // on the CWD.
+ //
+ else if(!strcmp(pcDirectory, ".."))
+ {
+ //
+ // Get the index to the last character in the current path.
+ //
+ ui32Idx = strlen(g_pcTmpBuf) - 1;
+
+ //
+ // Back up from the end of the path name until a separator (/)
+ // is found, or until we bump up to the start of the path.
+ //
+ while((g_pcTmpBuf[ui32Idx] != '/') && (ui32Idx > 1))
+ {
+ //
+ // Back up one character.
+ //
+ ui32Idx--;
+ }
+
+ //
+ // Now we are either at the lowest level separator in the
+ // current path, or at the beginning of the string (root).
+ // So set the new end of string here, effectively removing
+ // that last part of the path.
+ //
+ g_pcTmpBuf[ui32Idx] = 0;
+ }
+
+ //
+ // Otherwise this is just a normal path name from the current
+ // directory, and it needs to be appended to the current path.
+ //
+ else
+ {
+ //
+ // Test to make sure that when the new additional path is
+ // added on to the current path, there is room in the buffer
+ // for the full new path. It needs to include a new separator,
+ // and a trailing null character.
+ //
+ if(strlen(g_pcTmpBuf) + strlen(pcDirectory) + 2 > sizeof(g_pcCwdBuf))
+ {
+ return(FR_INVALID_OBJECT);
+ }
+
+ //
+ // The new path is okay, so add the separator and then append
+ // the new directory to the path.
+ //
+ else
+ {
+ //
+ // If not already at the root level, then append a /
+ //
+ if(strcmp(g_pcTmpBuf, "/"))
+ {
+ strcat(g_pcTmpBuf, "/");
+ }
+
+ //
+ // Append the new directory to the path.
+ //
+ strcat(g_pcTmpBuf, pcDirectory);
+ }
+ }
+
+ //
+ // At this point, a candidate new directory path is in g_pcTmpBuf.
+ // Try to open it to make sure it is valid.
+ //
+ fresult = f_opendir(&g_sDirObject, g_pcTmpBuf);
+
+ //
+ // This was a valid path, so copy it into the CWD.
+ //
+ if(fresult == FR_OK)
+ {
+ strncpy(g_pcCwdBuf, g_pcTmpBuf, sizeof(g_pcCwdBuf));
+ }
+
+ //
+ // Return success.
+ //
+ return(fresult);
+}
+
+//*****************************************************************************
+//
+// Fill the audio buffer with data from the open file.
+//
+//*****************************************************************************
+static void
+FillAudioBuffer(void)
+{
+ uint32_t ui32Count, ui32Size;
+ uint8_t *pui8Read;
+
+ //
+ // If already full return.
+ //
+ if(g_ui32ValidBytes == g_ui32BufferSize)
+ {
+ return;
+ }
+
+ //
+ // Snap shot the read pointer since it might change while we are filling
+ // the buffer. If the read pointer moved due to an interrupt we will
+ // get the update on the next call to this function.
+ //
+ pui8Read = (uint8_t *)g_pui8Read;
+
+ //
+ // If write is ahead of read then fill to the end of the buffer if
+ // possible.
+ //
+ if(pui8Read <= g_pui8Write)
+ {
+ //
+ // Calculate the amount of space we have.
+ //
+ ui32Size = g_ui32BufferSize -
+ (uint32_t)(g_pui8Write - g_pui8AudioBuffer);
+ ui32Count = WavRead(&g_sWavFile, (uint8_t *)g_pui8Write, ui32Size);
+
+ g_pui8Write += ui32Count;
+ IntMasterDisable();
+ g_ui32ValidBytes += ui32Count;
+ IntMasterEnable();
+
+ if(g_pui8Write == (g_pui8AudioBuffer + g_ui32BufferSize))
+ {
+ g_pui8Write = g_pui8AudioBuffer;
+ }
+
+ ui32Size = pui8Read - g_pui8AudioBuffer;
+
+ if(ui32Size)
+ {
+ ui32Count = WavRead(&g_sWavFile, (uint8_t *)g_pui8Write, ui32Size);
+ g_pui8Write += ui32Count;
+ IntMasterDisable();
+ g_ui32ValidBytes += ui32Count;
+ IntMasterEnable();
+ }
+ }
+
+ //
+ // If read is now ahead of write then fill to the read pointer.
+ //
+ if(pui8Read > g_pui8Write)
+ {
+ //
+ // Calculate the amount of space we have.
+ //
+ ui32Size = (uint32_t)(pui8Read - g_pui8Write);
+ ui32Count = WavRead(&g_sWavFile, (uint8_t *)g_pui8Write, ui32Size);
+
+ g_pui8Write += ui32Count;
+
+ IntMasterDisable();
+ g_ui32ValidBytes += ui32Count;
+ IntMasterEnable();
+ }
+}
+
+//*****************************************************************************
+//
+// This function handles the callback from the USB audio device when a buffer
+// has been played or a new buffer has been received.
+//
+//*****************************************************************************
+static void
+USBAudioOutCallback(void *pvBuffer, uint32_t ui32Event, uint32_t ui32Value)
+{
+ //
+ // If a buffer has been played then schedule a new one to play.
+ //
+ if((ui32Event == USB_EVENT_TX_COMPLETE) && (g_ePlayState == AUDIO_PLAYING))
+ {
+ //
+ // Indicate that a transfer was complete so that the non-interrupt
+ // code can read in more data from the file.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE) = 1;
+
+ //
+ // Increment the read pointer.
+ //
+ g_pui8Read += g_ui32TransferSize;
+
+ //
+ // Remove the specified number of bytes from the buffer.
+ //
+ if(g_ui32ValidBytes > g_ui32TransferSize)
+ {
+ g_ui32ValidBytes -= g_ui32TransferSize;
+ }
+ else
+ {
+ g_ui32ValidBytes = 0;
+ }
+
+ //
+ // Wrap the read pointer if necessary.
+ //
+ if(g_pui8Read >= (g_pui8AudioBuffer + g_ui32BufferSize))
+ {
+ g_pui8Read = g_pui8AudioBuffer;
+ }
+
+ //
+ // Increment the number of bytes that have been played.
+ //
+ g_ui32BytesPlayed += g_ui32TransferSize;
+
+ //
+ // Schedule a new USB audio buffer to be transmitted to the USB
+ // audio device.
+ //
+ USBSoundBufferOut((uint8_t *)g_pui8Read, g_ui32TransferSize,
+ USBAudioOutCallback);
+ }
+}
+
+//*****************************************************************************
+//
+// This is the callback for the play/pause button.
+//
+//*****************************************************************************
+static void
+PlayPause(tWidget *psWidget)
+{
+ int16_t i16Select;
+
+ //
+ // If we are stopped, then start playing.
+ //
+ if(g_ePlayState == AUDIO_STOPPED)
+ {
+ //
+ // Get the current selection from the list box.
+ //
+ i16Select = ListBoxSelectionGet(&g_sDirList);
+
+ //
+ // See if this is a valid .wav file that can be opened.
+ //
+ if(WavOpen(g_pcFilenames[i16Select], &g_sWavFile) == 0)
+ {
+ //
+ // Initialize the read and write pointers.
+ //
+ g_pui8Read = g_pui8AudioBuffer;
+ g_pui8Write = g_pui8AudioBuffer;
+ g_ui32ValidBytes = 0;
+
+ //
+ // Fill the audio buffer from the file.
+ //
+ FillAudioBuffer();
+
+ //
+ // Start the audio playback.
+ //
+ USBSoundBufferOut((uint8_t *)g_pui8Read, g_ui32TransferSize,
+ USBAudioOutCallback);
+ //
+ // Indicate that wave play back should start.
+ //
+ g_ePlayState = AUDIO_PLAYING;
+
+ //
+ // Change the button to indicate pause.
+ //
+ PushButtonTextSet(&g_sPlayPause, g_pcPause);
+ WidgetPaint((tWidget *)&g_sPlayPause);
+ }
+ else
+ {
+ //
+ // Play was pressed on an invalid file.
+ //
+ CanvasTextSet(&g_sStatusText, "Invalid wav format ");
+ WidgetPaint((tWidget *)&g_sPlayPause);
+
+ return;
+ }
+
+ //
+ // Initialize the read and write pointers.
+ //
+ g_pui8Read = g_pui8AudioBuffer;
+ g_pui8Write = g_pui8AudioBuffer;
+ g_ui32ValidBytes = 0;
+
+ //
+ // Fill the audio buffer from the file.
+ //
+ FillAudioBuffer();
+
+ //
+ // Start the audio playback.
+ //
+ USBSoundBufferOut((uint8_t *)g_pui8Read, g_ui32TransferSize,
+ USBAudioOutCallback);
+ }
+ else if(g_ePlayState == AUDIO_PLAYING)
+ {
+ //
+ // Now switching to a paused state, so change the button to say play.
+ //
+ PushButtonTextSet(&g_sPlayPause, g_pcPlay);
+ WidgetPaint((tWidget *)&g_sPlayPause);
+
+ g_ePlayState = AUDIO_PAUSED;
+ }
+ else if(g_ePlayState == AUDIO_PAUSED)
+ {
+ //
+ // Fill the audio buffer from the file.
+ //
+ FillAudioBuffer();
+
+ //
+ // Start the audio playback.
+ //
+ USBSoundBufferOut((uint8_t *)g_pui8Read, g_ui32TransferSize,
+ USBAudioOutCallback);
+
+ //
+ // Now switching to a play state, so change the button to say paused.
+ //
+ PushButtonTextSet(&g_sPlayPause, g_pcPause);
+ WidgetPaint((tWidget *)&g_sPlayPause);
+
+ g_ePlayState = AUDIO_PLAYING;
+ }
+}
+
+//*****************************************************************************
+//
+// Stopped pressed so stop the wav playback.
+//
+//*****************************************************************************
+static void
+Stop(tWidget *psWidget)
+{
+ //
+ // Stop play back if we are playing.
+ //
+ if((g_ePlayState == AUDIO_PLAYING) || (g_ePlayState == AUDIO_PAUSED))
+ {
+ WaveStop();
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called to read the contents of the current directory on
+// the SD card and fill the listbox containing the names of all files and
+// directories.
+//
+//*****************************************************************************
+static int
+PopulateFileListBox(bool bRepaint)
+{
+ uint32_t ui32ItemCount;
+ FRESULT iFResult;
+
+ //
+ // Empty the list box on the display.
+ //
+ ListBoxClear(&g_sDirList);
+
+ //
+ // Make sure the list box will be redrawn next time the message queue
+ // is processed.
+ //
+ if(bRepaint)
+ {
+ WidgetPaint((tWidget *)&g_sDirList);
+ }
+
+ //
+ // Open the current directory for access.
+ //
+ iFResult = f_opendir(&g_sDirObject, g_pcCwdBuf);
+
+ //
+ // Check for error and return if there is a problem.
+ //
+ if(iFResult != FR_OK)
+ {
+ //
+ // Change the text to reflect the change.
+ //
+ CanvasTextSet(&g_sStatusText, g_pcDirError);
+ WidgetPaint((tWidget *)&g_sStatusText);
+ return(iFResult);
+ }
+
+ //
+ // If not at the root then add the ".." entry.
+ //
+ if(g_pcCwdBuf[1] != 0)
+ {
+ g_pcFilenames[0][0] = '.';
+ g_pcFilenames[0][1] = '.';
+ g_pcFilenames[0][2] = 0;
+ ListBoxTextAdd(&g_sDirList, g_pcFilenames[0]);
+ ui32ItemCount = 1;
+ }
+ else
+ {
+ ui32ItemCount = 0;
+ }
+
+ //
+ // Enter loop to enumerate through all directory entries.
+ //
+ for(;;)
+ {
+ //
+ // Read an entry from the directory.
+ //
+ iFResult = f_readdir(&g_sDirObject, &g_sFileInfo);
+
+ //
+ // Check for error and return if there is a problem.
+ //
+ if(iFResult != FR_OK)
+ {
+ //
+ // Change the text to reflect the change.
+ //
+ CanvasTextSet(&g_sStatusText, g_pcDirError);
+ WidgetPaint((tWidget *)&g_sStatusText);
+ return(iFResult);
+ }
+
+ //
+ // If the file name is blank, then this is the end of the
+ // listing.
+ //
+ if(!g_sFileInfo.fname[0])
+ {
+ break;
+ }
+
+ //
+ // Add the information as a line in the listbox widget.
+ //
+ if(ui32ItemCount < NUM_LIST_STRINGS)
+ {
+ if(g_sFileInfo.fattrib & AM_DIR)
+ {
+ usnprintf(g_pcFilenames[ui32ItemCount],
+ MAX_FILENAME_STRING_LEN, "+ %s", g_sFileInfo.fname);
+ }
+ else
+ {
+ ustrncpy(g_pcFilenames[ui32ItemCount], g_sFileInfo.fname,
+ MAX_FILENAME_STRING_LEN);
+ }
+ ListBoxTextAdd(&g_sDirList, g_pcFilenames[ui32ItemCount]);
+ }
+
+ //
+ // Move to the next entry in the item array we use to populate the
+ // list box.
+ //
+ ui32ItemCount++;
+ }
+
+ //
+ // Made it to here, return with no errors.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+// The listbox widget callback function.
+//
+// This function is called whenever someone changes the selected entry in the
+// listbox containing the files and directories found in the current directory.
+//
+//*****************************************************************************
+void OnListBoxChange(tWidget *psWidget, int16_t i16Selected)
+{
+ int16_t i16Sel;
+
+ //
+ // Get the current selection from the list box.
+ //
+ i16Sel = ListBoxSelectionGet(&g_sDirList);
+
+ //
+ // Is there any selection?
+ //
+ if(i16Sel == -1)
+ {
+ return;
+ }
+ else
+ {
+ //
+ // Is the selection a directory name.
+ //
+ if(g_pcFilenames[i16Sel][0] == '+')
+ {
+ if((g_ePlayState != AUDIO_PLAYING) &&
+ (g_ePlayState != AUDIO_PAUSED))
+ {
+ CanvasTextSet(&g_sWaveInfoFileName, "");
+ CanvasTextSet(&g_sWaveInfoSample, "");
+ g_pcTime[0] = 0;
+ }
+
+ if(ChangeToDirectory(&g_pcFilenames[i16Sel][2]) == FR_OK)
+ {
+ PopulateFileListBox(true);
+ }
+ }
+ else if((g_pcFilenames[i16Sel][0] == '.') &&
+ (g_pcFilenames[i16Sel][1] == '.'))
+ {
+ //
+ // If this was the .. selection then move up a directory.
+ //
+ if(ChangeToDirectory("..") == FR_OK)
+ {
+ PopulateFileListBox(true);
+ }
+
+ if((g_ePlayState != AUDIO_PLAYING) &&
+ (g_ePlayState != AUDIO_PAUSED))
+ {
+ CanvasTextSet(&g_sWaveInfoFileName, "");
+ CanvasTextSet(&g_sWaveInfoSample, "");
+ g_pcTime[0] = 0;
+ }
+ }
+ else
+ {
+ //
+ // This was a normal file selections so see if it is a wav file.
+ //
+ CanvasTextSet(&g_sWaveInfoFileName, g_pcFilenames[i16Sel]);
+
+ if((g_ePlayState == AUDIO_PLAYING) ||
+ (g_ePlayState == AUDIO_PAUSED))
+ {
+ WaveStop();
+ }
+
+ if(WavOpen(g_pcFilenames[i16Sel], &g_sWavFile) == 0)
+ {
+ //
+ // Read the .wav file format.
+ //
+ WavGetFormat(&g_sWavFile, &g_sWavHeader);
+
+ //
+ // Print the formatted string so that it can be
+ // displayed.
+ //
+ usprintf(g_pcFormat, "%d Hz %d bit ",
+ g_sWavHeader.ui32SampleRate,
+ g_sWavHeader.ui16BitsPerSample);
+
+ //
+ // Concatenate the number of channels.
+ //
+ if(g_sWavHeader.ui16NumChannels == 1)
+ {
+ strcat(g_pcFormat, "Mono");
+ }
+ else
+ {
+ strcat(g_pcFormat, "Stereo");
+ }
+
+ CanvasTextSet(&g_sWaveInfoSample, g_pcFormat);
+
+ //
+ // Calculate the minutes and seconds in the file.
+ //
+ g_ui16Seconds = g_sWavHeader.ui32DataSize /
+ g_sWavHeader.ui32AvgByteRate;
+ g_ui16Minutes = g_ui16Seconds / 60;
+ g_ui16Seconds -= g_ui16Minutes * 60;
+
+ //
+ // Close the file, it will be re-opened on play.
+ //
+ WavClose(&g_sWavFile);
+
+ //
+ // Update the file time information.
+ //
+ DisplayTime(1);
+
+ //
+ // Close the file, it will be re-opened on play.
+ //
+ WavClose(&g_sWavFile);
+ }
+ else
+ {
+ CanvasTextSet(&g_sWaveInfoSample, "");
+ g_pcTime[0] = 0;
+ WidgetPaint((tWidget *)&g_sWaveInfoTime);
+ }
+ }
+
+ //
+ // Update the file name and wav file information
+ //
+ WidgetPaint((tWidget *)&g_sWaveInfoFileName);
+ WidgetPaint((tWidget *)&g_sWaveInfoSample);
+ WidgetPaint((tWidget *)&g_sWaveInfoTime);
+ }
+}
+
+//*****************************************************************************
+//
+// This is the handler for this SysTick interrupt. FatFs requires a
+// timer tick every 10ms for internal timing purposes.
+//
+//*****************************************************************************
+void
+SysTickHandler(void)
+{
+ //
+ // Call the FatFs tick timer.
+ //
+ disk_timerproc();
+}
+
+//*****************************************************************************
+//
+// Initializes the file system module.
+//
+// \param None.
+//
+// This function initializes the third party FAT implementation.
+//
+// \return Returns \e true on success or \e false on failure.
+//
+//*****************************************************************************
+static bool
+FileInit(void)
+{
+ //
+ // Mount the file system, using logical disk 0.
+ //
+ if(f_mount(0, &g_sFatFs) != FR_OK)
+ {
+ return(false);
+ }
+ return(true);
+}
+
+//*****************************************************************************
+//
+// This function handled global level events for the USB host audio. This
+// function was passed into the USBSoundInit() function.
+//
+//*****************************************************************************
+static void
+AudioEvent(uint32_t ui32Event, uint32_t ui32Param)
+{
+ switch(ui32Event)
+ {
+ case SOUND_EVENT_READY:
+ {
+ //
+ // Flag that a new audio device is present.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 1;
+
+ //
+ // Change the text to reflect the change.
+ //
+ CanvasTextSet(&g_sStatusText, "Ready ");
+ WidgetPaint((tWidget *)&g_sStatusText);
+
+ break;
+ }
+ case SOUND_EVENT_DISCONNECT:
+ {
+ //
+ // If playing return the button text to play.
+ //
+ if(g_ePlayState == AUDIO_PLAYING)
+ {
+ PushButtonTextSet(&g_sPlayPause, g_pcPlay);
+ WidgetPaint((tWidget *)&g_sPlayPause);
+
+ WaveStop();
+ }
+
+ //
+ // Device is no longer present.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 0;
+ g_ePlayState = AUDIO_NONE;
+
+ //
+ // Change the text to reflect the change.
+ //
+ CanvasTextSet(&g_sStatusText, g_pcNoDevice);
+ WidgetPaint((tWidget *)&g_sStatusText);
+
+ break;
+ }
+ case SOUND_EVENT_UNKNOWN_DEV:
+ {
+ if(ui32Param == 1)
+ {
+ //
+ // .
+ //
+ CanvasTextSet(&g_sStatusText, g_pcUnknownDevice);
+ }
+ else
+ {
+ //
+ // Unknown device disconnected.
+ //
+ CanvasTextSet(&g_sStatusText, g_pcNoDevice);
+ }
+ WidgetPaint((tWidget *)&g_sStatusText);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// The program main function. It performs initialization, then handles wav
+// file playback.
+//
+//*****************************************************************************
+int
+main(void)
+{
+ uint32_t ui32Temp;
+ uint32_t ui32Retcode, ui32PLLRate;
+#ifdef USE_ULPI
+ uint32_t ui32Setting;
+#endif
+
+ //
+ // Set the system clock to run at 120MHz from the PLL.
+ //
+ g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
+ SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
+ SYSCTL_CFG_VCO_480), 120000000);
+
+ //
+ // Configure the device pins.
+ //
+ PinoutSet();
+
+#ifdef USE_ULPI
+ //
+ // Switch the USB ULPI Pins over.
+ //
+ USBULPIPinoutSet();
+
+ //
+ // Enable USB ULPI with high speed support.
+ //
+ ui32Setting = USBLIB_FEATURE_ULPI_HS;
+ USBOTGFeatureSet(0, USBLIB_FEATURE_USBULPI, &ui32Setting);
+
+ //
+ // Setting the PLL frequency to zero tells the USB library to use the
+ // external USB clock.
+ //
+ ui32PLLRate = 0;
+#else
+ //
+ // Save the PLL rate used by this application.
+ //
+ ui32PLLRate = 480000000;
+#endif
+
+ //
+ // Configure SysTick for a 100Hz interrupt.
+ //
+ ROM_SysTickPeriodSet(g_ui32SysClock / TICKS_PER_SECOND);
+ ROM_SysTickEnable();
+ ROM_SysTickIntEnable();
+
+ //
+ // Enable Interrupts
+ //
+ ROM_IntMasterEnable();
+
+ //
+ // Initialize the display driver.
+ //
+ Kentec320x240x16_SSD2119Init(g_ui32SysClock);
+
+ //
+ // Initialize the graphics context.
+ //
+ GrContextInit(&g_sContext, &g_sKentec320x240x16_SSD2119);
+
+ //
+ // Draw the application frame.
+ //
+ FrameDraw(&g_sContext, "usb-host-audio");
+
+ //
+ // Initialize the touch screen driver.
+ //
+ TouchScreenInit(g_ui32SysClock);
+
+ //
+ // Set the touch screen event handler.
+ //
+ TouchScreenCallbackSet(WidgetPointerMessage);
+
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sDirList);
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sStatusPanel);
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sWaveInfoBackground);
+
+ //
+ // Issue the initial paint request to the widgets then immediately call
+ // the widget manager to process the paint message. This ensures that the
+ // display is drawn as quickly as possible and saves the delay we would
+ // otherwise experience if we processed the paint message after mounting
+ // and reading the SD card.
+ //
+ WidgetPaint(WIDGET_ROOT);
+ WidgetMessageQueueProcess();
+
+ //
+ // Determine whether or not an SD Card is installed. If not, print a
+ // warning and have the user install one and restart.
+ //
+ ui32Retcode = disk_initialize(0);
+
+ if(ui32Retcode != RES_OK)
+ {
+ CanvasTextSet(&g_sStatusText, "File system error! ");
+ WidgetPaint((tWidget *)&g_sStatusText);
+ while(1);
+ }
+ else
+ {
+ //
+ // Mount the file system, using logical disk 0.
+ //
+ f_mount(0, &g_sFatFs);
+
+ if(!FileInit())
+ {
+ CanvasTextSet(&g_sStatusText, "File system error! ");
+ WidgetPaint((tWidget *)&g_sStatusText);
+ while(1);
+ }
+ }
+
+ //
+ // Not playing anything right now.
+ //
+ g_ui32Flags = 0;
+ g_ePlayState = AUDIO_NONE;
+
+ PopulateFileListBox(true);
+
+ //
+ // Tell the USB library the CPU clock and the PLL frequency. This is a
+ // new requirement for TM4C129 devices.
+ //
+ USBHCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &g_ui32SysClock);
+ USBHCDFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate);
+
+ //
+ // Configure the USB host output.
+ //
+ USBSoundInit(0, AudioEvent);
+
+ //
+ // Enter an (almost) infinite loop for reading and processing commands from
+ // the user.
+ //
+ while(1)
+ {
+ //
+ // On connect change the device state to ready.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT))
+ {
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 0;
+
+ //
+ // Getting here means the device is ready.
+ // Reset the CWD to the root directory.
+ //
+ g_pcCwdBuf[0] = '/';
+ g_pcCwdBuf[1] = 0;
+
+ //
+ // Initiate a directory change to the root. This will
+ // populate a menu structure representing the root directory.
+ //
+ if(ChangeToDirectory("/") == FR_OK)
+ {
+ //
+ // Request a repaint so the file menu will be shown
+ //
+ WidgetPaint(WIDGET_ROOT);
+ }
+ else
+ {
+ CanvasTextSet(&g_sStatusText,
+ "Error accessing root directory ");
+ WidgetPaint((tWidget *)&g_sStatusText);
+ while(1);
+ }
+
+ //
+ // Attempt to set the audio format to 44100 16 bit stereo by
+ // default otherwise try 48000 16 bit stereo.
+ //
+ if(USBSoundOutputFormatSet(44100, 16, 2) == 0)
+ {
+ ui32Temp = 44100;
+ }
+ else if(USBSoundOutputFormatSet(48000, 16, 2) == 0)
+ {
+ ui32Temp = 48000;
+ }
+ else
+ {
+ ui32Temp = 0;
+ }
+
+ //
+ // If the audio device was support put the sample rate in the
+ // status line.
+ //
+ if(ui32Temp != 0)
+ {
+ //
+ // Calculate the number of bytes per USB frame.
+ //
+ g_ui32TransferSize = (ui32Temp * 4) / 1000;
+
+ //
+ // Calculate the size of the audio buffer.
+ //
+ g_ui32BufferSize = AUDIO_BUFFERS * g_ui32TransferSize;
+
+ //
+ // Print the time string in the format mm.ss/mm.ss
+ //
+ if(ui32Temp == 44100)
+ {
+ CanvasTextSet(&g_sStatusText, "44.1 kHz Ready ");
+ }
+ else if(ui32Temp == 48000)
+ {
+ CanvasTextSet(&g_sStatusText, "48 kHz Ready ");
+ }
+
+ g_ePlayState = AUDIO_STOPPED;
+ }
+ else
+ {
+ CanvasTextSet(&g_sStatusText, "Unsupported Audio Device ");
+ g_ePlayState = AUDIO_NONE;
+ }
+ WidgetPaint((tWidget *)&g_sStatusText);
+ }
+
+ //
+ // Handle the case when the wave file is playing.
+ //
+ if(g_ePlayState == AUDIO_PLAYING)
+ {
+ //
+ // Handle the transmit complete event.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE))
+ {
+ //
+ // Clear the transmit complete flag.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE) = 0;
+
+ //
+ // If there is space available then fill the buffer.
+ //
+ FillAudioBuffer();
+
+ //
+ // If we run out of valid bytes then stop the playback.
+ //
+ if(g_ui32ValidBytes == 0)
+ {
+ //
+ // No more data or error so stop playing.
+ //
+ WaveStop();
+ }
+
+ //
+ // Update the real display time.
+ //
+ DisplayTime(0);
+ }
+ }
+
+ //
+ // Need to periodically call the USBSoundMain() routine so that
+ // non-interrupt gets a chance to run.
+ //
+ USBSoundMain();
+
+ //
+ // Process any messages in the widget message queue.
+ //
+ WidgetMessageQueueProcess();
+ }
+}
+