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.

TM4C123GH6PM: How to enable the SSI Clock without touching the common FIFO?

Part Number: TM4C123GH6PM

I am communicating with an SD card via the SPI protocol through the SSI interface.

After I send my command frame to the SD card via the MOSI line, I need to provide an additional 16 clock cycles so that the SD card can send a response byte back via the MISO line. 

So far, I have been making those 16 additional clock cycles by transmitting two bytes of 0xFF.

The problem is, since the TM4C123GH6PM uses a common FIFO for transmitting and receiving, the data I intend to receive is always overwritten by the transmitted 0xFF. Every time I attempt to store the FIFO content into another buffer, I store the 0xFF that I am transmitting. 

So, my question is: How can I provide those additional 16 clock cycles without the TM4C123GH6PM putting data into the FIFO?

See attached image. CSCLKMISO, MOSIThe cyan MISO line contains the data (0x01) that I want to store, but it is always overwritten by the yellow MOSI line's data (0xFF).


  • There are actually separate transmit and receive FIFOs. The nature of the SSI (or SPI) is that for every bit transmitted, a bit is received. Have you read all of the bytes out of the receive FIFO? Did you send any bytes other than the two bytes of 0xFF? I looked at the code you posted earlier in this thread, and did not see your calls to SSIDataGet() after your two calls to SSIDataPut();

    https://e2e.ti.com/support/microcontrollers/other/f/908/t/868555

  • Hi Bob,

    I am very grateful for your continued help to a young rookie. I have made additions to my code since our last correspondence. 

    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/ssi.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    #include "console.h"
    
    void SSI2Init(void) {
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
        GPIOPinConfigure(GPIO_PB4_SSI2CLK);
        GPIOPinConfigure(GPIO_PB6_SSI2RX);
        GPIOPinConfigure(GPIO_PB7_SSI2TX);
    
        GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_6 |
                       GPIO_PIN_7);
    
        SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER, 350000, 8);
    
        SSIEnable(SSI2_BASE);
    
        uint32_t RxBuffer;
        // Flush receive FIFO buffer of any residual data and discard it
        while (SSIDataGetNonBlocking(SSI2_BASE, &RxBuffer)) {
        }
    
        GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_5);
        GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_5, GPIO_PIN_5);
    
        return;
    }
    
    void ClockResponse(uint32_t ui32Base, uint32_t TxByte) {
        SSIDataPut(SSI2_BASE, TxByte);
        SSIDataPut(SSI2_BASE, TxByte);
    }
    
    void SDCardInit(void) {
        // 1ms wait after SD card receives power
        SysCtlDelay(160000);
    
        uint32_t TxBuffer = 0xFF;
        uint32_t RxBuffer;
    
    //     80 (more than 74) dummy clock pulses
        uint8_t i;
        for (i = 0; i < 10; i++) {
            SSIDataPut(SSI2_BASE, TxBuffer);
        }
    
        // Wait until SSI transmitter is no longer busy
        while (SSIBusy(SSI2_BASE)) {
        }
    
        GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_5, 0);
    
        // Send CMD0 with CRC
        TxBuffer = 0x40;
        SSIDataPut(SSI2_BASE, TxBuffer);
        TxBuffer = 0x00;
        SSIDataPut(SSI2_BASE, TxBuffer);
        SSIDataPut(SSI2_BASE, TxBuffer);
        SSIDataPut(SSI2_BASE, TxBuffer);
        SSIDataPut(SSI2_BASE, TxBuffer);
        TxBuffer = 0x95;
        SSIDataPut(SSI2_BASE, TxBuffer);
    
        // Read out all unwanted data from receive FIFO until it is empty
        while (SSIDataGetNonBlocking(SSI2_BASE, &RxBuffer)) {
        }
    
        // Send 0x00 to provide clock to the response byte
        TxBuffer = 0x00;
        ClockResponse(SSI2_BASE, TxBuffer);
        // Keep reading the receive FIFO until it is empty.
        // The desired last byte of data should now be in RxBuffer
        while (SSIDataGetNonBlocking(SSI2_BASE, &RxBuffer)) {
        }
        UARTprintf("Received: %u\n", RxBuffer);
    
        // Wait until SSI transmitter is no longer busy
        while (SSIBusy(SSI2_BASE)) {
        }
    
    //    GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_5, GPIO_PIN_5);
    
    }
    
    int main(void) {
        InitConsole();
    
        UARTprintf("SysCtlClockGet(): %u\n", SysCtlClockGet());
    
        SSI2Init();
    
        SDCardInit();
    
        while (1) {
    
        }
    }
    

    I've also attached a zoomed-out view of my captured waveform for your reference. The last two sets of 8 clock cycles are necessary for the SD card to send back the 0x01 that I am trying to capture in RxBuffer. 

    But after the execution of the aforementioned code, I am still getting 255 in my RxBuffer instead of the desired 1.

  • What is happening is that you are not properly flushing the RX buffer. You have loaded the TX buffer and then do:

        // Read out all unwanted data from receive FIFO until it is empty
        while (SSIDataGetNonBlocking(SSI2_BASE, &RxBuffer)) {
        }
    

    The problem is that the while loop executes much faster than the SSI transmission. Perhaps one or two bytes have been transmitted and the same number received, but while the third byte is transmitting, the loop sees the RX buffer as empty and exits the loop.

    The simplest solution is to pair every SSIDataPut() with a blocking SSIDataGet(). That way you make sure the RX buffer is empty before you start the next transmission. It has the added benefit that you no longer need to call SSIBusy() because the SSIDataGet() function will not return until the SSI transmission has completed.

    Alternatively you can do the while(SSIBusy()) loop before you do the SSIDataGetNonBlocking() loop. You need to be careful that you don't overflow the RX FIFO. I don't see any real advantage in this method.

     

  • Greetings Bob,

    Nicely done - and might there be a, 'Teaching Lesson' here - which (even) may qualify to, 'Make it into the 'promised/teased' API and/or TivaWare Update?'

    To staff & my knowledge your keen poster guidance, "The problem is that the while loop executes much faster than the SSI transmission" does not appear (at least in an easily recognizable/notable form) elsewhere!     Minus that specific guidance - 'others' may, 'Fall into the same trap as this poster.'

    Indeed - as you further note - the simplest solution is the, 'Pairing of SSIDataPut() & SSIDataGet().'     Yet - that 'pairing' will cause (some) 'stutter effect' between 'cascaded' calls to SSIDataPut() - which are 'increasingly' noted (right here) recently.

    You provide the alternative method employing, "while (SSIBusy() loop" prior to the call to "SSIDataGetNonBlocking() loop."    Does not this 'alternate method' better (or even uniquely) enable the use of 'Back-to-Back' calls to, SSIDataPut() - which tends to, 'Escape the stutter effect imposed upon SPI_Clock?'     (and thus may 'distress' the connected Slave SPI Device)     And - if these 'multiple, back-to-back calls' to SSIDataPut() prove legal - may we (and others) then follow (those 'puts') w/'multiple, back-to-back calls' to SSIDataGet() ?     (while avoiding the 'overflow of the SPI's RX FIFO.)

    As always - thanks for your keenness, time & great attention...

    Finally - is poster's SD Card 'sufficiently standardized' that its 'specification' (timing chart in particular) need not be provided and/or linked?    The MCU (always) must 'adapt' its SPI behavior to that of the SPI Slave - thus withholding the SD Card's data (timing chart) may (not) prove in poster's best interest...