This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

基于TM4C123x的 文件操作与WAV音频播放实验程序解析



//文件操作与WAV音频播放实验程序解析

//头文件
#include <string.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "grlib/grlib.h"
#include "utils/cmdline.h"
#include "utils/uartstdio.h"
#include "fatfs/src/ff.h"
#include "fatfs/src/diskio.h"
#include "drivers/cfal96x64x16.h"
#include "drivers/buttons.h"

//*****************************************************************************
// Defines the size of the buffers that hold the path, or temporary data from
// the SD card.  There are two buffers allocated of this size.  The buffer size
// must be large enough to hold the longest expected full path name, including
// the file name, and a trailing null character.
//*****************************************************************************
#define PATH_BUF_SIZE           80

//*****************************************************************************
// Defines the size of the buffer that holds the command line.
//*****************************************************************************
#define CMD_BUF_SIZE            64

//*****************************************************************************
// This buffer holds the full path to the current working directory.  Initially
// it is root ("/").
//*****************************************************************************
static char g_cCwdBuf[PATH_BUF_SIZE] = "/";

//*****************************************************************************
// A temporary data buffer used when manipulating file paths, or reading data
// from the SD card.
//*****************************************************************************
static char g_cTmpBuf[PATH_BUF_SIZE];

//*****************************************************************************
// A flag to indicate the select button was pressed.
//*****************************************************************************
static volatile unsigned char bSelectPressed = 0;
static volatile unsigned char bUpPressed = 0;
static volatile unsigned char bDownPressed = 0;
static volatile unsigned char bLeftPressed = 0;
static volatile unsigned char bRightPressed = 0;

//*****************************************************************************
// The buffer that holds the command line.
//*****************************************************************************
static char g_cCmdBuf[CMD_BUF_SIZE];

//*****************************************************************************
// The following are data structures used by FatFs.
//*****************************************************************************
static FATFS g_sFatFs;
static DIR g_sDirObject;
static FILINFO g_sFileInfo;
static FIL g_sFileObject;
tRectangle sRect;
char tmp[100];
char cover[20]="                   ";

//*****************************************************************************
// A structure that holds a mapping between an FRESULT numerical code, and a
// string representation.  FRESULT codes are returned from the FatFs FAT file
// system driver.
//*****************************************************************************
typedef struct
{
    FRESULT fresult;
    char *pcResultStr;
}
tFresultString;

//wav文件头数据类型
typedef struct _tagMsWavPcmHeader44{
	char ChunkID[4]; // "RIFF"; The "RIFF" the mainchunk;
	unsigned long ChunkSize; // FileSize - 8; The size following this data
	char Format[4]; // "WAVE"; The "WAVE" format consists of two subchunks: "fmt " and "data"
	 
	char SubChunk1ID[4]; // "fmt "
	unsigned long SubChunk1Size; // 16 for PCM. This is the size of the rest of the subchunk which follows this data.
	unsigned short AudioFormat; // 1 for PCM. Linear quantization
	unsigned short NumChannels; // 1->Mono, 2->stereo, etc..
	unsigned long SampleRate; // 8000, 11025, 16000, 44100, 48000, etc..
	unsigned long ByteRate; // = SampleRate * NumChannels * BitsPerSample/8
	unsigned short BlockAlign; // = NumChannels * BitsPerSample / 8
	unsigned short BitsPerSample; // 8->8bits, 16->16bits, etc..
	 
	char SubChunk2ID[4]; // "data"
	unsigned long SubChun2Size; // = NumSamples * NumChannels * BitsPerSample / 8. The size of data
} wav_pcm_header44;


//*****************************************************************************
// A macro to make it easy to add result codes to the table.
//*****************************************************************************
#define FRESULT_ENTRY(f)        { (f), (#f) }

//*****************************************************************************
// A table that holds a mapping between the numerical FRESULT code and it's
// name as a string.  This is used for looking up error codes for printing to
// the console.
//*****************************************************************************
tFresultString g_sFresultStrings[] =
{
    FRESULT_ENTRY(FR_OK),
    FRESULT_ENTRY(FR_NOT_READY),
    FRESULT_ENTRY(FR_NO_FILE),
    FRESULT_ENTRY(FR_NO_PATH),
    FRESULT_ENTRY(FR_INVALID_NAME),
    FRESULT_ENTRY(FR_INVALID_DRIVE),
    FRESULT_ENTRY(FR_DENIED),
    FRESULT_ENTRY(FR_EXIST),
    FRESULT_ENTRY(FR_RW_ERROR),
    FRESULT_ENTRY(FR_WRITE_PROTECTED),
    FRESULT_ENTRY(FR_NOT_ENABLED),
    FRESULT_ENTRY(FR_NO_FILESYSTEM),
    FRESULT_ENTRY(FR_INVALID_OBJECT),
    FRESULT_ENTRY(FR_MKFS_ABORTED)
};

