/**********************************************************************
 *
 * USB Memory File Interface and Data Logger
 *   
 **********************************************************************
 * Description:
 * This application is described by AN1145 "Using a USB Flash Drive on an
 * Embedded Host".  The application integrates the Microchip USB Embedded Host
 * stack with the Microchip Memory Disk Drive library to create, manipulate, and
 * display files on a generic USB Flash Drive.
 *
 **********************************************************************
 * FileName:        USB Data Logger.c
 * Dependencies:    USB Host library; project requires File System library
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the “Company”) for its PICmicro® Microcontroller is intended and
 * supplied to you, the Company’s customer, for use solely and
 * exclusively on Microchip PICmicro Microcontroller products. The
 * software is owned by the Company and/or its supplier, and is
 * protected under applicable copyright laws. All rights are reserved.
 * Any use in violation of the foregoing restrictions may subject the
 * user to criminal sanctions under applicable laws, as well as to
 * civil liability for the breach of the terms and conditions of this
 * license.
 * 
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. 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. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Copyright (c) 2008 Microchip Technology, Inc.
 *
 **********************************************************************
 * Change History:
 * ID			Date            Changes
 * KO           10/15/2007		First release
 * C11842		12/01/2008		Modified for 80MHz operation, Updated comments.
***********************************************************************/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "GenericTypeDefs.h"
#include "HardwareProfile.h"
#include "MDD File System\FSIO.h"
#include "USB\usb.h"
#include "plib.h"


// *****************************************************************************
// *****************************************************************************
// Configuration Bits
// *****************************************************************************
// *****************************************************************************

#ifndef OVERRIDE_CONFIG_BITS
        
    #pragma config UPLLEN   = ON            // USB PLL Enabled
    #pragma config FPLLMUL  = MUL_20        // PLL Multiplier
    #pragma config UPLLIDIV = DIV_2         // USB PLL Input Divider
    #pragma config FPLLIDIV = DIV_2         // PLL Input Divider
    #pragma config FPLLODIV = DIV_1         // PLL Output Divider
    #pragma config FPBDIV   = DIV_1         // Peripheral Clock divisor
    #pragma config FWDTEN   = OFF           // Watchdog Timer 
    #pragma config WDTPS    = PS1           // Watchdog Timer Postscale
    #pragma config FCKSM    = CSDCMD        // Clock Switching & Fail Safe Clock Monitor
    #pragma config OSCIOFNC = OFF           // CLKO Enable
    #pragma config POSCMOD  = HS            // Primary Oscillator
    #pragma config IESO     = OFF           // Internal/External Switch-over
    #pragma config FSOSCEN  = OFF           // Secondary Oscillator Enable
    #pragma config FNOSC    = PRIPLL        // Oscillator Selection
    #pragma config CP       = OFF           // Code Protect
    #pragma config BWP      = OFF           // Boot Flash Write Protect
    #pragma config PWP      = OFF           // Program Flash Write Protect
    #pragma config ICESEL   = ICS_PGx2      // ICE/ICD Comm Channel Select
    #pragma config DEBUG    = OFF           // Debugger Disabled for Starter Kit
            
#endif // OVERRIDE_CONFIG_BITS


// *****************************************************************************
// *****************************************************************************
// Constants
// *****************************************************************************
// *****************************************************************************

#define COPY_BUFFER_SIZE            64          // Increase to improve copy efficiency
#define MAX_ALLOWED_CURRENT         500         // Maximum power we can supply in mA
#define MAX_BUFFERED_COMMANDS       8           // Must be a power of 2
#define MAX_COMMAND_LENGTH          50
#define MAX_LOG_BUFFER_SIZE         512
#define NUM_LOG_BUFFERS             2
#define VERSION                     "v1.00"
#define PRINT_LINE_SIZE             75
#define BLOCK_BUFFER_SIZE           512


// RTCC Format Correlation Constants
// The 16-bit and 32-bit architectures use two different formats for the RTCC.
// These constants can be used to access the byte in a four-byte date or time
// array.  Note: No constant is given for the weekday, because the weekday is
// stored in a different byte.  This is done to flag the user to take special
// care when writing code that utilizes the weekday.

#define PIC32MX_TIMER2_INTERRUPT    0x00000100
#define PIC32MX_TIMER3_INTERRUPT    0x00001000

#define DEFAULT_DATE                0x07081503
#define DEFAULT_TIME                0x04210000

#define INDEX_HOURS                 3
#define INDEX_MINUTES               2
#define INDEX_SECONDS               1
#define INDEX_YEAR                  3
#define INDEX_MONTH                 2
#define INDEX_DAY                   1


// We are taking Timer 3 for an acquisition timer.

// NOTE - The datasheet doesn't state this, but the timer does get reset to 0
// after a period register match.  So we don't have to worry about resetting
// the timer manually.

#define STOP_TIMER_IN_IDLE_MODE     0x2000
#define TIMER_SOURCE_INTERNAL       0x0000
#define TIMER_ON                    0x8000
#define GATED_TIME_DISABLED         0x0000
#define TIMER_16BIT_MODE            0x0000

#define TIMER_PRESCALER_1           0x0000
#define TIMER_PRESCALER_2           0x0010
#define TIMER_PRESCALER_4           0x0020
#define TIMER_PRESCALER_8           0x0030
#define TIMER_PRESCALER_16          0x0040
#define TIMER_PRESCALER_32          0x0050
#define TIMER_PRESCALER_64          0x0060
#define TIMER_PRESCALER_256         0x0070


// *****************************************************************************
// *****************************************************************************
// Data Structures
// *****************************************************************************
// *****************************************************************************

typedef struct _COMMAND
{
    char        buffer[MAX_COMMAND_LENGTH];
    BYTE        index;
    BYTE        command;
    BYTE        escFirstChar;
    struct
    {
        BYTE    reading             : 1;
        BYTE    escNeedFirstChar    : 1;
        BYTE    escNeedSecondChar   : 1;
    };
} COMMAND;


