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.

Problems with SPI communication between TM4C1236H6PM and Beaglebone Black (BBB)

Other Parts Discussed in Thread: TM4C1236H6PM

My questions are at the end. First, some background: I am new to programming in C and to this microcontroller family.

I have a TM4C1236H6PM connected to a Beaglebone Black. The BBB is the master and the TM4C is configured as the slave. I can confirm that the correct data stream is being sent from the BBB correctly as I have the signal lines connected to a logic analyzer at the pins to the TM4C.

The LA is configured for decoding 16-bit transfers, CPOL=0, CPHA=0 (data valid on clock leading edge), CS is active low. Basically, normal conditions.

I am writing (4) words:  0x5555 0x0003 0xAAAA 0x0004

The TM4C is configured as follows (shortened to just the relevant lines):

//Set the clock internal to the Tiva to 40 MHz

   ROM_SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

//Enable peripheral SSI1

   ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);          // Enable SPI Port #1 (BBB communications)

// GPIOF p0,p1,p2,p3 as SPI communications with BBB (SPI1)
// BBB is the master for this port. Tiva is the slave device.
    GPIOPinConfigure(GPIO_PF0_SSI1RX);
    GPIOPinConfigure(GPIO_PF1_SSI1TX);
    GPIOPinConfigure(GPIO_PF2_SSI1CLK);
    GPIOPinConfigure(GPIO_PF3_SSI1FSS);
    GPIOPinTypeSSI(GPIO_PORTF_BASE,GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0);

// Configure SPI1 for communications with the BBB (Master device).
    SSIDisable(SSI1_BASE);
    ROM_SSIConfigSetExpClk(SSI1_BASE,ROM_SysCtlClockGet(),SSI_FRF_MOTO_MODE_0,SSI_MODE_SLAVE,500000,8);
    SSIEnable(SSI1_BASE);

// ENABLE AN INTERRUPT ON THE SPI1 Rx FIFO
    IntRegister(INT_SSI1, SPI_ISR_Handler);
    IntEnable(INT_SSI1);
    SSIIntEnable(SSI1_BASE, SSI_RXFF);
    SSIIntClear(SSI1_BASE, SSI_RXFF | SSI_RXTO | SSI_RXOR);
    IntMasterEnable();

The ISR is as follows (including some debugging writes to the UART):

void SPI_ISR_Handler (void) {
    char ISR_Rx_FIFO[12];
    uint32_t ui32Index;
    uint32_t pui32DataRx[8];
    unsigned long isr_source = SSIIntStatus(SSI1_BASE, true);
    UARTprintf("--------------------------------------------\r");
    UARTprintf("RECEIVED AN SPI THINGY. Interrupt status: %d (4: FIFO >= Half Full, 0: FIFO Empty).\r", isr_source);
    UARTprintf("Reading 4 words from Rx FIFO.\r");
    for(ui32Index = 0; ui32Index < 8; ui32Index++) {
        SSIDataGetNonBlocking(SSI1_BASE, &pui32DataRx[ui32Index]);
        pui32DataRx[ui32Index] &= 0xFFFF;
        pui32DataRx[ui32Index] = Reverse(pui32DataRx[ui32Index]);
        snprintf(ISR_Rx_FIFO, 12, "0x%0.4X", pui32DataRx[ui32Index]);
        UARTprintf("Read: %s\r", ISR_Rx_FIFO);
    }
    UARTprintf("Attempting to clear interrupt.\r", isr_source);
    SSIIntClear(SSI1_BASE, isr_source);
    isr_source = SSIIntStatus(SSI1_BASE, true);
    UARTprintf("Revised interrupt status = %d.\r", isr_source);
}

My problems/questions:

1. The ISR is only triggered on every fourth transmission from the BBB. From my understanding of the SSI FIFO, the SSI_RXFF condition should mean that the ISR is triggered after 8 bytes (one complete transmission of my 4 data words). Correct?

