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.

tiva SPI slave mode interrupt and RX FIFO

Other Parts Discussed in Thread: TM4C123GH6PM, EK-TM4C123GXL

Good day, 

I am configuring a Tiva Launchpad EK-TM4C123GXL with a Tiva series TM4C123GH6PM with CCSv6. I am programming it to aggregate some analog sensor data, and send it via SPI slave mode to another TI MCU, which is the master. Also, I need to implement some software addressing, because there is another SPI slave on the bus and I can't get a separate chip select. 

But my immediate issue is the ISR that is configured to be called when the SSI RX FIFO is half full or more. The master is sending out 4 8-bit frames in a row. Since the FIFO is 8 elements deep, every time I get a poll (4 8-bit frames) from the master, it should trip the ISR. In the ISR, I loop through and get all the elements from the FIFO. And before, in the main loop, I have the slave output data already in the TX FIFO. Why is the interrupt only being called every 4 times I poll the slave with the master? Also, the first byte out of 4 is ending up in every element of the recieveData array. The last 3 bytes from the transmit aren't making it through. I have the 4 SPI lines on a scope, and can see the interrupt because I have a GPIO pin toggled high while in the ISR. The master data is correct on the scope. The frame format matches the master, CLK =0 and CPHA = 0. 

I attached the main.c file, thanks if you can help explain this. 

And right now the data is arbitrary for testing, but soon I am going try and detect the address in the first byte, and if it matches mine, I'll respond, otherwise not. Also, I plan on using:

SSI0_CR1_R &= ~SSI_CR1_SOD //disable the slave output disable
//transmit and other ISR code
SSI0_CR1_R |= SSI_CR1_SOD; //enable the slave output disable

Is this a valid plan of attack? I can't have the slave driving the output line when another slave is transmitting.

/*
 * main.c
 */

#include <stdint.h>
#include <stdbool.h>
#include "driverlib/sysctl.h"
//#include "inc/tm4c123gh6pm.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "inc/hw_memmap.h"
#include "driverlib/ssi.h"
#include "driverlib/adc.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/uart.h"
#include "inc/hw_ints.h"
#include "utils/uartstdio.h"
#include "driverlib/systick.h"

#define SSI0_CR1_R              (*((volatile uint32_t *)0x40008004))
#define SSI_CR1_SOD				0x00000008
#define NVIC_ST_CURRENT_R       (*((volatile uint32_t *)0xE000E018))
#define NVIC_ST_CTRL_R      	(*((volatile unsigned long *)0xE000E010))
#define dataLength				4
uint32_t returnData[dataLength] = {0};
uint32_t receiveData[dataLength] = {0};
uint32_t dataSent = 1;

//returns current system frequency which may not match requested frequency
void Clock_Init(){
	uint32_t ui32Config; //provides divisor of one the fixed PLL VCO settings
	//PLL is 400MHz: (400MHz/2)/4 = 50MHz
	ui32Config = (SYSCTL_SYSDIV_4 | SYSCTL_XTAL_16MHZ | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN);
	SysCtlClockSet(ui32Config);
}

void SysTick_Init(){
	//15e6/50e6 = 300ms period
	SysTickDisable();
	SysTickPeriodSet(15000000);
	SysTickEnable();
	NVIC_ST_CURRENT_R = 0;
}
//configure GPIO to use SSI0 on Port A, SSI1 on Port D
//and AIN0 from slide pot on pin PE3
//Nokia5110 DC
void GPIO_Init(){

	//SSI0 is for slave, SSI1 is for LCD

	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); //for slave
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); //for LCD
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); //for ADC channels

	SysCtlDelay(100);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	SysCtlDelay(3);
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

	//PA2-5:
	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
	//PD0-3:
	GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
	/*
	 * x axis: PE0 AIN3
	 * z axis: PE1 AIN2
	 * offloader force: PE2 AIN1
	 * temperature: PE3 AIN0
	 */
	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

	GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_7);//for Data/Command to LCD
	GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_6); //for reset to LCD
	GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6); //for SSI0 ISR signaling
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
	GPIOPinConfigure(GPIO_PA4_SSI0RX);
	GPIOPinConfigure(GPIO_PA5_SSI0TX);
	GPIOPinConfigure(GPIO_PD0_SSI1CLK);
	GPIOPinConfigure(GPIO_PD1_SSI1FSS);
	GPIOPinConfigure(GPIO_PD2_SSI1RX);//not used because LCD doesn't transmit back
	GPIOPinConfigure(GPIO_PD3_SSI1TX);
}