typedef enum _COMMANDS
{
    COMMAND_NO_COMMAND,
    COMMAND_CD,
    COMMAND_COPY,
    COMMAND_DATE,
    COMMAND_DEL,
    COMMAND_DIR,
    COMMAND_HELP,
    COMMAND_MD,
    COMMAND_RD,
    COMMAND_REN,
    COMMAND_RESET,
    COMMAND_RESUME,
    COMMAND_SUSPEND,
    COMMAND_TIME,
    COMMAND_TYPE,
    COMMAND_WHO,
    COMMAND_UNKNOWN

} COMMANDS;


typedef struct _LOG_DATA
{
    BYTE        buffer[MAX_LOG_BUFFER_SIZE];
    WORD        index;
    BOOL        bufferFull;
} LOG_DATA;


typedef struct _LOGGER_STATUS
{
    BOOL                mediaPresent;
    union
    {
        BYTE    value;
        struct
        {    
            BYTE        overcurrentStateUSB     : 1;
            BYTE        readingPotentiometer    : 1;
            BYTE        readingTemperature      : 1;
        };
    };

} LOGGER_STATUS;


typedef struct _OLD_COMMANDS
{
    char        lines[MAX_BUFFERED_COMMANDS][MAX_COMMAND_LENGTH];
    BYTE        oldest;
    BYTE        newest;
    BYTE        showing;
} OLD_COMMANDS;


typedef struct _VOLUME_INFO
{
    char        label[12];
    BYTE        valid;
} VOLUME_INFO;


// *****************************************************************************
// *****************************************************************************
// Internal Function Prototypes
// *****************************************************************************
// *****************************************************************************

void    DisableUSBInterrupts( void );
void    EnableUSBInterrupts( void );
BYTE    GetCommand( void );
DWORD   GetCurrentTick( void );
void    GetOneWord( char *buffer );
DWORD   GetUserDate( void );
DWORD   GetUserTime( void );
void    InitializeAnalogMonitor( void );
void    InitializeClock( void );
void    InitializeCommand( void );
void    MonitorMedia( void );
void    MonitorUser( void );
void    MonitorVBUS( void );
void    PrintFileInformation( SearchRec searchRecord );
void    RedoCommandPrompt( void );
void    WriteOneBuffer( FSFILE *fptr, BYTE *data, WORD size );
char    hex2ascii(unsigned short);


// *****************************************************************************
// *****************************************************************************
// Macros
// *****************************************************************************
// *****************************************************************************

#define IsNum(c)            ((('0' <= c) && (c <= '9')) ? TRUE : FALSE)
#define UpperCase(c)        (('a'<= c) && (c <= 'z') ? c - 0x20 : c)
#define SkipWhiteSpace()    { while (commandInfo.buffer[commandInfo.index] == ' ') commandInfo.index++; }


// *****************************************************************************
// *****************************************************************************
// Global Variables
// *****************************************************************************
// *****************************************************************************

volatile BOOL       commandEntrySwitchPressed;
OLD_COMMANDS        commandBuffer;
COMMAND             commandInfo;
volatile BYTE       logBufferReading;
BYTE                logBufferWriting;
LOGGER_STATUS       loggerStatus;
volatile DWORD      currentTick;
volatile LOG_DATA   logData[NUM_LOG_BUFFERS];
VOLUME_INFO         volume;
rtccDate            currentDate;
rtccTime            currentTime;
rtccTime            previousTime;

char                PrintLineBuffer[PRINT_LINE_SIZE];
char                BlockBuffer[BLOCK_BUFFER_SIZE + 4];


//******************************************************************************
//******************************************************************************
// USB Support Functions
//******************************************************************************
//******************************************************************************

/****************************************************************************
  Function:
    BOOL USB_ApplicationEventHandler( BYTE address, USB_EVENT event,
                void *data, DWORD size )

  Summary:
    This is the application event handler.  It is called when the stack has
    an event that needs to be handled by the application layer rather than
    by the client driver.

  Description:
    This is the application event handler.  It is called when the stack has
    an event that needs to be handled by the application layer rather than
    by the client driver.  If the application is able to handle the event, it
    returns TRUE.  Otherwise, it returns FALSE.

  Precondition:
    None

  Parameters:
    BYTE address    - Address of device where event occurred
    USB_EVENT event - Identifies the event that occured
    void *data      - Pointer to event-specific data
    DWORD size      - Size of the event-specific data

  Return Values:
    TRUE    - The event was handled
    FALSE   - The event was not handled

  Remarks:
    The application may also implement an event handling routine if it
    requires knowledge of events.  To do so, it must implement a routine that
    matches this function signature and define the USB_HOST_APP_EVENT_HANDLER
    macro as the name of that function.
  ***************************************************************************/

BOOL USB_ApplicationEventHandler( BYTE address, USB_EVENT event, void *data, DWORD size )
{

    switch( event )
    {
    case EVENT_VBUS_REQUEST_POWER:
        // The data pointer points to a byte that represents the amount of power
        // requested in mA, divided by two.  If the device wants too much power,
        // we reject it.
        if (((USB_VBUS_POWER_EVENT_DATA*)data)->current <= (MAX_ALLOWED_CURRENT / 2))
        {
            return TRUE;
        }
        else
        {
            PrintString( "\r\n***** USB Error - device requires too much current *****\r\n" );
        }
        break;

    case EVENT_VBUS_RELEASE_POWER:
        return TRUE;

    case EVENT_HUB_ATTACH:
        PrintString( "\r\n***** USB Error - hubs are not supported *****\r\n" );
        return TRUE;

    case EVENT_UNSUPPORTED_DEVICE:
        PrintString( "\r\n***** USB Error - device is not supported *****\r\n" );
        return TRUE;

    case EVENT_CANNOT_ENUMERATE:
        PrintString( "\r\n***** USB Error - cannot enumerate device *****\r\n" );
        return TRUE;

    case EVENT_CLIENT_INIT_ERROR:
        PrintString( "\r\n***** USB Error - client driver initialization error *****\r\n" );
        return TRUE;

    case EVENT_OUT_OF_MEMORY:
        PrintString( "\r\n***** USB Error - out of heap memory *****\r\n" );
        return TRUE;

    case EVENT_UNSPECIFIED_ERROR:   // This should never be generated.
        PrintString( "\r\n***** USB Error - unspecified *****\r\n" );
        return TRUE;

    }

    return FALSE;
}


