I am trying to configure SSI0 on GPIOA as master and SSI2 on GPIOB as slave on the same TI board. I have tested and found that SSI0 acting as master is working. I tested SSI0 uncommenting the loop found at line 424, and commenting out lines 408 - 422. Doing that , I was able to get the attached scope capture. However, while running unaltered version (loop at line 424 commented out, lines 408-422 running), the interrupt for SSI2 never triggers. I'm not sure if I have the interrupt configured correctly or not. I do have all the pins for SSI0 physically connected to SSI2 (following the guide at the top of the code except using GPIOB instead of GPIOH). Is there anything wrong with my code that would make the interrupt for SSI2 not fire?
Waveforms from top to bottom: GPIO_PB4_SSI2CLK, GPIO_PB5_SSI2FSS, GPIO_PB6_SSI2RX, GPIO_PB7_SSI2TX
//***************************************************************************** // // spi_slave.c - Example demonstrating how to configure RX timeout interrupt in // SPI slave mode. // // Copyright (c) 2013 Texas Instruments Incorporated. All rights reserved. // TI Information - Selective Disclosure // //***************************************************************************** #include <stdbool.h> #include <stdint.h> #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/debug.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/pin_map.h" #include "driverlib/pwm.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/hw_memmap.h" #include "driverlib/qei.h" #include "driverlib/sysctl.h" #include "driverlib/uart.h" #include "utils/uartstdio.h" #include "driverlib/fpu.h" #include "inc/hw_types.h" #include "inc/hw_memmap.h" // The error routine that is called if the driver library encounters an error. #include "inc/hw_types.h" // Defines common types and macros #include "inc/hw_gpio.h" // Defines Macros for GPIO hardware #include "inc/hw_qei.h" #include "driverlib/ssi.h" #include "driverlib/uartstdio.h" //***************************************************************************** // //! \addtogroup ssi_examples_list //! <h1>SPI Slave (spi_slave)</h1> //! //! This example configures the SSI0 as SPI Master, SSI2 as SPI Slave on an //! EK-LM4F232 evaluation board. RX timeout interrupt is configured for SSI2. //! Three characters are sent on the master TX, then SSI2 RX timeout interrupt //! is enabled. The code then waits for the interrupt to fire. Once the //! interrupt is fired the data from slave RX FIFO is read and compared to the //! transmitted packet and the appropriate status is displayed. If everything //! goes well you should see a "Test Passed." message on the terminal window. //! The status messages are transmitted over UART0 at 115200 baud and 8-n-1 //! mode. //! //! This example uses the following peripherals and I/O signals on EK-LM4F232. //! You must review these and change as needed for your own board: //! - SSI0 peripheral //! - GPIO Port A peripheral (for SSI0 pins) (available near the SD card slot) //! - SSI0CLK - PA2 //! - SSI0Fss - PA3 //! - SSI0Rx - PA4 //! - SSI0Tx - PA5 //! //! - SSI2 peripheral //! - GPIO Port M peripheral (for SSI2 pins) (available right below the OLED) //! - SSI2CLK - PH4 //! - SSI2Fss - PH5 //! - SSI2Rx - PH6 //! - SSI2Tx - PH7 //! //! For this example to work, the following connections are needed on the //! EK-LM4F232 evaluation board. //! - SSI0CLK(PA2) - SSI2CLK(PH4) //! - SSI0Fss(PA3) - SSI0Fss(PH5) //! - SSI0Rx(PA4) - SSI2Tx(PH7) //! - SSI0Tx(PA5) - SSI2Rx(PH6) //! //! The following UART signals are configured only for displaying console //! messages for this example. These are not required for operation of SSI0. //! - UART0 peripheral //! - GPIO Port A peripheral (for UART0 pins) //! - UART0RX - PA0 //! - UART0TX - PA1 //! //! This example uses the following interrupt handlers. To use this example //! in your own application you must add these interrupt handlers to your //! vector table. //! - SSI2IntHandler. //! // //***************************************************************************** //***************************************************************************** // // Number of bytes to send and receive. // //***************************************************************************** #define NUM_SSI_DATA 3 //***************************************************************************** // // Global variables used in interrupt handler and the main loop. // //***************************************************************************** //volatile unsigned long g_ulSSI2RXTO = 0; //unsigned long g_ulDataRx2[NUM_SSI_DATA]; uint32_t g_ulSSI2RXTO = 0; uint32_t g_ulDataRx2[NUM_SSI_DATA]; //***************************************************************************** // // Interrupt handler for SSI2 peripheral in slave mode. It reads the interrupt // status and if the interrupt is fired by a RX time out interrupt it reads the // SSI2 RX FIFO and increments a counter to tell the main loop that RX timeout // interrupt was fired. // //***************************************************************************** void SSI2IntHandler(void) { unsigned long ulStatus, ulIndex; // // Read interrupt status. // ulStatus = SSIIntStatus(SSI2_BASE, 1); // // Check the reason for the interrupt. // if(ulStatus & SSI_RXTO) { // // Interrupt is because of RX time out. So increment counter to tell // main loop that RX timeout interrupt occurred. // g_ulSSI2RXTO++; // // Read NUM_SSI_DATA bytes of data from SSI2 RX FIFO. // for(ulIndex = 0; ulIndex < NUM_SSI_DATA; ulIndex++) { SSIDataGet(SSI2_BASE, &g_ulDataRx2[ulIndex]); } } // // Clear interrupts. // SSIIntClear(SSI2_BASE, ulStatus); } //***************************************************************************** // // This function sets up UART0 to be used for a console to display information // as the example is running. // //***************************************************************************** void InitConsole(void) { // // Enable GPIO port A which is used for UART0 pins. // SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // // Configure the pin muxing for UART0 functions on port A0 and A1. // This step is not necessary if your part does not support pin muxing. // GPIOPinConfigure(GPIO_PA0_U0RX); GPIOPinConfigure(GPIO_PA1_U0TX); // // Select the alternate (UART) function for these pins. // GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1); // // Initialize the UART for console I/O. // //UARTStdioInit(0); UARTStdioConfig(0, 115200, 16000000); } //***************************************************************************** // // This function sets up SPI0 to be used as Master in freescale mode. // //***************************************************************************** void InitSPI0(void) { // // The SSI0 peripheral must be enabled for use. // SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0)) { } // // For this example SSI0 is used with PortA[5:2]. GPIO port A needs to be // enabled so these pins can be used. // SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA)) { } // // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5. // This step is not necessary if your part does not support pin muxing. // GPIOPinConfigure(GPIO_PA2_SSI0CLK); GPIOPinConfigure(GPIO_PA3_SSI0FSS); GPIOPinConfigure(GPIO_PA4_SSI0RX); GPIOPinConfigure(GPIO_PA5_SSI0TX); // // Configure the GPIO settings for the SSI pins. This function also gives // control of these pins to the SSI hardware. Consult the data sheet to // see which functions are allocated per pin. // The pins are assigned as follows: // PA5 - SSI0Tx // PA4 - SSI0Rx // PA3 - SSI0Fss // PA2 - SSI0CLK // GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2); SSIDisable(SSI0_BASE); // // Configure and enable the SSI0 port for SPI master mode. // SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_MASTER, 660000, 8); // // Enable the SSI0 module. // SSIEnable(SSI0_BASE); } //***************************************************************************** // // This function sets up SPI2 to be used as slave in freescale mode. // //***************************************************************************** void InitSPI2(void) { // // The SSI0 peripheral must be enabled for use. // SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI2)) { } // // For this example SSI2 is used with PortH[7:4]. GPIO port H needs to be // enabled so these pins can be used. // SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)) { } // // Configure the pin muxing for SSI2 functions on port H4, H5, H6 and H7. // This step is not necessary if your part does not support pin muxing. // GPIOPinConfigure(GPIO_PB4_SSI2CLK); GPIOPinConfigure(GPIO_PB5_SSI2FSS); GPIOPinConfigure(GPIO_PB6_SSI2RX); GPIOPinConfigure(GPIO_PB7_SSI2TX); // // Configure the GPIO settings for the SSI pins. This function also gives // control of these pins to the SSI hardware. Consult the data sheet to // see which functions are allocated per pin. // The pins are assigned as follows: // PH7 - SSI2Tx // PH6 - SSI2Rx // PH5 - SSI2Fss // PH4 - SSI2CLK // GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4); SSIDisable(SSI2_BASE); // // Configure and enable the SSI2 port for SPI slave mode. // SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_SLAVE, 660000, 8); // // Enable the SSI2 module. // SSIEnable(SSI2_BASE); // SSIIntRegister(SSI2_BASE, SSI2IntHandler); } //***************************************************************************** // // This example will send out 3 bytes of data from master, then waits for slave // RX timeout interrupt to fire (where these 3 bytes are read). Then the sent // and returned data are compared to give out appropriate status messages on // UART0. // //***************************************************************************** int main(void) { /* unsigned long ulDataTx0[NUM_SSI_DATA]; unsigned long ulDataRx0[NUM_SSI_DATA]; unsigned long ulindex; */ uint32_t ulDataTx0[NUM_SSI_DATA]; uint32_t ulDataRx0[NUM_SSI_DATA]; uint32_t ulindex; // // Set the clocking to run directly from the external crystal/oscillator. // SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // // Set up the serial console to use for displaying messages. This is // just for this example program and is not needed for SSI operation. // InitConsole(); // // Display the setup on the console. // UARTprintf("SSI ->\n"); UARTprintf(" Mode: SPI\n"); UARTprintf(" Data: 16-bit\n\n"); // // Init SPI0 as master. // InitSPI0(); // // Read any residual data from the SSI port. This makes sure the receive // FIFOs are empty, so we don't read any unwanted junk. This is done here // because the SPI SSI mode is full-duplex, which allows you to send and // receive at the same time. The SSIDataGetNonBlocking function returns // "true" when data was returned, and "false" when no data was returned. // The "non-blocking" function checks if there is any data in the receive // FIFO and does not "hang" if there isn't. This might not be needed here. // while(SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx0[0])) { } // // Init SPI2 as slave. // InitSPI2(); // // Enable RX timeout interrupt. // SSIIntEnable(SSI2_BASE, SSI_RXTO); // // Read any residual data from the SSI port. This makes sure the receive // FIFOs are empty, so we don't read any unwanted junk. This is done here // because the SPI SSI mode is full-duplex, which allows you to send and // receive at the same time. The SSIDataGetNonBlocking function returns // "true" when data was returned, and "false" when no data was returned. // The "non-blocking" function checks if there is any data in the receive // FIFO and does not "hang" if there isn't. // while(SSIDataGetNonBlocking(SSI2_BASE, &g_ulDataRx2[0])) { } // // Clear any pending interrupt // SSIIntClear(SSI2_BASE, SSI_RXTO); // // Initialize the data to send. // ulDataTx0[0] = 's'; ulDataTx0[1] = 'p'; ulDataTx0[2] = 'i'; // // Display indication that the SSI is transmitting data. // UARTprintf("Sent:\n "); // // Send 3 bytes of data. // for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++) { // // Display the data that SSI is transferring. // UARTprintf("'%c' ", ulDataTx0[ulindex]); // // Send the data using the "blocking" put function. This function // will wait until there is room in the send FIFO before returning. // This allows you to assure that all the data you send makes it into // the send FIFO. // SSIDataPut(SSI0_BASE, ulDataTx0[ulindex]); } /* while(1){ SSIDataPut(SSI0_BASE, 's'); } */ // // Wait until SSI0 is done transferring all the data in the transmit FIFO. // while(SSIBusy(SSI0_BASE)) { } // // Enable the SSI2 interrupts to ARM core. This as to be done here, // otherwise the RX timeout interrupt will fire before all the data has // been transferred. This is specific to this example as both the SSI // master and slave are on the same microcontroller. // IntEnable(INT_SSI2); // // Wait for the SSI2 RXTO interrupt to fire and data read from RXFIFO. // while(g_ulSSI2RXTO == 0) { } // // Display indication that salve has receiving data. // UARTprintf("\nReceived:\n "); // // Display the 3 bytes of data that were read from RX FIFO. // for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++) { UARTprintf("'%c' ", g_ulDataRx2[ulindex]); } // // Check that the data sent was the same as the data received. // for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++) { if(ulDataTx0[ulindex] != g_ulDataRx2[ulindex]) { // // Tell the user that the test failed. // UARTprintf("\n\nError: Data does not exactly match.\n"); UARTprintf("Check that Tx & Rx are connected correctly.\n\n"); // // Wait in infinite loop for debugging. // while(1) { } } } if(g_ulSSI2RXTO > 1) { // // Tell the user that the test failed and the reason. // UARTprintf("\n\nError: %d interrupt(s) fired when expecting only one." "\n", g_ulSSI2RXTO); } else { // // Tell the user that the test passed. // UARTprintf("\n\nTest Passed.\n\n"); } while(1) { } }