void UART_Init(){
	UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
	UARTStdioConfig(0, 9600, 16000000);
}

void SSI0_Handler() {
	GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_PIN_6);
	//SSI0_CR1_R &= ~SSI_CR1_SOD; //0 in slave output disable bit in order to drive line
	SSIIntClear(SSI0_BASE, SSI_RXFF);
	//UARTprintf("");
	int i = 0;
	for(i = 0; i < dataLength; i++){
		//wait until a value is in the RX FIFO if there isn't one.
		SSIDataGet(SSI0_BASE, &receiveData[i]);
	}
	for(i = 0; i < dataLength; i++){
		UARTprintf("receiveData[%1d] = %08x\n", i, receiveData[i]);
	}
	dataSent = 1;
	UARTprintf("\n");
	//disable slave output
	//SSI0_CR1_R |= SSI_CR1_SOD;
	GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_6, 0x00);
}
//configure and enable the SSI peripheral. Slave mode requires CLK < 12 x system clock (master uC runs SPI at 1.17MHz)
//Using the same polarity and phase as F28M335x: Polarity 0, Phase 1
// means clock idle state is zero, sample takes place on 2nd clock edge
void SSI_Init(){

	SSIClockSourceSet(SSI1_BASE, SSI_CLOCK_SYSTEM); //Nokia5110 max f_sclk(ext) is 4.0MHz
	SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_SLAVE, 3330000, 8);
	//Nokia5110 samples SDIN at positive edge of clock: phase bit should be 1, set idle clock high
	SSIConfigSetExpClk(SSI1_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 3330000, 8);
	SSIIntEnable(SSI0_BASE, SSI_RXFF); //SSI0_Handler() will be called when RXFF is half full or more
	//For SLAVE
	SSIEnable(SSI0_BASE);
	//For Nokia5110
	SSIEnable(SSI1_BASE);
}

//Configure Interrupts
void NVIC_Init(){
	IntEnable(INT_SSI0_TM4C123);
	IntPrioritySet(INT_SSI0_TM4C123, 0);
	IntMasterEnable();
}

void SysTick_Wait3sec(){
	int i;
	for (i = 0; i < 10; i++){
		while((NVIC_ST_CTRL_R & 0x00010000)==0){
		}
	}
}