//******************************************************************************
//******************************************************************************
//******************************************************************************
// Main
//******************************************************************************
//******************************************************************************
//******************************************************************************

int main (void)
{
    FSFILE              *filePointer1;
    FSFILE              *filePointer2;
    char                param1[MAX_COMMAND_LENGTH];
    char                param2[MAX_COMMAND_LENGTH];
    SearchRec           searchRecord;
    int                 value;

    // Configure the device for maximum performance, but do not change the PBDIV clock divisor.
	// Given the options, this function will change the program Flash wait states,
	// RAM wait state and enable prefetch cache, but will not change the PBDIV.
    // The PBDIV value is already set via the pragma FPBDIV option above.
    SYSTEMConfig(GetSystemClock(), SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
    
    INTEnableSystemMultiVectoredInt();
    
    // Initialize IO and display start up message
    InitTextIO();
    PrintString( "\r\n***** Microchip Explorer " VERSION " *****\r\n" );

    // Configure SW1 change notice & interrupt
    PORTSetPinsDigitalIn(IOPORT_D, BIT_6);
    mCNOpen(CN_ON|CN_IDLE_CON, CN15_ENABLE, CN15_PULLUP_ENABLE);
    value = mPORTDRead();   // Throw value away
    ConfigIntCN(CHANGE_INT_ON | CHANGE_INT_PRI_4);

    // Initialize USB layers
    USBInitialize( 0 );

    // Turn on the timer.
    InitializeClock();

    // Initialize application data
    commandBuffer.newest    = MAX_BUFFERED_COMMANDS;
    commandBuffer.oldest    = MAX_BUFFERED_COMMANDS;
    commandBuffer.showing   = MAX_BUFFERED_COMMANDS;
    loggerStatus.value      = 0;
    volume.valid            = FALSE;
    InitializeCommand();


    while (1)
    {
        MonitorMedia();
        MonitorUser();

        if (GetCommand())
        {
            SkipWhiteSpace();
            switch ( commandInfo.command )
            {
                case COMMAND_NO_COMMAND:
                    break;

                case COMMAND_CD:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    else
                    {
                        if (FSchdir( &(commandInfo.buffer[commandInfo.index]) ))
                        {
                            PrintString( " - Error performing command\r\n" );
                        }
                    }
                    break;

                case COMMAND_COPY:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    GetOneWord( param1 );
                    GetOneWord( param2 );
                    if ((param1[0] == 0) || (param2[0] == 0))
                    {
                        PrintString( " - Two parameters required\r\n" );
                    }
                    else
                    {
                        if (FindFirst( param1,
                                ATTR_DIRECTORY | ATTR_ARCHIVE | ATTR_READ_ONLY | ATTR_HIDDEN, &searchRecord ))
                        {
                            PrintString( " - File not found\r\n" );
                        }
                        else if ((filePointer1 = FSfopen( param1, "r" )) == NULL)
                        {
                            PrintString( " - Error opening file\r\n" );
                        }
                        else
                        {
                            if ((filePointer2 = FSfopen( param2, "w" )) == NULL)
                            {
                                PrintString( " - Error creating file\r\n" );
                            }
                            else
                            {
                                BYTE    charsRead;
                                BYTE    readBuffer[COPY_BUFFER_SIZE];

                                while (!FSfeof( filePointer1 ))
                                {
                                    charsRead = FSfread( readBuffer, 1, COPY_BUFFER_SIZE, filePointer1 );
                                    if (charsRead)
                                    {
                                        if (FSfwrite( readBuffer, 1, charsRead, filePointer2) != charsRead)
                                        {
                                            PrintString( " - Error writing file\r\n" );
                                            break;
                                        }
                                    }
                                }
                                FSfclose( filePointer2 );
                            }
                            FSfclose( filePointer1 );
                        }
                    }
                    break;

                case COMMAND_DATE:
                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( "Command not supported on PIC32 Starter Kit\r\n" );
                    }
                    break;

                case COMMAND_DEL:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    else
                    {
                        if (FindFirst( &(commandInfo.buffer[commandInfo.index]),
                                ATTR_DIRECTORY | ATTR_ARCHIVE | ATTR_READ_ONLY | ATTR_HIDDEN, &searchRecord ))
                        {
                            PrintString( " - File not found\r\n" );
                        }
                        else
                        {
                            if (FSremove( &(commandInfo.buffer[commandInfo.index] )))
                            {
                                PrintString( " - Error performing command\r\n" );
                            }
                        }
                    }
                    break;

                case COMMAND_DIR:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        strcpy( param1, "*.*" );
                    }
                    else
                    {
                        strcpy( param1, &(commandInfo.buffer[commandInfo.index]) );
                    }
                    if (!FindFirst( param1, ATTR_DIRECTORY | ATTR_ARCHIVE | ATTR_READ_ONLY | ATTR_HIDDEN, &searchRecord ))
                    {
                        PrintFileInformation( searchRecord );
                        while (!FindNext( &searchRecord ))
                        {
                            PrintFileInformation( searchRecord );
                        }
                    }
                    else
                    {
                        PrintString( "No files found.\r\n" );
                    }
                    break;

                case COMMAND_HELP:
                    PrintString( "Microchip Memory Key Explorer " VERSION );
                    PrintString( "Available commands:\r\n" );
                    PrintString( "    CD <name>            - change directory\r\n" );
                    PrintString( "    COPY <file1> <file2> - copy [file1] to [file2]\r\n" );
                    PrintString( "    DATE [yyyy-mm-dd]    - display or set the date\r\n" );
                    PrintString( "    DEL <file>           - delete file, current directory only\r\n" );
                    PrintString( "    DIR [file]           - display directory\r\n" );
                    PrintString( "    HELP or ?            - display help\r\n" );
                    PrintString( "    MD <name>            - make directory\r\n" );
                    PrintString( "    RD <name>            - remove directory\r\n" );
                    PrintString( "    REN <file1> <file2>  - rename [file1] to [file2]\r\n" );
                    PrintString( "    RESET <HARD|SOFT>    - reset media\r\n" );
                    PrintString( "    RESUME               - resume a suspended USB device\r\n" );
                    PrintString( "    SUSPEND              - suspend attached USB device\r\n" );
                    PrintString( "    TIME [hh:mm:ss]      - display or set the time (24 hr format)\r\n" );
                    PrintString( "    TYPE <file>          - print out contents of file, current directory only\r\n" );
                    break;

                case COMMAND_MD:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    else
                    {
                        if (FSmkdir( &(commandInfo.buffer[commandInfo.index])))
                        {
                            PrintString( " - Error performing command\r\n" );
                        }
                    }
                    break;

                case COMMAND_RD:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    else
                    {
                        if (FSrmdir( &(commandInfo.buffer[commandInfo.index]), TRUE ))
                        {
                            PrintString( " - Error performing command\r\n" );
                        }
                    }
                    break;

                case COMMAND_REN:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    GetOneWord( param1 );
                    GetOneWord( param2 );
                    if ((param1[0] == 0) || (param2[0] == 0))
                    {
                        PrintString( " - Two parameters required\r\n" );
                    }
                    else
                    {
                        if ((filePointer1 = FSfopen( param1, "r" )) == NULL)
                        {
                            PrintString( " - Cannot find file\r\n" );
                        }
                        else
                        {
                            if (FSrename( param2, filePointer1 ))
                            {
                                PrintString( " - Error performing command\r\n" );
                            }
                            FSfclose( filePointer1 );
                        }
                    }
                    break;

                case COMMAND_RESET:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    GetOneWord( param1 );
                    if (!strcmp( "SOFT", param1 ))
                    {
                        if (USBHostMSDSCSIMediaReset())
                        {
                            PrintString( " - Error performing command\r\n" );
                        }
                    }
                    else if (!strcmp( "HARD", param1 ))
                    {
                        if (USBHostResetDevice( USB_SINGLE_DEVICE_ADDRESS ))
                        {
                            PrintString( " - Error performing command\r\n" );
                        }
                    }
                    else
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    break;
                
                case COMMAND_RESUME:
                    if (commandInfo.buffer[commandInfo.index] != 0)
                    {
                        PrintString( " - Extra parameter\r\n" );
                    }
                    else
                    {
                        switch( USBHostResumeDevice( USB_SINGLE_DEVICE_ADDRESS ))
                        {
                            case USB_SUCCESS:
                                PrintString( "Device resumed.\r\n" );
                                break;

                            case USB_UNKNOWN_DEVICE:
                                PrintString( "Device not attached\r\n" );
                                break;

                            case USB_ILLEGAL_REQUEST:
                                PrintString( "Device cannot be resumed\r\n" );
                                break;
                        }
                    }
                    break;

                case COMMAND_SUSPEND:
                    if (commandInfo.buffer[commandInfo.index] != 0)
                    {
                        PrintString( " - Extra parameter\r\n" );
                    }
                    else if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                    }
                    else
                    {
                        switch( USBHostSuspendDevice( USB_SINGLE_DEVICE_ADDRESS ))
                        {
                            case USB_SUCCESS:
                                PrintString( "Device suspended.\r\n" );
                                break;

                            case USB_UNKNOWN_DEVICE:
                                PrintString( "Device not attached\r\n" );
                                break;

                            case USB_ILLEGAL_REQUEST:
                                PrintString( "Device cannot be suspended\r\n" );
                                break;
                        }
                    }
                    break;

                case COMMAND_TIME:
                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( "Command not supported on PIC32 Starter Kit\r\n" );
                    }
                    break;

                case COMMAND_TYPE:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }

                    if (commandInfo.buffer[commandInfo.index] == 0)
                    {
                        PrintString( " - Parameter required\r\n" );
                    }
                    else
                    {
                        if (FindFirst( &(commandInfo.buffer[commandInfo.index]),
                                ATTR_DIRECTORY | ATTR_ARCHIVE | ATTR_READ_ONLY | ATTR_HIDDEN, &searchRecord ))
                        {
                            PrintString( " - File not found\r\n" );
                        }
                        else if ((filePointer1 = FSfopen( &(commandInfo.buffer[commandInfo.index]), "r" )) == NULL)
                        {
                            PrintString( " - Error opening file\r\n" );
                        }
                        else
                        {
                            int iBuf, nBuf, iLine;

                            // Initialize the long-line suffix
                            BlockBuffer[BLOCK_BUFFER_SIZE+0] = '.';
                            BlockBuffer[BLOCK_BUFFER_SIZE+1] = '.';
                            BlockBuffer[BLOCK_BUFFER_SIZE+2] = '.';
                            BlockBuffer[BLOCK_BUFFER_SIZE+3] = 0;

                            iLine = 0;
                            while (!FSfeof( filePointer1 ))
                            {
                                iBuf  = 0;
                                nBuf  = FSfread( BlockBuffer, 1, BLOCK_BUFFER_SIZE, filePointer1 );

                                // While we have unprocessed data in the block buffer...
                                while (iBuf < nBuf)
                                {
                                    // Copy the data from the Block buffer to the Print Line buffer
                                    PrintLineBuffer[iLine] = BlockBuffer[iBuf];
                                    iBuf++;

                                    // Watch for the end of a print line
                                    if (PrintLineBuffer[iLine] == '\n')
                                    {
                                        // Early EOL, print it, and start a new one.
                                        PrintLineBuffer[iLine] = 0;
                                        PrintString(PrintLineBuffer);
                                        iLine = 0;
                                    }
                                    else
                                    {
                                        if (iLine < PRINT_LINE_SIZE)
                                        {
                                            // Increment the print line
                                            iLine++;
                                        }
                                        else
                                        {
                                            // Max line size:  print it (w/suffix) and start a new one
                                            PrintString(PrintLineBuffer);
                                            iLine = 0;
                                        }
                                    }
                                }
                            }

                            // Print the trailing line (if any)
                            if (iLine > 0)
                            {
                                PrintLineBuffer[iLine] = 0;
                                PrintString(PrintLineBuffer);
                                FSfclose( filePointer1 );
                            }
                        }
                    }
                    break;

                case COMMAND_WHO:
                    if (!loggerStatus.mediaPresent)
                    {
                        PrintString( "No media present.\r\n" );
                        break;
                    }
                    else
                    {
                        USB_DEVICE_DESCRIPTOR   *pDescriptor;

                        pDescriptor = (USB_DEVICE_DESCRIPTOR *)USBHostGetDeviceDescriptor( 1 );
                        strncpy(&PrintLineBuffer[0], "VID: 0x", 7 );
                        PrintLineBuffer[ 7] = hex2ascii( pDescriptor->idVendor >> 12 );
                        PrintLineBuffer[ 8] = hex2ascii( pDescriptor->idVendor >>  8 );
                        PrintLineBuffer[ 9] = hex2ascii( pDescriptor->idVendor >>  4 );
                        PrintLineBuffer[10] = hex2ascii( pDescriptor->idVendor >>  0 );
                        strncpy(&PrintLineBuffer[11], "  PID: 0x", 9 );
                        PrintLineBuffer[20] = hex2ascii( pDescriptor->idProduct >> 12 );
                        PrintLineBuffer[21] = hex2ascii( pDescriptor->idProduct >>  8 );
                        PrintLineBuffer[22] = hex2ascii( pDescriptor->idProduct >>  4 );
                        PrintLineBuffer[23] = hex2ascii( pDescriptor->idProduct >>  0 );
                        PrintLineBuffer[24] = 0;
                        PrintString( PrintLineBuffer );
                    }
                    break;

                default:
                    PrintString( "Unsupported command\r\n" );
                    break;
            }
            InitializeCommand();
        }
    }
}