2. Once triggered, the contents of my variable pui32DataR do not contain the contents of the transmission (at least not as I intended). What I see is:

As I am new to C, I am hoping somebody can help point out my obvious mistakes and/or misunderstanding about retrieving this data. I'm looking to learn, but have so far not found anything online. Please help get me back on track. Thanks!

  • Hello Ryan,

    I believe the issue to be multiple rerun of the SSI Module along with the use og SSIDataGetNonBlocking function without checking if the SSI FIFO has the data.

    #1: Reset the SSI Controller before every run using SysCtlPeripheralReset.
    #2: Read the data in the ISR using SSIDataGetNonBlocking with the while looping the RXFE bit
    #3: Avoid the use of UARTprints in the ISR.

    Regards
    Amit
  • Amit,

    Thank you for your reply.

    Regarding #1, Where would the reset be placed when you suggest "before every run"? Upon initiation of the peripheral or are you suggesting that this happen as part of the ISR?

    As for #2, would a while loop replace the for loop? Can you suggest a framework from which I can implement this change? I'm new to C.

    I removed the UARTprintf statements and the contents of the array are now different from before. I'd like to say that this is progress, but I'm not sure. :)

    Do you think the above items would cause the ISR triggering observation where it takes 4 transmissions (16 words) to trigger the ISR?

    Ryan
  • Hello Ryan,

    #1 Relates to the code which enables the clock to the peripheral and configures the peripheral. The use of reset before doing the same ensures that the peripheral is set back to its initial condition every time
    #2 Is the manner in which interrupt routine is made: Please note that when RXFIFO is not full then the RXTO interrupt will be required for the application code to know there are some bytes in the FIFO. A sample ISR would be
    if(RXTO){
    while(SSIFIFO Not Empty)
    {
    SSIDataGetNonBlocking()
    }
    }
    if(RXFF)
    {
    for(8 counts)
    {
    SSIDataGetNonBlocking()
    }
    }
    Regards
    Amit
  • A reset has been placed into the code. No change in behavior. That being said, I can understand that this would be good practice.

    As far as #2, I understand what you are suggesting. I just do not know how to implement this. Can you provide an example that is not pseudocode? Thanks again.
  • Hello Ryan,

    The Pseudo code can be easily be changed to a code. Use the same in conjunction with ssi.h/ssi.c file for the correct replacement of the API's. Me doing it for you would not add any learning for you, and without having a test rig to test I rarely send out code.

    Regards
    Amit
  • Two of us spent a very frustrating day yesterday making no appreciable progress. As of this morning:

    Interrupts are being triggered consistently, but not correctly when the Beaglebone writes out to the TM4C (RXTO).

    The inclusion of UARTprintf statements did not alter the response from the device, so we are leaving them in for now to ease diagnostics.

    If we send two 16-bit words from the BBB, the interrupt triggers (RXTO) as expected and two words are "read" from the FIFO. However, the result is always 0 no matter what we send to the TM4C. This is the same no matter which SSI peripheral is used (0, 1 or 2). The logic analyzer shows proper transmission at the input pins of the TM4C.

    The interrupt is cleared and, even if we wait a number of cycles, the interrupt immediately triggers a second time but the status value is 0 when read. So why did it trigger?

    I'm sending 0x1111 0x2222 (just the 0x1111 shown):


    My output on the console is:

    Our current, whittled down code just for testing the SPI comm from the BBB is as follows (many includes are left over from the larger program). Does anyone see any problem with this setup or have any suggestions? We can successfully read and write to ADCs and DACs from the Tiva TM4C using the same setup steps. It's communication with the BBB that is a problem.

    #include <stdint.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include "inc/hw_types.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_gpio.h"
    #include "inc/hw_ssi.h"
    #include "inc/tm4c1236h6pm.h"
    #include "driverlib/debug.h"
    #include "driverlib/fpu.h"
    #include "driverlib/ssi.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/adc.h"
    #include "driverlib/timer.h"
    #include "utils/uartstdio.h"
    #include "driverlib/rom.h"

    // Track the number of ISRs realated to SPI transmissions
    uint32_t isr_counter = 0;

    // This is the SPI receive array
    #define NUM_SSI_DATA 8
    uint32_t pui32DataRx[NUM_SSI_DATA];

    void ConfigureUART(void) {
        // Enable the GPIO peripheral used bu the UART
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        // Enable UART0
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        // Set GPIO A0 and A1 as UART0 pins.
        ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
        ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        // Use the internal 16MHz oscillator as the UART clock source.
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
        // Initialize the UART for console I/O.
        UARTStdioConfig(0, 9600, 16000000);
    }

    void SPI_ISR_Handler (void) {
        isr_counter++;
        char ISR_Rx_FIFO[12];
        uint32_t ui32Index = 0;
        uint16_t isr_source = SSIIntStatus(SSI0_BASE, 1);  //1 from true
        UARTprintf("ISR %d ---------- %d --------\r", isr_counter, isr_source);
        if (isr_source & SSI_RXTO) {
            UARTprintf("RXTO Trigger\r");
            while (SSIIntStatus(SSI0_BASE, 1) & SSI_RXTO) {
                SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[ui32Index]);
                snprintf(ISR_Rx_FIFO, 12, "0x%0.8X", pui32DataRx[ui32Index]);
                UARTprintf("RXTO Reading %d: %s\r", ui32Index, ISR_Rx_FIFO);
                ui32Index++;
            }
            SSIIntClear(SSI0_BASE,  SSI_RXTO);
        }
        if (isr_source & SSI_RXFF) {
            UARTprintf("RXFF Trigger\r");
            for(ui32Index = 0; ui32Index < NUM_SSI_DATA; ui32Index++) {
                SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[ui32Index]);
                snprintf(ISR_Rx_FIFO, 12, "0x%0.8X", pui32DataRx[ui32Index]);
                UARTprintf("RXFF Reading %d: %s\r", ui32Index, ISR_Rx_FIFO);
            }
            while(SSIBusy(SSI0_BASE)){}
            SSIIntClear(SSI0_BASE,  SSI_RXFF);
        }
        // Just for diagnostics while figuring this out...
        for(ui32Index = 0; ui32Index < 8; ui32Index++) {
            snprintf(ISR_Rx_FIFO, 12, "0x%0.8X", pui32DataRx[ui32Index]);
            UARTprintf("Value %d: %s\r", ui32Index, ISR_Rx_FIFO);
        }
        //wait a few cycles for the interrupt to clear
        ROM_SysCtlDelay(30);
    }

    //*****************************************************************************
    //
    //*****************************************************************************
    int main(void) {

        //Set the clock internal to the Tiva to 40 MHz
        ROM_SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
        ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_SSI0);
        

        // Enable necessary peripherals
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);         // UART, SPI0, ADC CONV, TP10
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);          // Enable SPI Port #1 (BBB communications)
        // GPIOA p0, p1 as UART0 -- Configure as B9600 8-N-1 tool
        ConfigureUART();
        

        //GPIOA p2, p3, p4, p5 as SPI communications (SPI0)
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);
        GPIOPinTypeSSI(GPIO_PORTA_BASE,GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_3|GPIO_PIN_2);
        

        //Disable, configure, and enable the SSI controller
        SSIDisable(SSI0_BASE);
        ROM_SSIConfigSetExpClk(SSI0_BASE,SysCtlClockGet()/4, SSI_FRF_MOTO_MODE_0,SSI_MODE_SLAVE,500000,8);
        SSIEnable(SSI0_BASE);

        // ENABLE AN INTERRUPT ON THE SSI0 Rx FIFO
        IntRegister(INT_SSI0, SPI_ISR_Handler);
        IntEnable(INT_SSI0);
        SSIIntClear(SSI0_BASE, SSI_RXFF | SSI_RXTO | SSI_RXOR);
        SSIIntEnable(SSI0_BASE, SSI_RXFF | SSI_RXTO);
        IntMasterEnable();

        UARTprintf("Waiting for an SPI transmission.\r");

        while(1) {
        }
    }

    Has anyone successfully communicated from a BBB to a TM4C with the BBB configured as a master (mode 0) and the TM4C configured as the slave device?

  • Feel your pain - was going to ask "if" your logic analyzer is connected at the (proper) TM4C pins. (note that your 1st post places those facts in evidence) Is that still the case?

    And - are you adequately powering both boards? Do both boards come up and go down - together?

    Speed often is the enemy - suggest that you (significantly) reduce SPI speed - see if that helps.

    You ask about BBB & TM4C. As you've multiple SPI ports on your TM4C (you mentioned trying multiple) would it not make sense to (temporarily) replace the BBB <-> TM4C with TM4C <-> TM4C - and test/observe? I'd bet that "that TM4C connection" is far more common than BBB - TM4C - and KISS dictates that small, simple steps lead best to a solution...  (i.e. too many "unknowns" in your current brew...)

  • Yes, the LA is connected properly. We actually run those pins out to a header, so the connection is quite good.

    Each board has adequate power. But, I went ahead and powered each from individual power supplies as well as a single supply and the result is the same. The interrupt triggers, but all zeros. They do not necessarily come up and down together on the bench. In the final implementation they will be powered simultaneously.

    I reduced the transfer speed to 10k from 500k. Same response. How closely would the BBB output speed need to match the speed indicated in the the second-to-last parameter of the SSIConfigSetExpClk API (ui32BitRate)?

    Unfortunately I have only one board with the TM4C installed with which to work. Otherwise, I agree with you.

    If the Rx FIFO was receiving anything I should see it within SSI0_SSI_DR (0x40008008) regardless of the final destination of the data, correct?
  • Ryan Smith46 said:
    Unfortunately I have only one board with the TM4C installed with which to work.

    Believe ~14 (USD) and Digi-Key order can rectify (one board) only situation.

    Or - with some monkey motion - you should be able to physically connect one SPI port as master - 2nd as slave - w/in your single MCU/board...  (gets the BBB out of the equation - believe that's been done here - don't believe that you want any "MCU automated" loop-back style connection...)

    I do recall several posts here where users succeeded w/SPI on TM4C slave.   I'd search/find/model those - again devoid of BBB at this stage...

  • Hello Ryan,

    Why is the SSI Module being configured with the system clock value incorrectly defined as SysCtlClockGet()/4 instead of SysCtlClockGet()?

    Regards
    Amit
  • Well, it's working (mostly). Thanks to all for your assistance.

    Amit, this was just testing on setup to see if we could make anything move. It was (and is again) the system clock value from SysCtlClockGet().

    It turns out after all this that it was simply PF0 was not being unlocked correctly. A simple error cutting and pasting. PF0 is the MOSI line, so it was clocking in all zeros. Chalk it up as a learning experience.
  • Is this not (still) more proof that the decision to default two (not just one) pin into NMI mode was short-sighted?   Is it possible that even 10% of the user-base know what NMI is - or its use - and pitfalls?   Might it be time to declare victory - and retreat?   (FIX this ongoing MESS!)
     
    Years pass - issues remain - excelsior... (but not here...)

    [edit] 27 May 2015: Poster earlier reported, "If we send two 16-bit words from the BBB, the interrupt triggers (RXTO) as expected and two words are "read" from the FIFO. However, the result is always 0 no matter what we send to the TM4C. This is the same no matter which SSI peripheral is used (0, 1 or 2)."

    Thus - while the horror of PF0/PD7 has been allowed by vendor to long persist - ascribing the fault to, "simply PF0 was not being unlocked correctly" fails as PF0 is incapable of victimizing 2 of those 3 SSI peripherals!