int main(void) {
	Clock_Init();
	GPIO_Init();
	UART_Init();
	SSI_Init();
	SysTick_Init();
	int i;
	for (i = 0; i < 4; i++){
		returnData[i]=i;
	}
	NVIC_Init();
	while(1){
		//get data ready for master initiation
		//only put data in return FIFO if the last packet was sent
		if (dataSent){
			UARTprintf("loading return data\n");
			for (i = 0; i < dataLength; i++){
				SSIDataPut(SSI0_BASE, returnData[i]);
				UARTprintf("returnData[%d]: %08x\n", i, returnData[i]);
			}

			dataSent = 0;
		}

		//clear recieveData
		for (i = 0; i < dataLength; i++){
			receiveData[i] = 0;
		}
		UARTprintf("Main loop\n");
		SysTick_Wait3sec();
	}

	return 0;

}



  • Hello Erik

    The interrupt will be called only when FIFO is half full. To understand the problem

    1. When the interrupt is asserted and you read the data only the first byte is put in the buffer. the remaining 3 bytes do not get read at all?
    2. Are you expecting 1 interrupt per byte or 1 interrupt for 4 bytes?

    Regards
    Amit
  • Yes, I'm expecting 1 interrupt for the 4 bytes. The interrupt is called when the FIFO is half fulI, which means it has 4 or more elements. In the ISR I'm counting on the for loop to get all 4 bytes out of the FIFO. And they must already be there, or else the interrupt wouldn't be called. I don't understand why the first byte is ending up in every location of the destination array. For example, if I send 0x4E, ,0x00, 0x00, 0x00 from the master, the 0x4E element ends up in all four locations of the destination array. And what is more, the ISR is only called every 4th time I send the 4 byte long data across from the master. Any ideas? 

  • Hello Erik,

    Note that you have a UART print in the ISR which is sending out data @ 115.2kbps and your SSI Slave is receiving data at 3.3Mbps.

    for(i = 0; i < dataLength; i++){
    UARTprintf("receiveData[%1d] = %08x\n", i, receiveData[i]);
    }

    This will slow down the interrupt response.

    To the other issue of data not coming properly into the array, I would first correct the interrupt frequency issue.

    Regards
    Amit
  • The behavior is the same with all the UARTprintf statements removed from the ISR. I put them in there to help troubleshoot anyway, but just verified the behavior is the same. It seems like on a transmit from master, only the first byte of four is getting into the RX FIFO each time, that is why every element of the receiveData array is filled with the first element. Because on each 4 byte transmit from master, the first frame gets pushed through the FIFO until it is half full of that first frame. The enable from the master is staying low the over the entire transmission of 4 bytes, too, which is how we want our master configured. So, is the Tiva SPI slave mode set up to only take 1 frame per enable or chip select? I'd like it to accept all four bytes while the enable stays low the whole time. I'm using the freescale format of clock and phase both 0. Is there anyway to get the behavior I'm looking for? I didn't see options for this in the configuration. Thanks Amit. 

  • Hello Erik,

    And that make sense now. I think the Mode 0 requires toggle of the SPI Frame Select. You must either use Mode 1 or Mode 3.

    Regards
    Amit
  • Amit, would I have any more flexibility using a software SPI, for example from the Tivaware Utilities:
    SW-TM4C-UTILS-UG ? I don't see any way to specify master or slave, however. Also, the master baud clock is 1MHz, which is maybe too fast for the software SSI?
  • Hello Erik

    Yes, it may be too fast for the software emulated SPI. Instead of doing that can you configure the SPI on the master side in either Mode-1 or Mode-3, so that the Slave on TM4C can also follow the same?

    Regards
    Amit
  • All right, I do see where it says that about continuous transfers requiring the CPHA bit to be set. So, I did that, and now the slave reads the master correctly.

    But there is still an issue. The slave is not reading and writing on the same clock edge, which is messing up the transfer from slave to master. 

    There is a screenshot below of the the slave UART output. It is trying to return the data in returnData, which is [0x04, 0x08, 0x0C, 0x010] in hex.The master is sending [0x4E, 0xAB, 0xAB, 0xAB], and the slave is reading this correctly, as you can see. 

    The image below is the Expressions view while debugging on a F28M35 configured as a SPI master. You can see the receivedData array is off by a factor of two from what it should be. But see the scope image below, too please. 

    The two scope images pretty clearly show how the slave is reading on the falling edge (as it should be). I have CPHA = 1 in my slave configuration on the tiva. But it is writing on the rising edge. I don't see any option for opposing edge read and writes in the data sheet. Have you seen this behaviour by any chance? I also tried configuring both slave and master for a falling edge clock, with CPHA=1, but it was the same problem. Thanks if you can help. 

  • Hello Erik,

    As mentioned in the data sheet, the Output is propagated on the Rising edge and Input is captured on the falling edge in Mode-1.

    Regards
    Amit
  • I think I see where that is referenced, but it is only in the TI SSI mode:

    It was my intention to use the freescale format, not the TI format. Don't I ensure this with the configuration:

    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_SLAVE, 1000000, 8);

    ? And anyway, none of the waveforms in the data sheet in the frame format discussion show sampling on different clock edges, which is what I saw on the scope image I shared. In the scope shot above, the master and slave lines were being driven on opposite edges. Am I wrong? 

  • Hello Erik,

    Yes, I am also referencing to the Fresscale format. From the data sheet

    "Data is then captured on the falling edges and propagated on the rising edges of the SSInClk signal."

    Regards
    Amit
  • Good morning Amit, I need more clarification, because what I am seeing on the scope is not what is described by you or the datasheet. I assume "data is then captured on the falling edge" means input data is sampled and shouldn't be changing on the rising edge (this is what I see). Then, I also am assuming data is "propagated on the rising edges of the SSInClk signal" means data changes, or has an edge, on the rising edge (this is not what I see on the MISO line on the scope screen shot I provided). This interpretation matches the data sheet waveforms of figure 15-6. However, it is not what I see. What I see is the slave propagating 180 degrees out of phase with the master. What I see is falling edges on MOSI and rising edges on MISO. What I see does not match the waveforms or the language of the data sheet. Do you agree?
  • Hello Erik,

    The propagation of a signal has delay due to the buffers and the IO. That is why it would have been launched on the rising edge of the clock which is also delayed and gives the impression that it is 180 degree out of phase.

    Regards
    Amit