//******************************************************************************
//******************************************************************************
// Internal Functions
//******************************************************************************
//******************************************************************************

/****************************************************************************
  Function:
    char hex2ascii( unsigned short hex )

  Description:
    This function converts a single hex digit to ASCII

  Precondition:
    None

  Parameters:
    None

  Return Values:
    The ASCII equivalent to the low-order digit of the hex value.

  Remarks:
    None
  ***************************************************************************/
char    hex2ascii( unsigned short hex )
{
    if ( (hex & 0xF) < 10)
    {
        return ( '0' + (hex & 0xF) );
    }
    else
    {
        return ( ('A' - 10) + (hex & 0xF) );
    }
}


/****************************************************************************
  Function:
    BYTE GetCommand( void )

  Description:
    This function returns whether or not the user has finished entering a
    command.  If so, then the command entered by the user is determined and
    placed in commandInfo.command.  The command line index
    (commandInfo.index) is set to the first non-space character after the
    command.

  Precondition:
    commandInfo.reading must be valid.

  Parameters:
    None

  Return Values:
    TRUE    - The user has entered a command.  The command is in
                  commandInfo.command.
    FALSE   - The user has not finished entering a command.

  Remarks:
    None
  ***************************************************************************/

BYTE GetCommand( void )
{
    char    firstWord[MAX_COMMAND_LENGTH];

    if (commandInfo.reading)
    {
        return FALSE;
    }
    else
    {
        commandInfo.index = 0;

        commandInfo.index = 0;
        GetOneWord( firstWord );
        SkipWhiteSpace();

        if (firstWord[0] == 0)
        {
            commandInfo.command = COMMAND_NO_COMMAND;
            return TRUE;
        }

        if (!strncmp( firstWord, "CD", 2 ) && (strlen(firstWord) == 2))
        {
            commandInfo.command = COMMAND_CD;
            return TRUE;
        }
        if (!strncmp( firstWord, "COPY", 4 ) && (strlen(firstWord) == 4))
        {
            commandInfo.command = COMMAND_COPY;
            return TRUE;
        }
        if (!strncmp( firstWord, "DATE", 4 ) && (strlen(firstWord) == 4))
        {
            commandInfo.command = COMMAND_DATE;
            return TRUE;
        }
        if (!strncmp( firstWord, "DEL", 3 ) && (strlen(firstWord) == 3))
        {
            commandInfo.command = COMMAND_DEL;
            return TRUE;
        }
        if (!strncmp( firstWord, "DIR", 3 ) && (strlen(firstWord) == 3))
        {
            commandInfo.command = COMMAND_DIR;
            return TRUE;
        }
        if ((!strncmp( firstWord, "HELP", 4 )  && (strlen(firstWord) == 4)) ||
            (!strncmp( firstWord, "?", 1 ) && (strlen(firstWord) == 1)))
        {
            commandInfo.command = COMMAND_HELP;
            return TRUE;
        }
        if (!strncmp( firstWord, "MD", 2 ) && (strlen(firstWord) == 2))
        {
            commandInfo.command = COMMAND_MD;
            return TRUE;
        }
        if (!strncmp( firstWord, "RD", 2 ) && (strlen(firstWord) == 2))
        {
            commandInfo.command = COMMAND_RD;
            return TRUE;
        }
        if (!strncmp( firstWord, "REN", 3 ) && (strlen(firstWord) == 3))
        {
            commandInfo.command = COMMAND_REN;
            return TRUE;
        }
        if (!strncmp( firstWord, "RESET", 5 ) && (strlen(firstWord) == 5))
        {
            commandInfo.command = COMMAND_RESET;
            return TRUE;
        }
        if (!strncmp( firstWord, "RESUME", 6 ) && (strlen(firstWord) == 6))
        {
            commandInfo.command = COMMAND_RESUME;
            return TRUE;
        }
        if (!strncmp( firstWord, "SUSPEND", 7 ) && (strlen(firstWord) == 7))
        {
            commandInfo.command = COMMAND_SUSPEND;
            return TRUE;
        }
        if (!strncmp( firstWord, "TIME", 4 ) && (strlen(firstWord) == 4))
        {
            commandInfo.command = COMMAND_TIME;
            return TRUE;
        }
        if (!strncmp( firstWord, "TYPE", 4 ) && (strlen(firstWord) == 4))
        {
            commandInfo.command = COMMAND_TYPE;
            return TRUE;
        }
        if (!strncmp( firstWord, "WHO", 3 ) && (strlen(firstWord) == 3))
        {
            commandInfo.command = COMMAND_WHO;
            return TRUE;
        }

        commandInfo.command = COMMAND_UNKNOWN;
        return TRUE;
    }
}


