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.

RTOS/CC2640R2F: SDFatFS with cc2640 ( sdspicc2640.c) - Sucessful SD write reconfigures SPI pins

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2640

Tool/software: TI-RTOS

Part 1:

The older Simplelink_cc2640r2_sdk_1_x_xx_xx (versions 1.4 and 1.5) did not support the SDFatFS driver for the cc2640r2f.

I wrote an implementation of SDSPI called sdspicc2640 that works with this. I am attaching these files in case they can help someone. The only issue is that you need to make sure that the CCS linked resources tab prioritizes the added SDSPI files over the ones inherent in the SDK. This can be specified in the 'project properties -> linked resources'. 

You will also need to specify a SDSPI instance to connect hardware. I did this in CC2640R2_LAUNCHXL. You could also declare it in main.c. 

/*
* ========================== SDSPI begin ===================================
*/

#include <SDSPI/SDSPICC2640.h>
#include <ti/drivers/SDSPI.h>

SDSPICC2640_Object sdspiCC2640_Object[CC2640_SPICOUNT];

const SDSPICC2640_HWAttrs sdspiCC2640HWAttrs[CC2640_SPICOUNT] = {
           {
             .baseAddr      = SSI0_BASE,
             .powerMngrId   = PowerCC26XX_PERIPH_SSI0,
             .pinSCK        = MEDIUM_SPI0_CLK,
             .pinMISO       = MEDIUM_SPI0_MISO,
             .pinMOSI       = MEDIUM_SPI0_MOSI,
             .pinCS         = MEDIUM_SPI0_CSN
           }
};


const SDSPI_Config SDSPI_config[] = {
         {
          .fxnTablePtr  = &SDSPICC2640_fxnTable,
          .object       = &sdspiCC2640_Object[0],
          .hwAttrs      = &sdspiCC2640HWAttrs[0]
         },
         {
          NULL,
          NULL,
          NULL
         },
};

const uint_least8_t SDSPI_count = CC2640_SPICOUNT;

You also need to add the enumeration to define CC2640_SPICOUNT 

/*!
 *  @def    
 *  @brief  Enum of SDSPI names 
 */
typedef enum CC2640_SDSPIName {
    CC2640_SDSPI0 = 0,

    CC2640_SPICOUNT
} CC2640_SDSPIName;

SDSPICC2640.h

SDSPICC2640.c
/*
 * Copyright (c) 2015, Texas Instruments Incorporated
 * All rights reserved.
 *
 * 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.
 */

/*-----------------------------------------------------------------------*/
/* MMC/SDC (in SPI mode) control module  (C)ChaN, 2007                   */
/*-----------------------------------------------------------------------*/

#include <stdint.h>
#include <stdbool.h>

#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Types.h>
#include <xdc/runtime/Timestamp.h>
#include <xdc/runtime/System.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>

#include "SDSPICC2640.h"
#include <ti/drivers/pin/PINCC26XX.h>
#include <ti/drivers/SDSPI.h>

/* driverlib header files */
#include <ti/devices/cc26x0r2/driverlib/ioc.h>
#include <ti/devices/cc26x0r2/driverlib/udma.h>
#include <ti/devices/cc26x0r2/inc/hw_ints.h>
#include <ti/devices/cc26x0r2/inc/hw_memmap.h>
#include <ti/devices/cc26x0r2/inc/hw_gpio.h>
#include <ti/devices/cc26x0r2/inc/hw_types.h>
#include <ti/devices/cc26x0r2/driverlib/ssi.h>
#include <ti/devices/cc26x0r2/driverlib/sys_ctrl.h>
#include <ti/sysbios/knl/Task.h>

#include <ti/devices/cc26x0r2/driverlib/prcm.h>
#include <ti/devices/cc26x0r2/driverlib/rom.h>

#include <mFATFS/ff.h>
#include <mFATFS/ffcio.h>
#include <mFATFS/ffconf.h>
#include <mFATFS/diskio.h>
#include <mFATFS/integer.h>

#include <ti/drivers/sdspi.h>

#include "Board.h"
/* 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 */

#define SD_SECTOR_SIZE              512

#define START_BLOCK_TOKEN           0xFE
#define START_MULTIBLOCK_TOKEN      0xFC
#define STOP_MULTIBLOCK_TOKEN       0xFD

#define DRIVE_NOT_MOUNTED           ~0

/*
 * Array of SDSPI_Handles to determine the association of the FatFs drive number
 * with a SDSPI_Handle
 * _VOLUMES is defined in <ti/sysbios/fatfs/ffconf.h>
 */
static SDSPI_Handle sdspiHandles[_VOLUMES];

/* ms scaling to function timeouts */
static uint32_t mSScalingFactor = 0;

/* Function prototypes */

static uint32_t       rcvr_datablock(SDSPICC2640_HWAttrs const *hwAttrs,
                                     uint8_t *buf, uint32_t btr);
static inline void    releaseSPIBus(SDSPICC2640_HWAttrs const *hwAttrs);
static inline uint8_t rxSPI(SDSPICC2640_HWAttrs const *hwAttrs);
static uint8_t        send_cmd(SDSPICC2640_HWAttrs const *hwAttrs, uint8_t cmd,
                               uint32_t arg);
static void           send_initial_clock_train(SDSPICC2640_HWAttrs const *hwAttrs);
static inline void    takeSPIBus(SDSPICC2640_HWAttrs const *hwAttrs);
static inline void    txSPI(SDSPICC2640_HWAttrs const *hwAttrs, uint8_t dat);
static uint8_t        wait_ready(SDSPICC2640_HWAttrs const *hwAttrs);
static bool           xmit_datablock(SDSPICC2640_HWAttrs const *hwAttrs,
                                     const uint8_t *buf, uint8_t token);


/* FatFs disk I/O functions */
DSTATUS  SDSPICC2640_diskInitialize(BYTE drv);
DRESULT  SDSPICC2640_diskIOctrl(BYTE drv, BYTE ctrl, void *buf);
DRESULT  SDSPICC2640_diskRead(BYTE drv, BYTE *buf,
                            DWORD sector, UINT count);