//*****************************************************************************
// A macro that holds the number of result codes.
//*****************************************************************************
#define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))

//*****************************************************************************
// Graphics context used to show text on the CSTN display.
//*****************************************************************************
tContext g_sContext;

//*****************************************************************************
// This function returns a string representation of an error code that was
// returned from a function call to FatFs.  It can be used for printing human
// readable error messages.
//*****************************************************************************
const char *
StringFromFresult(FRESULT fresult)
{
    unsigned int uIdx;

    //
    // Enter a loop to search the error code table for a matching error code.
    //
    for(uIdx = 0; uIdx < NUM_FRESULT_CODES; uIdx++)
    {
        //
        // If a match is found, then return the string name of the error code.
        //
        if(g_sFresultStrings[uIdx].fresult == fresult)
        {
            return(g_sFresultStrings[uIdx].pcResultStr);
        }
    }

    //
    // At this point no matching code was found, so return a string indicating
    // an unknown error.
    //
    return("UNKNOWN ERROR CODE");
}

//*****************************************************************************
// This is the handler for this SysTick interrupt.  FatFs requires a timer tick
// every 10 ms for internal timing purposes.
//*****************************************************************************
void
SysTickHandler(void)
{
    //
    // Call the FatFs tick timer.
    //
	unsigned char ucData, ucDelta;
    disk_timerproc();

    // Get buttons status using button debouncer driver
    ucData = ButtonsPoll(&ucDelta, 0);

    // See if the select button was just pressed.
    if(BUTTON_PRESSED(SELECT_BUTTON, ucData, ucDelta))
    {
        bSelectPressed = 1;
    }
    if(BUTTON_RELEASED(SELECT_BUTTON, ucData, ucDelta))
    {
        bSelectPressed = 0;
    }

	if(BUTTON_PRESSED(UP_BUTTON, ucData, ucDelta))
    {
        bUpPressed = 1;
    }
    if(BUTTON_RELEASED(UP_BUTTON, ucData, ucDelta))
    {
        bUpPressed = 0;
    }

	   
	if(BUTTON_PRESSED(DOWN_BUTTON, ucData, ucDelta))
    {
        bDownPressed = 1;
    }
    if(BUTTON_RELEASED(DOWN_BUTTON, ucData, ucDelta))
    {
        bDownPressed = 0;
    }

   	if(BUTTON_PRESSED(LEFT_BUTTON, ucData, ucDelta))
    {
        bLeftPressed = 1;
    }
    if(BUTTON_RELEASED(LEFT_BUTTON, ucData, ucDelta))
    {
        bLeftPressed = 0;
    }

	if(BUTTON_PRESSED(RIGHT_BUTTON, ucData, ucDelta))
    {
        bRightPressed = 1;
    }
    if(BUTTON_RELEASED(RIGHT_BUTTON, ucData, ucDelta))
    {
        bRightPressed = 0;
    }
}

