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.

Swapping SD Card FaTFS module to other pins

Other Parts Discussed in Thread: EK-TM4C123GXL

So I am using a tm4c123GXL and I amlooking at implementing something like this:

https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/335963/1172312

However I am looking to implement it using SS2 not SSI0. This is because I have other peripherals and a custom PCB to use the following: 

PB6 -> Miso
PB4 -> SCK
PB7 -> MOSI

PA3 -> SD_CS

Vcc -> Vcc

Gnd -> Gnd

The other thread i posted just seems to call 

f_mount(...)

and no indication of the pins or anything. Can anyone provide help?

  • user4317508 said:
    and no indication of the pins or anything

    Looking at the TivaWare sd_card example for the DK-TM4C123G:

    1) There is the mmc-dk-tm4c123g.c file in the third_party/fatfs/port directory which contains the SSI pins used for the SD card. E.g. the following definitions:

    /* Peripheral definitions for DK-TM4C123G board */
    // SSI port
    #define SDC_SSI_BASE            SSI0_BASE
    #define SDC_SSI_SYSCTL_PERIPH   SYSCTL_PERIPH_SSI0
    
    // GPIO for SSI pins
    #define SDC_GPIO_PORT_BASE      GPIO_PORTA_BASE
    #define SDC_GPIO_SYSCTL_PERIPH  SYSCTL_PERIPH_GPIOA
    #define SDC_SSI_CLK             GPIO_PIN_2
    #define SDC_SSI_TX              GPIO_PIN_5
    #define SDC_SSI_RX              GPIO_PIN_4
    #define SDC_SSI_FSS             GPIO_PIN_3
    #define SDC_SSI_PINS            (SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK |      \
                                     SDC_SSI_FSS)
    

    2) In the main() function in the in the sd_card.c source file there is the following which enables the SSI peripheral used for the SD card:

        //
        // Enable the peripherals used by this example.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    

    I think changing the definitions in these two files should be sufficient to change from SSI0 to SSI2.

    [Note that I haven't tried this]

  • user4317508 said:
    I am looking to implement it using SS2 not SSI0.

    Suspect that should be SSI2 - and if so - following observations should prove of value.

    a) All 4 (SSI2) pins appear at headers on the TM4C123 LPad.   Unfortunately they route to different headers - requiring 2 separate signal cables.

    b) SSI2 employs PB4, PB5, PB6 & PB7.  Note that PBs: 6,7 "enjoy" 0Ω resistors directly connecting each to PD port pins.   Those resistors (R9, R10) may be removed - or "tombstoned" - to break this (unfortunate, almost always unwanted) vendor's, (misguided) "help."

  • Hello Chester,

    You are correct. However there is another additional change that needs to be made. The change is for GPIOPinConfigure that needs to be added. The reasons is because SSI0 PCTL registers are configured at reset time and the port file does not do that again. But if needs to be changed to some other SSI then the GPIOPinConfigure needs to be called.

    Regards
    Amit
  • Amit Ashara said:
    there is another additional change that needs to be made.

    Hi Amit,

    Does the fact that one w/Chester's SW awareness/skills (may) have missed this not suggest that the "default" behavior of SSI0 needs to be (far better) noted and highlighted? 

    As SSI0's "model/example" code is often copied/adapted to all other SSI Ports - the missing link of required pin configuration (too often) bites your user/client - as evidenced herein.  

    Default pin/port behavior does have value - but when any, "extra-normal" code behaviors are "buried/obscured" - such should be well noted - to protect & guide the unwary.    (i.e. everyone but you!)

  • Hello cb1,

    This has been documented in the data sheet for TM4C123. However I agree this became a problem for example codes when non boot interfaces were used or when we moved to TM4C129. We have made changes in the next TivaWare release to address the same. The full changes are not complete yet, but a lot of important codes like boot_loader shall reflect the change.

    Regards
    Amit
  • Amit Ashara said:
    This has been documented in the data sheet for TM4C123

    As you well know - "devil (so) often in the details."  

    Are not PF0 & PD7's default NMI behavior "documented?"   And - has this not (overwhelmingly) proved a serious failure?  (forum count alone of NMI "misses" surely exceeds several hundred!)

    Noted and adequately (perhaps multiply) highlighted trumps: "mice-type" - same size/type (undistinguishable) - very brief mention!  (i.e. "documented")

    The unwillingness to fully/properly highlight any/all such "obvious & predictableclient-user traps" remains hard to understand while (continuing) to frustrate users...

  • Hello cb1,

    Yes, I do. The data sheets if done again shall take care of the oddities.

    Having said that (and knowing the PF0/PD7) how many posters skip data sheets and jump to code (come back admitting that they skimmed the DS). The deficiency is on both sides (ours arguably greater).

    Regards
    Amit
  • Hi Amit,

    In regards to:

    The change is for GPIOPinConfigure that needs to be added. The reasons is because SSI0 PCTL registers are configured at reset time and the port file does not do that again. But if needs to be changed to some other SSI then the GPIOPinConfigure needs to be called.

    What do i need to add and where? I already initialise the SPI previously like this:

    // run @80MHz, use 16MHz xtal
    MAP_SysCtlClockSet (
    SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN
    | SYSCTL_XTAL_16MHZ);
    MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOB);
    MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOE);
    MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_SSI2);
    MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOA);
    MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, LCD_CS_PIN); // LCD CS Pin is A4
    MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, SD_CS_PIN); // SD Card is A3
    MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, NRF_CS_PIN); // NRF is A2
    MAP_GPIOPinTypeGPIOOutput (GPIO_PORTE_BASE, LCD_DC_PIN);
    MAP_GPIOPinConfigure (GPIO_PB4_SSI2CLK);
    MAP_GPIOPinTypeSSI (GPIO_PORTB_BASE, GPIO_PIN_4);
    MAP_GPIOPinConfigure (GPIO_PB7_SSI2TX);
    MAP_GPIOPinTypeSSI (GPIO_PORTB_BASE, GPIO_PIN_7);
    // Configure Clocks 16000000
    MAP_SSIConfigSetExpClk (SSI2_BASE, MAP_SysCtlClockGet (),SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 16000000, 8);
    MAP_SSIEnable (SSI2_BASE);
    

    So, I initialise the pins like this, change the #define in the ports/mmc-tm4c123g.c and then take the code from the example project where they begin and mount the SD card? I change the speed to 400kHz later to work with the SPI.

  • Hello user4317508,

    The right place to do so is in the mmc-tm4c123g.c function power_on

    As an example following is the upcoming code for ek-tm4c1294 with SD Card.

    /*
    * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS)
    * signal is directly driven to ensure that we can hold it low through a
    * complete transaction with the SD card.
    */
    GPIOPinConfigure(GPIO_PB5_SSI1CLK);
    GPIOPinConfigure(GPIO_PE4_SSI1XDAT0);
    GPIOPinConfigure(GPIO_PE5_SSI1XDAT1);
    ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX);
    ROM_GPIOPinTypeSSI(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX);
    ROM_GPIOPinTypeSSI(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK);
    ROM_GPIOPinTypeGPIOOutput(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS);

    Regards
    Amit
  • Without "breaking" those "ready made" - (yet unwanted) dead shorts between 2 pairs of MCU pins (well described/detailed earlier) - above code solution may fail and (some - "Roberto for one") have reported MCU damage caused by this (unwise) 0Ω (pin to pin) board default!

  • Hello cb1,

    That is why I carefully did mention that it is TM4C129. But a good point indeed. The 0Ohm short between PB6-PD0 and PB7-PD1 must be broken.

    Regards
    Amit
  • Jordi Estivill-Dredge said:
    So I am using a tm4c123GXL

    Hi Amit,

    I saw this - atop poster's "opening!"

    And - as we know - that LPad is burdened with those (always) delightful 0Ω'ers...

  • Hi Amit,

    I modified the file to be the following but I keep getting back

    Command returned error code FR_NOT_READY

    These are the lines I changed in  mmc-dk-tm4c123g.c

    /* Peripheral definitions for DK-TM4C123G board */
    // SSI port
    #define SDC_SSI_BASE            SSI2_BASE // 0
    #define SDC_SSI_SYSCTL_PERIPH   SYSCTL_PERIPH_SSI2 // 0
    
    // GPIO for SSI pins
    #define SDC_GPIO_PORT_BASE      GPIO_PORTB_BASE // A
    #define SDC_CS_PORT_BASE        GPIO_PORTA_BASE // Was not here
    #define SDC_GPIO_SYSCTL_PERIPH  SYSCTL_PERIPH_GPIOB
    #define SDC_SSI_CLK             GPIO_PIN_4 // 2
    #define SDC_SSI_TX              GPIO_PIN_7 // 5
    #define SDC_SSI_RX              GPIO_PIN_6 // 4
    #define SDC_SSI_FSS             GPIO_PIN_3 // 3 (Port A though)
    
    **************************************************************
    // asserts the CS pin to the card
    static
    void SELECT (void)
    {
        ROM_GPIOPinWrite(SDC_CS_PORT_BASE, SDC_SSI_FSS, 0);
    }
    
    // de-asserts the CS pin to the card
    static
    void DESELECT (void)
    {
        ROM_GPIOPinWrite(SDC_CS_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS);
    }
    **************************************************************
    static
    void power_on (void)
    {
        /*
         * This doesn't really turn the power on, but initializes the
         * SSI port and pins needed to talk to the card.
         */
    
        /* Enable the peripherals used to drive the SDC on SSI */
        ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH);
        ROM_SysCtlPeripheralEnable(SDC_GPIO_SYSCTL_PERIPH);
    
        /*
         * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS)
         * signal is directly driven to ensure that we can hold it low through a
         * complete transaction with the SD card.
         */
        ROM_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK);
        ROM_GPIOPinTypeGPIOOutput(SDC_CS_PORT_BASE, SDC_SSI_FSS);
    
        /*
         * Set the SSI output pins to 4MA drive strength and engage the
         * pull-up on the receive line.
         */
        MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_RX, GPIO_STRENGTH_4MA,
                             GPIO_PIN_TYPE_STD_WPU);
        MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_CLK | SDC_SSI_TX,
                             GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
        /* vvvvvvvvvvvvvvvvvvvv*/
        MAP_GPIOPadConfigSet(SDC_CS_PORT_BASE,  SDC_SSI_FSS,
                             GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
        /* ^^^^^^^^^^^^^^^^^^^ */
    
        /* Configure the SSI0 port */
        ROM_SSIConfigSetExpClk(SDC_SSI_BASE, ROM_SysCtlClockGet(),
                               SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
        ROM_SSIEnable(SDC_SSI_BASE);
    
        /* 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();
    
        PowerFlag = 1;
    }
    

    I haven't changed anything else. Any hints as to what I might be missing? I noticed that the setup for my previous SPI device looks like this:

    void setSpeed(u_char device)
    {
    	switch(device)
    		{
    		case LCD_SPI:
    			MAP_SSIConfigSetExpClk (SSI2_BASE, MAP_SysCtlClockGet (),
    			                        SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, LCD_DISPLAY_SPEED, 8);
    			break;
    
    		case SDC_SPI:
    			MAP_SSIConfigSetExpClk (SSI2_BASE, MAP_SysCtlClockGet (),
    			                        SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SD_CARD_SPEED, 8);
    			break;
    		default :
    			break;
    		}
    
    	MAP_SSIEnable (SSI2_BASE);
    }
    
    void initTiva()
    {
    	// run @80MHz, use 16MHz xtal
    	MAP_SysCtlClockSet (
    	    SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN
    	    | SYSCTL_XTAL_16MHZ);
    	MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOB);
    	MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOE);
    	MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_SSI2);
    	MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOA);
    	MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, LCD_CS_PIN); // LCD CS Pin is A4
    	MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, SD_CS_PIN); // SD Card is A3
    	MAP_GPIOPinTypeGPIOOutput (GPIO_PORTA_BASE, NRF_CS_PIN); // NRF is A2
    	MAP_GPIOPinTypeGPIOOutput (GPIO_PORTE_BASE, LCD_DC_PIN);
    	MAP_GPIOPinConfigure (GPIO_PB4_SSI2CLK);
    	MAP_GPIOPinTypeSSI (GPIO_PORTB_BASE, GPIO_PIN_4);
    	MAP_GPIOPinConfigure (GPIO_PB7_SSI2TX);
    	MAP_GPIOPinTypeSSI (GPIO_PORTB_BASE, GPIO_PIN_7);
    	// Configure Clocks 16000000
    	setSpeed(LCD_SPI);
    }

    Should I be using MAP_ rather than ROM_ ?

  • Hello Jordi

    What about SSI2 RX Pin?

    EIDT: Where has that been configured for SSI2 using GPIOPinConfigure

    Regards
    Amit

  • I have added it in here but now it sits forever when I enter "ls"

    /* Peripheral definitions for DK-TM4C123G board */ 
    // These were taken from the ek-tm4c123g heaer file. #define GPIO_PB7_SSI2TX 0x00011C02 #define GPIO_P67_SSI2RX 0x00011802 // SSI port #define SDC_SSI_BASE SSI2_BASE // 0 #define SDC_SSI_SYSCTL_PERIPH SYSCTL_PERIPH_SSI2 // 0 // GPIO for SSI pins #define SDC_GPIO_PORT_BASE GPIO_PORTB_BASE // A #define SDC_CS_PORT_BASE GPIO_PORTA_BASE // Was not here #define SDC_GPIO_SYSCTL_PERIPH SYSCTL_PERIPH_GPIOB #define SDC_SSI_CLK GPIO_PIN_4 // 2 #define SDC_SSI_TX GPIO_PIN_7 // 5 #define SDC_SSI_RX GPIO_PIN_6 // 4 #define SDC_SSI_FSS GPIO_PIN_3 // 3 (Port A though) // asserts the CS pin to the card static void SELECT (void) { MAP_GPIOPinWrite(SDC_CS_PORT_BASE, SDC_SSI_FSS, 0); } // de-asserts the CS pin to the card static void DESELECT (void) { MAP_GPIOPinWrite(SDC_CS_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS); } static void xmit_spi(BYTE dat) { uint32_t ui32RcvDat; MAP_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */ MAP_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */ } /*-----------------------------------------------------------------------*/ /* Receive a byte from MMC via SPI (Platform dependent) */ /*-----------------------------------------------------------------------*/ static BYTE rcvr_spi (void) { uint32_t ui32RcvDat; MAP_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */ MAP_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */ return (BYTE)ui32RcvDat; } static void send_initial_clock_train(void) { unsigned int i; uint32_t ui32Dat; /* Ensure CS is held high. */ DESELECT(); /* Switch the SSI TX line to a GPIO and drive it high too. */ MAP_GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_TX); MAP_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX); /* Send 10 bytes over the SSI. This causes the clock to wiggle the */ /* required number of times. */ for(i = 0 ; i < 10 ; i++) { /* Write DUMMY data. SSIDataPut() waits until there is room in the */ /* FIFO. */ MAP_SSIDataPut(SDC_SSI_BASE, 0xFF); /* Flush data read during data write. */ MAP_SSIDataGet(SDC_SSI_BASE, &ui32Dat); } /* Revert to hardware control of the SSI TX line. */ MAP_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX); } // set the SSI speed to the max setting static void set_max_speed(void) { unsigned long i; /* Disable the SSI */ MAP_SSIDisable(SDC_SSI_BASE); /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */ i = MAP_SysCtlClockGet() / 2; if(i > 12500000) { i = 12500000; } /* Configure the SSI0 port to run at 12.5MHz */ MAP_SSIConfigSetExpClk(SDC_SSI_BASE, MAP_SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8); /* Enable the SSI */ //ROM_SSIEnable(SDC_SSI_BASE); MAP_SSIEnable (SSI2_BASE); } static void power_on (void) { /* * This doesn't really turn the power on, but initializes the * SSI port and pins needed to talk to the card. */ MAP_SysCtlPeripheralEnable (SDC_GPIO_PORT_BASE); MAP_SysCtlPeripheralEnable (SDC_CS_PORT_BASE); MAP_GPIOPinTypeGPIOOutput (SDC_CS_PORT_BASE, SDC_SSI_FSS); // SD Card is A3 MAP_GPIOPinConfigure (GPIO_PB4_SSI2CLK); MAP_GPIOPinTypeSSI (SDC_GPIO_PORT_BASE, SDC_SSI_CLK); MAP_GPIOPinConfigure (GPIO_PB7_SSI2TX); MAP_GPIOPinConfigure (GPIO_P67_SSI2RX); MAP_GPIOPinTypeSSI (SDC_GPIO_PORT_BASE, SDC_SSI_TX); MAP_GPIOPinTypeSSI (SDC_GPIO_PORT_BASE, SDC_SSI_RX); MAP_SSIConfigSetExpClk (SSI2_BASE, MAP_SysCtlClockGet (), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8); MAP_SSIEnable (SSI2_BASE); /* 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(); PowerFlag = 1; }

    Any ideas? I swapped a lot of those to MAP since that it was I had in a previous project with this SPI2.

  • Hello Jordi,

    Seems I need to build it as well. Should be able to test it out tomorrow.

    BTW, did you check Chester's first post where he did mention that you have to remove the 0Ohm resistor on the EK-TM4C123GXL?

    Regards
    Amit
  • I noticed his post. But I am not sure what i need to do about that.

    I have attached a zip of my project.3872.sd_card.zip

  • Hello Jordi,

    Remove the 0Ohm from the board :-)

    BTW how are you connecting the SDCard. Do you have a Booster Pack?

    Regards
    Amit
  • HI Amit,

    I have an ST7735R with built in SD card.
    This in particular. : www.adafruit.com/.../358

    Cheers,
    Jordi
  • Hi Amit,

    It seems I managed to get it going by modifying the mmc-tm4c123g.c file to be the following:

    /*-----------------------------------------------------------------------*/
    /* 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 an DK-TM4C123G development
     * board.
     */
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/tm4c123gh6pm.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/ssi.h"
    #include "driverlib/sysctl.h"
    #include "fatfs/src/diskio.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 */
    
    /* Peripheral definitions for DK-TM4C123G board */
    
    // Jordi
    /*****************************************************/
    #define GPIO_PB7_SSI2TX         0x00011C02
    #define GPIO_PB6_SSI2RX         0x00011802
    #define GPIO_PB4_SSI2CLK        0x00011002
    
    #define TIVA_SSI_BASE SSI2_BASE
    #define TIVA_SSI_SYSCTL_PERIPH  	SYSCTL_PERIPH_SSI2
    #define TIVA_CS_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOA
    #define TIVA_DC_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOE
    #define TIVA_GPIO_SYSCTL_PERIPH		SYSCTL_PERIPH_GPIOB
    
    #define TIVA_GPIO_PORT_BASE GPIO_PORTB_BASE
    #define TIVA_CS_PORT_BASE GPIO_PORTA_BASE
    #define TIVA_DC_PORT_BASE GPIO_PORTE_BASE
    
    // clock
    #define LCD_SCLK_PIN GPIO_PIN_4
    // data out
    #define LCD_MOSI_PIN GPIO_PIN_7
    #define LCD_MISO_PIN GPIO_PIN_6
    
    // chip select
    #define LCD_CS_PIN GPIO_PIN_4
    #define SD_CS_PIN GPIO_PIN_3
    #define NRF_CS_PIN GPIO_PIN_2
    // And ro patch
    #define LCD_SELECT GPIO_PORTA_DATA_R = ~LCD_CS_PIN
    #define LCD_DESELECT GPIO_PORTA_DATA_R |= LCD_CS_PIN
    #define SDC_SELECT GPIO_PORTA_DATA_R = ~SD_CS_PIN
    #define SDC_DESELECT GPIO_PORTA_DATA_R |= SD_CS_PIN
    #define NRF_SELECT GPIO_PORTA_DATA_R = ~NRF_CS_PIN
    #define NRF_DESELECT GPIO_PORTA_DATA_R |= NRF_CS_PIN
    // data/control
    #define LCD_DC_PIN GPIO_PIN_4
    #define LCD_DC_CMD GPIO_PORTE_DATA_R = ~LCD_DC_PIN
    #define LCD_DC_DATA GPIO_PORTE_DATA_R |= LCD_DC_PIN
    /*****************************************************/
    
    
    // asserts the CS pin to the card
    static
    void SELECT (void)
    {
      SDC_SELECT;
    }
    
    // de-asserts the CS pin to the card
    static
    void DESELECT (void)
    {
      SDC_DESELECT;
    }
    
    /*--------------------------------------------------------------------------
    
       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)
    {
        uint32_t ui32RcvDat;
    
        ROM_SSIDataPut(TIVA_SSI_BASE, dat); /* Write the data to the tx fifo */
    
        ROM_SSIDataGet(TIVA_SSI_BASE, &ui32RcvDat); /* flush data read during the write */
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a byte from MMC via SPI  (Platform dependent)                 */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE rcvr_spi (void)
    {
        uint32_t ui32RcvDat;
    
        ROM_SSIDataPut(TIVA_SSI_BASE, 0xFF); /* write dummy data */
    
        ROM_SSIDataGet(TIVA_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */
    
        return (BYTE)ui32RcvDat;
    }
    
    
    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();
        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)
    {
        unsigned int i;
        uint32_t ui32Dat;
    
        /* Ensure CS is held high. */
        DESELECT();
    
        /* Switch the SSI TX line to a GPIO and drive it high too. */
        ROM_GPIOPinTypeGPIOOutput(TIVA_GPIO_PORT_BASE, LCD_MOSI_PIN);
    
        GPIO_PORTB_DATA_R &=  LCD_MOSI_PIN;
    
        /* Send 10 bytes over the SSI. This causes the clock to wiggle the */
        /* required number of times. */
        for(i = 0 ; i < 10 ; i++)
        {
            /* Write DUMMY data. SSIDataPut() waits until there is room in the */
            /* FIFO. */
            ROM_SSIDataPut(TIVA_SSI_BASE, 0xFF);
    
            /* Flush data read during data write. */
            ROM_SSIDataGet(TIVA_SSI_BASE, &ui32Dat);
        }
    
        /* Revert to hardware control of the SSI TX line. */
        ROM_GPIOPinTypeSSI(TIVA_GPIO_PORT_BASE, LCD_MOSI_PIN);
    }
    
    /*-----------------------------------------------------------------------*/
    /* 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)
    {
    	ROM_SysCtlPeripheralEnable (TIVA_GPIO_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_DC_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_SSI_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_CS_SYSCTL_PERIPH);
    
      	ROM_GPIOPinConfigure (GPIO_PB4_SSI2CLK);
      	ROM_GPIOPinConfigure (GPIO_PB7_SSI2TX);
      	ROM_GPIOPinConfigure(GPIO_PB6_SSI2RX);
    
      	ROM_GPIOPinTypeSSI(TIVA_GPIO_PORT_BASE, LCD_SCLK_PIN | LCD_MISO_PIN | LCD_MOSI_PIN);
      	ROM_GPIOPinTypeGPIOOutput(TIVA_CS_PORT_BASE, LCD_CS_PIN | NRF_CS_PIN | SD_CS_PIN);
      	ROM_GPIOPinTypeGPIOOutput(TIVA_DC_PORT_BASE, LCD_DC_PIN);
    
      	 MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_MISO_PIN, GPIO_STRENGTH_4MA,
      	                         GPIO_PIN_TYPE_STD_WPU);
    	MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_SCLK_PIN | LCD_MOSI_PIN,
    			     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    	MAP_GPIOPadConfigSet(TIVA_CS_PORT_BASE,  SD_CS_PIN,
    			     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    
      	ROM_SSIConfigSetExpClk (TIVA_SSI_BASE, ROM_SysCtlClockGet (),
      				                        SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
    	ROM_SSIEnable (TIVA_SSI_BASE);
    
    
        /* 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();
    
        PowerFlag = 1;
    }
    
    // set the SSI speed to the max setting
    static
    void set_max_speed(void)
    {
        unsigned long i;
    
        /* Disable the SSI */
        ROM_SSIDisable(TIVA_SSI_BASE);
    
        /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */
        i = ROM_SysCtlClockGet() / 2;
        if(i > 12500000)
        {
            i = 12500000;
        }
    
        /* Configure the SSI0 port to run at 12.5MHz */
        ROM_SSIConfigSetExpClk(TIVA_SSI_BASE, ROM_SysCtlClockGet(),
                               SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8);
    
        /* Enable the SSI */
        ROM_SSIEnable(TIVA_SSI_BASE);
    }
    
    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 = 100;
        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 = 0;
            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 = 0xff;
        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();            /* Ensure the card is in SPI mode */
    
        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 nmuber (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    ((2007UL-1980) << 25)    // Year = 2007
                | (6UL << 21)            // Month = June
                | (5UL << 16)            // Day = 5
                | (11U << 11)            // Hour = 11
                | (38U << 5)            // Min = 38
                | (0U >> 1)                // Sec = 0
                ;
    
    }
    

  • Hello Jordi,

    That is good. Since the file is large (and I did not get to comparing it), can you let us know what the issue was in the end and how you got to isolate it (I could put this up as a reference goto)?

    Regards
    Amit
  • The process was changing everything bit by bit and seeing what the result was. 

    I swapped all the definitions of the pins and their GPIO configure. Like this:

    #define TIVA_SSI_BASE 				SSI2_BASE
    #define TIVA_SSI_SYSCTL_PERIPH  	SYSCTL_PERIPH_SSI2
    #define TIVA_CS_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOA
    #define TIVA_DC_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOE
    #define TIVA_GPIO_SYSCTL_PERIPH		SYSCTL_PERIPH_GPIOB
    
    #define TIVA_GPIO_PORT_BASE GPIO_PORTB_BASE
    #define TIVA_CS_PORT_BASE GPIO_PORTA_BASE
    #define TIVA_DC_PORT_BASE GPIO_PORTE_BASE
    
    // clock
    #define LCD_SCLK_PIN GPIO_PIN_4
    // data out
    #define LCD_MOSI_PIN GPIO_PIN_7
    #define LCD_MISO_PIN GPIO_PIN_6
    
    // chip select
    #define LCD_CS_PIN GPIO_PIN_4
    #define SD_CS_PIN GPIO_PIN_3
    #define NRF_CS_PIN GPIO_PIN_2
    /*******************************************************/
    static
    void power_on (void)
    {
    	ROM_SysCtlPeripheralEnable (TIVA_GPIO_SYSCTL_PERIPH);
    	ROM_SysCtlPeripheralEnable (TIVA_DC_SYSCTL_PERIPH);
    	ROM_SysCtlPeripheralEnable (TIVA_SSI_SYSCTL_PERIPH);
    	ROM_SysCtlPeripheralEnable (TIVA_CS_SYSCTL_PERIPH);
    	ROM_GPIOPinConfigure (GPIO_PB4_SSI2CLK);
    	ROM_GPIOPinConfigure (GPIO_PB7_SSI2TX);
    	ROM_GPIOPinConfigure(GPIO_PB6_SSI2RX);
    	ROM_GPIOPinTypeSSI(TIVA_GPIO_PORT_BASE,
    	                   LCD_SCLK_PIN | LCD_MISO_PIN | LCD_MOSI_PIN);
    	ROM_GPIOPinTypeGPIOOutput(TIVA_CS_PORT_BASE,
    	                          LCD_CS_PIN | NRF_CS_PIN | SD_CS_PIN);
    	ROM_GPIOPinTypeGPIOOutput(TIVA_DC_PORT_BASE, LCD_DC_PIN);
    	MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_MISO_PIN, GPIO_STRENGTH_4MA,
    	                     GPIO_PIN_TYPE_STD_WPU);
    	MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_SCLK_PIN | LCD_MOSI_PIN,
    	                     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    	MAP_GPIOPadConfigSet(TIVA_CS_PORT_BASE,  SD_CS_PIN,
    	                     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    	ROM_SSIConfigSetExpClk (TIVA_SSI_BASE, ROM_SysCtlClockGet (),
    	                        SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
    	ROM_SSIEnable (TIVA_SSI_BASE);
    	/* 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();
    	PowerFlag = 1;
    }
    

    But I kept getting back FR_NOT_READY. I figured, the SPI must be working a little, but something is a little off. The key difference between the example and what i was doing is that the CS pin is on different ports (and a different position) to the example. For example, you can do this:

    ROM_GPIOPinConfigure (GPIO_PB5_SSI2FSS);

    But not this:

    ROM_GPIOPinConfigure (GPIO_PA3_SSI2FSS);

    So I looked at the SSI enable and disable for the LCD display I had. I noticed the Select and Deselect commands where very different.

    //*********************************************
    // The Following is used in the SD Card Example
    //*********************************************
    // asserts the CS pin to the card
    static
    void SELECT (void)
    {
        ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, 0);
    }
    
    // de-asserts the CS pin to the card
    static
    void DESELECT (void)
    {
        ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS);
    }
    
    // asserts the CS pin to the card
    static
    void SELECT (void)
    {
    	SDC_SELECT;
    }
    //*********************************************
    // The Following is used by the LCD screem
    //*********************************************
    #define SDC_SELECT        GPIO_PORTA_DATA_R = ~SD_CS_PIN
    #define SDC_DESELECT      GPIO_PORTA_DATA_R |= SD_CS_PIN
    // de-asserts the CS pin to the card
    static
    void DESELECT (void)
    {
    	SDC_DESELECT;
    }

    So I swapped the Select and Deselect commands to write directly to the register and it worked.

  • For reference, I have attached by project folder and a copy of the mm-tm4c123g.c file

    /*-----------------------------------------------------------------------*/
    /* 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 an DK-TM4C123G development
     * board.
     */
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/tm4c123gh6pm.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/ssi.h"
    #include "driverlib/sysctl.h"
    #include "fatfs/src/diskio.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 */
    
    /* Peripheral definitions for DK-TM4C123G board */
    
    // Jordi
    /*****************************************************/
    #define GPIO_PB7_SSI2TX         0x00011C02
    #define GPIO_PB6_SSI2RX         0x00011802
    #define GPIO_PB4_SSI2CLK        0x00011002
    
    #define TIVA_SSI_BASE SSI2_BASE
    #define TIVA_SSI_SYSCTL_PERIPH  	SYSCTL_PERIPH_SSI2
    #define TIVA_CS_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOA
    #define TIVA_DC_SYSCTL_PERIPH   	SYSCTL_PERIPH_GPIOE
    #define TIVA_GPIO_SYSCTL_PERIPH		SYSCTL_PERIPH_GPIOB
    
    #define TIVA_GPIO_PORT_BASE GPIO_PORTB_BASE
    #define TIVA_CS_PORT_BASE GPIO_PORTA_BASE
    #define TIVA_DC_PORT_BASE GPIO_PORTE_BASE
    
    // clock
    #define LCD_SCLK_PIN GPIO_PIN_4
    // data out
    #define LCD_MOSI_PIN GPIO_PIN_7
    #define LCD_MISO_PIN GPIO_PIN_6
    
    // chip select
    #define LCD_CS_PIN GPIO_PIN_4
    #define SD_CS_PIN GPIO_PIN_3
    #define NRF_CS_PIN GPIO_PIN_2
    // And ro patch
    #define LCD_SELECT GPIO_PORTA_DATA_R = ~LCD_CS_PIN
    #define LCD_DESELECT GPIO_PORTA_DATA_R |= LCD_CS_PIN
    #define SDC_SELECT GPIO_PORTA_DATA_R = ~SD_CS_PIN
    #define SDC_DESELECT GPIO_PORTA_DATA_R |= SD_CS_PIN
    #define NRF_SELECT GPIO_PORTA_DATA_R = ~NRF_CS_PIN
    #define NRF_DESELECT GPIO_PORTA_DATA_R |= NRF_CS_PIN
    // data/control
    #define LCD_DC_PIN GPIO_PIN_4
    #define LCD_DC_CMD GPIO_PORTE_DATA_R = ~LCD_DC_PIN
    #define LCD_DC_DATA GPIO_PORTE_DATA_R |= LCD_DC_PIN
    /*****************************************************/
    
    
    // asserts the CS pin to the card
    static
    void SELECT (void)
    {
      SDC_SELECT;
    }
    
    // de-asserts the CS pin to the card
    static
    void DESELECT (void)
    {
      SDC_DESELECT;
    }
    
    /*--------------------------------------------------------------------------
    
       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)
    {
        uint32_t ui32RcvDat;
    
        ROM_SSIDataPut(TIVA_SSI_BASE, dat); /* Write the data to the tx fifo */
    
        ROM_SSIDataGet(TIVA_SSI_BASE, &ui32RcvDat); /* flush data read during the write */
    }
    
    
    /*-----------------------------------------------------------------------*/
    /* Receive a byte from MMC via SPI  (Platform dependent)                 */
    /*-----------------------------------------------------------------------*/
    
    static
    BYTE rcvr_spi (void)
    {
        uint32_t ui32RcvDat;
    
        ROM_SSIDataPut(TIVA_SSI_BASE, 0xFF); /* write dummy data */
    
        ROM_SSIDataGet(TIVA_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */
    
        return (BYTE)ui32RcvDat;
    }
    
    
    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();
        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)
    {
        unsigned int i;
        uint32_t ui32Dat;
    
        /* Ensure CS is held high. */
        DESELECT();
    
        /* Switch the SSI TX line to a GPIO and drive it high too. */
        ROM_GPIOPinTypeGPIOOutput(TIVA_GPIO_PORT_BASE, LCD_MOSI_PIN);
    
        GPIO_PORTB_DATA_R &=  LCD_MOSI_PIN;
    
        /* Send 10 bytes over the SSI. This causes the clock to wiggle the */
        /* required number of times. */
        for(i = 0 ; i < 10 ; i++)
        {
            /* Write DUMMY data. SSIDataPut() waits until there is room in the */
            /* FIFO. */
            ROM_SSIDataPut(TIVA_SSI_BASE, 0xFF);
    
            /* Flush data read during data write. */
            ROM_SSIDataGet(TIVA_SSI_BASE, &ui32Dat);
        }
    
        /* Revert to hardware control of the SSI TX line. */
        ROM_GPIOPinTypeSSI(TIVA_GPIO_PORT_BASE, LCD_MOSI_PIN);
    }
    
    /*-----------------------------------------------------------------------*/
    /* 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)
    {
    	ROM_SysCtlPeripheralEnable (TIVA_GPIO_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_DC_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_SSI_SYSCTL_PERIPH);
      	ROM_SysCtlPeripheralEnable (TIVA_CS_SYSCTL_PERIPH);
    
      	ROM_GPIOPinConfigure (GPIO_PB4_SSI2CLK);
      	ROM_GPIOPinConfigure (GPIO_PB7_SSI2TX);
      	ROM_GPIOPinConfigure(GPIO_PB6_SSI2RX);
    
      	ROM_GPIOPinTypeSSI(TIVA_GPIO_PORT_BASE, LCD_SCLK_PIN | LCD_MISO_PIN | LCD_MOSI_PIN);
      	ROM_GPIOPinTypeGPIOOutput(TIVA_CS_PORT_BASE, LCD_CS_PIN | NRF_CS_PIN | SD_CS_PIN);
      	ROM_GPIOPinTypeGPIOOutput(TIVA_DC_PORT_BASE, LCD_DC_PIN);
    
      	 MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_MISO_PIN, GPIO_STRENGTH_4MA,
      	                         GPIO_PIN_TYPE_STD_WPU);
    	MAP_GPIOPadConfigSet(TIVA_GPIO_PORT_BASE, LCD_SCLK_PIN | LCD_MOSI_PIN,
    			     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    	MAP_GPIOPadConfigSet(TIVA_CS_PORT_BASE,  SD_CS_PIN,
    			     GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    
      	ROM_SSIConfigSetExpClk (TIVA_SSI_BASE, ROM_SysCtlClockGet (),
      				                        SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
    	ROM_SSIEnable (TIVA_SSI_BASE);
    
    
        /* 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();
    
        PowerFlag = 1;
    }
    
    // set the SSI speed to the max setting
    static
    void set_max_speed(void)
    {
        unsigned long i;
    
        /* Disable the SSI */
        ROM_SSIDisable(TIVA_SSI_BASE);
    
        /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */
        i = ROM_SysCtlClockGet() / 2;
        if(i > 12500000)
        {
            i = 12500000;
        }
    
        /* Configure the SSI0 port to run at 12.5MHz */
        ROM_SSIConfigSetExpClk(TIVA_SSI_BASE, ROM_SysCtlClockGet(),
                               SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8);
    
        /* Enable the SSI */
        ROM_SSIEnable(TIVA_SSI_BASE);
    }
    
    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 = 100;
        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 = 0;
            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 = 0xff;
        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();            /* Ensure the card is in SPI mode */
    
        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 nmuber (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    ((2007UL-1980) << 25)    // Year = 2007
                | (6UL << 21)            // Month = June
                | (5UL << 16)            // Day = 5
                | (11U << 11)            // Hour = 11
                | (38U << 5)            // Min = 38
                | (0U >> 1)                // Sec = 0
                ;
    
    }
    

    sd_card_ssi2.zip