DSTATUS  SDSPICC2640_diskStatus(BYTE drv);
DRESULT  SDSPICC2640_diskWrite(BYTE drv, const BYTE *buf,
                             DWORD sector, UINT count);

/* SDSPICC2640 functions */
void         SDSPICC2640_close(SDSPI_Handle handle);
int          SDSPICC2640_control(SDSPI_Handle handle, unsigned int cmd, void *arg);
void         SDSPICC2640_init(SDSPI_Handle handle);
SDSPI_Handle SDSPICC2640_open(SDSPI_Handle handle, unsigned char drv,
                            SDSPI_Params *params);

/* SDSPI function table for SDSPICC2640 implementation */
const SDSPI_FxnTable SDSPICC2640_fxnTable = {
    SDSPICC2640_init,
    SDSPICC2640_open,
    SDSPICC2640_close,
    SDSPICC2640_control
};

/* Default SDSPI params */
extern const SDSPI_Params SDSPI_defaultParams;
extern PIN_Handle SDSPIPINHandle;



/*
 *  ======== rcvr_datablock ========
 *  Function to receive a block of data from the SDCard
 *
 *  btr count must be an even number
 */
static uint32_t rcvr_datablock(SDSPICC2640_HWAttrs const *hwAttrs,
                               uint8_t *buf, uint32_t btr)
{
    uint8_t   token;
    uint32_t  timestampTimeout;
    uint32_t  timestampStart;
    uint32_t  timestampCurrent;

    /* Wait for data packet in timeout of 100 ms */
    timestampStart = Timestamp_get32();
    timestampTimeout = timestampStart + 100 * mSScalingFactor;
    if (timestampTimeout > timestampStart) {
        timestampStart = ~0;
    }
    do {
        token = rxSPI(hwAttrs);
        timestampCurrent = Timestamp_get32();
    } while ((token == 0xFF) && ((timestampCurrent <= timestampTimeout) ||
                                 (timestampCurrent >= timestampStart)));

    if (token != START_BLOCK_TOKEN) {
        /* If not valid data token, return error */
        return (0);
    }

    /* Receive the data block into buffer */
    do {
        *(buf++) = rxSPI(hwAttrs);
    } while (--btr);

    /* Read the CRC, but discard it */
    rxSPI(hwAttrs);
    rxSPI(hwAttrs);

    /* Return with success */
    return (1);
}

/*
 *  ======== releaseSPIBus ========
 *  Function to release the SPI bus
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 */
static inline void releaseSPIBus(SDSPICC2640_HWAttrs const *hwAttrs)
{

    /* Deselect the SD card. */
    IOCPinTypeGpioOutput( hwAttrs->pinCS);
    PIN_setOutputValue(SDSPIPINHandle, hwAttrs->pinCS, 1);
}

/*
 *  ======== rxSPI ========
 *  Function to receive one byte onto the SPI bus. Polling (Blocked)
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 */
static inline uint8_t rxSPI(SDSPICC2640_HWAttrs const *hwAttrs)
{
    SDSPIDataType   rcvdat;

    /* write dummy data */
    SSIDataPut(hwAttrs->baseAddr, 0xFF);

    /* read data frm rx fifo */
    SSIDataGet(hwAttrs->baseAddr, &rcvdat);

    return ((uint8_t)rcvdat);
}

/*
 *  ======== send_cmd ========
 *  Function that will transmit an command to the SDCard
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 *
 *  @param  cmd         SD command
 *
 *  @param  arg         SD command argument
 */
static uint8_t send_cmd(SDSPICC2640_HWAttrs const *hwAttrs, uint8_t cmd, uint32_t arg)
{
    uint8_t n;
    uint8_t res;

    if (wait_ready(hwAttrs) != 0xFF) {
       Log_print1(Diags_USER1, "SDSPI:(%p) send_cmd: SD card wait time expired",
                                hwAttrs->baseAddr);
       return (0xFF);
    }

    /* Send command packet */
    txSPI(hwAttrs, cmd);                    /* Command */
    txSPI(hwAttrs, (uint8_t)(arg >> 24));   /* Argument[31..24] */
    txSPI(hwAttrs, (uint8_t)(arg >> 16));   /* Argument[23..16] */
    txSPI(hwAttrs, (uint8_t)(arg >> 8));    /* Argument[15..8] */
    txSPI(hwAttrs, (uint8_t)arg);           /* Argument[7..0] */

    if (cmd == CMD0) {
        /* CRC for CMD0(0) */
        n = 0x95;
    }
    else if (cmd == CMD8) {
        /* CRC for CMD8(0x1AA) */
        n = 0x87;
    }
    else {
        /* Default CRC should be at least 0x01 */
        n = 0x01;
    }

    /* Future enhancement to add CRC support */
    txSPI(hwAttrs, n);

    /* Receive command response */
    if (cmd == CMD12) {
        /* Skip a stuff byte when stop reading */
        rxSPI(hwAttrs);
    }

    /* Wait for a valid response in timeout; 10 attempts */
    n = 80;
    do {
        res = rxSPI(hwAttrs);
    } while ((res & 0x80) && --n);


    /* Return with the response value */
    return (res);
}

/*
 *  ======== send_initial_clock_train ========
 *  Function to get the SDCard into SPI mode
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 */
static void send_initial_clock_train(SDSPICC2640_HWAttrs const *hwAttrs)
{
    unsigned char   i;
    SDSPIDataType   dat;

    /* Deselect the SD card. */
    PIN_setOutputValue(SDSPIPINHandle, hwAttrs->pinCS, 1);
    /* Switch the SPI TX line to a GPIO and drive it high too. */
    IOCPinTypeGpioOutput( hwAttrs->pinMOSI);//????
    PIN_setOutputValue(SDSPIPINHandle, hwAttrs->pinMOSI, 1);
    /*
     * Send 10 bytes over the SPI bus. This causes the clock to toggle several
     * times to get the SD Card into SPI mode.
     */
    for (i = 0; i < 10; i++) {
        /*
         * Write DUMMY data. SSIDataPut() waits until there is room in the
         * FIFO.
         */
        SSIDataPut(hwAttrs->baseAddr, 0xFF);

        /* Flush data read during data write. */
        SSIDataGet(hwAttrs->baseAddr, &dat);
        //rxSPI(hwAttrs);
    }

    /* Revert to hardware control of the SPI TX line. */
    IOCPinTypeSsiMaster(hwAttrs->baseAddr,hwAttrs->pinMISO,hwAttrs->pinMOSI,hwAttrs->pinCS, hwAttrs->pinSCK);  //???

    Log_print1(Diags_USER1, "SDSPI:(%p) initialized SD card to SPI mode",
                             hwAttrs->baseAddr);
}