//*****************************************************************************
// This function implements the "ls" command.  It opens the current directory
// and enumerates through the contents, and prints a line for each item it
// finds.  It shows details such as file attributes, time and date, and the
// file size, along with the name.  It shows a summary of file sizes at the end
// along with free space.
//*****************************************************************************
int
Cmd_ls(int argc, char *argv[])
{
    unsigned long ulTotalSize;
    unsigned long ulFileCount;
    unsigned long ulDirCount;
    FRESULT fresult;
    FATFS *pFatFs;

    // Open the current directory for access.
    fresult = f_opendir(&g_sDirObject, g_cCwdBuf);

    // Check for error and return if there is a problem.
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    ulTotalSize = 0;
    ulFileCount = 0;
    ulDirCount = 0;

    // Give an extra blank line before the listing.
    UARTprintf("\n");

    // Enter loop to enumerate through all directory entries.
    for(;;)
    {
        // Read an entry from the directory.
        fresult = f_readdir(&g_sDirObject, &g_sFileInfo);


        // Check for error and return if there is a problem.
        if(fresult != FR_OK)
        {
            return(fresult);
        }

        // If the file name is blank, then this is the end of the listing.
        if(!g_sFileInfo.fname[0])
        {
            break;
        }

        // If the attribue is directory, then increment the directory count.
        if(g_sFileInfo.fattrib & AM_DIR)
        {
            ulDirCount++;
        }

        // Otherwise, it is a file.  Increment the file count, and add in the
        // file size to the total.
        else
        {
            ulFileCount++;
            ulTotalSize += g_sFileInfo.fsize;
        }

        // Print the entry information on a single line with formatting to show
        // the attributes, date, time, size, and name.
        UARTprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s\n",
                    (g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
                    (g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
                    (g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
                    (g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
                    (g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
                    (g_sFileInfo.fdate >> 9) + 1980,
                    (g_sFileInfo.fdate >> 5) & 15,
                     g_sFileInfo.fdate & 31,
                    (g_sFileInfo.ftime >> 11),
                    (g_sFileInfo.ftime >> 5) & 63,
                     g_sFileInfo.fsize,
                     g_sFileInfo.fname);
    }

    // Print summary lines showing the file, dir, and size totals.
    UARTprintf("\n%4u File(s),%10u bytes total\n%4u Dir(s)",
                ulFileCount, ulTotalSize, ulDirCount);

    // Get the free space.
    fresult = f_getfree("/", &ulTotalSize, &pFatFs);

    // Check for error and return if there is a problem.
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    // Display the amount of free space that was calculated.
    UARTprintf(", %10uK bytes free\n", ulTotalSize * pFatFs->sects_clust / 2);

    // Made it to here, return with no errors.
    return(0);
}

//*****************************************************************************
// This function implements the "cd" command.  It takes an argument that
// specifies the directory to make the current working directory.  Path
// separators must use a forward slash "/".  The argument to cd 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.
//*****************************************************************************
int
Cmd_cd(int argc, char *argv[])
{
    unsigned int uIdx;
    FRESULT fresult;

    // Copy the current working path into a temporary buffer so it can be
    // manipulated.
    strcpy(g_cTmpBuf, g_cCwdBuf);

    // If the first character is /, then this is a fully specified path, and it
    // should just be used as-is.
    if(argv[1][0] == '/')
    {
        // Make sure the new path is not bigger than the cwd buffer.
        if(strlen(argv[1]) + 1 > sizeof(g_cCwdBuf))
        {
            UARTprintf("Resulting path name is too long\n");
            return(0);
        }

        // 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_cTmpBuf, argv[1], sizeof(g_cTmpBuf));
        }
    }

    // If the argument is .. then attempt to remove the lowest level on the
    // CWD.
    else if(!strcmp(argv[1], ".."))
    {
        // Get the index to the last character in the current path.
        uIdx = strlen(g_cTmpBuf) - 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_cTmpBuf[uIdx] != '/') && (uIdx > 1))
        {
            //
            // Back up one character.
            //
            uIdx--;
        }

        // 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_cTmpBuf[uIdx] = 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_cTmpBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cCwdBuf))
        {
            UARTprintf("Resulting path name is too long\n");
            return(0);
        }

        // 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_cTmpBuf, "/"))
            {
                strcat(g_cTmpBuf, "/");
            }

            // Append the new directory to the path.
            strcat(g_cTmpBuf, argv[1]);
        }
    }

    // At this point, a candidate new directory path is in chTmpBuf.  Try to
    // open it to make sure it is valid.
    fresult = f_opendir(&g_sDirObject, g_cTmpBuf);

    // If it can't be opened, then it is a bad path.  Inform the user and
    // return.
    if(fresult != FR_OK)
    {
        UARTprintf("cd: %s\n", g_cTmpBuf);
        return(fresult);
    }

    // Otherwise, it is a valid new path, so copy it into the CWD.
    else
    {
        strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
    }

    // Return success.
    return(0);
}

//*****************************************************************************
// This function implements the "pwd" command.  It simply prints the current
// working directory.
//*****************************************************************************
int
Cmd_pwd(int argc, char *argv[])
{
    // Print the CWD to the console.
    UARTprintf("%s\n", g_cCwdBuf);

    // Return success.
    return(0);
}