/****************************************************************************
  Function:
    DWORD GetCurrentTick( void )

  Description:
    This function gets the current tick count in terms of milliseconds.
    Note that the timer is only accurate to MILLISECONDS_PER_TICK ms.

  Precondition:
    None

  Parameters:
    None

  Returns:
    DWORD - Number of milliseconds since the timer was turned on.

  Remarks:

  ***************************************************************************/
DWORD GetCurrentTick( void )
{
    return (currentTick * MILLISECONDS_PER_TICK);
}


/****************************************************************************
  Function:
    void GetOneWord( char *buffer )

  Description:
    This function copies the next word in the command line to the specified
    buffer.  Word deliniation is marked by a space character.  The returned
    word is null terminated.

  Precondition:
    commandInfo.buffer and commandInfo.index are valid

  Parameters:
    *buffer - Pointer to where the word is to be stored.

  Returns:
    None

  Remarks:

  ***************************************************************************/
void GetOneWord( char *buffer )
{
    SkipWhiteSpace();

    while ((commandInfo.buffer[commandInfo.index] != 0) &&
           (commandInfo.buffer[commandInfo.index] != ' '))
    {
        *buffer++ = commandInfo.buffer[commandInfo.index++];
    }
    *buffer = 0;
}


/****************************************************************************
  Function:
    DWORD GetUserDate( void )

  Description:
    This function extracts a user entered date from the command line and
    places it in a DWORD that matches the format required for the RTCC.
    The required format is:
                        YYYY-MM-DD
    where YY is between 2000 and 2099.  If the date is not in a valid format,
    0 is returned.

  Precondition:
    commandInfo.buffer and commandInfo.index are valid, and
    commandInfo.index points to the first character of the date

  Parameters:


  Returns:
    If the project is built for a PIC24F, this function returns a DWORD in
    the format <00><YY><MM><DD>.  If it is built for a PIC32MX, this function
    returns a DWORD in the format <YY><MM><DD><00>.

  Remarks:
    Range checks are not comprehensive.  The day is not qualified based on
    how many days are in the specified month.

    The values from the RTCC are assumed to be in BCD format.

    The two architectures have different formats for the date.  The index
    values are set above accordingly.
  ***************************************************************************/