/*
 *  ======== takeSPIBus ========
 *  Function to take the SPI bus
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 */
static inline void takeSPIBus(SDSPICC2640_HWAttrs const *hwAttrs)
{
     /* Select the SD card. */
    IOCPinTypeGpioOutput( hwAttrs->pinCS);
    PIN_setOutputValue(SDSPIPINHandle, hwAttrs->pinCS, 0);
}

/*
 *  ======== txSPI ========
 *  Function to transmit one byte onto the SPI bus. Polling (Blocked)
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 *
 *  @param  dat         Data to be sent onto the SPI bus
 */
static inline void txSPI(SDSPICC2640_HWAttrs const *hwAttrs, uint8_t dat)
{
    SDSPIDataType   rcvdat;

    /* Write the data to the tx fifo */
    SSIDataPut(hwAttrs->baseAddr, dat);

    /* flush data read during the write */
    SSIDataGet(hwAttrs->baseAddr, &rcvdat);
}

/*
 *  ======== wait_ready ========
 *  Function to check if the SDCard is busy
 *
 *  This function queries the SDCard to see if it is in a busy state or ready
 *  state
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 */
static uint8_t wait_ready(SDSPICC2640_HWAttrs const *hwAttrs)
{
    uint8_t   res;
    uint32_t  timestampTimeout;
    uint32_t  timestampStart;
    uint32_t  timestampCurrent;

    /* Wait for data packet in timeout of 500 ms */
    timestampStart = Timestamp_get32();
    timestampTimeout = timestampStart + 500 * mSScalingFactor;
    if (timestampTimeout > timestampStart) {
        timestampStart = ~0;
    }
    rxSPI(hwAttrs);
    do {
        res = rxSPI(hwAttrs);
        timestampCurrent = Timestamp_get32();
    } while ((res != 0xFF) && ((timestampCurrent <= timestampTimeout) ||
                               (timestampCurrent >= timestampStart)));

    return (res);
}

/* _READONLY is defined in <ti/sysbios/fatfs/diskio.h> */
#if _READONLY == 0
/*
 *  ======== xmit_datablock ========
 *  Function to transmit a block of data to the SDCard
 *
 *  @param  hwAttrs     Pointer to hardware attributes
 *
 *  @param  params      SDSPICC2640 hardware attributes
 *
 *  @param  buf         pointer to const data buffer
 *
 *  @param  token       command token to be sent to the SD card prior to
 *                      sending the data block. The available tokens are:
 *                      START_BLOCK_TOKEN
 *                      START_MULTIBLOCK_TOKEN
 *                      STOP_MULTIBLOCK_TOKEN
 */
static bool xmit_datablock(SDSPICC2640_HWAttrs const *hwAttrs,
                           const uint8_t *buf, uint8_t token)
{
    uint8_t resp;
    uint8_t wc;

    if (wait_ready(hwAttrs) != 0xFF) {
        /* Return with error */
        return (false);
    }

    /* Xmit data token */
    txSPI(hwAttrs, token);

    /* Send data only when token != STOP_MULTIBLOCK_TOKEN */
    if (token != STOP_MULTIBLOCK_TOKEN) {
        /* Is data token */
        wc = 0;
        /* Transferring 512 byte blocks using a 8 bit counter */
        do {
            /* Xmit the SD_SECTOR_SIZE byte data block */
            txSPI(hwAttrs, *buf++);
            txSPI(hwAttrs, *buf++);
        } while (--wc);

        /* Future enhancement to add CRC support */
        txSPI(hwAttrs, 0xFF);
        txSPI(hwAttrs, 0xFF);

        /* Reveive data response */
        resp = rxSPI(hwAttrs);

        /* If not accepted, return error */
        if ((resp & 0x1F) != 0x05) {
            return (false);
        }
    }

    /* Return with success */
    return (true);
}
#endif /* _READONLY */

/*
 *  ======== SDSPICC2640_close ========
 *  Function to unmount the FatFs filesystem and unregister the SDSPICC2640
 *  disk I/O functions from SYS/BIOS' FatFS module.
 *
 *  @param  handle      SDSPI_Handle returned by SDSPI_open()
 */
void SDSPICC2640_close(SDSPI_Handle handle)
{
    unsigned int               key;
    DRESULT                    dresult;
    FRESULT                    fresult;
    SDSPICC2640_Object          *object = handle->object;
    SDSPICC2640_HWAttrs const   *hwAttrs = handle->hwAttrs;
    TCHAR                      path[3];

    path[0] = '0' + object->driveNumber;
    path[1] = ':';
    path[2] = '\0';

    /* Unmount the FatFs drive */
    fresult = f_mount(NULL, path, 0);
    if (fresult != FR_OK) {
        Log_print2(Diags_USER1,
                   "SDSPI:(%p) Could not unmount FatFs volume @ drive number %d",
                   hwAttrs->baseAddr,
                   object->driveNumber);
    }

    /* Unregister the disk_*() functions */
    dresult = disk_unregister(object->driveNumber);
    if (dresult != RES_OK) {
        Log_print2(Diags_USER1,
                   "SDSPI:(%p) Error unregistering disk functions @ drive number %d",
                   hwAttrs->baseAddr,
                   object->driveNumber);
    }

    SSIDisable(hwAttrs->baseAddr);

    Log_print1(Diags_USER1, "SDSPI:(%p) closed", hwAttrs->baseAddr);


      /* Release power dependency on SPI. */
    Power_releaseDependency(hwAttrs->powerMngrId);

    key = Hwi_disable();
    object->driveNumber = DRIVE_NOT_MOUNTED;
    Hwi_restore(key);


}