//*****************************************************************************
// This function implements the "cat" command.  It reads the contents of a file
// and prints it to the console.  This should only be used on text files.  If
// it is used on a binary file, then a bunch of garbage is likely to printed on
// the console.
//*****************************************************************************
int
Cmd_cat(int argc, char *argv[])
{
    FRESULT fresult;
    unsigned short usBytesRead;

    // First, check to make sure that the current path (CWD), plus the file
    // name, plus a separator and trailing null, will all fit in the temporary
    // buffer that will be used to hold the file name.  The file name must be
    // fully specified, with path, to FatFs.
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }

    // Copy the current path to the temporary buffer so it can be manipulated.
    strcpy(g_cTmpBuf, g_cCwdBuf);

    // If not already at the root level, then append a separator.
    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    // Now finally, append the file name to result in a fully specified file.
    strcat(g_cTmpBuf, argv[1]);

    // Open the file for reading.
    fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);

    // If there was some problem opening the file, then return an error.
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    // Enter a loop to repeatedly read data from the file and display it, until
    // the end of the file is reached.
    do
    {
        // Read a block of data from the file.  Read as much as can fit in the
        // temporary buffer, including a space for the trailing null.
        fresult = f_read(&g_sFileObject, g_cTmpBuf, sizeof(g_cTmpBuf) - 1,
                         &usBytesRead);

        // If there was an error reading, then print a newline and return the
        // error to the user.
        if(fresult != FR_OK)
        {
            UARTprintf("\n");
            return(fresult);
        }

        // Null terminate the last block that was read to make it a null
        // terminated string that can be used with printf.
        g_cTmpBuf[usBytesRead] = 0;

        // Print the last chunk of the file that was received.
        UARTprintf("%s", g_cTmpBuf);
    }
    while(usBytesRead == sizeof(g_cTmpBuf) - 1);
   
    // Return success.
    return(0);
}

//*****************************************************************************
// This function implements the "help" command.  It prints a simple list of the
// available commands with a brief description.
//*****************************************************************************
int
Cmd_help(int argc, char *argv[])
{
    tCmdLineEntry *pEntry;

    // Print some header text.
    UARTprintf("\nAvailable commands\n");
    UARTprintf("------------------\n");

    // Point at the beginning of the command table.
    pEntry = &g_sCmdTable[0];

    // Enter a loop to read each entry from the command table.  The end of the
    // table has been reached when the command name is NULL.
    while(pEntry->pcCmd)
    {
        // Print the command name and the brief description.
        UARTprintf("%s%s\n", pEntry->pcCmd, pEntry->pcHelp);

        // Advance to the next entry in the table.
        pEntry++;
    }

    // Return success.
    return(0);
}
////////////////////////////////////////////////////////////////////////////////////////////////////

//在SD卡上建立新文件
int
Cmd_new(int argc, char *argv[])
{
    FRESULT fresult;

    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }

    strcpy(g_cTmpBuf, g_cCwdBuf);

    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    strcat(g_cTmpBuf, argv[1]);

    fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_CREATE_NEW);

    if(fresult != FR_OK)
    {
        return(fresult);
    }
	UARTprintf("File %s has been built\n",argv[1]);
    return(0);
}
//////////////////////////////////////////////////////////////////////////////

//删除SD卡上的选定文件
int
Cmd_delete(int argc, char *argv[])
{
    FRESULT fresult;
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }

    strcpy(g_cTmpBuf, g_cCwdBuf);

    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    strcat(g_cTmpBuf, argv[1]);

    fresult = f_unlink(g_cTmpBuf); //删除文件

    if(fresult != FR_OK)
    {
        return(fresult);
    }
	UARTprintf("File %s has been deleted\n",argv[1]);
    return(0);
}
//////////////////////////////////////////////////////////////////////////////////

//向SD卡上的文本文件末尾追加更新内容(uart输入)
int
Cmd_edit(int argc, char *argv[])
{
    FRESULT fresult;
    unsigned short usBytesedit;
	int strl;
	f_mount(0, NULL);		 			//更新扇区,避免原内存数据干扰更新信息的写入
	fresult = f_mount(0, &g_sFatFs);
    if(fresult != FR_OK)
    {
        UARTprintf("f_mount error: %s\n", StringFromFresult(fresult));
        return(1);
    }
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }
	
    strcpy(g_cTmpBuf, g_cCwdBuf);

    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    strcat(g_cTmpBuf, argv[1]);	
	UARTprintf("%d\n",g_sFileObject.fsize);
    fresult = f_open(&g_sFileObject, g_cTmpBuf,  FA_WRITE);
	
    if(fresult != FR_OK)
    {
        return(fresult);
    }

	UARTprintf(">");
    strl=UARTgets(tmp, sizeof(tmp));
	
	fresult = f_lseek(&g_sFileObject, g_sFileObject.fsize);	//移动读写指针到文件末尾
	if(fresult != FR_OK)
    {
        return(fresult);
    }   
    fresult = f_write(&g_sFileObject, tmp, strl,
	                         &usBytesedit);					//写入数据
	if(fresult != FR_OK)
	{
	   	UARTprintf("\n");
	   	return(fresult);
	}  

	UARTprintf("Succeed!");
	f_close(&g_sFileObject);
    return(0);
}
///////////////////////////////////////////////////////////////////////////////////

