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.

TM4C123GXL Cannot Read SSI data in MISO

Other Parts Discussed in Thread: TXB0108

I am using the TM4C123GXL and AEAT6012 and txb0108 to try and read the 12 bit encoder value, I have not been able to.

I am currently trying to use the ti_master example, but I am not really having any luck, all it does is crashes.

AEAT6012: 

www.google.com/url

txb0108: https://cdn-shop.adafruit.com/datasheets/txb0108.pdf

I do not really understand how to use the SSI MISO configuration, can someone please help?

  • Hello Jonathan,

    Can you please elaborate "crash". Did you check CLK, FSS, MOSI and MISO toggling?

    Regards
    Amit
  •  it crashes on SSIDataGet(SSI0_BASE, &pui32DataRx[ui32Index]);

    I am trying to read 12 bits

    Here is the code:

    #include <stdint.h> // Variable definitions for the C99 standard.
    #include <stdio.h> // Input and output facilities for the C99 standard.
    #include <stdbool.h> // Boolean definitions for the C99 standard.
    #include <math.h>
    
    #include "inc/tm4c123gh6pm.h" // Definitions for the interrupt and register assignments.
    #include "inc/hw_memmap.h" // Memory map definitions of the Tiva C Series device.
    #include "inc/hw_types.h" // Definitions of common types and macros.
    #include "inc/hw_ssi.h"
    
    #include "driverlib/sysctl.h" // Definitions and macros for System Control API of DriverLib.
    #include "driverlib/interrupt.h" // Defines and macros for NVIC Controller API of DriverLib.
    #include "driverlib/gpio.h" // Definitions and macros for GPIO API of DriverLib.
    #include "driverlib/timer.h" // Defines and macros for Timer API of DriverLib.
    #include "driverlib/pin_map.h" //Mapping of peripherals to pins for all parts.
    #include "driverlib/uart.h" // Definitions and macros for UART API of DriverLib.
    #include "driverlib/fpu.h" // Prototypes for the FPU manipulation routines.
    #include "driverlib/ssi.h" // Definitions and prototypes for the SSI/SPI routines.
    #include "driverlib/adc.h" // Definitions for ADC API of DriverLib.
    
    #include "utils/uartstdio.h" // Prototypes for the UART console functions.
    							 // Needs to add "utils/uartstdio.c" through a relative link.
    
    
    #define TIMER0_FREQ    2 // Freqency in Hz, heartbeat timer
    #define UART0_BAUDRATE    115200 // UART baudrate in bps
    
    #define SAMP_FREQ    44000 // Frequency in Hz, common sampling rates for digital audio: 44100, 48000
    #define SIG_FREQ    1000 // Signal frequency in Hz.
    #define SIG_LEN    44 // = SAMP_FREQ/SIG_FREQ, period of discrete-time signal
    
    #define SPI_BITRATE    15000000 // needs to > SAMP_FREQ*16, and < SysCtrlClock/2, max 20M
    #define SPI_DATA_MASK    0x0FFF // See datasheet, MCP4921
    #define SPI_CTRL_MASK    0x7000 // See datasheet, MCP4921
    
    #define MATH_PI    3.14159265358979323846
    
    #define RED_LED    GPIO_PIN_1
    #define BLUE_LED    GPIO_PIN_2
    #define GREEN_LED    GPIO_PIN_3
    
    #define NUM_DISP_TEXT_LINE    4
    
    #define NUM_SSI_DATA 2
    
    // function prototypes
    void init_LEDs(void);
    void init_timer(void);
    void init_UART(void);
    void init_SPI(void);
    
    
    //void init_signal(void);
    
    void Timer0_ISR(void);
    void Timer1_ISR(void);
    
    extern void UARTStdioIntHandler(void);
    
    
    // global variables
    uint8_t cur_LED = RED_LED, Pause=0;
    
    uint32_t pui32DataRx[NUM_SSI_DATA];
    uint32_t ui32Index;
    
    const char *disp_text[NUM_DISP_TEXT_LINE] = {
    		"\n",
    		"UART and LED Demo\n",
    		"H: help, R: red, G: green, B: blue, P: pause S: sample \n", "> " };
    
    
    uint32_t sys_clock;
    uint32_t sig_data=0, sig_data_f=0;
    uint16_t sig[SIG_LEN], sig_index=0;
    
    
    int main(void)
    {
    	uint32_t i;
    	unsigned char user_cmd;
    
    	// Configure system clock.
    	//SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); // 50 MHz
    	//SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); // 40 MHz
    	//sys_clock = SysCtlClockGet();
    	SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); // 80 MHz
    	sys_clock = 80000000; // Hard-coded for 80MHz because of a bug in SysCtlClockGet().
    
    	// Enable the floating-point unit (FPU).
    	FPUEnable();
    	// Configure FPU to perform lazy stacking of the floating-point state.
    	FPULazyStackingEnable();
    
    	init_LEDs();
    	init_UART();
    	init_timer();
    	init_SPI();
    
    	// Enable the processor to respond to interrupts.
    	IntMasterEnable();
    
    	// Start the timer by enabling operation of the timer module.
    	TimerEnable(TIMER0_BASE, TIMER_A);
    	TimerEnable(TIMER1_BASE, TIMER_A);
    
    	// Initial display on terminal.
    	for(i=0; i<NUM_DISP_TEXT_LINE; i++)
    		UARTprintf(disp_text[i]);
    
    	while(1) {
    		// Read user inputs from UART if available.
    		if(UARTRxBytesAvail())
    	        user_cmd = UARTgetc();
    		else
    			user_cmd = 0;
    
    		switch(user_cmd){
    		case '\r':
    		case ' ':
    		case 'H':
    		case 'h':
    			for(i=0; i<NUM_DISP_TEXT_LINE; i++)
    				UARTprintf(disp_text[i]);
    			break;
    		case 'R':
    		case 'r':
    			cur_LED = RED_LED;
    			UARTprintf("\n> ");
    			break;
    		case 'B':
    		case 'b':
    			cur_LED = BLUE_LED;
    			UARTprintf("\n> ");
    			break;
    		case 'G':
    		case 'g':
    			cur_LED = GREEN_LED;
    			UARTprintf("\n> ");
    			break;
    		}
    	}
    }
    
    
    void init_LEDs(void)
    {
    	// Enable and configure LED peripheral.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // Enable GPIO Port F.
    	// Three onboard LEDs, R:PF1, B:PF2, G:PF3.
    	GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
    }
    
    
    void init_timer(void)
    {
    	// Enable and configure Timer0 peripheral.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    	// Configure as a 32-bit timer in periodic mode.
    	TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    	// Initialize timer load register.
    	TimerLoadSet(TIMER0_BASE, TIMER_A, sys_clock/TIMER0_FREQ -1);
    
    	// Registers a function to be called when the interrupt occurs.
    	IntRegister(INT_TIMER0A, Timer0_ISR);
    	// The specified interrupt is enabled in the interrupt controller.
    	IntEnable(INT_TIMER0A);
    	// Enable the indicated timer interrupt source.
    	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    
    	// Enable and configure Timer1 peripheral.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    	// Configure as a 32-bit timer in periodic mode.
    	TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);
    	// Initialize timer load register.
    	TimerLoadSet(TIMER1_BASE, TIMER_A, sys_clock/SAMP_FREQ -1);
    
    	// Registers a function to be called when the interrupt occurs.
    	IntRegister(INT_TIMER1A, Timer1_ISR);
    	// The specified interrupt is enabled in the interrupt controller.
    	IntEnable(INT_TIMER1A);
    	// Enable the indicated timer interrupt source.
    	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    }
    
    
    void init_UART(void)
    {
    	// Enable and configure UART0 for debugging printouts.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	GPIOPinConfigure(GPIO_PA0_U0RX);
    	GPIOPinConfigure(GPIO_PA1_U0TX);
    	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    	// Registers a function to be called when the interrupt occurs.
    	IntRegister(INT_UART0, UARTStdioIntHandler);
    	UARTStdioConfig(0, UART0_BAUDRATE, sys_clock);
    }
    
    
    void init_SPI(void)
    {
    	// Enable peripheral for SSI/SPI.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
    	// Configure the muxing and GPIO settings to bring the SSI/SPI functions out to the pins
    	// PA2: SSI0CLK, PA3: SSI0FSS, PA5: SSI0TX
    	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    	GPIOPinConfigure(GPIO_PA4_SSI0RX);
    
    	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4);
    
    	SSIConfigSetExpClk(SSI0_BASE, sys_clock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SPI_BITRATE,12 );
    	SSIEnable(SSI0_BASE);
    	SSIAdvFrameHoldEnable(SSI0_BASE);
    }
    
    
    
    
    // Timer1 interrupt service routine
    void Timer1_ISR(void)
    {
    	// Clear the timer interrupt.
    	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    
    	for(ui32Index = 0; ui32Index < NUM_SSI_DATA; ui32Index++)
    	    {
    	        //
    	        // Receive the data using the "blocking" Get function. This function
    	        // will wait until there is data in the receive FIFO before returning.
    	        //
    
    	        SSIDataGet(SSI0_BASE, &pui32DataRx[ui32Index]);
    
    	        //
    	        // Since we are using 8-bit data, mask off the MSB.
    	        //
    	        pui32DataRx[ui32Index] &= 0x03FF;
    
    	        //
    	        // Display the data that SSI0 received.
    	        //
    	        UARTprintf("'%c' ", pui32DataRx[ui32Index]);
    	    }
    }
    
    
    // Timer0 interrupt service routine
    void Timer0_ISR(void)
    {
    	// Clear the timer interrupt.
    	TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    
    	// Blink LED. Read the current state of GPIO pins and write back the opposite state.
    	if(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3)) {
    		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
    	}
    	else {
    		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, cur_LED);
    	}
    }
    
    

  • Two obvious issues

    You never send any SPI data so there's no clock. A quick investigation with your 'scope or logic analyzer should have made that obvious

    You are performing a printf to a serial port in an interrupt!

    Robert
  • Hello Jonathan

    Also you plan to use Advance Mode? Note that advance mode requires data width to be 8 and not 12.

    SSIConfigSetExpClk(SSI0_BASE, sys_clock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SPI_BITRATE,12 );
    SSIEnable(SSI0_BASE);
    SSIAdvFrameHoldEnable(SSI0_BASE);

    Regards
    Amit
  • And again - poster becomes overwhelmed by the lack of, "Step by Step" Test/Verify - creates a large "brew" and then has NO means to effectively troubleshoot.
  • What if I'm not trying to send data, only receive it?
  • I reused code I know works except for the SSI in portion, it runs flawlessly otherwise
  • It doesn't matter. Just as writes require a read, reads require a write. The input and output registers are synchronous with the clock. Ask yourself how the clock is generated.

    And as I suggested, check with your 'scope. The lack of clock will be obvious.

    Robert
  • Jonathan Cotten said:
    I reused code I know works except for the SSI in portion...

    Bravo - that IS (almost) "KISS" compliant - yet (completely) unstated w/in your opening post.   Full KISS compliance would see you (first) experiment w/small, simple SPI based memory - which tests/verifies your, "MCU to device" transactions.   (both reads & writes)

    Recall that SPI (both) writes & reads on the same clock.   (sometimes /often upon different clock edges)

    If (it exists) and you've allowed the remote device's (unused) SPI pin to "float" - trouble (surely) awaits...

  • Hello Jonathan,

    To be able to receive data., the SSI controller requires that the data be written to the transmit path. This could be a dummy data, but it is required as based on the data being written for the TX path, the SSI controller generates the FSS and CLK, shifts in the MISO data into the RX FIFO.

    Regards
    Amit
  • Hi Amit,

    Might it be that poster's SPI Remote does NOT possess a, "MOSI" pin?

    In such case - has not our poster encountered a "slippery slope" - one NOT well anticipated by the MCU manual? (yours AND enemy ones!)

    Those experienced often realize that SPI_TX IS Required - even when (and especially when) connecting to a remote w/no SPI_RX capability.
  • Hello cb1,

    I guess so. Lack of a MOSI pin on the slave device may lead some to believe that MOSI function is not required. But it does not change the behavior of the master's implementation.

    Regards
    Amit
  • Amit,

    I have gotten it to work, by sending using the SSI TX line as you said, Thank you for the help.

    I was not able to look at the clock and the data when I posted this as I was not in the Lab at school, however today I was able to go to school and use the scope.

    Here is my final code:

    #include <stdint.h> // Variable definitions for the C99 standard.
    #include <stdio.h> // Input and output facilities for the C99 standard.
    #include <stdbool.h> // Boolean definitions for the C99 standard.
    #include "inc/tm4c123gh6pm.h" // Definitions for the interrupt and register assignments.
    #include "inc/hw_memmap.h" // Memory map definitions of the Tiva C Series device.
    #include "inc/hw_types.h" // Definitions of common types and macros.
    #include "inc/hw_ssi.h"
    #include "driverlib/sysctl.h" // Definitions and macros for System Control API of DriverLib.
    #include "driverlib/interrupt.h" // Defines and macros for NVIC Controller API of DriverLib.
    #include "driverlib/gpio.h" // Definitions and macros for GPIO API of DriverLib.
    #include "driverlib/timer.h" // Defines and macros for Timer API of DriverLib.
    #include "driverlib/pin_map.h" //Mapping of peripherals to pins for all parts.
    #include "driverlib/uart.h" // Definitions and macros for UART API of DriverLib.
    #include "driverlib/ssi.h" // Definitions and prototypes for the SSI/SPI routines.
    #include "utils/uartstdio.h" // Prototypes for the UART console function
    #define UART0_BAUDRATE    115200 // UART baudrate in bps
    #define SAMP_FREQ    200 // Frequency in Hz for the Encoder
    #define SPI_BITRATE    10000
    
    void init_timer(void);
    void init_UART(void);
    void init_SPI(void);
    void Timer1_ISR(void);
    extern void UARTStdioIntHandler(void);
    
    // global variables
    uint32_t pui32DataRx;
    uint32_t pui32DataTx=0;
    uint32_t sys_clock;
    int main(void)
    {
    	// Configure system clock.
    	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);// 50 MHz
    //	SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); // 40 MHz
    	sys_clock = SysCtlClockGet();
    	//sys_clock = 80000000; // Hard-coded for 80MHz because of a bug in SysCtlClockGet().
    	init_UART();
    	init_timer();
    	init_SPI();
    	// Enable the processor to respond to interrupts.
    	IntMasterEnable();
    	// Start the timer by enabling operation of the timer module.
    	TimerEnable(TIMER1_BASE, TIMER_A);
    
    	while(1) {
    
    		 while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx))
    		    {
    		    }
    
    		}
    	}
    void init_timer(void)
    {
    	// Enable and configure Timer1 peripheral.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    	// Configure as a 32-bit timer in periodic mode.
    	TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);
    	// Initialize timer load register.
    	TimerLoadSet(TIMER1_BASE, TIMER_A, sys_clock/SAMP_FREQ -1);
    
    	// Registers a function to be called when the interrupt occurs.
    	IntRegister(INT_TIMER1A, Timer1_ISR);
    	// The specified interrupt is enabled in the interrupt controller.
    	IntEnable(INT_TIMER1A);
    	// Enable the indicated timer interrupt source.
    	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    }
    
    
    void init_UART(void)
    {
    	// Enable and configure UART0 for debugging printouts.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	GPIOPinConfigure(GPIO_PA0_U0RX);
    	GPIOPinConfigure(GPIO_PA1_U0TX);
    	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    	// Registers a function to be called when the interrupt occurs.
    	IntRegister(INT_UART0, UARTStdioIntHandler);
    	UARTStdioConfig(0, UART0_BAUDRATE, sys_clock);
    }
    
    
    void init_SPI(void)
    {
    	// Enable peripheral for SSI/SPI.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
    	// Configure the muxing and GPIO settings to bring the SSI/SPI functions out to the pins
    	// PA2: SSI0CLK, PA3: SSI0FSS, PA5: SSI0TX
    	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    	GPIOPinConfigure(GPIO_PA4_SSI0RX);
    	GPIOPinConfigure(GPIO_PA5_SSI0TX);
    
    	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
    
    	//SSIConfigSetExpClk(SSI0_BASE, sys_clock, SSI_FRF_TI, SSI_MODE_MASTER, SPI_BITRATE,12 );
    	SSIConfigSetExpClk(SSI0_BASE, sys_clock, SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, SPI_BITRATE,12 );
    	SSIEnable(SSI0_BASE);
    
    }
    
    
    
    
    // Timer1 interrupt service routine
    void Timer1_ISR(void)
    {
    		// Clear the timer interrupt.
    		TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    		for ()
    		SSIDataPut(SSI0_BASE, pui32DataTx);
    	    // Wait until SSI0 is done transferring all the data in the transmit FIFO.
    	    while(SSIBusy(SSI0_BASE))
    	    {
    	    }
    	    SSIDataGet(SSI0_BASE, &pui32DataRx);
    	    UARTprintf("%d ", pui32DataRx);
    	    	        UARTprintf("\n");
    }

  • Hello Jonathan,

    That is great. Also do note that the SPI MOSI pin does not need configuration. It is the action of SSIDataPut that is required.

    Regards
    Amit