/*
 *  ======== SDSPICC2640_control ========
 *  @pre    Function assumes that the handle is not NULL
 */
int SDSPICC2640_control(SDSPI_Handle handle, unsigned int cmd, void *arg)
{
    /* No implementation yet */
    return (SDSPI_STATUS_UNDEFINEDCMD);
}

/*
 *  ======== SDSPICC2640_diskInitialize ========
 *  Function to initialize the SD Card.  This function is called by the FatFs
 *  module and must not be called by the application!
 *
 *  @param  drv         Drive Number
 */
DSTATUS SDSPICC2640_diskInitialize(BYTE drv)
{

    uint8_t                    n;
    uint8_t                    ocr[4];
    SDSPICC2640_CardType         cardType;
    Types_FreqHz               freq;
    uint32_t                   timestampTimeout;
    uint32_t                   timestampStart;
    uint32_t                   timestampCurrent;
    SDSPICC2640_Object          *object = sdspiHandles[drv]->object;
    SDSPICC2640_HWAttrs const   *hwAttrs = sdspiHandles[drv]->hwAttrs;

    /* No card in the socket */
    if (object->diskState & STA_NODISK) {
        Log_error1("SDSPI:(%p) disk initialization failed: No disk",
                    hwAttrs->baseAddr);

        return (object->diskState);
    }

    /* Initialize the SD Card for SPI mode */
    send_initial_clock_train(hwAttrs);

    /* Select the SD Card's chip select */
    takeSPIBus(hwAttrs);
    cardType = NOCARD;

    /* Send the CMD0 to put the SD Card in "Idle" state */
    if (send_cmd(hwAttrs, CMD0, 0) == 1) {

        /*
         * Determine what SD Card version we are dealing with
         * Depending on which SD Card version, we need to send different SD
         * commands to the SD Card, which will have different response fields.
         */

        if (send_cmd(hwAttrs, CMD8, 0x1AA) == 1) {
            /* SDC Ver2+ */
            for (n = 0; n < 4; n++) {
                ocr[n] = rxSPI(hwAttrs);
            }

            /*
             * Ensure that the card's voltage range is valid
             * The card can work at vdd range of 2.7-3.6V
             */
            if ((ocr[2] == 0x01) && (ocr[3] == 0xAA)) {
                /* Wait for data packet in timeout of 1s */
                timestampStart = Timestamp_get32();
                timestampTimeout = timestampStart + 1000 * mSScalingFactor;
                if (timestampTimeout > timestampStart) {
                    timestampStart = ~0;
                }
                do {
                    /* ACMD41 with HCS bit */
                    if (send_cmd(hwAttrs, CMD55, 0) <= 1 &&
                        send_cmd(hwAttrs, CMD41, 1UL << 30) == 0) {
                        timestampTimeout = 0;
                        break;
                    }
                    timestampCurrent = Timestamp_get32();
                } while ((timestampCurrent <= timestampTimeout) ||
                         (timestampCurrent >= timestampStart));

                /*
                 * Check CCS bit to determine which type of capacity we are
                 * dealing with
                 */
                if ((!timestampTimeout) && send_cmd(hwAttrs, CMD58, 0) == 0) {
                    for (n = 0; n < 4; n++) {
                        ocr[n] = rxSPI(hwAttrs);
                    }
                    cardType = (ocr[0] & 0x40) ? SDHC : SDSC;
                }
            }
        }

        /* SDC Ver1 or MMC */
        else {
            /*
             * The card version is not SDC V2+ so check if we are dealing with a
             * SDC or MMC card
             */
            if ((send_cmd(hwAttrs, CMD55, 0) <= 1 &&
                 send_cmd(hwAttrs, CMD41, 0) <= 1)) {
                cardType = SDSC;
            }
            else {
                cardType = MMC;
            }

            /* Wait for data packet in timeout of 1s */
            timestampStart = Timestamp_get32();
            timestampTimeout = timestampStart + 1000 * mSScalingFactor;
            if (timestampTimeout > timestampStart) {
                timestampStart = ~0;
            }
            do {
                if (cardType == SDSC) {
                    /* ACMD41 */
                    if (send_cmd(hwAttrs, CMD55, 0) <= 1 &&
                        send_cmd(hwAttrs, CMD41, 0) == 0) {
                        timestampTimeout = 0;
                        break;
                    }
                }
                else {
                    /* CMD1 */
                    if (send_cmd(hwAttrs, CMD1, 0) == 0) {
                        timestampTimeout = 0;
                        break;
                    }
                }
                timestampCurrent = Timestamp_get32();
            } while ((timestampCurrent <= timestampTimeout) ||
                     (timestampCurrent >= timestampStart));

            /* Select R/W block length */
            if ((timestampTimeout) || send_cmd(hwAttrs, CMD16, SD_SECTOR_SIZE) != 0) {
                cardType = NOCARD;
            }
        }
    }

    object->cardType = cardType;

    /* Deselect the SD Card's chip select */
    releaseSPIBus(hwAttrs);

    /* Idle (Release DO) */
    rxSPI(hwAttrs);

    /* Check to see if a card type was determined */
    if (cardType != NOCARD) {
        /* Reconfigure the SPI bust at the new frequency rate */
        BIOS_getCpuFreq(&freq);
        SSIDisable(hwAttrs->baseAddr);

        SSIConfigSetExpClk(hwAttrs->baseAddr,
                           freq.lo,
                           SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER,
                           object->bitRate,
                           8);

        SSIEnable(hwAttrs->baseAddr);

        Log_print3(Diags_USER1,
                   "SDSPI:(%p) CPU freq: %d; Reconfiguring SDSPI freq to %d",
                   hwAttrs->baseAddr,
                   freq.lo,
                   object->bitRate);

        /* Initialization succeeded */
        object->diskState &= ~STA_NOINIT;
    }
    else {
        Log_print1(Diags_USER1, "SDSPI:(%p) disk initialization failed",
                                 hwAttrs->baseAddr);
    }

    return (object->diskState);
}