//在评估板上的显示器上显示对应文件名的文本文件的内容
//g_cTmpBuf[]为存储文件名的字符串
int showtxt(char* g_cTmpBuf)
{
  	FRESULT fresult;
    unsigned short usBytesRead,i=0,j=0,modified=0;			//i为已读出的文本行数(10字符一行),j=0表示为首次更新
	char txt1[11],txt2[11],txt3[11],txt4[11],tmp[11];
	txt1[0]=0;txt2[0]=0;txt3[0]=0;txt4[0]=0;  				//更新用临时变量

	fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);
	if(fresult != FR_OK)
    {
        return(0);
    }

    do
    {
 	 	if (bSelectPressed==1) break;//按下Select键程序退出

		if (bDownPressed==1||(i<4&&j==0))//首次更新或者按下down键,显示内容下移
		{
			f_lseek(&g_sFileObject, (DWORD)(i*10));	//移动到读取位置
			fresult = f_read(&g_sFileObject, tmp, 10,
                         &usBytesRead);
			if(fresult != FR_OK)	return(0);
			tmp[usBytesRead]=0;
			if(usBytesRead!=0) 			//未到文件末尾		
			{
     			strcpy(txt1,txt2);
         		strcpy(txt2,txt3); 
         		strcpy(txt3,txt4);
				strcpy(txt4,tmp);
				i++;
				modified=1;
			}
			if (i==4||usBytesRead==0) j=1;
			if (i>4) SysCtlDelay(SysCtlClockGet() / 12);//暂停等待按键状态更新	
		}

		if (bUpPressed==1&&i>4)
		{
			f_lseek(&g_sFileObject, (DWORD)((i-5)*10));
			fresult = f_read(&g_sFileObject, tmp, 10,
                         &usBytesRead);
			if(fresult != FR_OK)	return(0);
			tmp[usBytesRead]=0;
			strcpy(txt4,txt3);
         	strcpy(txt3,txt2); 
         	strcpy(txt2,txt1);
			strcpy(txt1,tmp);
			i--;
			modified=1;
			SysCtlDelay(SysCtlClockGet() / 12);	
		}

		if (i>3&&modified==1) //若显示状态需要更新,更新屏幕输出
		{
			GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, true);
			GrStringDrawCentered(&g_sContext, txt1, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, true);
		    GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, true);
		    GrStringDrawCentered(&g_sContext, txt2, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, true);
			GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, true);   
		    GrStringDrawCentered(&g_sContext, txt3, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, true);
			GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 50, true); 
		    GrStringDrawCentered(&g_sContext, txt4, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 50, true);
			modified=0;		
		}
    }
    while(1);
    UARTprintf("\nDone!\n");
	return (0);
}
////////////////////////////////////////////////////////////////////////////////


//文本显示操作函数
int
Cmd_show(int argc, char *argv[])
{
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }

    strcpy(g_cTmpBuf, g_cCwdBuf);

    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    strcat(g_cTmpBuf, argv[1]);
	UARTprintf("%s",g_cTmpBuf);							//得到有效文件名
	showtxt(g_cTmpBuf);
	GrContextFontSet(&g_sContext, g_pFontFixed6x8);		//回复屏幕输出
	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, true);
    GrStringDrawCentered(&g_sContext, "Connect a", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 20, false);
	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, true);
    GrStringDrawCentered(&g_sContext, "terminal", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 30, false);
	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, true);
    GrStringDrawCentered(&g_sContext, "to UART0.", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 40, false);
	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 50, true);
    GrStringDrawCentered(&g_sContext, "115000,N,8,1", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 50, false);
  	 
    return(0);
}

/////////////////////////////////////////////////////////////////////////////////////////

