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.

SSI example request (LM4F232H5QD, PGA116)

Other Parts Discussed in Thread: PGA116

Hi there,

I'm new to the M4 environment, and I'm trying to write modules for controlling the SSI module to carry out SPI function.

The goal is for me to know how to control the PGA116 through the SSI module.

I tried to modify the spi_master.c and ti_master.c from the examples given in Stellaris software, but I have a few questions which I hope someone can help me answer:-

1. In GPIOPinConfigure(), I was not able to call the macros for GPIO_PA2_SSI0CLK, GPIO_PA3_SSI0FSS, GPIO_PA4_SSI0RX, and GPIO_PA5_SSI0TX. The compiler couldn't find the definitions so I had to manually type in the hex digits from the pin_map.h

2. After enabling the SSI module, I do not observe the clock rate on the SSI0CLK pin. Is the clock rate not passed to the pin? Or did I miss out any API to set it?

If possible, could someone show me a sample working code for controlling the SSI module on M4? I've attached a copy of my code below for your reference. Thanks in advance!

/* Test_SSI
*
* A sample project to test out SSI API to control the SSI module
*
* main.c
*/

#include "inc/hw_memmap.h"
#include "inc/hw_ssi.h"
#include "inc/hw_types.h"
#include "driverlib/ssi.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"

#define NUM_SSI_DATA 3

// Configure SSI0 in master Freescale (SPI) mode. This example will send out
// 3 bytes of data. This will all be done using the polling method.

void main(void) {
   unsigned long ulDataTx[NUM_SSI_DATA];
   unsigned long ulDataRx[NUM_SSI_DATA];
   unsigned long ulindex;

   // Set the clocking to run directly from the external crystal/oscillator.
   // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
   // crystal on your board.
   SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

  // The SSI0 peripheral must be enabled for use.
  SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);

// For this example SSI0 is used with PortA[5:2]. The actual port and pins
// used may be different on your part, consult the data sheet for more
// information. GPIO port A needs to be enabled so these pins can be used.
// TODO: change this to whichever GPIO port you are using.
  SysCtlPeripheralEnable(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.
// TODO: change this to select the port/pin you are using. (lookup pin_map.h)
  GPIOPinConfigure(0x00000802);
  GPIOPinConfigure(0x00000C02);
  GPIOPinConfigure(0x00001002);
  GPIOPinConfigure(0x00001402);

// 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
// TODO: change this to select the port/pin you are using.
  GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);

// Configure and enable the SSI port for SPI master mode. Use SSI0,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
// For SPI mode, you can set the polarity of the SSI clock when the SSI
// unit is idle. You can also configure what clock edge you want to
// capture data on. Please reference the datasheet for more information on
// the different SPI modes.
  SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 1000000, 8);

// Enable the SSI0 module.
  SSIEnable(SSI0_BASE);

while(1){
// 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(SSI0_BASE, &ulDataRx[0]))
  {
  }

// Initialize the data to send.
  ulDataTx[0] = 's';
  ulDataTx[1] = 'p';
  ulDataTx[2] = 'i';

// Send 3 bytes of data.
  for(ulindex = 0; ulindex < NUM_SSI_DATA; 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, ulDataTx[ulindex]);
  }

