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/TM4C123GH6PM: SPI communication (float datatype) Slave configuration

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: EK-TM4C123GXL, , TMS320F28377D

Tool/software: Code Composer Studio

Hello All,

I am trying to (want to) send an array of float datatype through SPI communication. I have started with the "TivaWare_C_Series-2.1.3.156" ssi example,which is in master mode and enables user to communicate the data in uint32 datatype.

Here is my code which I am working on

float pui32DataTx[4] = {0000,1001,768,1010};
float pui32DataRx[4];
uint32_t ui32Index;
int i;


int
main(void)
{

    //
    // 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.
    //
    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
    // 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);

    //
    // 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, &pui32DataRx));

    //
    // Send n bytes of data.
    //
    for(ui32Index = 0; ui32Index < 4; ui32Index++)
    {
        
        for(i=0;i<4;i++)
        {
            //
            // 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, pui32DataTx[ui32Index]>>(i*8));

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

            //
            //
            SSIDataGet(SSI0_BASE, &pui32DataRx);

            //
            // Wait until SSI0 is done transferring all the data in the transmit FIFO.
            //
            //while(SSIBusy(SSI0_BASE));
        //}
        SysCtlDelay(2000); // I had to add this delay to avoid missing the communication in between

    }

    while(1);
}

The ssi library is used from tivaware mentioned above in which the transmission is handled with this function

void
SSIDataPut(uint32_t ui32Base, uint32_t ui32Data)
{
    //
    // Check the arguments.
    //
    ASSERT(_SSIBaseValid(ui32Base));
    ASSERT((ui32Data & (0xfffffffe << (HWREG(ui32Base + SSI_O_CR0) &
                                       SSI_CR0_DSS_M))) == 0);

    //
    // Wait until there is space.
    //
    while(!(HWREG(ui32Base + SSI_O_SR) & SSI_SR_TNF))
    {
    }

    //
    // Write the data to the SSI.
    //
    HWREG(ui32Base + SSI_O_DR) = ui32Data;
}

*Please check the "driverlib/ssi.c" for details

This code is in Master mode and uint32 datatype -> I want to use TM4C123 in Slave mode & float datatype

Kindly suggest the required changes. I have tried a few like changing the datatype manually in the "driverlib/ssi.c" file in the function mentioned above. BUT FAILED

For slave mode - I did not try anything as of yet - but will

"SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 1000000, 8);"

changed to 

"SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_SLAVE, 1000000, 8);"

help do the needful?

  • As to the "needful" - your Slave SPI configuration must properly match that of your (external) SPI "Master."     The "SPI Master" is the "boss of you" (apologies to ex consorts) and controls data transfers. 

    Slave mode proves sufficiently complicated that your "starting" w/float may cause (at least add to) your (potential) pain/suffering.

    Friend "KISS" - always and only - directs, "Fight ONE Battle at a time" - and that aimed towards the, "smallest, easiest, most focused/measurable" objective. Proceed via systematic "refinement chunks" - each achieving a targeted "milestone" - not via (an always wishful) "giant gulp!"

  • You did not mention how it failed, so here are the issues I saw when trying to compile the first code you posted.

    First, you need to include the header files that define the constants you used as part of the TivaWare library. I added these #includes:

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    

    Next, there was an error on the function SSIDataPut() since the second argument is now a float instead of an unsigned integer. The simplest solution (but for many reasons not the best solution) is to cast the float to an integer. Then the line looks like this:

                SSIDataPut(SSI0_BASE, (uint32_t)pui32DataTx[ui32Index]>>(i*8));
    

    Finally, you commented out one of the closing braces that is needed to balance the code. I simply corrected that by removing the "\\".

    Now, why do I say this may not be the best solution. First, this only works because in this case the size of a float is the same as the size of an unsigned integer, 32 bits. Second, many coding standards highly discourage casting as it can cause confusion which leads to coding mistakes. Other solutions would be the use of void pointers, or the use of an array of unions. These may be topics that you are not familiar with if you are just starting out with C coding. There are many experience C coders who monitor this forum and they may have even better suggestions on how to code this program making it easy to read and understand. (The easier it is to read and understand, the less likely it is to have mistakes.)

  • Hi  &  

    Bob Crosby said:

    First, you need to include the header files ...:

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    

    Yes obviously I did include the headers in my program, just not here. and theses are the ones I added in mine too. thank you anyway for other posters who might come across this post though :)

    Bob Crosby said:

    SSIDataPut(SSI0_BASE, (uint32_t)pui32DataTx[ui32Index]>>(i*8));

    Yes, I tried this but this wouldn't work for decimals. the main motive I want to use float for this SPI is I (may) have signed decimal numbers in array. I tried typecasting the values and it didnot solve the problem

    Bob Crosby said:

    this only works because in this case the size of a float is the same as the size of an unsigned integer, 32 bits.

    Oh sorry, if I was not clear earlier that the 4 numbers in "pui32DataTx" array are just "dummy" numbers for the trials. and spi needed to communicate here actually demands a float datatype and not "uint32" or "int". I can understand that point might have been missed by me in mentioning the problem statement. :D 

     

    cb1_mobile said:

    As to the "needful" - your Slave SPI configuration must properly match that of your (external) SPI "Master."

    Yes true, I understand that. and I intentionally want to configure tm4c as slave and othe uC as Master.

    cb1_mobile said:

    Slave mode proves sufficiently complicated that your "starting" w/float may cause (at least add to) your (potential) pain/suffering.

    Yes, from my time here on this forum -- one thing I surely learnt is following KISS;D

    so to start with this problem I wanted to tackle the simplest one - "JUST GET THE SPI DONE BETWEEN MCUs"

    Turned out the ssi example shared in TIVAware - sends only uint32 datatype.

    Now i want to improve it to float datatype. and once it is done, I would want to make the same configuration in Slave mode.

    I hope that's how i should proceed. Do you know how to solve this problem - Sending float datatype instead of uint32?

  • The cast (uint32_t) works for decimals.  What error are you receiving? Here is an example that compiles without error using an array of floats.

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    
    float pui32DataTx[4] = {3.14159, 8.75, 7.375, 3.0};
    float pui32DataRx[4];
    uint32_t ui32Index;
    int i;
    
    
    int
    main(void)
    {
    
        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);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
                       GPIO_PIN_2);
        SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER, 1000000, 8);
        SSIEnable(SSI0_BASE);
        //
        // Send n bytes of data.
        //
        for(ui32Index = 0; ui32Index < 4; ui32Index++)
        {
    
            for(i=0;i<4;i++)
            {
                //
                // 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, (uint32_t)pui32DataTx[ui32Index]>>(i*8));
    
                //
                // Wait until SSI0 is done transferring all the data in the transmit FIFO.
                //
                while(SSIBusy(SSI0_BASE));
            }
        }
    
        while(1);
    }
    

  • Hi  

    I have just copied your code snippet and added this line before SSIDataPut

    temp[(ui32Index*4)+i] = (uint32_t)pui32DataTx[ui32Index]>>(i*8);
    SSIDataPut(SSI0_BASE, temp[(ui32Index*4)+i]);
    

    This would show the uint data that is getting transfered in each iteration.

    as you can see in image below - only integer part is being communicated

  • Rohan Chawhan said:
    I am trying to (want to) send an array of float datatype through SPI communication.

    In general this is a very bad idea. It's seldom a good idea to send floating point binary around. There's just too many different formats and exceptions.

    Robert

  • Rohan Chawhan said:

    I have just copied your code snippet and added this line before SSIDataPut

    temp[(ui32Index*4)+i] = (uint32_t)pui32DataTx[ui32Index]>>(i*8);
    SSIDataPut(SSI0_BASE, temp[(ui32Index*4)+i]);
    

    This would show the uint data that is getting transfered in each iteration.

    as you can see in image below - only integer part is being communicated

    You just converted the result to an integer, what else would you expect?

    You do not want to change the underlying type only the type of the pointer.

    Robert

    And TI? This is a good example of why using uint32_t as the base type for everything is a bad idea.

  • Hi Robert Adsett (3681028)

    I did look up on internet that float has [sign |exponent| mantissa] but i am not sure for the fact thatwhere it stays constant/unchanged for a particular uC or not (My guess - it should be)

    And I appreciate that you brought this up that it is a very bad idea to send floating points across SPI but sadly i am stuck with a float array after some calculations, which i want to communicate to somewhere. What is a floating point binary around- i don't know [i will look up on google] if you want to share, please do. :)

  • Rohan Chawhan said:
    I did look up on internet that float has [sign |exponent| mantissa] but i am not sure for the fact thatwhere it stays constant/unchanged for a particular uC or not (My guess - it should be)

    Perhaps for a single micro/compiler but assuming you stay with that combination is a bit of a leap

    I've seen a dozen or more variations on internal floating point formats and that's w/o getting into the question of how many and of what type of NaN's exist and how they must be dealt with or how to deal with denormalized floats.

    Rohan Chawhan said:
    sadly i am stuck with a float array after some calculations

    Why? I have worked with a number of SCADA/industrial setups which must communicate non-integer values. None of them used floating point to do so. It's actually rather difficult to end up in a situation where floating point is a useful or even necessary communication format. Especially since integer representations necessarily have a higher precision for the same number of bits.

    Rohan Chawhan said:
    What is a floating point binary around- i don't know

    Care to try re-phrasing that so it makes sense ? ;)

    Robert

  • Care to try re-phrasing that so it makes sense ? ;)

    [\quote]

    Oops I read it wrong. :D:D:D My Bad

    Anyway... I have tried it like this and could send few bytes but after that it was skipping few bytes in between...

    unsigned char *send8Bit

    send8Bit = &float_start

    for(i=1 to 4)

    Tx *send8Bit++;

  • Should it be noted that poster's SPI implementation is that of an "SPI Slave" - thus the earlier (responder) code:

    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
    SSI_MODE_MASTER, 1000000, 8);

    is unlikely to meet poster's (Slave) requirement.

    It should be noted that there has long been (and continues to be) a well-recognized absence of "MCU as SPI Slave examples!"    (surely proving useful to many here - especially vendor staff  (who - minus examples - must start (repeatedly) from ground zero)...)

  • What you are asking to do is to take a floating point number, treat it like an unsigned integer (because the SSIDataPut and SSIDataGet functions require unsigned integers) and then break the integer into bytes for the SSI. When receiving, you need to reverse the process. If you just connect GPIO_PA4_SSI0RX and GPIO_PA5_SSI0TX with a jumper wire on your EK-TM4C123GXL board and run this simple code, it will transmit and read the array of four floating point numbers.

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    
    float pf32DataTx[4] = {3.14159, 8.75, 7.375, 3.0};
    float pf32DataRx[4];
    
    
    int
    main(void)
    {
        uint32_t *pui32DataTx, *pui32DataRx, ui32Data;
        uint32_t i, ui32Index;
    
        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);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
                       GPIO_PIN_2);
        SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER, 1000000, 8);
        SSIEnable(SSI0_BASE);
    
        // Use unsigned int pointers to point to the floating point array
        pui32DataTx = (uint32_t *)pf32DataTx;
        pui32DataRx = (uint32_t *)pf32DataRx;
        //
        // Send n bytes of data.
        //
        for(ui32Index = 0; ui32Index < 4; ui32Index++)
        {
            pui32DataRx[ui32Index] = 0u;
            for(i = 0; i < sizeof(float); i++)
            {
                //
                // 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, pui32DataTx[ui32Index]>>(i*8u));
                SSIDataGet(SSI0_BASE, &ui32Data);
                pui32DataRx[ui32Index] |= (ui32Data << (i*8u));
            }
        }
    
        while(1);
    }
    

  • Bob Crosby said:

    If you just connect GPIO_PA4_SSI0RX and GPIO_PA5_SSI0TX with a jumper wire on your EK-TM4C123GXL board and run this simple code, it will transmit and read the array of four floating point numbers

    Hey Bob, It probably is a good way to do it (I haven't tried it) But can you explain me why should I connect my TX and RX together? I mean, what will I accomplish with that? 
    @all, 
    Okay so, I might be playing in a bit of dark here and not explaining the whole requirement in detail, "as I felt by now"
    So, here it is
    I am using 2 boards, a TM4C123GH6PM (a Tiva board for a specific part of the project and send the data to C2000 MCU) and TMS320F28377D (a C2000 MCU which is already doing the major part of the project that I am working on).
    and I want to communicate between the two but--- in below configuration 
    TMS320F28377D- Master , also because it handles othe SPI slaves already
    TM4C123GH6PM - Slave , which is going to be the sender of data in SPI communication.
     
    now,

    I assume I have answered the question of "what is this kid trying to do?", Haven't I?
    and I know that there might be another ways to do it (feel free to share those too over my email:P), but I prefered this SPI communication for now.
     I looked at the way you tried to solve the problem is by defining a pointer and then fetching 8 places with that pointer... isn't that the same which I did in the previous post? How is it different in working principle?
    And if it is different, then did it solve the problem which i posted earlier with the image (that was skipping some bytes in between)... please let me know, it will be very helpful :)
    Regards all,
  • In defense of vendor's Bob - should not "you" invest the time/effort required to, "test Bob's suggested code?"     You must anticipate the, "clear & unwanted impact" such demands place upon skilled (yet time limited) vendor staff.     (so that other forum users are not denied nor delayed, "skilled vendor service.")

    Through (your) effort - might your (proper) "follow-on" questions prove, "easier for you to further examine - gain insight - thus self resolve?"     (or lead you to more probing/impacting questions and/or issues...)

  • Rohan Chawhan said:
    I am using 2 boards, a TM4C123GH6PM (a Tiva board for a specific part of the project and send the data to C2000 MCU) and TMS320F28377D (a C2000 MCU which is already doing the major part of the project that I am working on).

    And you're sending binary floating point back and forth!?

    Have you verified (bit by bit) that they use the same representation? It's far from unusual for a DSP to have its own floating point representation.

    Personally I wouldn't even attempt it. You certainly have provided no justification for why it must be floating point.

    Rohan Chawhan said:
    Hey Bob, It probably is a good way to do it (I haven't tried it) But can you explain me why should I connect my TX and RX together? I mean, what will I accomplish with that? 

    Verify your operation?

    Robert

  • Hi Rohan,
    My suggestion was just a simple way to demonstrate a piece of code that successfully compiles with no errors, transfers an array of floating point numbers across the SPI and shows how to reassemble them at the other end. No data is lost in this example. I realize that in your application one processor will be transmitting the floating point numbers and a different one receiving them.

    Are we past the original question about transmitting and receiving floating point numbers over SPI? If so, we should close this thread and start a new one for the missing data.
  • Hey Bob,

    Yes, you are correct that transmitting and receiving floating point numbers over SPI is "solved" (rather I would say NOT a good idea to do that, because only I am able to  transmit and receive the 4 bytes correctly but the reconstruction of floating numbers on C2000 microcontroller is not solved)

    But I am still keeping this thread open because the part where Title says - "Slave configuration" still persists.

    Can you (or anyone else) help me configure the TM4C as slave? - Then I will close this thread

  • The change to slave configuration is easy. Just change the word "Master" to "Slave" in line 32.

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

    There are some things that you should think about. First, the slave device must run first so that its transmit buffer is ready before the master starts the first transfer.

    The second is that this program will hang in the function SSIDataGet() waiting for the first transfer. If that is not acceptable, then a more complex routine can load the FIFO of the TX buffer and use interrupts to empty the RX buffer and reload the TX buffer.