//刷新屏幕显示的文件列表
//i为屏幕上最后一行对应的文件在列表中位置,j为选定文件在列表中位置
int 
lsupdate(int i,int j,char sf[])
{
	int ulfileCount=0;
	tRectangle sRect1;
	if (j==0) return (i);  	//列表到顶,不更新
	if (j>i) i++;			//列表下移更新
	if (j<=i-4)	i--;		//列表上移更新
	f_opendir(&g_sDirObject, g_cCwdBuf);	//打开路径供读取
	GrContextFontSet(&g_sContext, g_pFontFixed6x8);		//掩盖旧信息
	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, true);

	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, true);

	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, true);

	GrStringDrawCentered(&g_sContext, cover, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 50, true);
								  
	while(1)
	{
		f_readdir(&g_sDirObject, &g_sFileInfo);
		if(!g_sFileInfo.fname[0])
        {
			i=ulfileCount; 
			return (i);
        }
		if(!(g_sFileInfo.fattrib & AM_DIR))
        {
            ulfileCount++;
			if (ulfileCount==j)	//记录选定文件的信息,并在屏幕上进行高亮显示
			{
				strcpy(sf,g_sFileInfo.fname);
				sRect1.sXMin = 0;
				sRect1.sYMin = (3-i+j)*10+17;
    			sRect1.sXMax = GrContextDpyWidthGet(&g_sContext) - 1;
    			sRect1.sYMax = (3-i+j)*10+23;
    			GrContextForegroundSet(&g_sContext, ClrDarkGreen);
    			GrRectFill(&g_sContext, &sRect1);
				GrContextForegroundSet(&g_sContext, ClrWhite);
				
			}
			//更新屏幕列表信息,共四行
			if (ulfileCount==i-3)
			{			
	    		if (i!=3) 
				{
					GrContextFontSet(&g_sContext, g_pFontFixed6x8);
					GrStringDrawCentered(&g_sContext, g_sFileInfo.fname, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, false);
				}
			}
			if (ulfileCount==i-2)
			{	
				GrContextFontSet(&g_sContext, g_pFontFixed6x8);		
	    		GrStringDrawCentered(&g_sContext, g_sFileInfo.fname, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, false);
			}
			if (ulfileCount==i-1)
			{		
				GrContextFontSet(&g_sContext, g_pFontFixed6x8);		
	    		GrStringDrawCentered(&g_sContext, g_sFileInfo.fname, -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, false);
			}
			
			if (ulfileCount==i)
			{
				GrContextFontSet(&g_sContext, g_pFontFixed6x8);			
	    		GrStringDrawCentered(&g_sContext, g_sFileInfo.fname, -1,
	                         GrContextDpyWidthGet(&g_sContext) / 2, 50, false);
				break;
			}		  
		}
	}
	return (i);	
}
///////////////////////////////////////////////////////////////////////////////////////////////

//在评估板上显示屏显示SD卡上的文件列表
int
Cmd_showls(int argc, char *argv[])
{
	 int i=4,j=1,modified=0;
	 //i为屏幕上最后一行对应的文件在列表中位置,j为选定文件在列表中位置,modified为刷新标志
	 int ti,tj;
	 char sf[PATH_BUF_SIZE];
	 ti=i;tj=j;
	 sf[0]=0;
	 i=lsupdate(i,j,sf);
	 while(1)
	 {								 
	 	if (bUpPressed==1&&j!=0)//按下up,选定位置减一
		{
			j--;
			modified=1;
			SysCtlDelay(SysCtlClockGet() / 12);
		}
		if (bDownPressed==1)//按下down,选定位置加一
		{
			j++;
			modified=1;
			SysCtlDelay(SysCtlClockGet() / 12);
		}
		if (modified==1)//跟新屏幕内容
		{
			ti=i;tj=j;
			i=lsupdate(i,j,sf);
			if (j>i) j=i;
			modified=0;
		}
		if (bSelectPressed==1&&j==0)//列表到且按下Select,程序退出,恢复显示内容
		{
			GrContextFontSet(&g_sContext, g_pFontFixed6x8);
			GrStringDrawCentered(&g_sContext, cover, -1,
				                         GrContextDpyWidthGet(&g_sContext) / 2, 20, true);
		    GrStringDrawCentered(&g_sContext, "Connect a", -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 20, false);
			GrStringDrawCentered(&g_sContext, cover, -1,
				                         GrContextDpyWidthGet(&g_sContext) / 2, 30, true);
		    GrStringDrawCentered(&g_sContext, "terminal", -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 30, false);
			GrStringDrawCentered(&g_sContext, cover, -1,
				                         GrContextDpyWidthGet(&g_sContext) / 2, 40, true);
		    GrStringDrawCentered(&g_sContext, "to UART0.", -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 40, false);
			GrStringDrawCentered(&g_sContext, cover, -1,
				                         GrContextDpyWidthGet(&g_sContext) / 2, 50, true);
		    GrStringDrawCentered(&g_sContext, "115000,N,8,1", -1,
		                         GrContextDpyWidthGet(&g_sContext) / 2, 50, false);
    		return(0);			
		}
		if (bSelectPressed==1&&j!=0)//在浏览界面点击Select,打开选定文件,显示文本内容 
		{
			strcpy(g_cTmpBuf, g_cCwdBuf);
			bSelectPressed=0;
		    if(strcmp("/", g_cCwdBuf))
		    {
		        strcat(g_cTmpBuf, "/");
		    }
		    strcat(g_cTmpBuf, sf);
			SysCtlDelay(SysCtlClockGet() / 4);
			showtxt(g_cTmpBuf);
			SysCtlDelay(SysCtlClockGet() / 4);
			lsupdate(ti,tj,sf);	
		}
	 }

}
///////////////////////////////////////////////////////////////////////////////////////////////

