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.

LAUNCHXL-F28379D: How do i connect sd card to read/write?

Part Number: LAUNCHXL-F28379D
Other Parts Discussed in Thread: C2000WARE

Hi,

I have been trying to connect sd card to my launchpad for a few days now and  I am unable to do anything with thr example code given by TI. First, how do i access UART to test mysd card connection?? Why are the ports given in the code(GPIO 28 and GPIO 29) which are not even accessible on the launchpad? How do i connect my sd card? GPIO 122,123,124 and 125  were declared but no proper explanation to the connections are given. What do i connect it to? The UART printf never works for me.  I do not want to use the command line at all. I just want to write a something and save it.  

fresult=f_mount(0,&g_sFatFs);
    fresult=f_open(&g_sFileObject, "E:/Msg.ext", FA_READ |  FA_WRITE );
    ReadCount = 512;
    fresult=f_write(&g_sFileObject, "1000.00,1001.00,1002.00,1003.00,1004.00,1005.00,1006.00,150,151,152,1,10000,10001,10002,360.1234\n", sizeof("1000.00,1001.00,1002.00,1003.00,1004.00,1005.00,1006.00,150,151,152,1,10000,10001,10002,360.1234\n"), &Count);
    f_sync(&g_sFileObject);

    fresult=f_close(&g_sFileObject);