DWORD GetUserDate( void )
{
    DWORD_VAL   date;

    date.Val = 0;

    // Get the year.
    if (commandInfo.buffer[commandInfo.index++] != '2')                     return 0;
    if (commandInfo.buffer[commandInfo.index++] != '0')                     return 0;

    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_YEAR] =  (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_YEAR] |= (commandInfo.buffer[commandInfo.index++] - '0');

    if (commandInfo.buffer[commandInfo.index++] != '-')                     return 0;

    // Get the month.
    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_MONTH] =  (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_MONTH] |= (commandInfo.buffer[commandInfo.index++] - '0');
    if (!((0x01 <= date.v[INDEX_MONTH]) && (date.v[INDEX_MONTH] <= 0x12)))  return 0;

    if (commandInfo.buffer[commandInfo.index++] != '-')                     return 0;

    // Get the day.
    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_DAY] =  (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] ))                    return 0;
    date.v[INDEX_DAY] |= (commandInfo.buffer[commandInfo.index++] - '0');
    if (!((0x01 <= date.v[INDEX_DAY]) && (date.v[INDEX_DAY] <= 0x31)))      return 0;

    return date.Val;
}



/****************************************************************************
  Function:
    DWORD GetUserTime( void )

  Description:
    This function extracts a user entered time from the command line and
    places it in a DWORD that matches the format required for the RTCC.  The
    required format is:
                        HH:MM:SS
    in 24-hour format.  If the time is not in a valid format, 0 is returned.

  Precondition:
    commandInfo.buffer and commandInfo.index are valid;
    commandInfo.index points to the first character of the time

  Parameters:
    None

  Return Values:
    If the project is built for a PIC24F, this function returns a DWORD in
    the format <00><HH><MM><SS>.  If it is built for a PIC32MX, this function
    returns a DWORD in the format <HH><MM><SS><00>.

  Remarks:
    The values from the RTCC are assumed to be in BCD format.

    The two architectures have different formats for the date. The index
    values are set above accordingly.
  ***************************************************************************/

DWORD GetUserTime( void )
{
    DWORD_VAL   time;

    time.Val = 0;

    // Get the hours.
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_HOURS] |= (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_HOURS] |= (commandInfo.buffer[commandInfo.index++] - '0');
    if (time.v[INDEX_HOURS] > 0x23)                      return 0;

    if (commandInfo.buffer[commandInfo.index++] != ':')  return 0;

    // Get the minutes.
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_MINUTES] |= (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_MINUTES] |= (commandInfo.buffer[commandInfo.index++] - '0');
    if (time.v[INDEX_MINUTES] > 0x59)                    return 0;

    if (commandInfo.buffer[commandInfo.index++] != ':')  return 0;

    // Get the seconds.
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_SECONDS] |= (commandInfo.buffer[commandInfo.index++] - '0') << 4;
    if (!IsNum( commandInfo.buffer[commandInfo.index] )) return 0;
    time.v[INDEX_SECONDS] |= (commandInfo.buffer[commandInfo.index++] - '0');
    if (time.v[INDEX_SECONDS] > 0x59)                    return 0;

    return time.Val;
}


