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.

CCS/TM4C129ENCPDT: QSSI Write from slave - not working

Part Number: TM4C129ENCPDT


Tool/software: Code Composer Studio

HI,

I have two boards, one is custom design with TM4CENCZAD,  the second is the TI TM4C129EXL dev board. These two boards are connected via QSSI, port 1 on the custom board, port 3 on the TI dev board. I have been testing QSSI with a few modes of operation:

1.custom set as master QSSI, writing 8 bit frames to header connector (probing connector with DSO) - CSS, CLK, and 4data lines work as expected. NOTE: Custom board does not have an external cyrstal; clock is via on-chip clock source.

2.TI board set as master QSSI, writing 8 bit frames to BoosterPack hdr pins corresponding to port3. Again, probing connector pins with DSO, works as expected.

3. custom board set as master QSSI, TI dev set as slave QSSI, with master sending one 8 bit sequence to slave (custom bd to TI dev bd), slave receives the data, both board's  switch modes so that master switches from write to read, and TI dev bd switches from read to write, then slave board sends data back to master (or master reads data from slave - This is part of my confusion, how is this part of the transaction initiated, the slave sending data back to master?)

At this point, case #3 fails when master has issued SSIDataGet, and slave has reached the call to SSIDataPut. No data gets sent (watching on the scope)  after the initial write from master to slave, and the two debugger sessions (one connected to master, second connected to slave) are stuck at these two SSIDataXXX calls.

I've been studying the following previous forums;

https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/341195/1202177#1202177

e2e.ti.com/.../1384533

I believe have applied all the suggestions that were provided. I think I'm doing something fundamentally wrong here but can't seem to get my head around it. Hoping someone here can get me on-track.

The master code segment from main:

    InitGPIO();
    SysCtlClockFreqSet(SYSCTL_OSC_INT, SYSCTL_SYSDIV_1 | SYSCTL_USE_PLL );
    clock = SysCtlClockGet();

      Board_initQSSI(clock, baud_rate, 0);


      SSIDataPut (SSI1_BASE, 0xA5);
      SSIAdvModeSet(SSI1_BASE, SSI_ADV_MODE_QUAD_READ);
      //SSIDataPutNonBlocking(SSI1_BASE, 0x4000);  //dummy write to get clock running to config slave, 0x20=QuadMode  ---> This didn't make any difference
      while(1)
          {
          SSIDataGet(SSI1_BASE, rcv_data); ---------------> hangs here
          System_printf("receved: %d\n", rcv_data[0]);
          //SysCtlDelay(1000);
          }

Code segment from slave's main
     Board_initGeneral();
    Board_initGPIO();
    Board_initEMAC();
    Board_initUART();    
    //Board_initSPI();   

    clock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
            SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
            SYSCTL_CFG_VCO_480), 120000000);
            
    Board_initQSSI(clock, baud_rate, 1);

     while( go == 0 )
       {
           SSIDataGet(SSI3_BASE, dataBuffer);
           if( dataBuffer[0]==165 )
              go = 1;
       }
       SSIAdvModeSet(SSI3_BASE, SSI_ADV_MODE_QUAD_WRITE);
       SSIDataPutNonBlocking(SSI3_BASE, 0x4000);  //dummy write to get clock running to config slave
       System_printf("Switched to transmit mode\n");
       while(1) {
            SSIDataPut (SSI3_BASE, 0x53); ---------------> hangs here
            System_printf("Sent %x\n", 0x53);
            //SysCtlDelay(1000);
        }

Finally, the call to Board_initQSSI in each of the above, where the last input arg is 0 corresponding to master, and 1, corrsp to slave:


void Board_initQSSI(uint32_t SysClk_Freq, uint32_t baud_rate, bool slave)
{
     // Enable QSSI Peripherals
   if(slave==0) {   //using ports B, D, E   on QSSI1 - RevX master
       MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
   }
   else {  //using ports P, Q  on QSSI3 - TI dev bd slave
      MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);      
   }


     // Pin function via mux setting. This macro is mapped to GPIOPinConfigure in rom_map.h.
   // GPIOPinConfigure is def in gpio.c
   if(slave==0) {  //using ports B, D, E  on QSSI1
         MAP_GPIOPinConfigure(GPIO_PB5_SSI1CLK);
         MAP_GPIOPinConfigure(GPIO_PB4_SSI1FSS);
         MAP_GPIOPinConfigure(GPIO_PE4_SSI1XDAT0);
         MAP_GPIOPinConfigure(GPIO_PE5_SSI1XDAT1);
         MAP_GPIOPinConfigure(GPIO_PD4_SSI1XDAT2);
         MAP_GPIOPinConfigure(GPIO_PD5_SSI1XDAT3);
     }
     else {  //using ports P, Q  on QSSI3
         MAP_GPIOPinConfigure(GPIO_PQ0_SSI3CLK);
         MAP_GPIOPinConfigure(GPIO_PQ1_SSI3FSS);
         MAP_GPIOPinConfigure(GPIO_PQ2_SSI3XDAT0);
         MAP_GPIOPinConfigure(GPIO_PQ3_SSI3XDAT1);
         MAP_GPIOPinConfigure(GPIO_PP0_SSI3XDAT2);
         MAP_GPIOPinConfigure(GPIO_PP1_SSI3XDAT3);     
     }
   
   // Pin operation settings. This macro is mapped to GPIOPinTypeSSI in rom_map.h.
   // GPIOPinTypeSSI is def in gpio.c
   if(slave==0) {   //using ports B, D, E  on QSSI1    
        MAP_GPIOPinTypeSSI(GPIO_PORTB_AHB_BASE, GPIO_PIN_5 | GPIO_PIN_4);
        MAP_GPIOPinTypeSSI(GPIO_PORTD_AHB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
        MAP_GPIOPinTypeSSI(GPIO_PORTE_AHB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
   }
   else {  //using ports P, Q  on QSSI3
        MAP_GPIOPinTypeSSI(GPIO_PORTP_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        MAP_GPIOPinTypeSSI(GPIO_PORTQ_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
   }      

     // Set to SPI Mode0 for QSSI (Advanced) mode; set master mode
     if(slave==0) {
        MAP_SSIConfigSetExpClk(SSI1_BASE, SysClk_Freq, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, baud_rate, 8);
        SSIAdvModeSet(SSI1_BASE, SSI_ADV_MODE_QUAD_WRITE);
     }
     else {
         SSIConfigSetExpClk(SSI3_BASE, SysClk_Freq, SSI_FRF_MOTO_MODE_0, SSI_MODE_SLAVE, baud_rate, 8);
         SSIAdvModeSet(SSI3_BASE, SSI_ADV_MODE_QUAD_READ);     
      SSIDataPut(SSI3_BASE, 0x4000);  //dummy write to get clock running to config slave
     }
     // Enable QSSI
     if(slave==0) {   
        MAP_SSIEnable(SSI1_BASE);
     }
     else {
      MAP_SSIEnable(SSI3_BASE);
   }  

The calls to SSIDataPut were added based on guidance from the above referenced forums.

In summary, initially, master sends a frame to slave, slave receives that frame, then proceeds to switch mode from read to write. Meanwhile, master switches from write to read.

I am running this so that slave is ready to go, waiting at the first (and only) SSIDataGet. Stepping thru with debugger on both boards, the action stops after completing the calls to switch mode, on both boards.

I could use some help getting this straightened out  - any suggestions appreciated.

Thank you.

  • Hi Erik,

    I setup a simple project to demonstrate to myself how to do this. I used a single EK-TM4C129XL board with SSI1 as a master and SSI3 as a slave. I actually used BI mode instead of QUAD mode because of some pin conflicts on the hardware, but the principle is the same.

    In BI and QUAD modes, the SSI is half-duplex, but it still behaves in some ways as full duplex. That is why sending from master to slave, we prefill the slave with some dummy data so that it is ready for the transmission. Likewise, when we transmit from slave to master, we do a dummy SSIDataPut() on the master to generate the clocks.

    Here is my simple example. It does not make use of the FIFO, interrupts or DMA, but it may help you understand and get started:

    //*****************************************************************************
    //
    // QSSI.c - Simple example to transfer data by SSI in 2 bit (BI) mode.
    //
    // Copyright (c) 2013-2017 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    // 
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    // 
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    // 
    //
    //*****************************************************************************
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "tm4c1294ncpdt.h"
    #include "sysctl.h"
    #include "rom_map.h"
    #include "ssi.h"
    #include "pin_map.h"
    #include "hw_memmap.h"
    #include "gpio.h"
    
    #define MASTER SSI1_BASE
    #define SLAVE SSI3_BASE
    
    
    void QSSIMasterInit(uint32_t SysClk_Freq, uint32_t baud_rate);
    void QSSIMasterReadMode(void);
    void QSSIMasterWriteMode(void);
    
    
    void QSSISlaveInit(uint32_t SysClk_Freq, uint32_t baud_rate);
    void QSSISlaveReadMode(void);
    void QSSISlaveWriteMode(void);
    
    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>QSSI (QSSI)</h1>
    //!
    //! A very simple example that blinks the on-board LED using direct register
    //! access and sends and received using BI SSI mode
    //
    //*****************************************************************************
    
    int
    main(void)
    {
        uint32_t ui32Loop;
        uint32_t SysClk_Freq, baud_rate, data;
    
        SysCtlClockFreqSet(SYSCTL_OSC_INT, SYSCTL_SYSDIV_1 | SYSCTL_USE_PLL );
        SysClk_Freq = SysCtlClockGet();
        baud_rate = SysClk_Freq >> 4u;
    
        //
        // Enable the GPIO port that is used for the on-board LED.
        //
        SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R12;
    
        //
        // Do a dummy read to insert a few cycles after enabling the peripheral.
        //
        ui32Loop = SYSCTL_RCGCGPIO_R;
    
        //
        // Enable the GPIO pin for the LED (PN0).  Set the direction as output, and
        // enable the GPIO pin for digital function.
        //
        GPIO_PORTN_DIR_R = 0x01u;
        GPIO_PORTN_DEN_R = 0x01u;
    
        // Initialize SSI1 as BI master
        QSSIMasterInit(SysClk_Freq, baud_rate);
    
        // Initialize SSI3 as BI slave
        QSSISlaveInit(SysClk_Freq, baud_rate);
    
    
    
    
        //
        // Loop forever.
        //
        while(1)
        {
            //
            // Turn on the LED.
            //
            GPIO_PORTN_DATA_R |= 0x01;
    
    
            //
            // Send data out master and read in by slave
            //
            QSSISlaveReadMode();
            QSSIMasterWriteMode();
    
            for(ui32Loop = 0u; ui32Loop < 256u; ui32Loop++)
            {
                SSIDataPutNonBlocking(SLAVE, 0u);  //dummy write to make slave ready
            	SSIDataPut(MASTER, ui32Loop);
            	SSIDataGet(SLAVE, &data);
            	if(data != ui32Loop)
            	{
            		for(;;);
            	}
            }
    
            //
            // Turn off the LED.
            //
            GPIO_PORTN_DATA_R &= ~(0x01u);
    
            //
            // Send data out slave and read in by master
            //
            QSSIMasterReadMode();
            QSSISlaveWriteMode();
    
            for(ui32Loop = 0u; ui32Loop < 256u; ui32Loop++)
            {
                SSIDataPut(SLAVE, ui32Loop);  //write data to slave
                SSIDataPut(MASTER, 0u);  //dummy write to master to generate clocks
            	SSIDataGet(MASTER, &data);
            	if(data != ui32Loop)
            	{
            		for(;;);
            	}
            }
        }
    }
    
    // Initialize QSSI1 as master
    void QSSIMasterInit(uint32_t SysClk_Freq, uint32_t baud_rate)
    {
    	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
        MAP_GPIOPinConfigure(GPIO_PB5_SSI1CLK);
        MAP_GPIOPinConfigure(GPIO_PB4_SSI1FSS);
        MAP_GPIOPinConfigure(GPIO_PE4_SSI1XDAT0);
        MAP_GPIOPinConfigure(GPIO_PE5_SSI1XDAT1);
    
        MAP_GPIOPinTypeSSI(GPIO_PORTB_AHB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
        MAP_GPIOPinTypeSSI(GPIO_PORTE_AHB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    
        MAP_SSIConfigSetExpClk(MASTER, SysClk_Freq, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, baud_rate, 8);
        MAP_SSIEnable(MASTER);
    }
    
    void QSSIMasterReadMode()
    {
        SSIAdvModeSet(MASTER, SSI_ADV_MODE_BI_READ);
    }
    
    void QSSIMasterWriteMode()
    {
        SSIAdvModeSet(MASTER, SSI_ADV_MODE_BI_WRITE);
    }
    
    
    void QSSISlaveInit(uint32_t SysClk_Freq, uint32_t baud_rate)
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);
    
        MAP_GPIOPinConfigure(GPIO_PQ0_SSI3CLK);
        MAP_GPIOPinConfigure(GPIO_PQ1_SSI3FSS);
        MAP_GPIOPinConfigure(GPIO_PQ2_SSI3XDAT0);
        MAP_GPIOPinConfigure(GPIO_PQ3_SSI3XDAT1);
    
        MAP_GPIOPinTypeSSI(GPIO_PORTQ_BASE, GPIO_PIN_0 | GPIO_PIN_1 |GPIO_PIN_2 | GPIO_PIN_3);
    
        MAP_SSIConfigSetExpClk(SLAVE, SysClk_Freq, SSI_FRF_MOTO_MODE_0, SSI_MODE_SLAVE, baud_rate, 8);
        MAP_SSIEnable(SLAVE);
    
    }
    
    void QSSISlaveReadMode()
    {
        SSIAdvModeSet(SLAVE, SSI_ADV_MODE_BI_READ);
    }
    
    void QSSISlaveWriteMode()
    {
        SSIAdvModeSet(SLAVE, SSI_ADV_MODE_BI_WRITE);
    }
    
    

  • This example is clear and definitive as to how to do both master-to-slave writes, and slave-to-master reads. Thank you.
  • Agree w/poster Erik (and Like) although it appears that, "Master/Slave defines" (lines 34/35) conflict w/ comments (lines 85/88).

    (i.e. SSI1 is defined as Master (line 34) yet comment (line 85) notes SSI3 as (new) Master...)

  • CB1,
    Thank you for catching that error. As always, having a good software review is very valuable. I have edited my earlier post and corrected the comments for lines 85 and 88.