//播放wav格式文件
int 
Cmd_playwav(int argc, char *argv[])
{
    FRESULT fresult;
    unsigned short usBytesRead,i=0,refresh=1;
	volatile unsigned short left,right;
	wav_pcm_header44 hwav;//文件头信息
	char data[64];//数据缓存数组
	unsigned long delaytime;
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UARTprintf("Resulting path name is too long\n");
        return(0);
    }
	strcpy(g_cTmpBuf, g_cCwdBuf);
	if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }
	strcat(g_cTmpBuf, argv[1]);

	fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);
	fresult = f_read(&g_sFileObject, &hwav, sizeof(hwav),&usBytesRead);
	//读入文件信息
	if(fresult != FR_OK)
    {
		UARTprintf("\n");
        return(fresult);
    }
	if (!strcmp(hwav.Format,"WAVE")) return (0);//若文件不为wav文件,退出
	delaytime=ROM_SysCtlClockGet()/hwav.SampleRate;
	delaytime/=3;
	
	ROM_SSIDisable(SSI1_BASE);					//使能SSI模块
    ROM_SysCtlDelay(100);
    ROM_SSIConfigSetExpClk(SSI1_BASE, ROM_SysCtlClockGet(),
                           SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER,
                           hwav.SampleRate*hwav.BitsPerSample, 16);
    ROM_SSIEnable(SSI1_BASE);
	ROM_SysCtlDelay(100);
	while(1)
	{
		if (refresh==1)//每读64字节,更新一次缓存
		{
			fresult = f_read(&g_sFileObject, data,64,&usBytesRead);
			UARTprintf("%d\n",usBytesRead);
			if (usBytesRead==0)	break;
			refresh=0;
		}
		if(fresult != FR_OK)
	    {
			UARTprintf("\n");
	        return(fresult);
	    }
		//根据不同的通道数与比特率进行数据读取,读取信息存储为16bits的left,right
		if (hwav.BitsPerSample==8)
		{
			left=(unsigned short)data[i++];
			if (hwav.NumChannels==1)
			{
			 	right=left;
			}
			else
			{
				right=(unsigned short)data[i++];
			}
		}
		else 
		{
			left=(unsigned short)data[i+1];
			left=left<<8;
			left+=(unsigned short)data[i];
			i+=2;
			if (hwav.NumChannels==1)
			{
				 right=left;
			}
			else 
			{
				right=(unsigned short)data[i+1];
				right=right<<8;
				right+=(unsigned short)data[i];
				i+=2;	
			}
			left=left>>4;
			right=right>>4;		//DAC为12位,舍去四位,
		}	
		left+=0x4000;	//加入控制字		
		right+=0x8000;
		ROM_SSIDataPut(SSI1_BASE, left);
	    while(ROM_SSIBusy(SSI1_BASE))
	    {
	    }
		ROM_SSIDataPut(SSI1_BASE, right);
	    while(ROM_SSIBusy(SSI1_BASE))
	    {
		}  
		SysCtlDelay(delaytime-100);
		if (i==64) 
		{
			i=0;
			refresh=1;
//			UARTprintf("%d\n",left);
		}
		if (usBytesRead<64&&i>=usBytesRead) break;//读到文件尾,退出
	}
	return(0);		
}

//*****************************************************************************
// This is the table that holds the command names, implementing functions, and
// brief description.
//*****************************************************************************
tCmdLineEntry g_sCmdTable[] =
{
    { "help",   Cmd_help,      " : Display list of commands" },
    { "h",      Cmd_help,   "    : alias for help" },
    { "?",      Cmd_help,   "    : alias for help" },
    { "ls",     Cmd_ls,      "   : Display list of files" },
    { "chdir",  Cmd_cd,         ": Change directory" },
    { "cd",     Cmd_cd,      "   : alias for chdir" },
    { "pwd",    Cmd_pwd,      "  : Show current working directory" },
    { "cat",    Cmd_cat,      "  : Show contents of a text file" },
    { "new",    Cmd_new,      "  : Create a new file" },
	{ "delete", Cmd_delete,    "  : Delete a existed file " }, 
	{ "edit",   Cmd_edit,     "  : Edit a existed file " },    
    { "show",   Cmd_show,      "  : show the file content on the screen" },
    { "showls",  Cmd_showls,      "  : show the list of files on the screen" },
    { "playwav",  Cmd_playwav,      "  : play the wav music file" },			 
    { 0, 0, 0 }
};