// Wait until SSI0 is done transferring all the data in the transmit FIFO.
  while(SSIBusy(SSI0_BASE))
  {
  }
 }
}

  • As the MCU functionality expands - so too the set-up requirements.  Here - I believe - is the key data you require (this from SW-DRL-UG):

    "The available mappings are supplied on a per-device basis in "pin_map.h.""  (and we do not find this included w/in your listing)   

    In addition - you must include, "PART_IS_ <partno> (this will enable the appropriate set of defines for your chosen device.)

    Your M4 device requires both GPIOPinConfigure() and GPIOPinType() to fully/properly configure your SSI peripheral.  Give these a try and I bet that you'll be well on your way...

  • Hi cb1_mobile,

    Thank you very much for your reply!

    I've included the "driverlib/pin_map.h", but how and where do I include the "PART_IS_LM4F232H5QD"?

    I tried this...

    #include "driverlib/pin_map.h/PART_IS_LM4F232H5QD"

    but to no avail...

    Would you be able to advise how to declare that? Thanks in advance!

  • @ William,

    We made such entry w/in our IDE.  (IAR in our case)  Early tomorrow I'll cut/paste - show you exactly how/where.  (that detail is more easily accessed fm. my mobile)

    Should you need faster - search (upper left, top - this forum) on, "PART_IS" may yield faster results. 

  • Hi cb1,

    Thanks for your prompt response. I tried the following and got no errors in building:

    #ifdef PART_IS_LM4F232H5QD
       GPIOPinConfigure(GPIO_PA2_SSI0CLK);
       GPIOPinConfigure(GPIO_PA3_SSI0FSS);
       GPIOPinConfigure(GPIO_PA4_SSI0RX);
       GPIOPinConfigure(GPIO_PA5_SSI0TX);
    #endif

    but when i tried to run the program, it my SSI0CLK pin still didn't show me any clock rate, although I was able to observe something on the SSI0TX pin during transmission of some characters.

    Am I missing out something?

  • I got it to work! Learnt a few things about SSI/SPI:

    1. The SSI0CLK only clocks when the SSI0FSS pin is drop to low, and only when the program is asked to transmit data.
    2. We need to define the M4 part that we are using when we are calling the GPIO_ macros from pin_map.h, ie. #ifdef PART_IS_LM4F232H5QD    <your code>   #endif
    3. If you are observing the signals from the pins on a oscilloscope, set your coupling mode to "Normal" instead of "Auto" (I'm using Agilent scopes).
    4. Lastly, remember to set your system clock! 

    I'm convinced that the spi_master.c and ti_master.c works but not without a bit of modification.

    Now I can start to program and control my PGA116!

    Thank you very much for cb1_mobile's help! =)

  • @ William/interested others:

    Good for you - being smart, methodical - you kept driving toward your goal and succeeded!  Well played. 

    I am concerned about your handling of "PART_IS" - believe that your present defining is, "too confining" - and that future, "ill-effects" may result.  (i.e. other aspects of your program code will not have the same, "visibility" into the Part Identification...   Here's how our IAR IDE handles this:  (note our M4F MCU is smaller ver. than yours)

    For completeness - I ran an IAR, "Find in Project Files" search for: LM4F231H5QR - it appears in none of the 51 files related to our project.  (11 are "C" Source files)  Thus - at least for the IAR IDE - definition as listed above appears successful - and complete!  (and is far easier to track and/or modify when we employ a variant MCU! {as opposed to searching thru endless Source or related files})

    Good luck to you - suspect that your "adaptation" of this "one-stop" MCU definition w/in your IDE will avoid future unpleasantness...

  • @cb1

    Message well-received. I do find it bug-prone if I had to do the #ifdef everytime I change a new microcontroller.

    For the CCS IDE, I had to go into the project Properties page, ARM Compiler, under Predefined Symbols, I inserted the PART_LM4F232H5QD in the Pre-define NAME.

    And you're right! It is much easier to track for the entire project rather than going through all the source files now!

    Thanks for the tip!

  • Hi William,

    One question. I'm having the same problem to get the signal clock when I do a reading, I only see it during writing. In my system I only need to read so I made a dummy "write" instruction and imediatly after a "read" instruction, this is in order to generate the clock and the peripheral uses that clock to send me the data. This is obviusly not how it suppose to work. I saw in your comment that SSI0FSS must be drop low, do you mean by connecting this pin to ground or through some instruction? Thanks!


    Regards,

    Max

  • Perhaps yours is a different issue - do realize that data is both "written" and "read" (possibly on different clock edges) when you "write" to your SSI Slave.  It is not possible to achieve a "read" by itself - as your writing suggests. 

    For Stellaris - the SSIxFSS signal usually is automatically controlled by the SSI module.  (assuming you are using the normal, StellarisWare SSI implementation)

  • Hi cb1

    You see, if I have the instruction

    SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx);

    I get no clock signal so I don't get any data from the slave. If instead i do

    SSIDataPutNonBlocking(SSI0_BASE, 0x00);
    SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx);

    A clock signal is generated and then I get the data in ulDataRx. However, I don't think this is the way it should be done, at least this is not how is shown in the "psi_master.c" example.

  • The non-blocking functions always return immediately whether or not they can perform the desired operation.

    Using SSIDataPutNonBlocking() when there's no data in the FIFO will work, so will be safe to use as you've done.  If the FIFO is full, though, it will return immediately without sending your data out.  However, when you immediately follow it with SSIDataGetNonBlocking(), there will be no data in the FIFO (at least none from the put that you just attempted because it won't have had time for the data to clock through), and the function will return immediately with a status value indicating that it did not work.

    When you use the non blocking functions, you must always check the return status value, and take appropriate action.  Often the appropriate action is to retry the function until it succeeds - in which case the normal (blocking) version of the function would have been a better choice.

    For your case, you probably should not be calling the non-blocking functions at all, but instead call the normal blocking versions that wait until they can complete if they can't perform their function immediately.

  • Your sense of order not withstanding - you've proved that w/out the SSI clock signal - no data is forthcoming.

    You may want to check w/in driverlib - to see just how SSIData Put & SSIDataGet impact the SSI clock.

    Justification for the "write" requirement is the Slave's "inability" to know, "what is requested" - should a "lone" read request be made.  (think about that) 

    The "write" prepares the slave - during the next write the "answering read" (which accomanies that write) will contain valid data...

  • Hi slandrum,

    That's all very nice but I don't see how it relates to the issue of generating the clock signal. If I only use the Get function independently if it's the blocking or non-blocking version, there is still no clock signal for the slave to transmit.

    Hi cb1,

    I see your point, however, it's just weird that you have to do a dummy write call to be able to read.

  • The master generates clock signal when it transmits data.  When the master is transmitting, it is also receiving data from the slave.  SPI communication is always simultaneously bidirectional.

    The most common sequence used for SPI communication (from the master side) is:

    1) Send command word

    2) Read and discard dummy response

    repeat steps 1 and 2 until the entire command sequence is sent.

    If there's data to read back, then:

    3) Send dummy word (so that clocks are provided for the slave)

    4) Read back response word

    repeat steps 3 and 4 until the response data is read back

    This is the most typical sequence, but others are possible, it depends on the device you are talking to.  I've implemented protocols that eliminate nearly all the dummy data by overlapping commands and responses, but this was for custom protocols where I controlled everything on both ends of the line.

  • Thanks slandrum, you made the point very clear. I guess if the dummy is need it, that's the way it is (I just need readings). I just find weird that in the example provided in the StellarisWare it seems like no dummies are needed (psi_master.c).

  • Can u upload your code(which is working) I too have the same Problem .