/****************************************************************************
  Function:
     void InitializeClock( void )

  Description:
    This function initializes the tick timer.  It configures and starts the
    timer, and enables the timer interrupt.  We are using Timer 3 so we can
    also trigger an A/D conversion when the timer rolls over.

  Precondition:
    None

  Parameters:
    None

  Returns:
    None

  Remarks:
    None
  ***************************************************************************/

void InitializeClock( void )
{
    currentTick = 0;

    //OpenTimer3(T3_ON | T3_SOURCE_INT | T3_PS_1_8, TIMER_PERIOD);
    OpenTimer3(T3_ON | T3_SOURCE_INT | T3_PS_1_16, TIMER_PERIOD);

    // set up the timer interrupt with a priority of 2
    ConfigIntTimer3(T3_INT_ON | T3_INT_PRIOR_2);

    return;
}


/****************************************************************************
  Function:
    void InitializeCommand( void )

  Description:
    This function prints a command prompt and initializes the command line
    information.  If available, the command prompt format is:
                    [Volume label]:[Current directory]>

  Precondition:
    None

  Parameters:
    None

  Returns:
    None

  Remarks:
    None
  ***************************************************************************/

void InitializeCommand( void )
{
    int             i=0;

    if (loggerStatus.mediaPresent)
    {
        if (volume.valid)
        {
            while (volume.label[i] != 0)
            {
                PrintLineBuffer[i] = volume.label[i];
                i++;
            } 
        }

        PrintLineBuffer[i++] = ':';
        PrintLineBuffer[i++] = ' ';
        FSgetcwd( &PrintLineBuffer[i], PRINT_LINE_SIZE - i );
        PrintString( PrintLineBuffer );
    }

    PrintString( "<Press SW1 to enter a command>" );

    commandInfo.command     = COMMAND_NO_COMMAND;
    commandInfo.index       = 0;
    commandInfo.reading     = TRUE;

    memset( commandInfo.buffer, 0x00, MAX_COMMAND_LENGTH );
}


/****************************************************************************
  Function:
    void MonitorMedia( void )

  Description:
    This function calls the background tasks necessary to support USB Host
    operation.  Upon initial insertion of the media, it initializes the file
    system support and reads the volume label.  Upon removal of the media,
    the volume label is marked invalid.

  Precondition:
    None

  Parameters:
    None

  Returns:
    None

  Remarks:
    None
  ***************************************************************************/

void MonitorMedia( void )
{
    BYTE            mediaPresentNow;
    BYTE            mountTries;
    SearchRec       searchRecord;

    USBTasks();

    if (loggerStatus.overcurrentStateUSB)
    {
        // An overcurrent situation exists, and the USB has been shut down.
        if (loggerStatus.mediaPresent)
        {
            PrintString( "\r\n***** USB Error - overcurrent detected *****\r\n" );
            loggerStatus.mediaPresent = FALSE;
        }
    }
    else
    {
        mediaPresentNow = USBHostMSDSCSIMediaDetect();
        if (mediaPresentNow != loggerStatus.mediaPresent)
        {
            if (mediaPresentNow)
            {
                mountTries = 10;
                while(!FSInit() && mountTries--);
                if (!mountTries)
                {
                    PrintString( "APP: Could not mount media\r\n" );
                    loggerStatus.mediaPresent = FALSE;
                }
                else
                {
                    loggerStatus.mediaPresent = TRUE;

                    // Find the volume label.  We need to do this here while we are at the
                    // root directory.
                    if (!FindFirst( "*.*", ATTR_VOLUME, &searchRecord ))
                    {
                        strcpy( volume.label, searchRecord.filename );
                        volume.valid = TRUE;
                    }
                    
                    RedoCommandPrompt();
                }
            }
            else
            {
                loggerStatus.mediaPresent   = FALSE;
                volume.valid                = FALSE;
                
                RedoCommandPrompt();
            }
        }
    }
}



/****************************************************************************
  Function:
    void MonitorUser( void )

  Description:
    This routine monitors command line input from the user.  The
    UART is polled rather than using interrupts.  If the previous
    command line is still being processed, the characters are
    ignored.  All text input is converted to upper case for
    simplification.  When the user presses the Enter or Return key,
    the command line is considered terminated.  Escape sequences are
    captured and checked.  If the up-arrow is pressed, the previous
    buffered command is displayed, until the oldest command is
    reached.  If the down-arrow is pressed, the next buffered
    command is displayed, until the newest command is displayed.
    If the down-arrow is pressed one additional time, the command
    line is erased.

  Precondition:
    None

  Parameters:
    None

  Returns:
    None

  Remarks:
    Currently, blank entries are added to the old command buffer.
  ***************************************************************************/

void MonitorUser( void )
{
    if (commandEntrySwitchPressed)
    {
        // Read the input from the debugger.
        memset(commandInfo.buffer, 0, sizeof(commandInfo.buffer));
        DBGETS(commandInfo.buffer, sizeof(commandInfo.buffer));
        commandInfo.buffer[MAX_COMMAND_LENGTH-1] = 0; // just in case

        // Convert to upper case to make later processing simpler
        for (commandInfo.index=0; commandInfo.index < MAX_COMMAND_LENGTH; commandInfo.index++)
        {  
            commandInfo.buffer[commandInfo.index] = toupper( commandInfo.buffer[commandInfo.index] );
        }

        // Echo the command
        PrintString( commandInfo.buffer );

        // Clear the flags.
        commandEntrySwitchPressed = FALSE;
        commandInfo.reading       = FALSE;

        // Note: Command History not supported on PIC32 Starter Kit
    }
}