//*****************************************************************************
// The error routine that is called if the driver library encounters an error.
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
// The program main function.  It performs initialization, then runs a command
// processing loop to read commands from the console.
//*****************************************************************************
int
main(void)
{
    int nStatus;
    FRESULT fresult;
 

    // The FPU should be enabled because some compilers will use floating-
    // point registers, even for non-floating-point code.  If the FPU is not
    // enabled this will cause a fault.  This also ensures that floating-
    // point operations could be added to this application and would work
    // correctly and use the hardware floating-point unit.  Finally, lazy
    // stacking is enabled for interrupt handlers.  This allows floating-
    // point instructions to be used within interrupt handlers, but at the
    // expense of extra stack usage.
    FPUEnable();
    FPULazyStackingEnable();

    // Set the system clock to run at 50MHz from the PLL.
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);

    // Enable the peripherals used by this example.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);

    // Configure SysTick for a 100Hz interrupt.  The FatFs driver wants a 10 ms
    // tick.
    ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / 100);
    ROM_SysTickEnable();
    ROM_SysTickIntEnable();

	//configure the ssi port
	ROM_GPIOPinConfigure(GPIO_PD0_SSI1CLK);
    ROM_GPIOPinConfigure(GPIO_PD1_SSI1FSS);
    ROM_GPIOPinConfigure(GPIO_PD3_SSI1TX);
	ROM_GPIOPinTypeSSI(GPIO_PORTD_BASE, (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_3));
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTG_BASE,GPIO_PIN_3);
	//how to adjust the ssi mode for the high-quality radio
 	ROM_GPIOPinWrite(GPIO_PORTG_BASE,GPIO_PIN_3, GPIO_PIN_3);

    // Enable Interrupts
    ROM_IntMasterEnable();

    // Set GPIO A0 and A1 as UART.
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    // Initialize the UART as a console for text I/O.
    UARTStdioInit(0);

	ButtonsInit();

    // Initialize the display driver.
    CFAL96x64x16Init();

    // Initialize the graphics context.
    GrContextInit(&g_sContext, &g_sCFAL96x64x16);

    // Fill the top part of the screen with blue to create the banner.
    sRect.sXMin = 0;
    sRect.sYMin = 0;
    sRect.sXMax = GrContextDpyWidthGet(&g_sContext) - 1;
    sRect.sYMax = 9;
    GrContextForegroundSet(&g_sContext, ClrDarkBlue);
    GrRectFill(&g_sContext, &sRect);

    // Change foreground for white text.
    GrContextForegroundSet(&g_sContext, ClrWhite);

    // Put the application name in the middle of the banner.
    GrContextFontSet(&g_sContext, g_pFontFixed6x8);
    GrStringDrawCentered(&g_sContext, "sd_card", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 4, 0);

    // Show some instructions on the display
    GrContextFontSet(&g_sContext, g_pFontFixed6x8);
    GrStringDrawCentered(&g_sContext, "Connect a", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 20, false);
    GrStringDrawCentered(&g_sContext, "terminal", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 30, false);
    GrStringDrawCentered(&g_sContext, "to UART0.", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 40, false);
    GrStringDrawCentered(&g_sContext, "115000,N,8,1", -1,
                         GrContextDpyWidthGet(&g_sContext) / 2, 50, false);

    // Print hello message to user.
    UARTprintf("\n\nSD Card Example Program\n");
    UARTprintf("Type \'help\' for help.\n");

    // Mount the file system, using logical disk 0.
    fresult = f_mount(0, &g_sFatFs);
    if(fresult != FR_OK)
    {
        UARTprintf("f_mount error: %s\n", StringFromFresult(fresult));
        return(1);
    }

    // Enter an infinite loop for reading and processing commands from the user.
    while(1)
    {
        //
        // Print a prompt to the console.  Show the CWD.
        //
        UARTprintf("\n%s> ", g_cCwdBuf);

        // Get a line of text from the user.
        UARTgets(g_cCmdBuf, sizeof(g_cCmdBuf));

        // Pass the line from the user to the command processor.  It will be
        // parsed and valid commands executed.
        nStatus = CmdLineProcess(g_cCmdBuf);

        // Handle the case of bad command.
        if(nStatus == CMDLINE_BAD_CMD)
        {
            UARTprintf("Bad command!\n");
        }

        // Handle the case of too many arguments.
        else if(nStatus == CMDLINE_TOO_MANY_ARGS)
        {
            UARTprintf("Too many arguments for command processor!\n");
        }

        // Otherwise the command was executed.  Print the error code if one was returned.
        else if(nStatus != 0)
        {
            UARTprintf("Command returned error code %s\n",
                        StringFromFresult((FRESULT)nStatus));
        }
    }
}