/*
 *  ======== SDSPICC2640_diskIOctrl ========
 *  Function to perform specifed disk operations. This function is called by the
 *  FatFs module and must not be called by the application!
 *
 *  @param  drv         Drive Number
 *
 *  @param  ctrl        Control code
 *
 *  @param  buf         Buffer to send/receive control data
 */
DRESULT SDSPICC2640_diskIOctrl(BYTE drv, BYTE ctrl, void *buf)
{
    DRESULT                   res = RES_ERROR;
    uint8_t                   n;
    uint8_t                   csd[16];
    WORD                      csize;
    SDSPICC2640_Object         *object = sdspiHandles[drv]->object;
    SDSPICC2640_HWAttrs const  *hwAttrs = sdspiHandles[drv]->hwAttrs;

    if (object->diskState & STA_NOINIT) {
        Log_error1("SDSPI:(%p) disk IO control: disk not initialized",
                    hwAttrs->baseAddr);

        return (RES_NOTRDY);
    }

    /* Select the SD Card's chip select */
    takeSPIBus(hwAttrs);

    switch (ctrl) {
        case GET_SECTOR_COUNT:
            /* Get number of sectors on the disk (uint32_t) */
            if ((send_cmd(hwAttrs, CMD9, 0) == 0) &&
                 rcvr_datablock(hwAttrs, csd, 16)) {

                /* SDC ver 2.00 */
                if ((csd[0] >> 6) == 1) {
                    csize = csd[9] + ((WORD)csd[8] << 8) + 1;
                    *(uint32_t*)buf = (uint32_t)csize << 10;
                }
                /* MMC or SDC ver 1.XX */
                else {
                    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;

                    *(uint32_t*)buf = (uint32_t)csize << (n - 9);
                }
                Log_print2(Diags_USER2,
                           "SDSPI:(%p) disk IO control: sector count: %d",
                           hwAttrs->baseAddr,
                           *(uint32_t*)buf);
                res = RES_OK;
            }
            break;

        case GET_SECTOR_SIZE:
            /* Get sectors on the disk (WORD) */
            *(WORD*)buf = SD_SECTOR_SIZE;
            Log_print2(Diags_USER2,
                       "SDSPI:(%p) disk IO control: sector size: %d",
                       hwAttrs->baseAddr,
                       *(WORD*)buf);
            res = RES_OK;
            break;

        case CTRL_SYNC:
            /* Make sure that data has been written */
            if (wait_ready(hwAttrs) == 0xFF) {
                Log_print1(Diags_USER2,
                           "SDSPI:(%p) disk IO control: control sync: ready",
                           hwAttrs->baseAddr);
                res = RES_OK;
            }
            else {
                Log_print1(Diags_USER2,
                           "SDSPI:(%p) disk IO control: control sync: not ready",
                           hwAttrs->baseAddr);
                res = RES_NOTRDY;
            }
            break;

        default:
            Log_print1(Diags_USER2,
                       "SDSPI:(%p) disk IO control: parameter error",
                       hwAttrs->baseAddr);
            res = RES_PARERR;
            break;
    }

    /* Deselect the SD Card's chip select */
    releaseSPIBus(hwAttrs);

    /* Idle (Release DO) */
    rxSPI(hwAttrs);

    return (res);
}

/*
 *  ======== SDSPICC2640_diskRead ========
 *  Function to perform a disk read from the SDCard. This function is called by
 *  the FatFs module and must not be called by the application!
 *
 *  @param  drv         Drive Number
 *
 *  @param  buf         Pointer to a buffer on which to store data
 *
 *  @param  sector      Starting sector number (LBA)
 *
 *  @param  count       Sector count (1...255)
 */
DRESULT SDSPICC2640_diskRead(BYTE drv, BYTE *buf,
                           DWORD sector, UINT count)
{
    SDSPICC2640_Object          *object = sdspiHandles[drv]->object;
    SDSPICC2640_HWAttrs const   *hwAttrs = sdspiHandles[drv]->hwAttrs;

    if (!count) {
        Log_print1(Diags_USER1, "SDSPI:(%p) disk read: 0 sectors to read",
                                 hwAttrs->baseAddr);

        return (RES_PARERR);
    }

    if (object->diskState & STA_NOINIT) {
        Log_error1("SDSPI:(%p) disk read: disk not initialized",
                    hwAttrs->baseAddr);

        return (RES_NOTRDY);
    }

    /*
     * On a SDSC card, the sector address is a byte address on the SD Card
     * On a SDHC card, the sector address is address by sector blocks
     */
    if (object->cardType != SDHC) {
        /* Convert to byte address */
        sector *= SD_SECTOR_SIZE;
    }

    /* Select the SD Card's chip select */
    takeSPIBus(hwAttrs);

    /* Single block read */
    if (count == 1) {
        if ((send_cmd(hwAttrs, CMD17, sector) == 0) &&
             rcvr_datablock(hwAttrs, buf, SD_SECTOR_SIZE)) {
            count = 0;
        }
    }
    /* Multiple block read */
    else {
        if (send_cmd(hwAttrs, CMD18, sector) == 0) {
            do {
                if (!rcvr_datablock(hwAttrs, buf, SD_SECTOR_SIZE)) {
                    break;
                }
                buf += SD_SECTOR_SIZE;
            } while (--count);

            /* STOP_TRANSMISSION */
            send_cmd(hwAttrs, CMD12, 0);
        }
    }

    /* Deselect the SD Card's chip select */
    releaseSPIBus(hwAttrs);

    /* Idle (Release DO) */
    rxSPI(hwAttrs);

    return (count ? RES_ERROR : RES_OK);
}

/*
 *  ======== SDSPICC2640_diskStatus ========
 *  Function to return the current disk status. This function is called by
 *  the FatFs module and must not be called by the application!
 *
 *  @param(drv)         Drive Number
 */