When i try the above code, I get FR_NOT_READY error. I am new to  SD CARD and C2000. Please help me with it.

  • First of all, do you know how to use putty or any serial com application on your pc?

  • Yes, I used both putty and tera term to connect it
  • And you see the values you sent to the board and the values sent back from the board?
  • No, i dont see anything on it.
  • I was able to print values on the terminal. I had to add _launchxl_f28379d to the predefined symbols settings. However, when i run the sd card example, it always says that the FR_NOT_READY. Can you help me with it?
  • Yes you need the _LAUNCHXL_F28379D so that sysctl init can set the clocks correctly.

    Can you share a picture of how you have the SPI signals connected to the SD card?
  • yes,  the following image shows the connections with respect to sd card.

    So, the coonections are as follows:

    c2000                                                     sdcard

    3.3v                                                          3.3v

    gnd                                                          gnd

    pin18(gpio123)                                         clk

    pin17(gpio122)                                        DO

    pin13(gpio 124)                                       DI

    pin12(gpio 125)                                       CS

    Please correct if I"m wrong. 

  • I have also tried reversing the DO and DI pins as well. The result is the same. I just want to save the data using the sdcard but the error keeps popping up saying it is not ready. I tried every option in the book and I still cant get the output.
  • Are you seeing the output of SPI coming out of the pins on a scope?

  • That board should also be a 5v board with a level shifter. I'm not sure if that will cause an issue. Also let me know on your MCU which GPIO is SIMO, SOMI, CLK and CSn.
  • I have not yet tested it on scope or Logic analyzer. I will update you with that soon. 

    Nima Eskandari said:
    That board should also be a 5v board with a level shifter. I'm not sure if that will cause an issue. Also let me know on your MCU which GPIO is SIMO, SOMI, CLK and CSn.

    It does have a 5V level shifter.  My current connections are these:

    CS->GPIO125[sd1 clk2], DI->GPIO122[sd1 d1], DO->GPIO124[sd1 d2], CLK->GPIO123[sd1 clk1]

    Also, when i toggle gpio 125, the micro sd break out board toggles accordingly. I am using the latest c2000ware(1.06.00). The sd card works as well. I used arduino uno to code it and i got the outptuts as well.

  • I have tried so many versions of the code that I lost count.  I am now trying the following code:

    fresult = f_mount(0, &g_sFatFs);

        fresult = f_open(&g_sFileObject, "test.txt", FA_READ | FA_WRITE);

        fresult = f_read(&g_sFileObject, &g_cTmpBuf, sizeof(g_cTmpBuf) - 1, &nStatus);

        fresult = f_write(&g_sFileObject, &g_cTmpBuf, sizeof(g_cTmpBuf) - 1, &nStatus);

        fresult = f_close(&g_sFileObject);

    It goes to f_open and says that the file is not ready. Can you tell me if there are any changes that i need to make  to the sd_card_cpu01 code? I am attaching my files below. Is there something that I might be missing?

    /*-----------------------------------------------------------------------*/
    /* MMC/SDC (in SPI mode) control module  (C)ChaN, 2007                   */
    /*-----------------------------------------------------------------------*/
    /* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros         */
    /* are platform dependent.                                               */
    /*-----------------------------------------------------------------------*/
    
    /*
     * This file was modified from a sample available from the FatFs
     * web site. It was modified to work with a F2837xD device.
     */
    
    #include "F28x_Project.h"
    
    //#include "F2837xD_types.h"
    #include "inc/hw_types.h"
    #include "fatfs/src/diskio.h"
    
    /*
     * The following header defines the hardware connections used to connect
     * the SDCard.  This can be found under the relevant board directory.
     */
    //*****************************************************************************
    //
    // SDCard SPI port
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // GPIO for SDCard SPI pins
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // GPIO for the SDCard chip select
    //
    //*****************************************************************************
    
    
    /* Definitions for MMC/SDC command */
    #define CMD0    (0x40+0)    /* GO_IDLE_STATE */
    #define CMD1    (0x40+1)    /* SEND_OP_COND */
    #define CMD8    (0x40+8)    /* SEND_IF_COND */
    #define CMD9    (0x40+9)    /* SEND_CSD */
    #define CMD10    (0x40+10)    /* SEND_CID */
    #define CMD12    (0x40+12)    /* STOP_TRANSMISSION */
    #define CMD16    (0x40+16)    /* SET_BLOCKLEN */
    #define CMD17    (0x40+17)    /* READ_SINGLE_BLOCK */
    #define CMD18    (0x40+18)    /* READ_MULTIPLE_BLOCK */
    #define CMD23    (0x40+23)    /* SET_BLOCK_COUNT */
    #define CMD24    (0x40+24)    /* WRITE_BLOCK */
    #define CMD25    (0x40+25)    /* WRITE_MULTIPLE_BLOCK */
    #define CMD41    (0x40+41)    /* SEND_OP_COND (ACMD) */
    #define CMD55    (0x40+55)    /* APP_CMD */
    #define CMD58    (0x40+58)    /* READ_OCR */
    
    // asserts the CS pin to the card
    static
    void SELECT (void)  //EDITED
    {
        //
        // Select the SD card.
        //
        GpioDataRegs.GPDCLEAR.bit.GPIO125 = 1;
    
    }
    
    // De-asserts the CS pin to the card.
    static
    void DESELECT (void) //EDITED
    {
        GpioDataRegs.GPDSET.bit.GPIO125 = 1;
    }
    
    /*--------------------------------------------------------------------------
    
       Module Private Functions
    
    ---------------------------------------------------------------------------*/
    
    static volatile
    DSTATUS Stat = STA_NOINIT;    /* Disk status */
    
    static volatile
    BYTE Timer1, Timer2;    /* 100Hz decrement timer */
    
    static
    BYTE CardType;            /* b0:MMC, b1:SDC, b2:Block addressing */
    
    static
    BYTE PowerFlag = 0;     /* indicates if "power" is on */
    
    /*-----------------------------------------------------------------------*/
    /* Transmit a byte to MMC via SPI  (Platform dependent)                  */
    /*-----------------------------------------------------------------------*/
    
    static
    void xmit_spi (BYTE dat)    //EDITED
    {
        volatile DWORD rcvdat;                      //included file like integer.h for DWORD definition
    
        /* Write the data to the tx fifo */
        while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);    //Wait for room to write data
        SpicRegs.SPITXBUF = ((DWORD)dat)<<8;        //Write data
    
        /* flush data read during the write */
        while(SpicRegs.SPISTS.bit.INT_FLAG !=1);    //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
        rcvdat = (SpicRegs.SPIRXBUF && 0xFF);       //Clear Receive Buffer
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a byte from MMC via SPI  (Platform dependent)                 */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE rcvr_spi (void)        //EDITED
    {
        volatile DWORD rcvdat;
    
        //Disable transmission channel
        //SpicRegs.SPICTL.bit.TALK = 0;
    
        /* write dummy data */
        while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);    //Wait for space to write
        SpicRegs.SPITXBUF = 0xFF00;                 //Write dummy data
    
        /* read data from RX fifo */
        while(SpicRegs.SPISTS.bit.INT_FLAG !=1);    //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
        rcvdat = (SpicRegs.SPIRXBUF & 0xFF);        //Read Transferred data
    
        return (BYTE)rcvdat;
    }
    
    
    static
    void rcvr_spi_m (BYTE *dst)
    {
        *dst = rcvr_spi();
    }
    
    /*-----------------------------------------------------------------------*/
    /* Wait for card ready                                                   */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE wait_ready (void)
    {
        BYTE res;
    
    
        Timer2 = 50;    /* Wait for ready in timeout of 500ms */
        rcvr_spi();
        do{
            res = rcvr_spi();
            DELAY_US(175);
        } while ((res != 0xFF) && Timer2);
    
        return res;
    }
    
    /*-----------------------------------------------------------------------*/
    /* Send 80 or so clock transitions with CS and DI held high. This is     */
    /* required after card power up to get it into SPI mode                  */
    /*-----------------------------------------------------------------------*/
    static
    void send_initial_clock_train (void)        //EDITED
    {
        volatile DWORD dat;
    
        /* Ensure CS (STE) is held high. */
        DESELECT();
    
        /* Switch the SPI TX line to a GPIO and drive it high too. */
        EALLOW;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDDIR.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        EDIS;
        GpioDataRegs.GPDSET.bit.GPIO122 = 1;
    
        /* Send 10 bytes over the SPI. This causes the clock to wiggle the */
        /* required number of times. */
        unsigned int i;
        for(i = 0 ; i < 10 ; i++)
        {
            /* Write DUMMY data */
            while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);
            SpicRegs.SPITXBUF = 0xFF00;
    
            /* Flush data read during data write. */
            while(SpicRegs.SPISTS.bit.INT_FLAG !=1);        //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
            dat = (SpicRegs.SPIRXBUF & 0xFF);
        }
    
        /* Revert to hardware control of the SPI TX line. */
        EALLOW;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 2;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        EDIS;
    }
    
    /*-----------------------------------------------------------------------*/
    /* Power Control  (Platform dependent)                                   */
    /*-----------------------------------------------------------------------*/
    /* When the target system does not support socket power control, there   */
    /* is nothing to do in these functions and chk_power always returns 1.   */
    
    static
    void power_on (void)    //EDITED
    {
        /*
         * This doesn't really turn the power on, but initializes the
         * SPI port and pins needed to talk to the SD card.
         */
        EALLOW;
        /* Enable the peripherals used to drive the SDC on SPI */
        CpuSysRegs.PCLKCR8.bit.SPI_C = 1;
    
        /*
         * Configure the appropriate pins to be SPI instead of GPIO. The CS
         * signal is directly driven to ensure that we can hold it low through a
         * complete transaction with the SD card.
         */
    
    
        //Unlock the SD-Card SPI GPIOs
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO123 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO124 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO125 = 0;
    
        //Set up  MUX & DIR
        GpioCtrlRegs.GPDMUX2.bit.GPIO125 = 0;    //Leave as GPIO for manual CS control
    
        GpioCtrlRegs.GPDGMUX2.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDGMUX2.bit.GPIO123 = 1;
        GpioCtrlRegs.GPDGMUX2.bit.GPIO124 = 1;
    
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 2;
        GpioCtrlRegs.GPDMUX2.bit.GPIO123 = 2;
        GpioCtrlRegs.GPDMUX2.bit.GPIO124 = 2;
    
        GpioCtrlRegs.GPDDIR.bit.GPIO125 = 1;
    
        //Set up GPIO Pull-up disables/enables
        GpioCtrlRegs.GPDPUD.bit.GPIO122 = 0;    //Needs to be normally pulled high
        GpioCtrlRegs.GPDPUD.bit.GPIO123 = 0;    //Needs to be normally pulled high
        GpioCtrlRegs.GPDPUD.bit.GPIO124 = 1;
        GpioCtrlRegs.GPDPUD.bit.GPIO125 = 1;
    
        //Set up GPIOs in asynch mode
        GpioCtrlRegs.GPDQSEL2.bit.GPIO122 = 3; // Asynch input
        GpioCtrlRegs.GPDQSEL2.bit.GPIO123 = 3;
        GpioCtrlRegs.GPDQSEL2.bit.GPIO124 = 3;
        GpioCtrlRegs.GPDQSEL2.bit.GPIO125 = 3;
    
        //Configure GPIOs for CPU1
        GpioCtrlRegs.GPDCSEL4.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO123 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO124 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO125 = 0;
    
        //Lock the SD-Card SPI GPIOs
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO123 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO124 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO125 = 1;
        EDIS;
    
    
        /* Deassert the SPIC chip selects for both the SD card and serial flash */
        DESELECT();
    
        /* Configure the SPI C port */
        SpicRegs.SPICCR.bit.SPISWRESET = 0;         //Set reset bit low
        SpicRegs.SPICTL.bit.CLK_PHASE = 0;
        SpicRegs.SPICCR.bit.CLKPOLARITY = 1;
        SpicRegs.SPICTL.bit.MASTER_SLAVE = 1;       //Master mode
        SpicRegs.SPIBRR.all = 63;                       //update value to proper setting for correct bitrate ( current: ~500kHz)
        SpicRegs.SPICCR.bit.SPICHAR = 0x7;          //Set char length to 8 bits
        SpicRegs.SPICTL.bit.TALK = 1;
        SpicRegs.SPICCR.bit.SPISWRESET = 1;         //Release SPI from reset
        SpicRegs.SPIPRI.bit.FREE = 1;
        SpicRegs.SPIPRI.bit.SOFT = 1;
    
        /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */
        /* to be able to accept a native command. */
        send_initial_clock_train();
    
        //DELAY_US(50);
    
        PowerFlag = 1;
    }
    
    // set the SPI speed to the max setting
    static
    void set_max_speed(void)            //EDIT
    {
        /* Disable the SPI*/
        //DevCfgRegs.DC9.bit.SPI_C = 0;
        SpicRegs.SPICCR.bit.SPISWRESET = 0;     //Place in reset
    
        /* Configure the SPI C port */
        SpicRegs.SPICTL.bit.CLK_PHASE = 0;
        SpicRegs.SPICCR.bit.CLKPOLARITY = 1;
        SpicRegs.SPICTL.bit.MASTER_SLAVE = 1;   //Set Master Mode
    
        SpicRegs.SPIBRR.all = 1; // <- (No enforced limit)
    
        SpicRegs.SPICCR.bit.SPICHAR = 0x7;      //8 bit char length
        SpicRegs.SPICTL.bit.TALK = 1;
    
        /* Enable the SPI */
        SpicRegs.SPICCR.bit.SPISWRESET = 1;     //Release from reset
        //DevCfgRegs.DC9.bit.SPI_C = 1;
    }
    
    static
    void power_off (void)
    {
        PowerFlag = 0;
    }
    
    static
    int chk_power(void)        /* Socket power state: 0=off, 1=on */
    {
        return PowerFlag;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a data packet from MMC                                        */
    /*-----------------------------------------------------------------------*/
    
    static
    BOOL rcvr_datablock (
        BYTE *buff,            /* Data buffer to store received data */
        UINT btr            /* Byte count (must be even number) */
    )
    {
        BYTE token;
    
    
        Timer1 = 10;
        do                              /* Wait for data packet in timeout of 100ms */
        {
            token = rcvr_spi();
        }
        while ((token == 0xFF) && Timer1);
        if(token != 0xFE) return FALSE;    /* If not valid data token, retutn with error */
    
        do                              /* Receive the data block into buffer */
        {
            rcvr_spi_m(buff++);
            rcvr_spi_m(buff++);
        }
        while (btr -= 2);
        rcvr_spi();                        /* Discard CRC */
        rcvr_spi();
    
        return TRUE;                    /* Return with success */
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Send a data packet to MMC                                             */
    /*-----------------------------------------------------------------------*/
    
    #if _READONLY == 0
    static
    BOOL xmit_datablock (
        const BYTE *buff,    /* 512 byte data block to be transmitted */
        BYTE token            /* Data/Stop token */
    )
    {
        BYTE resp, wc;
    
    
        if (wait_ready() != 0xFF) return FALSE;
    
        xmit_spi(token);                    /* Xmit data token */
        if (token != 0xFD)      /* Is data token */
        {
            wc = 256;
            do                              /* Xmit the 512 byte data block to MMC */
            {
                xmit_spi(*buff++);
                xmit_spi(*buff++);
            }
            while (--wc);
            xmit_spi(0xFF);                    /* CRC (Dummy) */
            xmit_spi(0xFF);
            resp = rcvr_spi();                /* Reveive data response */
            if ((resp & 0x1F) != 0x05)        /* If not accepted, return with error */
                return FALSE;
        }
    
        return TRUE;
    }
    #endif /* _READONLY */
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Send a command packet to MMC                                          */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE send_cmd (
        BYTE cmd,        /* Command byte */
        DWORD arg        /* Argument */
    )
    {
        BYTE n, res;
    
    
        if (wait_ready() != 0xFF) return 0xFF;
    
        /* Send command packet */
        xmit_spi(cmd);                        /* Command */
        xmit_spi((BYTE)(arg >> 24));        /* Argument[31..24] */
        xmit_spi((BYTE)(arg >> 16));        /* Argument[23..16] */
        xmit_spi((BYTE)(arg >> 8));            /* Argument[15..8] */
        xmit_spi((BYTE)arg);                /* Argument[7..0] */
        n = 0;
        if (cmd == CMD0) n = 0x95;            /* CRC for CMD0(0) */
        if (cmd == CMD8) n = 0x87;            /* CRC for CMD8(0x1AA) */
        xmit_spi(n);
    
        /* Receive command response */
        if (cmd == CMD12) rcvr_spi();        /* Skip a stuff byte when stop reading */
        n = 10;                                /* Wait for a valid response in timeout of 10 attempts */
        do
            res = rcvr_spi();
        while ((res & 0x80) && --n);
    
        return res;            /* Return with the response value */
    }
    
    /*-----------------------------------------------------------------------*
     * Send the special command used to terminate a multi-sector read.
     *
     * This is the only command which can be sent while the SDCard is sending
     * data. The SDCard spec indicates that the data transfer will stop 2 bytes
     * after the 6 byte CMD12 command is sent and that the card will then send
     * 0xFF for between 2 and 6 more bytes before the R1 response byte.  This
     * response will be followed by another 0xFF byte.  In testing, however, it
     * seems that some cards don't send the 2 to 6 0xFF bytes between the end of
     * data transmission and the response code.  This function, therefore, merely
     * reads 10 bytes and, if the last one read is 0xFF, returns the value of the
     * latest non-0xFF byte as the response code.
     *
     *-----------------------------------------------------------------------*/
    
    static
    BYTE send_cmd12 (void)
    {
        BYTE n, res, val;
    
        /* For CMD12, we don't wait for the card to be idle before we send
         * the new command.
         */
    
        /* Send command packet - the argument for CMD12 is ignored. */
        xmit_spi(CMD12);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
    
        /* Read up to 10 bytes from the card, remembering the value read if it's
           not 0xFF */
        for(n = 0; n < 10; n++)
        {
            val = rcvr_spi();
            if(val != 0xFF)
            {
                res = val;
            }
        }
    
        return res;            /* Return with the response value */
    }
    
    /*--------------------------------------------------------------------------
    
       Public Functions
    
    ---------------------------------------------------------------------------*/
    
    
    /*-----------------------------------------------------------------------*/
    /* Initialize Disk Drive                                                 */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_initialize (
        BYTE drv        /* Physical drive nmuber (0) */
    )
    {
        BYTE n, ty, ocr[4];
    
    
        if (drv) return STA_NOINIT;            /* Supports only single drive */
        if (Stat & STA_NODISK) return Stat;    /* No card in the socket */
    
        power_on();                            /* Force socket power on */
        send_initial_clock_train();
    
        SELECT();                /* CS = L */
        ty = 0;
        if (send_cmd(CMD0, 0) == 1)              /* Enter Idle state */
        {
            Timer1 = 100;                        /* Initialization timeout of 1000 msec */
            if (send_cmd(CMD8, 0x1AA) == 1)      /* SDC Ver2+ */
            {
                for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                if (ocr[2] == 0x01 && ocr[3] == 0xAA)      /* The card can work at vdd range of 2.7-3.6V */
                {
                    do
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0)    break;    /* ACMD41 with HCS bit */
                    }
                    while (Timer1);
                    if (Timer1 && send_cmd(CMD58, 0) == 0)      /* Check CCS bit */
                    {
                        for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                        ty = (ocr[0] & 0x40) ? 6 : 2;
                    }
                }
            }
            else                                /* SDC Ver1 or MMC */
            {
                ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1;    /* SDC : MMC */
                do
                {
                    if (ty == 2)
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break;    /* ACMD41 */
                    }
                    else
                    {
                        if (send_cmd(CMD1, 0) == 0) break;                                /* CMD1 */
                    }
                }
                while (Timer1);
                if (!Timer1 || send_cmd(CMD16, 512) != 0)    /* Select R/W block length */
                    ty = 0;
            }
        }
        CardType = ty;
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        if (ty)              /* Initialization succeded */
        {
            Stat &= ~STA_NOINIT;        /* Clear STA_NOINIT */
            set_max_speed();
        }
        else                /* Initialization failed */
        {
            power_off();
        }
    
        return Stat;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Get Disk Status                                                       */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_status (
        BYTE drv        /* Physical drive number (0) */
    )
    {
        if (drv) return STA_NOINIT;        /* Supports only single drive */
        return Stat;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Read Sector(s)                                                        */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_read (
        BYTE drv,            /* Physical drive nmuber (0) */
        BYTE *buff,            /* Pointer to the data buffer to store read data */
        DWORD sector,        /* Start sector number (LBA) */
        BYTE count            /* Sector count (1..255) */
    )
    {
        if (drv || !count) return RES_PARERR;
        if (Stat & STA_NOINIT) return RES_NOTRDY;
    
        if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */
    
        SELECT();            /* CS = L */
    
        if (count == 1)      /* Single block read */
        {
            if ((send_cmd(CMD17, sector) == 0)    /* READ_SINGLE_BLOCK */
                    && rcvr_datablock(buff, 512))
                count = 0;
        }
        else                  /* Multiple block read */
        {
            if (send_cmd(CMD18, sector) == 0)      /* READ_MULTIPLE_BLOCK */
            {
                do
                {
                    if (!rcvr_datablock(buff, 512)) break;
                    buff += 512;
                }
                while (--count);
                send_cmd12();                /* STOP_TRANSMISSION */
            }
        }
    
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        return count ? RES_ERROR : RES_OK;
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Write Sector(s)                                                       */
    /*-----------------------------------------------------------------------*/
    
    #if _READONLY == 0
    DRESULT disk_write (
        BYTE drv,            /* Physical drive nmuber (0) */
        const BYTE *buff,    /* Pointer to the data to be written */
        DWORD sector,        /* Start sector number (LBA) */
        BYTE count            /* Sector count (1..255) */
    )
    {
        if (drv || !count) return RES_PARERR;
        if (Stat & STA_NOINIT) return RES_NOTRDY;
        if (Stat & STA_PROTECT) return RES_WRPRT;
    
        if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */
    
        SELECT();            /* CS = L */
    
        if (count == 1)      /* Single block write */
        {
            if ((send_cmd(CMD24, sector) == 0)    /* WRITE_BLOCK */
                    && xmit_datablock(buff, 0xFE))
                count = 0;
        }
        else                  /* Multiple block write */
        {
            if (CardType & 2)
            {
                send_cmd(CMD55, 0);
                send_cmd(CMD23, count);    /* ACMD23 */
            }
            if (send_cmd(CMD25, sector) == 0)      /* WRITE_MULTIPLE_BLOCK */
            {
                do
                {
                    if (!xmit_datablock(buff, 0xFC)) break;
                    buff += 512;
                }
                while (--count);
                if (!xmit_datablock(0, 0xFD))    /* STOP_TRAN token */
                    count = 1;
            }
        }
    
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        return count ? RES_ERROR : RES_OK;
    }
    #endif /* _READONLY */
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Miscellaneous Functions                                               */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_ioctl (
        BYTE drv,        /* Physical drive nmuber (0) */
        BYTE ctrl,        /* Control code */
        void *buff        /* Buffer to send/receive control data */
    )
    {
        DRESULT res;
        BYTE n, csd[16], *ptr = buff;
        WORD csize;
    
    
        if (drv) return RES_PARERR;
    
        res = RES_ERROR;
    
        if (ctrl == CTRL_POWER)
        {
            switch (*ptr)
            {
            case 0:        /* Sub control code == 0 (POWER_OFF) */
                if (chk_power())
                    power_off();        /* Power off */
                res = RES_OK;
                break;
            case 1:        /* Sub control code == 1 (POWER_ON) */
                power_on();                /* Power on */
                res = RES_OK;
                break;
            case 2:        /* Sub control code == 2 (POWER_GET) */
                *(ptr+1) = (BYTE)chk_power();
                res = RES_OK;
                break;
            default :
                res = RES_PARERR;
            }
        }
        else
        {
            if (Stat & STA_NOINIT) return RES_NOTRDY;
    
            SELECT();        /* CS = L */
    
            switch (ctrl)
            {
            case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (DWORD) */
                if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16))
                {
                    if ((csd[0] >> 6) == 1)      /* SDC ver 2.00 */
                    {
                        csize = csd[9] + ((WORD)csd[8] << 8) + 1;
                        *(DWORD*)buff = (DWORD)csize << 10;
                    }
                    else                        /* MMC or SDC ver 1.XX */
                    {
                        n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                        csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
                        *(DWORD*)buff = (DWORD)csize << (n - 9);
                    }
                    res = RES_OK;
                }
                break;
    
            case GET_SECTOR_SIZE :    /* Get sectors on the disk (WORD) */
                *(WORD*)buff = 512;
                res = RES_OK;
                break;
    
            case CTRL_SYNC :    /* Make sure that data has been written */
                if (wait_ready() == 0xFF)
                    res = RES_OK;
                break;
    
            case MMC_GET_CSD :    /* Receive CSD as a data block (16 bytes) */
                if (send_cmd(CMD9, 0) == 0        /* READ_CSD */
                        && rcvr_datablock(ptr, 16))
                    res = RES_OK;
                break;
    
            case MMC_GET_CID :    /* Receive CID as a data block (16 bytes) */
                if (send_cmd(CMD10, 0) == 0        /* READ_CID */
                        && rcvr_datablock(ptr, 16))
                    res = RES_OK;
                break;
    
            case MMC_GET_OCR :    /* Receive OCR as an R3 resp (4 bytes) */
                if (send_cmd(CMD58, 0) == 0)      /* READ_OCR */
                {
                    for (n = 0; n < 4; n++)
                        *ptr++ = rcvr_spi();
                    res = RES_OK;
                }
    
    //        case MMC_GET_TYPE :    /* Get card type flags (1 byte) */
    //            *ptr = CardType;
    //            res = RES_OK;
    //            break;
    
            default:
                res = RES_PARERR;
            }
    
            DESELECT();            /* CS = H */
            rcvr_spi();            /* Idle (Release DO) */
        }
    
        return res;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Device Timer Interrupt Procedure  (Platform dependent)                */
    /*-----------------------------------------------------------------------*/
    /* This function must be called in period of 10ms                        */
    
    void disk_timerproc (void)
    {
    //    BYTE n, s;
        BYTE n;
    
    
        n = Timer1;                        /* 100Hz decrement timer */
        if (n) Timer1 = --n;
        n = Timer2;
        if (n) Timer2 = --n;
    
    }
    
    /*---------------------------------------------------------*/
    /* User Provided Timer Function for FatFs module           */
    /*---------------------------------------------------------*/
    /* This is a real time clock service to be called from     */
    /* FatFs module. Any valid time must be returned even if   */
    /* the system does not support a real time clock.          */
    
    DWORD get_fattime (void)
    {
    
        return    ((2008UL-1980) << 25)    // Year = 2008
                  | (2UL << 21)            // Month = February
                  | (26UL << 16)            // Day = 26
                  | (14U << 11)            // Hour = 14
                  | (0U << 5)            // Min = 0
                  | (0U >> 1)                // Sec = 0
                  ;
    
    }
    

    //###########################################################################
    //
    // FILE:   sd_card.c
    //
    // TITLE:  Example program for reading files from an SD card.
    //
    //! \addtogroup cpu01_example_list
    //! <h1>SD card using FAT file system (sd_card)</h1>
    //!
    //! This example application demonstrates reading a file system from
    //! an SD card.  It makes use of FatFs, a FAT file system driver.
    //!
    //! For additional details about FatFs, see the following site:
    //! http://elm-chan.org/fsw/ff/00index_e.html
    //!
    //! The application may be operated via a serial terminal attached to
    //! UART0. The RS232 communication parameters should be set to 115,200 bits
    //! per second, and 8-n-1 mode.  When the program is started a message will be
    //! printed to the terminal.  Type ``help'' for command help.
    //!
    //
    //###########################################################################
    // $TI Release: F2837xD Support Library v3.05.00.00 $
    // $Release Date: Thu Oct 18 15:48:42 CDT 2018 $
    // $Copyright:
    // Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "F28x_Project.h"
    #include <string.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "fatfs/src/ff.h"
    #include "fatfs/src/diskio.h"
    
    //
    // Defines
    //
    #define PATH_BUF_SIZE   80     // 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 CMD_BUF_SIZE    64     // Defines the size of the buffer that holds
                                   // the command line.
    #define FRESULT_ENTRY(f)        { (f), (# f) } // A macro to make it easy to
                                                   // add result codes to the table
    #define NAME_TOO_LONG_ERROR     1              // Error reasons returned
    #define OPENDIR_ERROR           2              // by ChangeDirectory().
    #define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))
    #define TICKS_PER_SECOND        100
    #define NUM_LIST_STRINGS        48
    #define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
    #define NUM_STATUS_STRINGS      6
    #define MAX_STATUS_STRING_LEN   (36 + 1)
    
    //
    // Globals
    //
    static char g_cCwdBuf[PATH_BUF_SIZE] = "/";  // This buffer holds the full path
                                                 // to the current working
                                                 // directory. Initially it is
                                                 // root ("/").
    static char g_cTmpBuf[PATH_BUF_SIZE];        // A temporary data buffer used
                                                 // when manipulating file paths,or
                                                 // reading data from the SD card.
    static char g_cCmdBuf[CMD_BUF_SIZE];         // The buffer that holds the
                                                 // command line.
    static FATFS g_sFatFs;
    static DIR g_sDirObject;
    static FILINFO g_sFileInfo;
    static FIL g_sFileObject;
    unsigned short usBytesRead;
    
    //
    // 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;
    
    //
    // 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)
    };
    
    const char *g_ppcDirListStrings[NUM_LIST_STRINGS]; // Storage for the filename
                                                       // listbox widget string
                                                       // table.
    
    //
    // Storage for the names of the files in the current directory. Filenames
    // are stored in format "(D) filename.ext" for directories or
    // "(F) filename.ext" for files.
    //
    char g_pcFilenames[NUM_LIST_STRINGS][MAX_FILENAME_STRING_LEN];
    
    //
    // Storage for the strings which appear in the status box at the bottom of the
    // display.
    //
    char g_pcStatus[NUM_STATUS_STRINGS][MAX_STATUS_STRING_LEN];
    
    //
    // Storage for the status listbox widget string table.
    //
    const char *g_ppcStatusStrings[NUM_STATUS_STRINGS] =
    {
        g_pcStatus[0],
        g_pcStatus[1],
        g_pcStatus[2],
        g_pcStatus[3],
        g_pcStatus[4],
        g_pcStatus[5]
    };
    unsigned long g_ulStatusStringIndex = 0;
    
    //
    // Forward declarations for functions called by the widgets used in the user
    // interface.
    //
    static FRESULT ChangeToDirectory(char *pcDirectory, unsigned long *pulReason);
    static const char *StringFromFresult(FRESULT fresult);
    
    //
    // Function Prototypes
    //
    extern void UARTStdioIntHandler(void);
    
    //
    // StringFromFresult - 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.
    //
    static 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 unknown error.
        //
        return("UNKNOWN ERROR CODE");
    }
    
    //
    // SysTickHandler - This is the handler for this SysTick interrupt.  FatFs
    //                  requires a timer tick every 10 ms for internal timing
    //                  purposes.
    //
    __interrupt void
    SysTickHandler(void)
    {
        //
        // Call the FatFs tick timer.
        //
        disk_timerproc();
        PieCtrlRegs.PIEACK.all |= 1;
    }
    
    //
    // Cmd_ls - 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, ulItemCount, ulFileCount, 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;
        ulItemCount = 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;
            }
    
            //
            // 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) ? (uint32_t)'D' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_RDO) ? (uint32_t)'R' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_HID) ? (uint32_t)'H' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_SYS) ? (uint32_t)'S' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_ARC) ? (uint32_t)'A' : (uint32_t)'-',
                       (uint32_t)((g_sFileInfo.fdate >> 9) + 1980),
                       (uint32_t)((g_sFileInfo.fdate >> 5) & 15),
                       (uint32_t)(g_sFileInfo.fdate & 31),
                       (uint32_t)((g_sFileInfo.ftime >> 11)),
                       (uint32_t)((g_sFileInfo.ftime >> 5) & 63),
                       (uint32_t)(g_sFileInfo.fsize),
                       g_sFileInfo.fname);
    
            //
            // Add the information as a line in the listbox widget.
            //
            if(ulItemCount < NUM_LIST_STRINGS)
            {
                usprintf(g_pcFilenames[ulItemCount], "(%c) %12s",
                         (g_sFileInfo.fattrib & AM_DIR) ? 'D' : 'F',
                         g_sFileInfo.fname);
            }
    
            //
            // If the attribute 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;
            }
    
            //
            // Move to the next entry in the item array we use to populate the
            // list box.
            //
            ulItemCount++;
    
            //
            // Wait for the UART transmit buffer to empty.
            //
    //        UARTFlushTx(false);
        }
    
        //
        // 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);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Made it to here, return with no errors.
        //
        return(0);
    }
    
    //
    // ChangeToDirectory - 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 don't try
    //                     something like this: ("../my/new/path")
    //                     Once the new directory is specified, it attempts to open
    //                     the directory to make sure it exists.  If the new path
    //                     is opened successfully, then the current working
    //                     directory (cwd) is changed to the new path. In cases of
    //                     error, the pulReason parameter will be written with one
    //                     of the following values:
    //
    static FRESULT
    ChangeToDirectory(char *pcDirectory, unsigned long *pulReason)
    {
        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(pcDirectory[0] == '/')
        {
            //
            // Make sure the new path is not bigger than the cwd buffer.
            //
            if(strlen(pcDirectory) + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_OK);
            }
            //
            // If the new path name (in argv[1])  is not too long, then
            // copy it into the temporary buffer so it can be checked.
            //
            else
            {
                strncpy(g_cTmpBuf, pcDirectory, sizeof(g_cTmpBuf));
            }
        }
        //
        // If the argument is .. then attempt to remove the lowest level
        // on the CWD.
        //
        else if(!strcmp(pcDirectory, ".."))
        {
            //
            // Get the index to the last character in the current path.
            //
            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(pcDirectory) + 1 + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_INVALID_OBJECT);
            }
            //
            // The new path is okay, so add the separator and then append
            // the new directory to the path.
            //
            else
            {
                //
                // If not already at the root level, then append a /
                //
                if(strcmp(g_cTmpBuf, "/"))
                {
                    strcat(g_cTmpBuf, "/");
                }
    
                //
                // Append the new directory to the path.
                //
                strcat(g_cTmpBuf, pcDirectory);
            }
        }
    
        //
        // 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 cant be opened, then it is a bad path.  Inform
        // user and return.
        //
        if(fresult != FR_OK)
        {
            *pulReason = OPENDIR_ERROR;
            return(fresult);
        }
        //
        // Otherwise, it is a valid new path, so copy it into the CWD and update
        // the screen.
        //
        else
        {
            strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
        }
    
        //
        // Return success.
        //
        return(FR_OK);
    }
    
    //
    // Cmd_cd - 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 long ulReason;
        FRESULT fresult;
    
        //
        // Try to change to the directory provided on the command line.
        //
        fresult = ChangeToDirectory(argv[1], &ulReason);
    
        //
        // If an error was reported, try to offer some helpful information.
        //
        if(fresult != FR_OK)
        {
            switch(ulReason)
            {
                case OPENDIR_ERROR:
                    UARTprintf("Error opening new directory.\n");
                    break;
    
                case NAME_TOO_LONG_ERROR:
                    UARTprintf("Resulting path name is too long.\n");
                    break;
    
                default:
                    UARTprintf("An unrecognized error was reported.\n");
                    break;
            }
        }
    
        //
        // Return the appropriate error code.
        //
        return(fresult);
    }
    
    //
    // Cmd_pwd - 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);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_cat - 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);
    
            //
            // Continue reading until less than the full number of bytes are
            // read.  That means the end of the buffer was reached.
            //
        }
        while(usBytesRead == sizeof(g_cTmpBuf) - 1);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_help - 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_psCmdTable[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);
    }
    
    //
    // This is the table that holds the command names, implementing functions,
    // and brief description.
    //
    tCmdLineEntry g_psCmdTable[] =
    {
        { "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" },
        { 0, 0, 0 }
    };
    
    //
    // __error__ - The error routine that is called if the driver library
    //             encounters an error.
    //
    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
    }
    #endif
    
    //
    // ConfigureUART - Configure the UART and its pins.  This must be called
    //                 before UARTprintf().
    //
    void
    ConfigureUART(void)
    {
        //
        // Enable UART0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SCI1);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        EALLOW;
        GPIO_SetupPinMux(43, GPIO_MUX_CPU1, 15);
          GPIO_SetupPinOptions(43, GPIO_INPUT, GPIO_PUSHPULL);
          GPIO_SetupPinMux(42, GPIO_MUX_CPU1, 15);
          GPIO_SetupPinOptions(42, GPIO_OUTPUT, GPIO_ASYNC);
        EDIS;
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, SysCtlLowSpeedClockGet(SYSTEM_CLOCK_SPEED));
    }
    
    //
    // Main - It performs initialization, then runs a command processing loop to
    //        read commands from the console.
    //
    int
    main(void)
    {
        int nStatus;
        FRESULT fresult;
    
        //
        // Initialize System Control
        //
        InitSysCtrl();
    
    #ifdef _FLASH
    //
    // Copy time critical code and Flash setup code to RAM
    // This includes the following functions:  InitFlash();
    // The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
    // symbols are created by the linker. Refer to the device .cmd file.
    //
        memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
    
    //
    // Call Flash Initialization to setup flash waitstates
    // This function must reside in RAM
    //
        InitFlash();
    #endif
    
        //
        // Initialize interrupt controller and vector table
        //
        InitPieCtrl();
        InitPieVectTable();
    
        //
        // Set the system tick to fire 100 times per second.
        //
        SysTickInit();
        SysTickPeriodSet(SysCtlClockGet(SYSTEM_CLOCK_SPEED) / 100);
        SysTickIntRegister(SysTickHandler);
        SysTickIntEnable();
        SysTickEnable();
    
        //
        // Enable Interrupts
        //
        IntMasterEnable();
    
        //
        // Configure UART0 for debug output.
        //
        ConfigureUART();
    
        //
        // 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);
         fresult = f_open(&g_sFileObject, "test.txt", FA_READ | FA_WRITE);
    
         fresult = f_read(&g_sFileObject, &g_cTmpBuf, sizeof(g_cTmpBuf) - 1, &nStatus);
    
         fresult = f_write(&g_sFileObject, &g_cTmpBuf, sizeof(g_cTmpBuf) - 1, &nStatus);
    
         fresult = f_close(&g_sFileObject);
        if(fresult != FR_OK)
        {
            UARTprintf("f_mount error: %s\n", StringFromFresult(fresult));
            return(1);
        }
    
        //
        // Enter an (almost) 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));
            }
        }
    }
    
    //
    // End of file
    //
    

  • Can anyone help me  regarding this?

  • Can you check on the scope to make sure that the SPI signals are showing up on the pins?
  • Hi,

    I  cannot see any data on logic analyzer. It is completely blank. However, when i connect it with Arduino uno, I can clearly see the values.  What is happening here?

  • Can anyone help me with it?

  • Okay there are two small snippets of your code that I need in order to figure out why your SPI signals are not showing up.

    1. You SPI initialization.
    2. You full pinmux initialization.

    The issue seems to be with your SPI module not controlling the pins from our previous conversation.

    This is assuming that when you say you don’t see any activity on the pins, it means that there is NEVER any SPI signals on the clk or mosi pins.
  • OH also I wanted to ask, are you using an MMC card? The type of card you use will affect whether the card will respond or not. For support on all types of SD cards you will need to implement support for different type yourself. Different types I can think of, off the top of my head are SDHC, SDXC and MMC. Make sure you are using the MMC type. The library Arduino has implement is far larger and more memory inefficient but will support all types.

    I see that you are using the standard pins mentioned in the example project so I bet the SD card type is your issue.
  • I have not changed anythign the original example code. 

    1.SPI initiation

    //
    // InitSPI - This function initializes the SPI to a known state
    //
    void InitSpi(void)
    {
    // Initialize SPI-A
    
    // Set reset low before configuration changes
    // Clock polarity (0 == rising, 1 == falling)
    // 16-bit character
    // Enable loop-back
    SpiaRegs.SPICCR.bit.SPISWRESET = 0;
    SpiaRegs.SPICCR.bit.CLKPOLARITY = 0;
    SpiaRegs.SPICCR.bit.SPICHAR = (16-1);
    SpiaRegs.SPICCR.bit.SPILBK = 1;
    
    // Enable master (0 == slave, 1 == master)
    // Enable transmission (Talk)
    // Clock phase (0 == normal, 1 == delayed)
    // SPI interrupts are disabled
    SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1;
    SpiaRegs.SPICTL.bit.TALK = 1;
    SpiaRegs.SPICTL.bit.CLK_PHASE = 0;
    SpiaRegs.SPICTL.bit.SPIINTENA = 0;
    
    // Set the baud rate
    SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = SPI_BRR;
    
    // Set FREE bit
    // Halting on a breakpoint will not halt the SPI
    SpiaRegs.SPIPRI.bit.FREE = 1;
    
    // Release the SPI from reset
    SpiaRegs.SPICCR.bit.SPISWRESET = 1;
    }
    
    //
    // InitSpiGpio - This function initializes GPIO pins to function as SPI pins.
    // Each GPIO pin can be configured as a GPIO pin or up to 3
    // different peripheral functional pins. By default all pins come
    // up as GPIO inputs after reset.
    //
    // Caution:
    // For each SPI peripheral
    // Only one GPIO pin should be enabled for SPISOMO operation.
    // Only one GPIO pin should be enabled for SPISOMI operation.
    // Only one GPIO pin should be enabled for SPICLK operation.
    // Only one GPIO pin should be enabled for SPISTE operation.
    // Comment out other unwanted lines.
    //
    void InitSpiGpio()
    {
    InitSpiaGpio();
    }
    
    //
    // InitSpiaGpio - Initialize SPIA GPIOs
    //
    void InitSpiaGpio()
    {
    EALLOW;
    
    //
    // Enable internal pull-up for the selected pins
    //
    // Pull-ups can be enabled or disabled by the user.
    // This will enable the pullups for the specified pins.
    // Comment out other unwanted lines.
    //
    GpioCtrlRegs.GPAPUD.bit.GPIO16 = 0; // Enable pull-up on GPIO16 (SPISIMOA)
    // GpioCtrlRegs.GPAPUD.bit.GPIO5 = 0; // Enable pull-up on GPIO5 (SPISIMOA)
    GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0; // Enable pull-up on GPIO17 (SPISOMIA)
    // GpioCtrlRegs.GPAPUD.bit.GPIO3 = 0; // Enable pull-up on GPIO3 (SPISOMIA)
    GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0; // Enable pull-up on GPIO18 (SPICLKA)
    GpioCtrlRegs.GPAPUD.bit.GPIO19 = 0; // Enable pull-up on GPIO19 (SPISTEA)
    
    //
    // Set qualification for selected pins to asynch only
    //
    // This will select asynch (no qualification) for the selected pins.
    // Comment out other unwanted lines.
    //
    GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3; // Asynch input GPIO16 (SPISIMOA)
    // GpioCtrlRegs.GPAQSEL1.bit.GPIO5 = 3; // Asynch input GPIO5 (SPISIMOA)
    GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3; // Asynch input GPIO17 (SPISOMIA)
    // GpioCtrlRegs.GPAQSEL1.bit.GPIO3 = 3; // Asynch input GPIO3 (SPISOMIA)
    GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3; // Asynch input GPIO18 (SPICLKA)
    GpioCtrlRegs.GPAQSEL2.bit.GPIO19 = 3; // Asynch input GPIO19 (SPISTEA)
    
    //
    //Configure SPI-A pins using GPIO regs
    //
    // This specifies which of the possible GPIO pins will be SPI functional
    // pins.
    // Comment out other unwanted lines.
    //
    GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1; // Configure GPIO16 as SPISIMOA
    // GpioCtrlRegs.GPAMUX1.bit.GPIO5 = 2; // Configure GPIO5 as SPISIMOA
    GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1; // Configure GPIO17 as SPISOMIA
    // GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 2; // Configure GPIO3 as SPISOMIA
    GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1; // Configure GPIO18 as SPICLKA
    GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 1; // Configure GPIO19 as SPISTEA
    
    EDIS;
    }
    

    2. PINMUX

    //
        // Configure GPIO Pins for UART mode.
        //
        EALLOW;
        GPIO_SetupPinMux(43, GPIO_MUX_CPU1, 15);
          GPIO_SetupPinOptions(43, GPIO_INPUT, GPIO_PUSHPULL);
          GPIO_SetupPinMux(42, GPIO_MUX_CPU1, 15);
          GPIO_SetupPinOptions(42, GPIO_OUTPUT, GPIO_ASYNC);
        EDIS;

  • Yes, I am using Sdhc card. I will order an mmc card and try it.
  • I wanted to know if it is possible to import the arduino's sd card code and use it in here.
  • It is definitely possible but that is a large and time consuming project for you. I would just use an MMC SD card.
  • True. The reason why I wanted to use Arduino code is because there is scarcity of MMC cards. I have contacted kingston and sandisk directly and they informed me that the mmc cards are not available in the market. Can you help me with that?
  • have you taken a look at MSP432P, MPS432E ? they have a very nice built in code support for SD cards of all types.

    Arduino is for hobbyists.
  • I have to stick to c2000 as a part of research. So, I'm trying to find the quickest fix to it as of now.
  • Its still shocking ot me that the data saving workso nly for  mmcs but not other sdcs.  There are various posts like:

  • The implementation of the SD support for the other SD card types would just consume more memory.
  • Hi,

    I received MMC card and I ran the code. I have connected it similar to how SD card is connected. However, the result is the same. Can you help me with it?
  • At this point you have not changed the example code, connected the MMC card, ran the code and no output?
  • These are the only changes i made to the code:

    I added f_open, f_write and f_close to the code. I changed the Pinmux from gpio18,19 to gpio42,43. I added _LAUNCHXL_F28379D to predefined symbols.

    After that, I connected MMC and the error pops up as FR_NOT_READY
  • Can we try the code without the code you added or the GPIO change? Keep the _LAUNCHXL_F28379D in the predefined symbols.
  • When only Fmount is used, it works. I also wanted to inform you that I kept the previous definitions of GPIO but added gpio 43 and 42 so that I can see the output on terminal.
  • Fmount doesnt use SPI or any hardware. It is setting up the software. What we need to do is use the exact example, and send commands through the serial putty, using the original GPIO pinmux and make sure that we have a working setup. Then we can incrementally make our changes and debug step by step.
  • Here is the ouput that I see when only Fmount is used:

  • But pins 28 and 29 doesnot show the output on putty side.
  • Hi,

    I tried decoding the code and found out that the CMD is not being accessed. That means, it doesn't enter the following logic in disk_initialize:

    if (send_cmd(CMD0, 0) == 1)              /* Enter Idle state */
        {
            Timer1 = 100;                        /* Initialization timeout of 1000 msec */
            if (send_cmd(CMD8, 0x1AA) == 1)      /* SDC Ver2+ */
            {
                for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                if (ocr[2] == 0x01 && ocr[3] == 0xAA)      /* The card can work at vdd range of 2.7-3.6V */
                {
                    do
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0)    break;    /* ACMD41 with HCS bit */
                    }
                    while (Timer1);
                    if (Timer1 && send_cmd(CMD58, 0) == 0)      /* Check CCS bit */
                    {
                        for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                        ty = (ocr[0] & 0x40) ? 6 : 2;
                    }
                }
            }
            else                                /* SDC Ver1 or MMC */
            {
                ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1;    /* SDC : MMC */
                do
                {
                    if (ty == 2)
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break;    /* ACMD41 */
                    }
                    else
                    {
                        if (send_cmd(CMD1, 0) == 0) break;                                /* CMD1 */
                    }
                }
                while (Timer1);
                if (!Timer1 || send_cmd(CMD16, 512) != 0)    /* Select R/W block length */
                    ty = 0;
            }
        }

    Can you help me understand what the issue is?

  • Can anyone help me with this? I'm still waiting to get this issue solved
  • For the writing a file to the SD card (FAT file system) is ok.

    I have changed the called functions sequence as the following:

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

    //    if(fresult == FR_OK)
        {
            fresult =  f_open(&file, "0:/1stfile.txt", FA_CREATE_NEW);
            f_close(&file);
            fresult =  f_open(&file, "0:/1stfile.txt", FA_WRITE);
            if(fresult != FR_OK)
            {
                UARTprintf("f_open error: %s\n", StringFromFresult(fresult));
                return(1);
            }
            buff[0]='0';
            buff[1]='1';
            buff[2]='2';
            buff[3]='3';
            buff[4]='4';
            buff[5]='5';
            buff[6]='6';
            buff[7]='7';
            buff[8]='8';
            buff[9]='9';
            fresult = f_write (&file, buff, 10, (UINT *)&nStatus);

            if(fresult != FR_OK)
            {
                UARTprintf("f_write error: %s\n", StringFromFresult(fresult));
                //return(1);
            }
    //        fresult = f_sync (&file);

            f_sync(&file);
            f_close(&file);
        }

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

    //###########################################################################
    //
    // FILE:   sd_card.c
    //
    // TITLE:  Example program for reading files from an SD card.
    //
    //! \addtogroup cpu01_example_list
    //! <h1>SD card using FAT file system (sd_card)</h1>
    //!
    //! This example application demonstrates reading a file system from
    //! an SD card.  It makes use of FatFs, a FAT file system driver.
    //!
    //! For additional details about FatFs, see the following site:
    //! http://elm-chan.org/fsw/ff/00index_e.html
    //!
    //! The application may be operated via a serial terminal attached to
    //! UART0. The RS232 communication parameters should be set to 115,200 bits
    //! per second, and 8-n-1 mode.  When the program is started a message will be
    //! printed to the terminal.  Type ``help'' for command help.
    //!
    //
    //###########################################################################
    // $TI Release: F2837xD Support Library v210 $
    // $Release Date: Tue Nov  1 14:46:15 CDT 2016 $
    // $Copyright: Copyright (C) 2013-2016 Texas Instruments Incorporated -
    //             http://www.ti.com/ ALL RIGHTS RESERVED $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "F28x_Project.h"
    #include <string.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "ff.h"
    #include "diskio.h"
    //#include "third_party/fatfs/src/ff.h"
    //#include "third_party/fatfs/src/diskio.h"
    
    
    //
    // Defines
    //
    #define PATH_BUF_SIZE   80     // 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 CMD_BUF_SIZE    64     // Defines the size of the buffer that holds
                                   // the command line.
    #define FRESULT_ENTRY(f)        { (f), (# f) } // A macro to make it easy to
                                                   // add result codes to the table
    #define NAME_TOO_LONG_ERROR     1              // Error reasons returned
    #define OPENDIR_ERROR           2              // by ChangeDirectory().
    #define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))
    #define TICKS_PER_SECOND        100
    #define NUM_LIST_STRINGS        48
    #define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
    #define NUM_STATUS_STRINGS      6
    #define MAX_STATUS_STRING_LEN   (36 + 1)
    
    //
    // Globals
    //
    char g_cCwdBuf[PATH_BUF_SIZE] = "/";  // This buffer holds the full path
                                                 // to the current working
                                                 // directory. Initially it is
                                                 // root ("/").
    char g_cTmpBuf[PATH_BUF_SIZE];        // A temporary data buffer used
                                                 // when manipulating file paths,or
                                                 // reading data from the SD card.
    char g_cCmdBuf[CMD_BUF_SIZE];         // The buffer that holds the
                                                 // command line.
    FATFS g_sFatFs;
    DIR g_sDirObject;
    FILINFO g_sFileInfo;
    FIL g_sFileObject;
    
    //
    // 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;
    
    //
    // 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)
    };
    
    const char *g_ppcDirListStrings[NUM_LIST_STRINGS]; // Storage for the filename
                                                       // listbox widget string
                                                       // table.
    
    //
    // Storage for the names of the files in the current directory. Filenames
    // are stored in format "(D) filename.ext" for directories or
    // "(F) filename.ext" for files.
    //
    char g_pcFilenames[NUM_LIST_STRINGS][MAX_FILENAME_STRING_LEN];
    
    //
    // Storage for the strings which appear in the status box at the bottom of the
    // display.
    //
    char g_pcStatus[NUM_STATUS_STRINGS][MAX_STATUS_STRING_LEN];
    
    //
    // Storage for the status listbox widget string table.
    //
    const char *g_ppcStatusStrings[NUM_STATUS_STRINGS] =
    {
        g_pcStatus[0],
        g_pcStatus[1],
        g_pcStatus[2],
        g_pcStatus[3],
        g_pcStatus[4],
        g_pcStatus[5]
    };
    unsigned long g_ulStatusStringIndex = 0;
    
    //
    // Forward declarations for functions called by the widgets used in the user
    // interface.
    //
    FRESULT ChangeToDirectory(char *pcDirectory, unsigned long *pulReason);
    const char *StringFromFresult(FRESULT fresult);
    
    //
    // Function Prototypes
    //
    extern void UARTStdioIntHandler(void);
    void    die(FRESULT rc );
    void    WriteFile();
    DWORD clust2sect (FATFS *fs, DWORD clst);
    
    //
    // StringFromFresult - 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 unknown error.
        //
        return("UNKNOWN ERROR CODE");
    }
    
    //
    // SysTickHandler - This is the handler for this SysTick interrupt.  FatFs
    //                  requires a timer tick every 10 ms for internal timing
    //                  purposes.
    //
    __interrupt void SysTickHandler(void)
    {
        //
        // Call the FatFs tick timer.
        //
        disk_timerproc();
        PieCtrlRegs.PIEACK.all |= 1;
    }
    
    //
    // Cmd_ls - 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, ulItemCount, ulFileCount, 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;
        ulItemCount = 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;
            }
    
            //
            // 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) ? (uint32_t)'D' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_RDO) ? (uint32_t)'R' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_HID) ? (uint32_t)'H' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_SYS) ? (uint32_t)'S' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_ARC) ? (uint32_t)'A' : (uint32_t)'-',
                       (uint32_t)((g_sFileInfo.fdate >> 9) + 1980),
                       (uint32_t)((g_sFileInfo.fdate >> 5) & 15),
                       (uint32_t)(g_sFileInfo.fdate & 31),
                       (uint32_t)((g_sFileInfo.ftime >> 11)),
                       (uint32_t)((g_sFileInfo.ftime >> 5) & 63),
                       (uint32_t)(g_sFileInfo.fsize),
                       g_sFileInfo.fname);
    
            //
            // Add the information as a line in the listbox widget.
            //
            if(ulItemCount < NUM_LIST_STRINGS)
            {
                usprintf(g_pcFilenames[ulItemCount], "(%c) %12s",
                         (g_sFileInfo.fattrib & AM_DIR) ? 'D' : 'F',
                         g_sFileInfo.fname);
            }
    
            //
            // If the attribute 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;
            }
    
            //
            // Move to the next entry in the item array we use to populate the
            // list box.
            //
            ulItemCount++;
    
            //
            // Wait for the UART transmit buffer to empty.
            //
    //        UARTFlushTx(false);
        }
    
        //
        // 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.
        //
        pFatFs->sects_clust=clust2sect (pFatFs, pFatFs->sects_clust);
        UARTprintf(", %10uK bytes free\n", ulTotalSize * pFatFs->sects_clust / 2);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Made it to here, return with no errors.
        //
        return(0);
    }
    
    //
    // ChangeToDirectory - 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 don't try
    //                     something like this: ("../my/new/path")
    //                     Once the new directory is specified, it attempts to open
    //                     the directory to make sure it exists.  If the new path
    //                     is opened successfully, then the current working
    //                     directory (cwd) is changed to the new path. In cases of
    //                     error, the pulReason parameter will be written with one
    //                     of the following values:
    //
    FRESULT ChangeToDirectory(char *pcDirectory, unsigned long *pulReason)
    {
        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(pcDirectory[0] == '/')
        {
            //
            // Make sure the new path is not bigger than the cwd buffer.
            //
            if(strlen(pcDirectory) + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_OK);
            }
            //
            // If the new path name (in argv[1])  is not too long, then
            // copy it into the temporary buffer so it can be checked.
            //
            else
            {
                strncpy(g_cTmpBuf, pcDirectory, sizeof(g_cTmpBuf));
            }
        }
        //
        // If the argument is .. then attempt to remove the lowest level
        // on the CWD.
        //
        else if(!strcmp(pcDirectory, ".."))
        {
            //
            // Get the index to the last character in the current path.
            //
            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(pcDirectory) + 1 + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_INVALID_OBJECT);
            }
            //
            // The new path is okay, so add the separator and then append
            // the new directory to the path.
            //
            else
            {
                //
                // If not already at the root level, then append a /
                //
                if(strcmp(g_cTmpBuf, "/"))
                {
                    strcat(g_cTmpBuf, "/");
                }
    
                //
                // Append the new directory to the path.
                //
                strcat(g_cTmpBuf, pcDirectory);
            }
        }
    
        //
        // 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 cant be opened, then it is a bad path.  Inform
        // user and return.
        //
        if(fresult != FR_OK)
        {
            *pulReason = OPENDIR_ERROR;
            return(fresult);
        }
        //
        // Otherwise, it is a valid new path, so copy it into the CWD and update
        // the screen.
        //
        else
        {
            strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
        }
    
        //
        // Return success.
        //
        return(FR_OK);
    }
    
    //
    // Cmd_cd - 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 long ulReason;
        FRESULT fresult;
    
        //
        // Try to change to the directory provided on the command line.
        //
        fresult = ChangeToDirectory(argv[1], &ulReason);
    
        //
        // If an error was reported, try to offer some helpful information.
        //
        if(fresult != FR_OK)
        {
            switch(ulReason)
            {
                case OPENDIR_ERROR:
                    UARTprintf("Error opening new directory.\n");
                    break;
    
                case NAME_TOO_LONG_ERROR:
                    UARTprintf("Resulting path name is too long.\n");
                    break;
    
                default:
                    UARTprintf("An unrecognized error was reported.\n");
                    break;
            }
        }
    
        //
        // Return the appropriate error code.
        //
        return(fresult);
    }
    
    //
    // Cmd_pwd - 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);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_cat - 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,
                             (UINT *)&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);
    
            //
            // Continue reading until less than the full number of bytes are
            // read.  That means the end of the buffer was reached.
            //
        }
        while(usBytesRead == sizeof(g_cTmpBuf) - 1);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_help - 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_psCmdTable[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);
    }
    
    //
    // This is the table that holds the command names, implementing functions,
    // and brief description.
    //
    tCmdLineEntry g_psCmdTable[] =
    {
        { "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" },
        { 0, 0, 0 }
    };
    
    //
    // __error__ - The error routine that is called if the driver library
    //             encounters an error.
    //
    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
    }
    #endif
    
    //
    // ConfigureUART - Configure the UART and its pins.  This must be called
    //                 before UARTprintf().
    //
    void ConfigureUART(void)
    {
        //
        // Enable UART0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SCI1);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        EALLOW;
        GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;
        GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;
        GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;
        GpioCtrlRegs.GPADIR.bit.GPIO28 = 0;
    
        GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;
        GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;
        GpioCtrlRegs.GPADIR.bit.GPIO29 = 1;
        EDIS;
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, SysCtlLowSpeedClockGet(SYSTEM_CLOCK_SPEED));
    }
    
    //
    // Main - It performs initialization, then runs a command processing loop to
    //        read commands from the console.
    //
    char buff[10];
    
    int main(void)
    {
        int nStatus;
        BYTE diskResult;
        FRESULT fresult;
        FIL file;
    
    
        //
        // Set the clocking to run from the PLL at 50MHz
        //
        SysCtlClockSet(SYSCTL_OSCSRC_OSC2 | SYSCTL_PLL_ENABLE | SYSCTL_IMULT(10) |SYSCTL_SYSDIV(2));
        SysCtlAuxClockSet(SYSCTL_OSCSRC_OSC2 | SYSCTL_PLL_ENABLE |SYSCTL_IMULT(12) | SYSCTL_SYSDIV(2));        //60 MHz
    
    #ifdef _FLASH
    //
    // Copy time critical code and Flash setup code to RAM
    // This includes the following functions:  InitFlash();
    // The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
    // symbols are created by the linker. Refer to the device .cmd file.
    //
        memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
    
    //
    // Call Flash Initialization to setup flash waitstates
    // This function must reside in RAM
    //
        InitFlash();
    #endif
    
        //
        // Initialize interrupt controller and vector table
        //
        InitPieCtrl();
        InitPieVectTable();
    
        //
        // Set the system tick to fire 100 times per second.
        //
        SysTickInit();
        SysTickPeriodSet(SysCtlClockGet(SYSTEM_CLOCK_SPEED) / 100);
        SysTickIntRegister(SysTickHandler);
        SysTickIntEnable();
        SysTickEnable();
    
        //
        // Enable Interrupts
        //
        IntMasterEnable();
    
        //
        // Configure UART0 for debug output.
        //
        ConfigureUART();
    
        //
        // 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);
        }
    
        //
    //    WriteFile();
    
        diskResult = disk_initialize(0);
    
        diskResult = disk_status(0);
    
    //    if(fresult == FR_OK)
        {
            fresult =  f_open(&file, "0:/1stfile.txt", FA_CREATE_NEW);
            f_close(&file);
            fresult =  f_open(&file, "0:/1stfile.txt", FA_WRITE);
            if(fresult != FR_OK)
            {
                UARTprintf("f_open error: %s\n", StringFromFresult(fresult));
                return(1);
            }
            buff[0]='0';
            buff[1]='1';
            buff[2]='2';
            buff[3]='3';
            buff[4]='4';
            buff[5]='5';
            buff[6]='6';
            buff[7]='7';
            buff[8]='8';
            buff[9]='9';
            fresult = f_write (&file, buff, 10, (UINT *)&nStatus);
    
            if(fresult != FR_OK)
            {
                UARTprintf("f_write error: %s\n", StringFromFresult(fresult));
                //return(1);
            }
    //        fresult = f_sync (&file);
    
            f_sync(&file);
            f_close(&file);
        }
    
        //
        // Enter an (almost) infinite loop for reading and processing commands from
        // the user.
        //
    //    disk_status(0);
        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));
            }
        }
    }
    //Write a file
    void WriteFile()
    {
        FIL *fp;
    //    FATFS fatfs;            /* File system object */
        UINT bw ;
        FRESULT rc;
    
        #define _USE_WRITE 1
        #define _USE_DIR 1
    
    //    if(rc != FR_OK)
    //    {
    //        UARTprintf("\nMount a volume to write a file.\n");
    //        rc = f_mount(0,&fatfs);
    //        if (rc){
    //                die(rc);
    //                UARTprintf("f_mount error: %s\n", StringFromFresult(rc));
    //                return(1);
    //        }
    //    }
    
    
    #if _USE_WRITE
        UARTprintf("\nOpen a file to write (write.txt).\n");
        rc = f_open(fp, "WRITE.TXT", FA_WRITE);
        if (rc) die(rc);
        UARTprintf("\nWrite a text data. (Hello world!)\n");
        for (;;) {
            rc = f_write(fp,"Hello world!\r\n", 14,(UINT *)&bw);
            if (rc || !bw) break;
        }
        if (rc) die(rc);
    
        UARTprintf("\nTerminate the file write process.\n");
        rc = f_write(fp, 0, 0, (UINT *)&bw);
        if (rc) die(rc);
        f_close(fp);
        UARTprintf("\nWrite Test completed.\n");
    #endif
    
    }
    
    void die(FRESULT rc )
    {
        if(rc != FR_OK)
        {
            UARTprintf("Failed with rc=%u.\n", rc);
        }
        return;
    }
    
    
    //
    // End of file
    //
    

  • Sorry, I was on vacation. Are you having trouble reading from a file?

    Nima Eskandari

  • Hi,

    I tried the same code but I still get the FR_NOT_READY error at fopen. Here is the output i received:

    Also, I'm attaching the codes that i have used and modified as you have mentioned. Please let me know if i need to make any more changes.ff.h

    /*-----------------------------------------------------------------------*/
    /* MMC/SDC (in SPI mode) control module  (C)ChaN, 2007                   */
    /*-----------------------------------------------------------------------*/
    /* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros         */
    /* are platform dependent.                                               */
    /*-----------------------------------------------------------------------*/
    
    /*
     * This file was modified from a sample available from the FatFs
     * web site. It was modified to work with a F2837xD device.
     */
    
    #include "F28x_Project.h"
    
    //#include "F2837xD_types.h"
    #include "inc/hw_types.h"
    #include "fatfs/src/diskio.h"
    
    /*
     * The following header defines the hardware connections used to connect
     * the SDCard.  This can be found under the relevant board directory.
     */
    //*****************************************************************************
    //
    // SDCard SPI port
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // GPIO for SDCard SPI pins
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // GPIO for the SDCard chip select
    //
    //*****************************************************************************
    
    
    /* Definitions for MMC/SDC command */
    #define CMD0    (0x40+0)    /* GO_IDLE_STATE */
    #define CMD1    (0x40+1)    /* SEND_OP_COND */
    #define CMD8    (0x40+8)    /* SEND_IF_COND */
    #define CMD9    (0x40+9)    /* SEND_CSD */
    #define CMD10    (0x40+10)    /* SEND_CID */
    #define CMD12    (0x40+12)    /* STOP_TRANSMISSION */
    #define CMD16    (0x40+16)    /* SET_BLOCKLEN */
    #define CMD17    (0x40+17)    /* READ_SINGLE_BLOCK */
    #define CMD18    (0x40+18)    /* READ_MULTIPLE_BLOCK */
    #define CMD23    (0x40+23)    /* SET_BLOCK_COUNT */
    #define CMD24    (0x40+24)    /* WRITE_BLOCK */
    #define CMD25    (0x40+25)    /* WRITE_MULTIPLE_BLOCK */
    #define CMD41    (0x40+41)    /* SEND_OP_COND (ACMD) */
    #define CMD55    (0x40+55)    /* APP_CMD */
    #define CMD58    (0x40+58)    /* READ_OCR */
    
    // asserts the CS pin to the card
    static
    void SELECT (void)  //EDITED
    {
        //
        // Select the SD card.
        //
        GpioDataRegs.GPDCLEAR.bit.GPIO125 = 1;
    
    }
    
    // De-asserts the CS pin to the card.
    static
    void DESELECT (void) //EDITED
    {
        GpioDataRegs.GPDSET.bit.GPIO125 = 1;
    }
    
    /*--------------------------------------------------------------------------
    
       Module Private Functions
    
    ---------------------------------------------------------------------------*/
    
    static volatile
    DSTATUS Stat = STA_NOINIT;    /* Disk status */
    
    static volatile
    BYTE Timer1, Timer2;    /* 100Hz decrement timer */
    
    static
    BYTE CardType;            /* b0:MMC, b1:SDC, b2:Block addressing */
    
    static
    BYTE PowerFlag = 0;     /* indicates if "power" is on */
    
    /*-----------------------------------------------------------------------*/
    /* Transmit a byte to MMC via SPI  (Platform dependent)                  */
    /*-----------------------------------------------------------------------*/
    
    static
    void xmit_spi (BYTE dat)    //EDITED
    {
        volatile DWORD rcvdat;                      //included file like integer.h for DWORD definition
    
        /* Write the data to the tx fifo */
        while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);    //Wait for room to write data
        SpicRegs.SPITXBUF = ((DWORD)dat)<<8;        //Write data
    
        /* flush data read during the write */
        while(SpicRegs.SPISTS.bit.INT_FLAG !=1);    //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
        rcvdat = (SpicRegs.SPIRXBUF && 0xFF);       //Clear Receive Buffer
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a byte from MMC via SPI  (Platform dependent)                 */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE rcvr_spi (void)        //EDITED
    {
        volatile DWORD rcvdat;
    
        //Disable transmission channel
        //SpicRegs.SPICTL.bit.TALK = 0;
    
        /* write dummy data */
        while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);    //Wait for space to write
        SpicRegs.SPITXBUF = 0xFF00;                 //Write dummy data
    
        /* read data from RX fifo */
        while(SpicRegs.SPISTS.bit.INT_FLAG !=1);    //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
        rcvdat = (SpicRegs.SPIRXBUF & 0xFF);        //Read Transferred data
    
        return (BYTE)rcvdat;
    }
    
    
    static
    void rcvr_spi_m (BYTE *dst)
    {
        *dst = rcvr_spi();
    }
    
    /*-----------------------------------------------------------------------*/
    /* Wait for card ready                                                   */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE wait_ready (void)
    {
        BYTE res;
    
    
        Timer2 = 50;    /* Wait for ready in timeout of 500ms */
        rcvr_spi();
        do{
            res = rcvr_spi();
            DELAY_US(175);
        } while ((res != 0xFF) && Timer2);
    
        return res;
    }
    
    /*-----------------------------------------------------------------------*/
    /* Send 80 or so clock transitions with CS and DI held high. This is     */
    /* required after card power up to get it into SPI mode                  */
    /*-----------------------------------------------------------------------*/
    static
    void send_initial_clock_train (void)        //EDITED
    {
        volatile DWORD dat;
    
        /* Ensure CS (STE) is held high. */
        DESELECT();
    
        /* Switch the SPI TX line to a GPIO and drive it high too. */
        EALLOW;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDDIR.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        EDIS;
        GpioDataRegs.GPDSET.bit.GPIO122 = 1;
    
        /* Send 10 bytes over the SPI. This causes the clock to wiggle the */
        /* required number of times. */
        unsigned int i;
        for(i = 0 ; i < 10 ; i++)
        {
            /* Write DUMMY data */
            while(SpicRegs.SPISTS.bit.BUFFULL_FLAG);
            SpicRegs.SPITXBUF = 0xFF00;
    
            /* Flush data read during data write. */
            while(SpicRegs.SPISTS.bit.INT_FLAG !=1);        //May be possible to switch to '!SpicRegs.SPISTS.bit.INT_FLAG'
            dat = (SpicRegs.SPIRXBUF & 0xFF);
        }
    
        /* Revert to hardware control of the SPI TX line. */
        EALLOW;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 2;
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        EDIS;
    }
    
    /*-----------------------------------------------------------------------*/
    /* Power Control  (Platform dependent)                                   */
    /*-----------------------------------------------------------------------*/
    /* When the target system does not support socket power control, there   */
    /* is nothing to do in these functions and chk_power always returns 1.   */
    
    static
    void power_on (void)    //EDITED
    {
        /*
         * This doesn't really turn the power on, but initializes the
         * SPI port and pins needed to talk to the SD card.
         */
        EALLOW;
        /* Enable the peripherals used to drive the SDC on SPI */
        CpuSysRegs.PCLKCR8.bit.SPI_C = 1;
    
        /*
         * Configure the appropriate pins to be SPI instead of GPIO. The CS
         * signal is directly driven to ensure that we can hold it low through a
         * complete transaction with the SD card.
         */
    
    
        //Unlock the SD-Card SPI GPIOs
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO123 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO124 = 0;
        GpioCtrlRegs.GPDLOCK.bit.GPIO125 = 0;
    
        //Set up  MUX & DIR
        GpioCtrlRegs.GPDMUX2.bit.GPIO125 = 0;    //Leave as GPIO for manual CS control
    
        GpioCtrlRegs.GPDGMUX2.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDGMUX2.bit.GPIO123 = 1;
        GpioCtrlRegs.GPDGMUX2.bit.GPIO124 = 1;
    
        GpioCtrlRegs.GPDMUX2.bit.GPIO122 = 2;
        GpioCtrlRegs.GPDMUX2.bit.GPIO123 = 2;
        GpioCtrlRegs.GPDMUX2.bit.GPIO124 = 2;
    
        GpioCtrlRegs.GPDDIR.bit.GPIO125 = 1;
    
        //Set up GPIO Pull-up disables/enables
        GpioCtrlRegs.GPDPUD.bit.GPIO122 = 0;    //Needs to be normally pulled high
        GpioCtrlRegs.GPDPUD.bit.GPIO123 = 0;    //Needs to be normally pulled high
        GpioCtrlRegs.GPDPUD.bit.GPIO124 = 1;
        GpioCtrlRegs.GPDPUD.bit.GPIO125 = 1;
    
        //Set up GPIOs in asynch mode
        GpioCtrlRegs.GPDQSEL2.bit.GPIO122 = 3; // Asynch input
        GpioCtrlRegs.GPDQSEL2.bit.GPIO123 = 3;
        GpioCtrlRegs.GPDQSEL2.bit.GPIO124 = 3;
        GpioCtrlRegs.GPDQSEL2.bit.GPIO125 = 3;
    
        //Configure GPIOs for CPU1
        GpioCtrlRegs.GPDCSEL4.bit.GPIO122 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO123 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO124 = 0;
        GpioCtrlRegs.GPDCSEL4.bit.GPIO125 = 0;
    
        //Lock the SD-Card SPI GPIOs
        GpioCtrlRegs.GPDLOCK.bit.GPIO122 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO123 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO124 = 1;
        GpioCtrlRegs.GPDLOCK.bit.GPIO125 = 1;
        EDIS;
    
    
        /* Deassert the SPIC chip selects for both the SD card and serial flash */
        DESELECT();
    
        /* Configure the SPI C port */
        SpicRegs.SPICCR.bit.SPISWRESET = 0;         //Set reset bit low
        SpicRegs.SPICTL.bit.CLK_PHASE = 0;
        SpicRegs.SPICCR.bit.CLKPOLARITY = 1;
        SpicRegs.SPICTL.bit.MASTER_SLAVE = 1;       //Master mode
        SpicRegs.SPIBRR.all = 63;                       //update value to proper setting for correct bitrate ( current: ~500kHz)
        SpicRegs.SPICCR.bit.SPICHAR = 0x7;          //Set char length to 8 bits
        SpicRegs.SPICTL.bit.TALK = 1;
        SpicRegs.SPICCR.bit.SPISWRESET = 1;         //Release SPI from reset
        SpicRegs.SPIPRI.bit.FREE = 1;
        SpicRegs.SPIPRI.bit.SOFT = 1;
    
        /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */
        /* to be able to accept a native command. */
        //send_initial_clock_train();
    
        //DELAY_US(50);
    
        PowerFlag = 1;
    }
    
    // set the SPI speed to the max setting
    static
    void set_max_speed(void)            //EDIT
    {
        /* Disable the SPI*/
        //DevCfgRegs.DC9.bit.SPI_C = 0;
        SpicRegs.SPICCR.bit.SPISWRESET = 0;     //Place in reset
    
        /* Configure the SPI C port */
        SpicRegs.SPICTL.bit.CLK_PHASE = 0;
        SpicRegs.SPICCR.bit.CLKPOLARITY = 1;
        SpicRegs.SPICTL.bit.MASTER_SLAVE = 1;   //Set Master Mode
    
        SpicRegs.SPIBRR.all = 1; // <- (No enforced limit)
    
        SpicRegs.SPICCR.bit.SPICHAR = 0x7;      //8 bit char length
        SpicRegs.SPICTL.bit.TALK = 1;
    
        /* Enable the SPI */
        SpicRegs.SPICCR.bit.SPISWRESET = 1;     //Release from reset
        //DevCfgRegs.DC9.bit.SPI_C = 1;
    }
    
    static
    void power_off (void)
    {
        PowerFlag = 0;
    }
    
    static
    int chk_power(void)        /* Socket power state: 0=off, 1=on */
    {
        return PowerFlag;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a data packet from MMC                                        */
    /*-----------------------------------------------------------------------*/
    
    static
    BOOL rcvr_datablock (
        BYTE *buff,            /* Data buffer to store received data */
        UINT btr            /* Byte count (must be even number) */
    )
    {
        BYTE token;
    
    
        Timer1 = 10;
        do                              /* Wait for data packet in timeout of 100ms */
        {
            token = rcvr_spi();
        }
        while ((token == 0xFF) && Timer1);
        if(token != 0xFE) return FALSE;    /* If not valid data token, retutn with error */
    
        do                              /* Receive the data block into buffer */
        {
            rcvr_spi_m(buff++);
            rcvr_spi_m(buff++);
        }
        while (btr -= 2);
        rcvr_spi();                        /* Discard CRC */
        rcvr_spi();
    
        return TRUE;                    /* Return with success */
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Send a data packet to MMC                                             */
    /*-----------------------------------------------------------------------*/
    
    static
    BOOL xmit_datablock (
        const BYTE *buff,    /* 512 byte data block to be transmitted */
        BYTE token            /* Data/Stop token */
    )
    {
        BYTE resp, wc;
    
    
        if (wait_ready() != 0xFF) return FALSE;
    
        xmit_spi(token);                    /* Xmit data token */
        if (token != 0xFD)      /* Is data token */
        {
            wc = 256;
            do                              /* Xmit the 512 byte data block to MMC */
            {
                xmit_spi(*buff++);
                xmit_spi(*buff++);
            }
            while (--wc);
            xmit_spi(0xFF);                    /* CRC (Dummy) */
            xmit_spi(0xFF);
            resp = rcvr_spi();                /* Reveive data response */
            if ((resp & 0x1F) != 0x05)        /* If not accepted, return with error */
                return FALSE;
        }
    
        return TRUE;
    }
    
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Send a command packet to MMC                                          */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE send_cmd (
        BYTE cmd,        /* Command byte */
        DWORD arg        /* Argument */
    )
    {
        BYTE n, res;
    
    
        if (wait_ready() != 0xFF) return 0xFF;
    
        /* Send command packet */
        xmit_spi(cmd);                        /* Command */
        xmit_spi((BYTE)(arg >> 24));        /* Argument[31..24] */
        xmit_spi((BYTE)(arg >> 16));        /* Argument[23..16] */
        xmit_spi((BYTE)(arg >> 8));            /* Argument[15..8] */
        xmit_spi((BYTE)arg);                /* Argument[7..0] */
        n = 0;
        if (cmd == CMD0) n = 0x95;            /* CRC for CMD0(0) */
        if (cmd == CMD8) n = 0x87;            /* CRC for CMD8(0x1AA) */
        xmit_spi(n);
    
        /* Receive command response */
        if (cmd == CMD12) rcvr_spi();        /* Skip a stuff byte when stop reading */
        n = 10;                                /* Wait for a valid response in timeout of 10 attempts */
        do
            res = rcvr_spi();
        while ((res & 0x80) && --n);
    
        return res;            /* Return with the response value */
    }
    
    /*-----------------------------------------------------------------------*
     * Send the special command used to terminate a multi-sector read.
     *
     * This is the only command which can be sent while the SDCard is sending
     * data. The SDCard spec indicates that the data transfer will stop 2 bytes
     * after the 6 byte CMD12 command is sent and that the card will then send
     * 0xFF for between 2 and 6 more bytes before the R1 response byte.  This
     * response will be followed by another 0xFF byte.  In testing, however, it
     * seems that some cards don't send the 2 to 6 0xFF bytes between the end of
     * data transmission and the response code.  This function, therefore, merely
     * reads 10 bytes and, if the last one read is 0xFF, returns the value of the
     * latest non-0xFF byte as the response code.
     *
     *-----------------------------------------------------------------------*/
    
    static
    BYTE send_cmd12 (void)
    {
        BYTE n, res, val;
    
        /* For CMD12, we don't wait for the card to be idle before we send
         * the new command.
         */
    
        /* Send command packet - the argument for CMD12 is ignored. */
        xmit_spi(CMD12);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
        xmit_spi(0);
    
        /* Read up to 10 bytes from the card, remembering the value read if it's
           not 0xFF */
        for(n = 0; n < 10; n++)
        {
            val = rcvr_spi();
            if(val != 0xFF)
            {
                res = val;
            }
        }
    
        return res;            /* Return with the response value */
    }
    
    /*--------------------------------------------------------------------------
    
       Public Functions
    
    ---------------------------------------------------------------------------*/
    
    
    /*-----------------------------------------------------------------------*/
    /* Initialize Disk Drive                                                 */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_initialize (
        BYTE drv        /* Physical drive nmuber (0) */
    )
    {
        BYTE n, ty, ocr[4];
    
    
        if (drv) return STA_NOINIT;            /* Supports only single drive */
        if (Stat & STA_NODISK) return Stat;    /* No card in the socket */
    
        power_on();                            /* Force socket power on */
        send_initial_clock_train();
    
        SELECT();                /* CS = L */
        ty = 0;
        if (send_cmd(CMD0, 0) == 1)              /* Enter Idle state */
        {
            Timer1 = 100;                        /* Initialization timeout of 1000 msec */
            if (send_cmd(CMD8, 0x1AA) == 1)      /* SDC Ver2+ */
            {
                for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                if (ocr[2] == 0x01 && ocr[3] == 0xAA)      /* The card can work at vdd range of 2.7-3.6V */
                {
                    do
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0)    break;    /* ACMD41 with HCS bit */
                    }
                    while (Timer1);
                    if (Timer1 && send_cmd(CMD58, 0) == 0)      /* Check CCS bit */
                    {
                        for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                        ty = (ocr[0] & 0x40) ? 6 : 2;
                    }
                }
            }
            else                                /* SDC Ver1 or MMC */
            {
                ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1;    /* SDC : MMC */
                do
                {
                    if (ty == 2)
                    {
                        if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break;    /* ACMD41 */
                    }
                    else
                    {
                        if (send_cmd(CMD1, 0) == 0) break;                                /* CMD1 */
                    }
                }
                while (Timer1);
                if (!Timer1 || send_cmd(CMD16, 512) != 0)    /* Select R/W block length */
                    ty = 0;
            }
        }
        CardType = ty;
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        if (ty)              /* Initialization succeded */
        {
            Stat &= ~STA_NOINIT;        /* Clear STA_NOINIT */
            set_max_speed();
        }
        else                /* Initialization failed */
        {
            power_off();
        }
    
        return Stat;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Get Disk Status                                                       */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_status (
        BYTE drv        /* Physical drive number (0) */
    )
    {
        if (drv) return STA_NOINIT;        /* Supports only single drive */
        return Stat;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Read Sector(s)                                                        */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_read (
        BYTE drv,            /* Physical drive nmuber (0) */
        BYTE *buff,            /* Pointer to the data buffer to store read data */
        DWORD sector,        /* Start sector number (LBA) */
        BYTE count            /* Sector count (1..255) */
    )
    {
        if (drv || !count) return RES_PARERR;
        if (Stat & STA_NOINIT) return RES_NOTRDY;
    
        if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */
    
        SELECT();            /* CS = L */
    
        if (count == 1)      /* Single block read */
        {
            if ((send_cmd(CMD17, sector) == 0)    /* READ_SINGLE_BLOCK */
                    && rcvr_datablock(buff, 512))
                count = 0;
        }
        else                  /* Multiple block read */
        {
            if (send_cmd(CMD18, sector) == 0)      /* READ_MULTIPLE_BLOCK */
            {
                do
                {
                    if (!rcvr_datablock(buff, 512)) break;
                    buff += 512;
                }
                while (--count);
                send_cmd12();                /* STOP_TRANSMISSION */
            }
        }
    
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        return count ? RES_ERROR : RES_OK;
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Write Sector(s)                                                       */
    /*-----------------------------------------------------------------------*/
    DRESULT disk_write (
        BYTE drv,            /* Physical drive nmuber (0) */
        const BYTE *buff,    /* Pointer to the data to be written */
        DWORD sector,        /* Start sector number (LBA) */
        BYTE count            /* Sector count (1..255) */
    )
    {
        if (drv || !count) return RES_PARERR;
        if (Stat & STA_NOINIT) return RES_NOTRDY;
        if (Stat & STA_PROTECT) return RES_WRPRT;
    
        if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */
    
        SELECT();            /* CS = L */
    
        if (count == 1)      /* Single block write */
        {
            if ((send_cmd(CMD24, sector) == 0)    /* WRITE_BLOCK */
                    && xmit_datablock(buff, 0xFE))
                count = 0;
        }
        else                  /* Multiple block write */
        {
            if (CardType & 2)
            {
                send_cmd(CMD55, 0);
                send_cmd(CMD23, count);    /* ACMD23 */
            }
            if (send_cmd(CMD25, sector) == 0)      /* WRITE_MULTIPLE_BLOCK */
            {
                do
                {
                    if (!xmit_datablock(buff, 0xFC)) break;
                    buff += 512;
                }
                while (--count);
                if (!xmit_datablock(0, 0xFD))    /* STOP_TRAN token */
                    count = 1;
            }
        }
    
        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    
        return count ? RES_ERROR : RES_OK;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Miscellaneous Functions                                               */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_ioctl (
        BYTE drv,        /* Physical drive nmuber (0) */
        BYTE ctrl,        /* Control code */
        void *buff        /* Buffer to send/receive control data */
    )
    {
        DRESULT res;
        BYTE n, csd[16], *ptr = buff;
        WORD csize;
    
    
        if (drv) return RES_PARERR;
    
        res = RES_ERROR;
    
        if (ctrl == CTRL_POWER)
        {
            switch (*ptr)
            {
            case 0:        /* Sub control code == 0 (POWER_OFF) */
                if (chk_power())
                    power_off();        /* Power off */
                res = RES_OK;
                break;
            case 1:        /* Sub control code == 1 (POWER_ON) */
                power_on();                /* Power on */
                res = RES_OK;
                break;
            case 2:        /* Sub control code == 2 (POWER_GET) */
                *(ptr+1) = (BYTE)chk_power();
                res = RES_OK;
                break;
            default :
                res = RES_PARERR;
            }
        }
        else
        {
            if (Stat & STA_NOINIT) return RES_NOTRDY;
    
            SELECT();        /* CS = L */
    
            switch (ctrl)
            {
            case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (DWORD) */
                if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16))
                {
                    if ((csd[0] >> 6) == 1)      /* SDC ver 2.00 */
                    {
                        csize = csd[9] + ((WORD)csd[8] << 8) + 1;
                        *(DWORD*)buff = (DWORD)csize << 10;
                    }
                    else                        /* MMC or SDC ver 1.XX */
                    {
                        n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                        csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
                        *(DWORD*)buff = (DWORD)csize << (n - 9);
                    }
                    res = RES_OK;
                }
                break;
    
            case GET_SECTOR_SIZE :    /* Get sectors on the disk (WORD) */
                *(WORD*)buff = 512;
                res = RES_OK;
                break;
    
            case CTRL_SYNC :    /* Make sure that data has been written */
                if (wait_ready() == 0xFF)
                    res = RES_OK;
                break;
    
            case MMC_GET_CSD :    /* Receive CSD as a data block (16 bytes) */
                if (send_cmd(CMD9, 0) == 0        /* READ_CSD */
                        && rcvr_datablock(ptr, 16))
                    res = RES_OK;
                break;
    
            case MMC_GET_CID :    /* Receive CID as a data block (16 bytes) */
                if (send_cmd(CMD10, 0) == 0        /* READ_CID */
                        && rcvr_datablock(ptr, 16))
                    res = RES_OK;
                break;
    
            case MMC_GET_OCR :    /* Receive OCR as an R3 resp (4 bytes) */
                if (send_cmd(CMD58, 0) == 0)      /* READ_OCR */
                {
                    for (n = 0; n < 4; n++)
                        *ptr++ = rcvr_spi();
                    res = RES_OK;
                }
    
    //        case MMC_GET_TYPE :    /* Get card type flags (1 byte) */
    //            *ptr = CardType;
    //            res = RES_OK;
    //            break;
    
            default:
                res = RES_PARERR;
            }
    
            DESELECT();            /* CS = H */
            rcvr_spi();            /* Idle (Release DO) */
        }
    
        return res;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Device Timer Interrupt Procedure  (Platform dependent)                */
    /*-----------------------------------------------------------------------*/
    /* This function must be called in period of 10ms                        */
    
    void disk_timerproc (void)
    {
    //    BYTE n, s;
        BYTE n;
    
    
        n = Timer1;                        /* 100Hz decrement timer */
        if (n) Timer1 = --n;
        n = Timer2;
        if (n) Timer2 = --n;
    
    }
    
    /*---------------------------------------------------------*/
    /* User Provided Timer Function for FatFs module           */
    /*---------------------------------------------------------*/
    /* This is a real time clock service to be called from     */
    /* FatFs module. Any valid time must be returned even if   */
    /* the system does not support a real time clock.          */
    
    DWORD get_fattime (void)
    {
    
        return    ((2008UL-1980) << 25)    // Year = 2008
                  | (2UL << 21)            // Month = February
                  | (26UL << 16)            // Day = 26
                  | (14U << 11)            // Hour = 14
                  | (0U << 5)            // Min = 0
                  | (0U >> 1)                // Sec = 0
                  ;
    
    }
    
    //###########################################################################
    //
    // FILE:   sd_card.c
    //
    // TITLE:  Example program for reading files from an SD card.
    //
    //! \addtogroup cpu01_example_list
    //! <h1>SD card using FAT file system (sd_card)</h1>
    //!
    //! This example application demonstrates reading a file system from
    //! an SD card.  It makes use of FatFs, a FAT file system driver.
    //!
    //! For additional details about FatFs, see the following site:
    //! http://elm-chan.org/fsw/ff/00index_e.html
    //!
    //! The application may be operated via a serial terminal attached to
    //! UART0. The RS232 communication parameters should be set to 115,200 bits
    //! per second, and 8-n-1 mode.  When the program is started a message will be
    //! printed to the terminal.  Type ``help'' for command help.
    //!
    //
    //###########################################################################
    // $TI Release: F2837xD Support Library v3.05.00.00 $
    // $Release Date: Thu Oct 18 15:48:42 CDT 2018 $
    // $Copyright:
    // Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "F28x_Project.h"
    #include <string.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "fatfs/src/ff.h"
    #include "fatfs/src/diskio.h"
    
    //
    // Defines
    //
    #define PATH_BUF_SIZE   80     // 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 CMD_BUF_SIZE    64     // Defines the size of the buffer that holds
                                   // the command line.
    #define FRESULT_ENTRY(f)        { (f), (# f) } // A macro to make it easy to
                                                   // add result codes to the table
    #define NAME_TOO_LONG_ERROR     1              // Error reasons returned
    #define OPENDIR_ERROR           2              // by ChangeDirectory().
    #define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))
    #define TICKS_PER_SECOND        100
    #define NUM_LIST_STRINGS        48
    #define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
    #define NUM_STATUS_STRINGS      6
    #define MAX_STATUS_STRING_LEN   (36 + 1)
    
    //
    // Globals
    //
    static char g_cCwdBuf[PATH_BUF_SIZE] = "/";  // This buffer holds the full path
                                                 // to the current working
                                                 // directory. Initially it is
                                                 // root ("/").
    static char g_cTmpBuf[PATH_BUF_SIZE];        // A temporary data buffer used
                                                 // when manipulating file paths,or
                                                 // reading data from the SD card.
    static char g_cCmdBuf[CMD_BUF_SIZE];         // The buffer that holds the
                                                 // command line.
     FATFS g_sFatFs;
     DIR g_sDirObject;
     FILINFO g_sFileInfo;
     FIL g_sFileObject;
    
    //
    // 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;
    
    //
    // 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)
    };
    
    const char *g_ppcDirListStrings[NUM_LIST_STRINGS]; // Storage for the filename
                                                       // listbox widget string
                                                       // table.
    
    //
    // Storage for the names of the files in the current directory. Filenames
    // are stored in format "(D) filename.ext" for directories or
    // "(F) filename.ext" for files.
    //
    char g_pcFilenames[NUM_LIST_STRINGS][MAX_FILENAME_STRING_LEN];
    
    //
    // Storage for the strings which appear in the status box at the bottom of the
    // display.
    //
    char g_pcStatus[NUM_STATUS_STRINGS][MAX_STATUS_STRING_LEN];
    
    //
    // Storage for the status listbox widget string table.
    //
    const char *g_ppcStatusStrings[NUM_STATUS_STRINGS] =
    {
        g_pcStatus[0],
        g_pcStatus[1],
        g_pcStatus[2],
        g_pcStatus[3],
        g_pcStatus[4],
        g_pcStatus[5]
    };
    unsigned long g_ulStatusStringIndex = 0;
    
    //
    // Forward declarations for functions called by the widgets used in the user
    // interface.
    //
    static FRESULT ChangeToDirectory(char *pcDirectory, unsigned long *pulReason);
    static const char *StringFromFresult(FRESULT fresult);
    
    //
    // Function Prototypes
    //
    extern void UARTStdioIntHandler(void);
    void    die(FRESULT rc );
    void    WriteFile();
    //
    // StringFromFresult - 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.
    //
    static 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 unknown error.
        //
        return("UNKNOWN ERROR CODE");
    }
    
    //
    // SysTickHandler - This is the handler for this SysTick interrupt.  FatFs
    //                  requires a timer tick every 10 ms for internal timing
    //                  purposes.
    //
    __interrupt void
    SysTickHandler(void)
    {
        //
        // Call the FatFs tick timer.
        //
        disk_timerproc();
        PieCtrlRegs.PIEACK.all |= 1;
    }
    
    //
    // Cmd_ls - 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, ulItemCount, ulFileCount, 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;
        ulItemCount = 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;
            }
    
            //
            // 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) ? (uint32_t)'D' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_RDO) ? (uint32_t)'R' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_HID) ? (uint32_t)'H' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_SYS) ? (uint32_t)'S' : (uint32_t)'-',
                       (g_sFileInfo.fattrib & AM_ARC) ? (uint32_t)'A' : (uint32_t)'-',
                       (uint32_t)((g_sFileInfo.fdate >> 9) + 1980),
                       (uint32_t)((g_sFileInfo.fdate >> 5) & 15),
                       (uint32_t)(g_sFileInfo.fdate & 31),
                       (uint32_t)((g_sFileInfo.ftime >> 11)),
                       (uint32_t)((g_sFileInfo.ftime >> 5) & 63),
                       (uint32_t)(g_sFileInfo.fsize),
                       g_sFileInfo.fname);
    
            //
            // Add the information as a line in the listbox widget.
            //
            if(ulItemCount < NUM_LIST_STRINGS)
            {
                usprintf(g_pcFilenames[ulItemCount], "(%c) %12s",
                         (g_sFileInfo.fattrib & AM_DIR) ? 'D' : 'F',
                         g_sFileInfo.fname);
            }
    
            //
            // If the attribute 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;
            }
    
            //
            // Move to the next entry in the item array we use to populate the
            // list box.
            //
            ulItemCount++;
    
            //
            // Wait for the UART transmit buffer to empty.
            //
    //        UARTFlushTx(false);
        }
    
        //
        // 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);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Made it to here, return with no errors.
        //
        return(0);
    }
    
    //
    // ChangeToDirectory - 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 don't try
    //                     something like this: ("../my/new/path")
    //                     Once the new directory is specified, it attempts to open
    //                     the directory to make sure it exists.  If the new path
    //                     is opened successfully, then the current working
    //                     directory (cwd) is changed to the new path. In cases of
    //                     error, the pulReason parameter will be written with one
    //                     of the following values:
    //
    static FRESULT
    ChangeToDirectory(char *pcDirectory, unsigned long *pulReason)
    {
        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(pcDirectory[0] == '/')
        {
            //
            // Make sure the new path is not bigger than the cwd buffer.
            //
            if(strlen(pcDirectory) + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_OK);
            }
            //
            // If the new path name (in argv[1])  is not too long, then
            // copy it into the temporary buffer so it can be checked.
            //
            else
            {
                strncpy(g_cTmpBuf, pcDirectory, sizeof(g_cTmpBuf));
            }
        }
        //
        // If the argument is .. then attempt to remove the lowest level
        // on the CWD.
        //
        else if(!strcmp(pcDirectory, ".."))
        {
            //
            // Get the index to the last character in the current path.
            //
            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(pcDirectory) + 1 + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_INVALID_OBJECT);
            }
            //
            // The new path is okay, so add the separator and then append
            // the new directory to the path.
            //
            else
            {
                //
                // If not already at the root level, then append a /
                //
                if(strcmp(g_cTmpBuf, "/"))
                {
                    strcat(g_cTmpBuf, "/");
                }
    
                //
                // Append the new directory to the path.
                //
                strcat(g_cTmpBuf, pcDirectory);
            }
        }
    
        //
        // 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 cant be opened, then it is a bad path.  Inform
        // user and return.
        //
        if(fresult != FR_OK)
        {
            *pulReason = OPENDIR_ERROR;
            return(fresult);
        }
        //
        // Otherwise, it is a valid new path, so copy it into the CWD and update
        // the screen.
        //
        else
        {
            strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
        }
    
        //
        // Return success.
        //
        return(FR_OK);
    }
    
    //
    // Cmd_cd - 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 long ulReason;
        FRESULT fresult;
    
        //
        // Try to change to the directory provided on the command line.
        //
        fresult = ChangeToDirectory(argv[1], &ulReason);
    
        //
        // If an error was reported, try to offer some helpful information.
        //
        if(fresult != FR_OK)
        {
            switch(ulReason)
            {
                case OPENDIR_ERROR:
                    UARTprintf("Error opening new directory.\n");
                    break;
    
                case NAME_TOO_LONG_ERROR:
                    UARTprintf("Resulting path name is too long.\n");
                    break;
    
                default:
                    UARTprintf("An unrecognized error was reported.\n");
                    break;
            }
        }
    
        //
        // Return the appropriate error code.
        //
        return(fresult);
    }
    
    //
    // Cmd_pwd - 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);
    
        //
        // Wait for the UART transmit buffer to empty.
        //
    //    UARTFlushTx(false);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_cat - 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);
    
            //
            // Continue reading until less than the full number of bytes are
            // read.  That means the end of the buffer was reached.
            //
        }
        while(usBytesRead == sizeof(g_cTmpBuf) - 1);
    
        //
        // Return success.
        //
        return(0);
    }
    
    //
    // Cmd_help - 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_psCmdTable[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);
    }
    
    //
    // This is the table that holds the command names, implementing functions,
    // and brief description.
    //
    tCmdLineEntry g_psCmdTable[] =
    {
        { "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" },
        { 0, 0, 0 }
    };
    
    //
    // __error__ - The error routine that is called if the driver library
    //             encounters an error.
    //
    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
    }
    #endif
    
    //
    // ConfigureUART - Configure the UART and its pins.  This must be called
    //                 before UARTprintf().
    //
    void
    ConfigureUART(void)
    {
        //
        // Enable UART0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SCI1);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        EALLOW;
        GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;
        GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;
        GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;
        GpioCtrlRegs.GPADIR.bit.GPIO28 = 0;
    
        GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;
        GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;
        GpioCtrlRegs.GPADIR.bit.GPIO29 = 1;
        GPIO_SetupPinMux(43, GPIO_MUX_CPU1, 15);
        GPIO_SetupPinOptions(43, GPIO_INPUT, GPIO_PUSHPULL);
        GPIO_SetupPinMux(42, GPIO_MUX_CPU1, 15);
        GPIO_SetupPinOptions(42, GPIO_OUTPUT, GPIO_ASYNC);
    
        EDIS;
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, SysCtlLowSpeedClockGet(SYSTEM_CLOCK_SPEED));
    }
    void WriteFile()
    {
        FIL *fp;
    //    FATFS fatfs;            /* File system object */
        UINT bw ;
        FRESULT rc;
    
        #define _USE_WRITE 1
        #define _USE_DIR 1
    
    //    if(rc != FR_OK)
    //    {
    //        UARTprintf("\nMount a volume to write a file.\n");
    //        rc = f_mount(0,&fatfs);
    //        if (rc){
    //                die(rc);
    //                UARTprintf("f_mount error: %s\n", StringFromFresult(rc));
    //                return(1);
    //        }
    //    }
    
    
    #if _USE_WRITE
        UARTprintf("\nOpen a file to write (write.txt).\n");
        rc = f_open(fp, "WRITE.TXT", FA_WRITE);
        if (rc) die(rc);
        UARTprintf("\nWrite a text data. (Hello world!)\n");
        for (;;) {
            rc = f_write(fp,"Hello world!\r\n", 14,(UINT *)&bw);
            if (rc || !bw) break;
        }
        if (rc) die(rc);
    
        UARTprintf("\nTerminate the file write process.\n");
        rc = f_write(fp, 0, 0, (UINT *)&bw);
        if (rc) die(rc);
        f_close(fp);
        UARTprintf("\nWrite Test completed.\n");
    #endif
    
    }
    
    void die(FRESULT rc )
    {
        if(rc != FR_OK)
        {
            UARTprintf("Failed with rc=%u.\n", rc);
        }
        return;
    }
    //
    // Main - It performs initialization, then runs a command processing loop to
    //        read commands from the console.
    //
    char buff[10];
    int
    main(void)
    {
        int nStatus;
        FRESULT fresult;
        BYTE diskResult;
        FIL fil;
    
        //
        // Initialize System Control
        //
        InitSysCtrl();
    
    #ifdef _FLASH
    //
    // Copy time critical code and Flash setup code to RAM
    // This includes the following functions:  InitFlash();
    // The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
    // symbols are created by the linker. Refer to the device .cmd file.
    //
        memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
    
    //
    // Call Flash Initialization to setup flash waitstates
    // This function must reside in RAM
    //
        InitFlash();
    #endif
    
        //
        // Initialize interrupt controller and vector table
        //
        InitPieCtrl();
        InitPieVectTable();
    
        //
        // Set the system tick to fire 100 times per second.
        //
        SysTickInit();
        SysTickPeriodSet(SysCtlClockGet(SYSTEM_CLOCK_SPEED) / 100);
        SysTickIntRegister(SysTickHandler);
        SysTickIntEnable();
        SysTickEnable();
    
        //
        // Enable Interrupts
        //
        IntMasterEnable();
    
        //
        // Configure UART0 for debug output.
        //
        ConfigureUART();
    
        //
        // 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);
            }
        //
           WriteFile();
    
           diskResult = disk_initialize(0);
    
           diskResult = disk_status(0);
           if(fresult == FR_OK)
              {
                  fresult =  f_open(&fil, "0:/1stfile.txt", FA_CREATE_NEW);
                  f_close(&fil);
                  fresult =  f_open(&fil, "0:/1stfile.txt", FA_WRITE);
                  if(fresult != FR_OK)
                  {
                      UARTprintf("f_open error: %s\n", StringFromFresult(fresult));
                      return(1);
                  }
                  buff[0]='0';
                  buff[1]='1';
                  buff[2]='2';
                  buff[3]='3';
                  buff[4]='4';
                  buff[5]='5';
                  buff[6]='6';
                  buff[7]='7';
                  buff[8]='8';
                  buff[9]='9';
                  fresult = f_write (&fil, buff, 10, (UINT *)&nStatus);
    
                  if(fresult != FR_OK)
                  {
                      UARTprintf("f_write error: %s\n", StringFromFresult(fresult));
                      //return(1);
                  }
          //        fresult = f_sync (&file);
    
                  f_sync(&fil);
                  f_close(&fil);
              }
        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));
            }
        }
    }
    
    //
    // End of file
    //
    

  • Yes, I'm still having trouble with it. I am unsure whats causing it either.

  • You were not able to write to the file either?
  • No, I can send you my project. Maybe there is something else i'm missing?
  • Hi,

    I still get an error with rc=589833. Can you give me any suggestions related to it? It always occurs at the same place- when i try to write or open-FR_NOT_READY.

  • your SD card has formatted in FAT or not?

  • Yes, it is. Here is the image supporting it.

  • No FAT32,

    should be FAT and

    Allocation unit size=512

  • I tried with FAT as well. When i use "default allocation size", the sd card is formatted but produces the same error. However, when i used command prompt and format it using :

    H:\>FORMAT E: /FS:FAT /A:512

    to get 512 bytes of memory allocation, it says:

    "The specified cluster size is too small for FAT16/12.
    Format failed."

    The sd card I'm using is a kingston's 2GB sd card.

  • Try  another disks or not?

    Or change another TI control card? 

    Attached SD example code with "FAT and FAT32" file systems mentioned above is ready for microSD 16GB and 32GB in my F28377D and F28379D control cards.