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.

EK-TM4C1294XL: trouble with SPI-read - erratic behavior of the uC, signal on scope seems okay

Part Number: EK-TM4C1294XL

Dear Team,

I'm running into difficulties writing an SPI-Driver for an Analog Devices ADE7913 ADC. Reading the value from the SSI-resource yields strange behavior that I can't explain, yet the scope shows the expected value being written on the MISO line. I'll first go through the SW-steps, describe the problem including some screenshots, and will then provide a minimal working example. Any help is very appreciated!

Here's what the code does:

1. Issue a write command to the ADC, specifying the register we want to write to: 0x70

2. Send the value we want written into the register: 0xFF (I have tried other values, as well. The behavior is identical.)

3. Issue a read command to the ADC, specifying the register we want to read from (the same as was written before): 0x74

4. Execute a dummy write in order for the CS and SPI-clock-signals to be on the line: 0x00

5. Execute a read-command on the Tiva and write the data inside the FIFO into a variable

6. Print the variable and the status of the read command to the console

7. Restart loop at 1

As you can see on the scope-screenshot, the ADC sends the correct value in response to the read-command - The MISO-line is clearly receiving 0xFF:

However, the Tiva only reads the value correctly during the pass 4 and pass 8 of the loop (255 == 0xFF), and subsequently doesn't anymore:

Here's the code:

#include <iostream>
extern "C"
{
#include <stdint.h>
#include <stdio.h>
#include "inc/hw_memmap.h"
#include "inc/hw_ssi.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/ssi.h"
}


//*****************************************************************************
//
// System clock rate in Hz.
//
//*****************************************************************************
uint32_t g_ui32SysClock;

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif


// global constants
constexpr uint32_t ssiBitsPerSecond = 250'000;
constexpr uint32_t ssiDataWidthInBits = 8; // must be between 4 and 16


// global variables
int SSIreadStatus = -1;
uint32_t data = 0xffffffff;


// functions
void spiInit(void)
{
    // Enable SSI-Peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);

    // Enable GPIO-Ports
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    // Configure GPIO Pin Mux for SSI1
    GPIOPinConfigure(GPIO_PB5_SSI1CLK);
    GPIOPinConfigure(GPIO_PB4_SSI1FSS);
    GPIOPinConfigure(GPIO_PE4_SSI1XDAT0);
    GPIOPinConfigure(GPIO_PE5_SSI1XDAT1);

    // Configure the GPIO settings for the SSI pins. This function also relays
    // control of these pins to the SSI hardware.
    GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    GPIOPinTypeSSI(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    // Configure and enable SSI1 port for SPI master mode
    SSIConfigSetExpClk(SSI1_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_3,
                       SSI_MODE_MASTER, ssiBitsPerSecond, ssiDataWidthInBits);

    // Enable modules SSI0, SSI1, SSI3.
    SSIEnable(SSI1_BASE);
}



int main(void)
{
    // Run from the PLL at 120 MHz.
    // Note: SYSCTL_CFG_VCO_240 is a new setting provided in TivaWare 2.2.x and
    // later to better reflect the actual VCO speed due to SYSCTL#22.
     g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                             SYSCTL_OSC_MAIN |
                                             SYSCTL_USE_PLL |
                                             SYSCTL_CFG_VCO_240), 120000000);
     printf("g_ui32SysClock = %d\n\n", g_ui32SysClock);

    spiInit();

    int count = 0;

    while(1)
    {

        // SPI-Test: Write to EMI_CTRL register of ADE7912. Then read written value from ADE7912.
        {
            SSIDataPut(SSI1_BASE, ((0xE << 3) | 0b000));
            SSIDataPut(SSI1_BASE, 0xFF);

            while(SSIBusy(SSI1_BASE))
            {
            }

            SSIDataPut(SSI1_BASE, ((0xE << 3) | 0b100));
//            HWREG(SSI1_BASE + SSI_O_DR) = ((0xE << 3) | 0b100); // write-command on register level (vs. TivaWare)
            SSIDataPut(SSI1_BASE, 0); // a secondary write is necessary for the SPI clk and CS~ to activate during reading. any value can be written.
//            HWREG(SSI1_BASE + SSI_O_DR) = ((0xE << 3) | 0b000);

            SSIreadStatus = SSIDataGetNonBlocking(SSI1_BASE, &data);
//            data = HWREG(SSI1_BASE + SSI_O_DR); // read-command on register level (vs. TivaWare)

            printf("Pass %d - EMI_CTRL register value: %d\n", count, data);
            printf("SSIreadStatus: %d\n\n", SSIreadStatus);

            ++count;
        }

        SysCtlDelay(g_ui32SysClock / 250 / 3);
    }
}

I'm a bit clueless at this point. Does anyone have an idea what I might be doing wrong?

  • After some help from our senior engineer who got back from his holidays today, we found a solution:

    The SSI read-FIFO gets filled with everything on the MISO-line, i. e. the three 0x00s get put there before the first 0xFF. Therefore, this is what the first three SSIDataGet()-operations read, before they get to the desired value. In order to get the desired value every time, three empty reads need to be executed first to clear the 0x00s out of the FIFO.

    Here's the working code (I'll only post the loop, the rest remains identical):

        while(1)
        {

            // SPI-Test: Write to EMI_CTRL register of ADE7912. Then read written value from ADE7912.
            {
                SSIDataPut(SSI1_BASE, ((0xE << 3) | 0b000));
                SSIDataPut(SSI1_BASE, 0xF0);

                while(SSIBusy(SSI1_BASE))
                {
                }

                SSIDataPut(SSI1_BASE, ((0xE << 3) | 0b100));
    //            HWREG(SSI1_BASE + SSI_O_DR) = ((0xE << 3) | 0b100); // write-command on register level (vs. TivaWare)


                SSIDataPut(SSI1_BASE, 0); // a secondary write is necessary for the SPI clk and CS~ to activate during reading. any value can be written.
    //            HWREG(SSI1_BASE + SSI_O_DR) = ((0xE << 3) | 0b000);

                while(SSIBusy(SSI1_BASE))
                {
                }

                // dummy reads to clear first three 0x00-entries of the FIFO
                HWREG(SSI1_BASE + SSI_O_DR);
                HWREG(SSI1_BASE + SSI_O_DR);
                HWREG(SSI1_BASE + SSI_O_DR);

                SSIDataGet(SSI1_BASE, &data);
    //            SSIreadStatus = SSIDataGetNonBlocking(SSI1_BASE, &data);
    //            data = HWREG(SSI1_BASE + SSI_O_DR); // read-command on register level (vs. TivaWare)

                printf("Pass %d - EMI_CTRL register value: %d\n", count, data);
                printf("SSIreadStatus: %d\n\n", SSIreadStatus);

                ++count;
            }

            SysCtlDelay(g_ui32SysClock / 250 / 3);
        }