DSTATUS SDSPICC2640_diskStatus(BYTE drv)
{
    /* Get the pointer to the object */
    SDSPICC2640_Object  *object = sdspiHandles[drv]->object;

    /* Use Diags_USER1 to reduce noise in the logs */
    Log_print2(Diags_USER2,
               "SDSPI:(%p) disk status: diskState: %d",
               ((SDSPICC2640_HWAttrs const *)(sdspiHandles[drv]->hwAttrs))->baseAddr,
               object->diskState);

    return (object->diskState);
}

#if _READONLY == 0
/*
 *  ======== SDSPICC2640_diskWrite ========
 *  Function to perform a disk write from the SDCard. This function is called by
 *  the FatFs module and must not be called by the application!
 *
 *  @param  drv         Drive Number
 *
 *  @param  buf         Pointer to a buffer from which to read data
 *
 *  @param  sector      Starting sector number (LBA)
 *
 *  @param  count       Sector count (1...255)
 */
DRESULT SDSPICC2640_diskWrite(BYTE drv, const BYTE *buf,
                            DWORD sector, UINT count)
{
    SDSPICC2640_Object          *object = sdspiHandles[drv]->object;
    SDSPICC2640_HWAttrs const   *hwAttrs = sdspiHandles[drv]->hwAttrs;

    if (!count) {
        Log_print1(Diags_USER1, "SDSPI:(%p) disk write: 0 sectors to write",
                                 hwAttrs->baseAddr);

        return (RES_PARERR);
    }
    if (object->diskState & STA_NOINIT) {
        Log_error1("SDSPI:(%p) disk write: disk not initialized",
                    hwAttrs->baseAddr);

        return (RES_NOTRDY);
    }
    if (object->diskState & STA_PROTECT) {
        Log_error1("SDSPI:(%p) disk write: disk protected",
                    hwAttrs->baseAddr);

        return (RES_WRPRT);
    }

    /*
     * On a SDSC card, the sector address is a byte address on the SD Card
     * On a SDHC card, the sector address is address by sector blocks
     */
    if (object->cardType != SDHC) {
        /* Convert to byte address if needed */
        sector *= SD_SECTOR_SIZE;
    }

    /* Select the SD Card's chip select */
    takeSPIBus(hwAttrs);

    /* Single block write */
    if (count == 1) {
        if ((send_cmd(hwAttrs, CMD24, sector) == 0) &&
             xmit_datablock(hwAttrs, buf, START_BLOCK_TOKEN)) {
            count = 0;
        }
    }
    /* Multiple block write */
    else {
        if ((object->cardType == SDSC) || (object->cardType == SDHC)) {
            send_cmd(hwAttrs, CMD55, 0);
            send_cmd(hwAttrs, CMD23, count);    /* ACMD23 */
        }
        /* WRITE_MULTIPLE_BLOCK */
        if (send_cmd(hwAttrs, CMD25, sector) == 0) {
            do {
                if (!xmit_datablock(hwAttrs, buf, START_MULTIBLOCK_TOKEN)) {
                    break;
                }
                buf += SD_SECTOR_SIZE;
            } while (--count);

            /* STOP_TRAN token */
            if (!xmit_datablock(hwAttrs, 0, STOP_MULTIBLOCK_TOKEN)) {
                count = 1;
            }
        }
    }

    /* Deselect the SD Card's chip select */
    releaseSPIBus(hwAttrs);

    /* Idle (Release DO) */
    rxSPI(hwAttrs);

    return (count ? RES_ERROR : RES_OK);
}
#endif /* _READONLY */

/*
 *  ======== SDSPICC2640_init ========
 *  Function to initialize SDSPI module
 */
void SDSPICC2640_init(SDSPI_Handle handle)
{
    SDSPICC2640_Object            *object = handle->object;

    /* Mark the object as available */
    object->driveNumber = DRIVE_NOT_MOUNTED;
    object->diskState = STA_NOINIT;
    object->cardType = NOCARD;
    FATFS sdspiFATS;
    object->filesystem = &sdspiFATS;

}

/*
 *  ======== SDSPICC2640_open ========
 *  Function to mount the FatFs filesystem and register the SDSPICC2640 disk
 *  I/O functions with SYS/BIOS' FatFS module.
 *
 *  This function also configures some basic GPIO settings needed for the
 *  software chip select with the SDCard.
 *
 *  @param  handle      SDSPI handle
 *  @param  drv         Drive Number
 *  @param  params      SDSPI parameters
 */