/****************************************************************************
  Function:
    void PrintFileInformation( SearchRec searchRecord )

  Description:
    This function prints the file information that is contained in
    searchRecord.  Information printed is:
                <date> <time> [<DIR>] [<size>] <name>

  Precondition:
    None

  Parameters:
    SearchRec searchRecord  - File information

  Returns:
    None

  Remarks:
    The timestamp is assumed to be in the following format:
            Date format:    Bits 15-9:  Year (0 = 1980, 127 = 2107)
                            Bits 8-5:   Month (1 = January, 12 = December)
                            Bits 4-0:   Day (1 - 31)

            Time format:    Bits 15-11: Hours (0-23)
                            Bits 10-5:  Minutes (0-59)
                            Bits 4-0:   Seconds/2 (0-29)
  ***************************************************************************/

void PrintFileInformation( SearchRec searchRecord )
{
    // Display the file's date/time stamp.
    sprintf( &PrintLineBuffer[0], "%04d-%02d-%02d ", 
             ((((DWORD_VAL)(searchRecord.timestamp)).w[1] & 0xFE00) >> 9) + 1980,
             (((DWORD_VAL)(searchRecord.timestamp)).w[1] & 0x01E0) >> 5,
             ((DWORD_VAL)(searchRecord.timestamp)).w[1] & 0x001F );
    sprintf( &PrintLineBuffer[11], "%02d:%02d:%02d ",  
             (((DWORD_VAL)(searchRecord.timestamp)).w[0] & 0xF800) >> 11,
             (((DWORD_VAL)(searchRecord.timestamp)).w[0] & 0x07E0) >> 5,
             (((DWORD_VAL)(searchRecord.timestamp)).w[0] & 0x001F) << 1 );

    // Display the file size.  If the file is actually a directory, display an indication.
    if (searchRecord.attributes & ATTR_DIRECTORY)
    {
        sprintf( &PrintLineBuffer[20], "<DIR>           " );
    }
    else
    {
        sprintf( &PrintLineBuffer[20], "     %10ld ", ((DWORD_VAL)(searchRecord.filesize)).Val );
    }

    // Display the file name.
    sprintf( &PrintLineBuffer[36], searchRecord.filename );

    // Print the string
    PrintString( PrintLineBuffer );
}


/****************************************************************************
  Function:
    void RedoCommandPrompt( void )

  Description:
    This function is called when the user either removes or inserts media.
    We want to let the user know right away that something has changed, so
    we change the command prompt immediately.  If the user is entering a
    command, we rebuild his command.

  Precondition:
    None

  Parameters:
    None

  Returns:
    None

  Remarks:
    None
  ***************************************************************************/

void RedoCommandPrompt( void )
{
    int i = 0;
    int j;

    if (volume.valid)
    {
        while (volume.label[i] != 0)
        {
            PrintLineBuffer[i] = volume.label[i];
            i++;
        }
        PrintLineBuffer[i++] = ':';
        PrintLineBuffer[i++] = '\\';
    }

    if (commandInfo.reading)
    {
        for (j=0; j < commandInfo.index; j++)
        {
            PrintLineBuffer[i++] = commandInfo.buffer[j];
        }
    }

    PrintLineBuffer[i] = 0;
    PrintString( PrintLineBuffer );
    PrintString( "<Press SW1 to enter a command>" );
}


/****************************************************************************
  Function:
    void WriteOneBuffer( FSFILE *fptr, BYTE *data, WORD size )

  Description:
    This function writes one log buffer to the indicated file.  It then
    advances the pointer to the current buffer to write.

  Precondition:
    None

  Parameters:
    FSFILE *fptr    - Pointer to an open file
    BYTE *data      - Pointer to data to write
    WORD size       - How many bytes of data to write

  Return Values:
    None

  Remarks:
    None
  ***************************************************************************/

void WriteOneBuffer( FSFILE *fptr, BYTE *data, WORD size )
{
    if (FSfwrite( data, 1, size, fptr) != size)
    {
        PrintString( "! Error writing log file\r\n" );
    }
    logData[logBufferWriting].bufferFull    = FALSE;
    logData[logBufferWriting].index         = 0;

    logBufferWriting++;
    if (logBufferWriting >= NUM_LOG_BUFFERS)
    {
        logBufferWriting = 0;
    }
}


/****************************************************************************
  Function:
    void __ISR(_CHANGE_NOTICE_VECTOR, ipl2) _ChangeNoticeHandler(void)

  Description:
    Change Notice (CN) ISR.  This routine is called when a change occures on 
    one of the configured CN ports.

  Precondition:
    None

  Parameters:
    None

  Return Values:
    None

  Remarks:
    None
  ***************************************************************************/
void __ISR(_CHANGE_NOTICE_VECTOR, ipl4) _ChangeNoticeHandler(void)
{
	unsigned int temp;

    // clear the mismatch condition
    temp = mPORTDRead();

    // clear the interrupt flag
    mCNClearIntFlag();

    // Set the flag to indicate the switch has been pressed.
    commandEntrySwitchPressed = TRUE;

}


/****************************************************************************
  Function:
    void __attribute__((__interrupt__, auto_psv)) _T3Interrupt(void)

  Description:
    Timer ISR, used to update the tick count.  Since we are using Timer 3, we
    can also trigger an A/D conversion off of the timer.

  Precondition:
    None

  Parameters:
    None

  Return Values:
    None

  Remarks:
    None
  ***************************************************************************/

void __ISR(_TIMER_3_VECTOR, ipl2) _T3Interrupt(void)
{
    if (IFS0 & PIC32MX_TIMER3_INTERRUPT)
    {
        // Clear the interrupt flag
        mT3ClearIntFlag();

        // Count the timer tick
        currentTick++;
    }
}