SDSPI_Handle SDSPICC2640_open(SDSPI_Handle handle,
                            unsigned char drv,
                            SDSPI_Params *params)
{
    DRESULT                   dresult;
    FRESULT                   fresult;
    unsigned int              key;
    Types_FreqHz              freq;

    SDSPICC2640_Object         *object = handle->object;
    SDSPICC2640_HWAttrs const  *hwAttrs = handle->hwAttrs;

    TCHAR                     path[3];

    /* Determine if the device was already opened */
    key = Hwi_disable();
    if (object->driveNumber != DRIVE_NOT_MOUNTED) {
        Hwi_restore(key);
        return (NULL);
    }
    /* Mark as being used */
    object->driveNumber = drv;
    Hwi_restore(key);

    /* Store the SDSPI parameters */
    if (params == NULL) {
        /* No params passed in, so use the defaults */
        params = (SDSPI_Params *) &SDSPI_defaultParams;
    }

    /* Determine time scaling for ms timeouts */
    Timestamp_getFreq(&freq);
    mSScalingFactor = freq.lo / 1000;
    Assert_isTrue(mSScalingFactor != 0, NULL);

    object->bitRate = params->bitRate;
    IOCPinTypeSsiMaster(hwAttrs->baseAddr,hwAttrs->pinMISO,hwAttrs->pinMOSI,hwAttrs->pinCS, hwAttrs->pinSCK);  //???

    /* Pin used for Chip Select */
    IOCPinTypeGpioOutput(hwAttrs->pinCS);   //????

//    /* Raise the chip select pin */
    PIN_setOutputValue(SDSPIPINHandle, hwAttrs->pinCS, 1);
//      GPIOPinWrite(hwAttrs->pinCS, hwAttrs->pinCS);
    /* Register power dependency - i.e. power up and enable clock for SPI. */
    Power_setDependency(hwAttrs->powerMngrId);

    /*
     * Configure the SPI bus to 400 kHz as required per SD specs. This frequency
     * will be adjusted later once the SD card has been successfully initialized
     */
    BIOS_getCpuFreq(&freq);
    SSIConfigSetExpClk(hwAttrs->baseAddr, freq.lo, SSI_FRF_MOTO_MODE_0,
                       SSI_MODE_MASTER, 400000, 8);

    Log_print2(Diags_USER1, "SDSPI:(%p) CPU freq: %d; SDSPI freq to 400000 kHz",
               hwAttrs->baseAddr, freq.lo);

    SSIEnable(hwAttrs->baseAddr);


    /* Register the new disk_*() functions */
    dresult = disk_register(object->driveNumber,
                            SDSPICC2640_diskInitialize,
                            SDSPICC2640_diskStatus,
                            SDSPICC2640_diskRead,
                            SDSPICC2640_diskWrite,
                            SDSPICC2640_diskIOctrl);

    /* Check for drive errors */
    if (dresult != RES_OK) {
        Log_error1("SDSPI:(%p) disk functions not registered",
                    hwAttrs->baseAddr);

        SDSPICC2640_close(handle);
        return (NULL);
    }

    path[0] = '0' + object->driveNumber;
    path[1] = ':';
    path[2] = '\0';

    /*
     * Register the filesystem with FatFs. This operation does not access the
     * SDCard yet.
     */

   /* int temi;
    for(temi = 0; temi < 1000; temi++);
*/
    fresult = f_mount((object->filesystem), path, 0);
    if (fresult != FR_OK) {
        Log_error2("SDSPI:(%p) drive %d not mounted",
                    hwAttrs->baseAddr, object->driveNumber);

        SDSPICC2640_close(handle);
        return (NULL);
    }

    /* Store the new SDSPI handle for this FatFs drive number */
    sdspiHandles[drv] = handle;
    Log_print1(Diags_USER1, "SDSPI:(%p) opened", hwAttrs->baseAddr);

    return (handle);
}

-------------------------------------------------------------------------

---

-----------------------------------------------------------------

Part 2:

The new Simplelink_cc2640r2_sdk_2_x_xx_xx does had SDFatFS supported for the cc2640R2. I followed an example available online and wrote this implementation: 

/* Initialization */

#include <xdc/std.h>
#include <xdc/runtime/System.h>

 #include <file.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>

/* Drivers */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/SDFatFS.h>
#include <ti/drivers/SD.h>

/* Board Header files */
#include "Board.h"
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <third_party/fatfs/ff.h>
#include <third_party/fatfs/diskio.h>
#include <third_party/fatfs/ffcio.h>

#define DRIVE_NUM       0

#define SD_DRIVE_NUM    0

 const char inputfile[] = "fat:"STR(DRIVE_NUM)":input.txt";
 const char outputfile[] = "fat:"STR(DRIVE_NUM)":output.txt";

 /* Set this to the current UNIX time in seconds */
 const struct timespec ts = {
     .tv_sec = 1469647026,
     .tv_nsec = 0
 };
 /* File name prefix for this filesystem for use with TI C RTS */
 char fatfsPrefix[] = "fat";

 /*
  * Note: The SDFatFS driver provides interface functions to enable FatFs
  * but relies on the SD driver to communicate with SD cards.  Opening a
  * SDFatFs driver instance will internally try to open a SD driver instance
  * reusing the same index number (opening SDFatFs driver at index 0 will try to
  * open SD driver at index 0).  This requires that all SDFatFs driver instances
  * have an accompanying SD driver instance defined with the same index.  It is
  * acceptable to have more SD driver instances than SDFatFs driver instances
  * but the opposite is not supported & the SDFatFs will fail to open.
  */
 SDFatFS_Object sdfatfsObjects[1];

 const SDFatFS_Config SDFatFS_config[1] = {
     {
         .object = &sdfatfsObjects[0]
     }
 };

 const uint_least8_t SDFatFS_count = 1;




/*
*    to write to an SD card -- this is run in a task 
*/
  */
               SDFatFS_Handle sdfatfsHandle;
               FILE *src;
               sdfatfsHandle = SDFatFS_open(0, DRIVE_NUM);
               if (sdfatfsHandle != NULL) {
                   // Try to open the source file 4 times
                   uint8_t timeout = 0;
                   src = fopen(inputfile, "a");
                   while (!src && (timeout < 3)) {
                       timeout++;
                       src = fopen(inputfile, "a");
                   }
                   if(src != NULL){
                       // Print accelerometer data!
                       char str[80];
                       sprintf(str, "%d,%d,%d\n", adxl_data[0], adxl_data[1] , adxl_data[2]);
                       fwrite(str, 1, strlen(str), src);
                       fflush(src);
                       fclose(src);
                   }
// Release SPI0!
               SDFatFS_close(sdfatfsHandle);







/*
* ======== fatfs_getFatTime ========
*/
int32_t fatfs_getFatTime(void)
{
    time_t seconds;
    uint32_t fatTime;
    struct tm *pTime;

    seconds = time(NULL);

    pTime = localtime(&seconds);

    fatTime = ((uint32_t)(pTime->tm_year - 80) << 25) |
    ((uint32_t)(pTime->tm_mon) << 21) |
    ((uint32_t)(pTime->tm_mday) << 16) |
    ((uint32_t)(pTime->tm_hour) << 11) |
    ((uint32_t)(pTime->tm_min) << 5) |
    ((uint32_t)(pTime->tm_sec) >> 1);

    return ((int32_t)fatTime);
}

I use the SDFatFS driver to write to an SD card every 500ms. Interestingly, if the data writes successfully, the next call of fopen will fail. This wastes 23 ms. I looked at an oscilloscope trace and noticed that the MOSI pin ends low after a successful write and stays low. It needs to be pulled high. 

Should I add a task_sleep somewhere to fix this? Currently, I call fopen twice to ensure the data is written. 

 

 

  • Hi,

    Thanks for sharing! Regarding Part 2, have you tried to step into the driver to see what is making the open call fail?

    As you are doing a close following the write, the underlying SPI driver would release the data pins to the default state.
    If you look into the board file you should find the "BoardGpioInitTable" which contains the state of the IOs when not configured by anyone else, the default here is that MOSI is pulled low when not used by anyone (i.e. closing the SPI driver).
  • Hi Jo Nordrum,

    thank you very much for sharing!

    I followed Part 2 since I'm using the latest SDK. However I'm facing the following linker errors:

    <Linking>
     
     undefined       first referenced                                                                                  
      symbol             in file                                                                                       
     ---------       ----------------                                                                                  
     disk_register   C:/ti/simplelink_cc2640r2_sdk_2_20_00_49/source/ti/drivers/lib/drivers_cc26x0r2.aem3<SDFatFS.oem3>
     disk_unregister C:/ti/simplelink_cc2640r2_sdk_2_20_00_49/source/ti/drivers/lib/drivers_cc26x0r2.aem3<SDFatFS.oem3>
     f_mount         C:/ti/simplelink_cc2640r2_sdk_2_20_00_49/source/ti/drivers/lib/drivers_cc26x0r2.aem3<SDFatFS.oem3>
     
    error #10234-D: unresolved symbols remain
    error #10010: errors encountered during linking; "sd_CC2640R2_LAUNCHXL_tirtos_ccs.out" not built

    Do you have idea what's causing them? Did you have the same issue by chance? I started with an empty project, replaced all POSIX code with SYS/BIOS code (as I did successfully other times) and plugged in your code. Should I have also modified something in the board files?

    Best regards (and thanks again for the code and your time),

    Jack

    EDIT


    This is the code

    7180.empty.c
    /*
     * Copyright (c) 2015-2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * 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.
     */
    
    /*
     *  ======== empty.c ========
     */
    
    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    
    #include <file.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <unistd.h>
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Semaphore.h>
    
    /* Third Party Header files */
    #include <third_party/fatfs/ff.h>
    #include <third_party/fatfs/diskio.h>
    #include <third_party/fatfs/ffcio.h>
    
    /* Driver Header files */
    #include <ti/drivers/Power.h>
    #include <ti/drivers/I2C.h>
    #include <ti/drivers/SPI.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/UART.h>
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/SDFatFS.h>
    #include <ti/drivers/SD.h>
    // #include <ti/drivers/Watchdog.h>
    
    /* Board Header file */
    #include "Board.h"
    
    #define TASKSTACKSIZE   1024
    
    #define DRIVE_NUM       0
    #define SD_DRIVE_NUM    0
    
    Task_Struct task0Struct;
    Char task0Stack[TASKSTACKSIZE];
    
    const char inputfile[] = "fat:0:input.txt";
    const char outputfile[] = "fat:0:output.txt";
    
    /* Set this to the current UNIX time in seconds */
    const struct timespec ts = {
        .tv_sec = 1469647026,
        .tv_nsec = 0
    };
    
    /* File name prefix for this filesystem for use with TI C RTS */
    char fatfsPrefix[] = "fat";
    
    /*
     * Note: The SDFatFS driver provides interface functions to enable FatFs
     * but relies on the SD driver to communicate with SD cards.  Opening a
     * SDFatFs driver instance will internally try to open a SD driver instance
     * reusing the same index number (opening SDFatFs driver at index 0 will try to
     * open SD driver at index 0).  This requires that all SDFatFs driver instances
     * have an accompanying SD driver instance defined with the same index.  It is
     * acceptable to have more SD driver instances than SDFatFs driver instances
     * but the opposite is not supported & the SDFatFs will fail to open.
     */
    SDFatFS_Object sdfatfsObjects[1];
    
    const SDFatFS_Config SDFatFS_config[1] = {
        {
            .object = &sdfatfsObjects[0]
        }
    };
    
    const uint_least8_t SDFatFS_count = 1;
    
    
    /*
     *  ======== heartBeatFxn ========
     *  Toggle the Board_LED0. The Task_sleep is determined by arg0 which
     *  is configured for the heartBeat Task instance.
     */
    Void task0Fxn(UArg arg0, UArg arg1)
    {
        SDFatFS_Handle sdfatfsHandle;
        FILE *src;
        sdfatfsHandle = SDFatFS_open(0, DRIVE_NUM);
        if (sdfatfsHandle != NULL) {
            // Try to open the source file 4 times
            uint8_t timeout = 0;
            src = fopen(inputfile, "a");
            while (!src && (timeout < 3)) {
                timeout++;
                src = fopen(inputfile, "a");
            }
            if(src != NULL){
                // Print accelerometer data!
                char str[80];
                sprintf(str, "%d,%d,%d\n", 20, 30 , 40);
                fwrite(str, 1, strlen(str), src);
                fflush(src);
                fclose(src);
            }
    
            // Release SPI0!
            SDFatFS_close(sdfatfsHandle);
        }
    
        Task_sleep(BIOS_WAIT_FOREVER);
    }
    
    
    /*
     *  ======== main ========
     */
    int main(void)
    {
        Task_Params taskParams;
    
        /* Call board init functions */
    
        Power_init();
        if (PIN_init(BoardGpioInitTable) != PIN_SUCCESS)
        {
            System_abort("Error with PIN_init\n");
        }
    
        /* Call driver init functions */
        SDFatFS_init();
    
        /* Construct heartBeat Task  thread */
        Task_Params_init(&taskParams);
        taskParams.stackSize = TASKSTACKSIZE;
        taskParams.stack = &task0Stack;
        Task_construct(&task0Struct, (Task_FuncPtr)task0Fxn, &taskParams, NULL);
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

    The only other modification I did was changing

    #define CC2640R2_LAUNCHXL_SPI0_CSN              PIN_UNASSIGNED

    to

    #define CC2640R2_LAUNCHXL_SPI0_CSN              IOID_11

    in CC2640R2_LAUNCHXL.h

  • Hi Jack,

    Have you tried to add the fatfs library to your project? It is found in the third part folder in the SDK:

    <SDK>/source/third_party/fatfs/lib/ccs/m3